From fbb8fecceff8a055bd88649d7abfcdc70119c2c0 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 29 Dec 2021 18:41:53 +0100 Subject: [PATCH] wanpipe-3.5.11.tgz --- .router_version | 1 + ChangeLog.3.5 | 77 + Makefile | 47 +- Setup | 353 +- api/legacy/aft/aft_api | Bin 20358 -> 20358 bytes api/lib/hdlc/wanpipe_hdlc.h | 33 +- api/libsangoma/.svn/all-wcprops | 92 +- api/libsangoma/.svn/dir-prop-base | 3 +- api/libsangoma/.svn/entries | 267 +- .../.svn/prop-base/ltmain.sh.svn-base | 5 - .../.svn/text-base/Makefile.Windows.svn-base | 8 - .../.svn/text-base/cleanup.sh.svn-base | 2 +- .../.svn/text-base/config.guess.svn-base | 1411 ---- .../.svn/text-base/config.sub.svn-base | 1500 ---- .../.svn/text-base/configure.in.svn-base | 4 +- .../.svn/text-base/configure.svn-base | 22 +- .../.svn/text-base/libsangoma.c.svn-base | 1753 +++-- .../.svn/text-base/libsangoma.def.svn-base | 81 - .../.svn/text-base/libsangoma.h.svn-base | 787 +- .../.svn/text-base/libsangoma.sln.svn-base | 4 +- ...angoma.vcproj.DAVIDRNEW.User.user.svn-base | 65 - .../.svn/text-base/libsangoma.vcproj.svn-base | 15 +- .../.svn/text-base/libsangoma_hwec.c.svn-base | 547 ++ .../.svn/text-base/ltmain.sh.svn-base | 6911 ----------------- .../.svn/text-base/sources.svn-base | 8 +- api/libsangoma/.svn/tmp/tempfile.17.tmp | 3060 ++++++++ api/libsangoma/.svn/tmp/tempfile.18.tmp | 1574 ++++ api/libsangoma/.svn/tmp/tempfile.19.tmp | 2982 +++++++ api/libsangoma/.svn/tmp/tempfile.20.tmp | 1623 ++++ api/libsangoma/.svn/tmp/tempfile.21.tmp | 1667 ++++ api/libsangoma/.svn/tmp/tempfile.22.tmp | 1963 +++++ api/libsangoma/.svn/tmp/tempfile.23.tmp | 1965 +++++ api/libsangoma/Makefile.Windows | 8 - api/libsangoma/cleanup.sh | 2 +- api/libsangoma/configure | 22 +- api/libsangoma/configure.in | 4 +- api/libsangoma/docs/.svn/entries | 2 +- api/libsangoma/docs/doxygen/.svn/entries | 2 +- api/libsangoma/examples/.svn/entries | 2 +- .../examples/hptdm_api/.svn/entries | 12 +- .../examples/hptdm_api/docs/.svn/entries | 2 +- .../hptdm_api/docs/doxygen/.svn/entries | 2 +- .../examples/priserver/.svn/entries | 12 +- api/libsangoma/libsangoma.c | 1753 +++-- api/libsangoma/libsangoma.def | 81 - api/libsangoma/libsangoma.h | 787 +- api/libsangoma/libsangoma.sln | 4 +- api/libsangoma/libsangoma.vcproj | 15 +- .../libsangoma.vcproj.DAVIDRNEW.User.user | 65 - api/libsangoma/libsangoma_hwec.c | 547 ++ api/libsangoma/sample_c/.svn/all-wcprops | 10 +- api/libsangoma/sample_c/.svn/dir-prop-base | 6 + api/libsangoma/sample_c/.svn/entries | 43 +- .../.svn/text-base/Makefile.Windows.svn-base | 8 - .../sample_c/.svn/text-base/sample.c.svn-base | 4 +- api/libsangoma/sample_c/Makefile.Windows | 8 - api/libsangoma/sample_c/sample.c | 4 +- api/libsangoma/sample_cpp/.svn/all-wcprops | 42 +- api/libsangoma/sample_cpp/.svn/dir-prop-base | 3 +- api/libsangoma/sample_cpp/.svn/entries | 136 +- .../.svn/text-base/Makefile.Windows.svn-base | 8 - .../.svn/text-base/sample.cpp.svn-base | 422 +- .../.svn/text-base/sample.h.svn-base | 14 +- .../.svn/text-base/sample.sln.svn-base | 6 +- .../.svn/text-base/sample.vcproj.svn-base | 15 +- .../text-base/sangoma_interface.cpp.svn-base | 747 +- .../text-base/sangoma_interface.h.svn-base | 68 +- .../.svn/text-base/sangoma_port.cpp.svn-base | 4 +- .../.svn/text-base/sangoma_port.h.svn-base | 1 - .../sangoma_port_configurator.cpp.svn-base | 73 +- .../sangoma_port_configurator.h.svn-base | 10 +- .../.svn/text-base/sources.svn-base | 6 +- api/libsangoma/sample_cpp/Makefile.Windows | 8 - api/libsangoma/sample_cpp/sample.cpp | 422 +- api/libsangoma/sample_cpp/sample.h | 14 +- api/libsangoma/sample_cpp/sample.sln | 6 +- api/libsangoma/sample_cpp/sample.vcproj | 15 +- .../sample_cpp/sangoma_interface.cpp | 747 +- api/libsangoma/sample_cpp/sangoma_interface.h | 68 +- api/libsangoma/sample_cpp/sangoma_port.cpp | 4 +- api/libsangoma/sample_cpp/sangoma_port.h | 1 - .../sample_cpp/sangoma_port_configurator.cpp | 73 +- .../sample_cpp/sangoma_port_configurator.h | 10 +- api/libsangoma/sample_cpp/sources | 6 +- api/libsangoma/sources | 8 +- api/libstelephony/.svn/all-wcprops | 36 +- api/libstelephony/.svn/dir-prop-base | 7 + api/libstelephony/.svn/entries | 124 +- .../.svn/text-base/Makefile.Windows.svn-base | 9 - .../.svn/text-base/Makefile.am.svn-base | 2 +- .../.svn/text-base/PToneDecoder.cpp.svn-base | 4 +- .../.svn/text-base/PToneDecoder.h.svn-base | 4 +- .../.svn/text-base/PToneEncoder.h.svn-base | 4 +- .../.svn/text-base/libstelephony.h.svn-base | 13 +- .../.svn/text-base/stelephony.cpp.svn-base | 4 +- .../.svn/text-base/stelephony.def.svn-base | 10 - .../.svn/text-base/stelephony.h.svn-base | 2 +- .../.svn/text-base/stelephony.sln.svn-base | 4 +- ...ephony.vcproj.DAVIDRNEW.User.user.svn-base | 65 - .../.svn/text-base/stelephony.vcproj.svn-base | 63 +- api/libstelephony/Makefile.Windows | 9 - api/libstelephony/Makefile.am | 2 +- api/libstelephony/PToneDecoder.cpp | 4 +- api/libstelephony/PToneDecoder.h | 4 +- api/libstelephony/PToneEncoder.h | 4 +- api/libstelephony/libstelephony.h | 13 +- api/libstelephony/stel_tone/.svn/all-wcprops | 80 +- .../stel_tone/.svn/dir-prop-base | 6 + api/libstelephony/stel_tone/.svn/entries | 198 +- .../.svn/text-base/Makefile.Windows.svn-base | 9 - .../text-base/libstelephony_tone.h.svn-base | 11 +- .../stel_tone/.svn/text-base/sources.svn-base | 8 +- .../.svn/text-base/stel_tone.c.svn-base | 81 +- .../{fsk.c.svn-base => wp_fsk.c.svn-base} | 4 +- .../{fsk.h.svn-base => wp_fsk.h.svn-base} | 2 +- .../{g711.h.svn-base => wp_g711.h.svn-base} | 0 .../text-base/wp_libteletone.h.svn-base} | 4 +- .../wp_libteletone_detect.c.svn-base} | 2 +- .../wp_libteletone_detect.h.svn-base} | 4 +- .../wp_libteletone_generate.c.svn-base} | 2 +- ...ase => wp_libteletone_generate.h.svn-base} | 4 +- .../text-base/wp_uart.c.svn-base} | 2 +- .../{uart.h.svn-base => wp_uart.h.svn-base} | 0 api/libstelephony/stel_tone/Makefile.Windows | 9 - .../stel_tone/libstelephony_tone.h | 11 +- api/libstelephony/stel_tone/sources | 8 +- api/libstelephony/stel_tone/stel_tone.c | 81 +- .../stel_tone/{fsk.c => wp_fsk.c} | 4 +- .../stel_tone/{fsk.h => wp_fsk.h} | 2 +- .../stel_tone/{g711.h => wp_g711.h} | 0 ...ibteletone.h.svn-base => wp_libteletone.h} | 4 +- ...ect.c.svn-base => wp_libteletone_detect.c} | 2 +- ...ect.h.svn-base => wp_libteletone_detect.h} | 4 +- ...e.c.svn-base => wp_libteletone_generate.c} | 2 +- ...e_generate.h => wp_libteletone_generate.h} | 4 +- .../text-base/uart.c.svn-base => wp_uart.c} | 2 +- .../stel_tone/{uart.h => wp_uart.h} | 0 api/libstelephony/stelephony.cpp | 4 +- api/libstelephony/stelephony.def | 10 - api/libstelephony/stelephony.h | 2 +- api/libstelephony/stelephony.sln | 4 +- api/libstelephony/stelephony.vcproj | 63 +- .../stelephony.vcproj.DAVIDRNEW.User.user | 65 - api/tdm_api/aft_tdm_hdlc_test.c | 31 +- api/tdm_api/aft_tdm_voice_api.c | 31 + deb_control/wanpipe.deb | 2 +- doc/.README.xdlc_api.swp | Bin 12288 -> 0 bytes patches/Fix_below_2211.gz | Bin 231 -> 0 bytes .../kdrivers/include/.wanpipe_kernel.h.swp | Bin 16384 -> 0 bytes patches/kdrivers/include/aft_core.h | 205 +- patches/kdrivers/include/aft_core_bert.h | 175 + patches/kdrivers/include/aft_core_options.h | 73 +- patches/kdrivers/include/aft_core_private.h | 52 +- patches/kdrivers/include/aft_core_user.h | 113 +- patches/kdrivers/include/aft_core_utils.h | 111 +- patches/kdrivers/include/if_wanpipe_common.h | 10 +- patches/kdrivers/include/sdla_a600_remora.h | 15 +- patches/kdrivers/include/sdla_adsl.h | 2 +- patches/kdrivers/include/sdla_remora.h | 11 +- patches/kdrivers/include/sdla_tdmv.h | 20 +- patches/kdrivers/include/sdla_te1.h | 97 +- patches/kdrivers/include/sdla_te1_ds.h | 1 + patches/kdrivers/include/sdla_te3.h | 1 + patches/kdrivers/include/sdla_x25.h | 16 +- patches/kdrivers/include/sdladrv.h | 48 +- patches/kdrivers/include/sdlapci.h | 3 + patches/kdrivers/include/sdlasfm.h | 17 +- patches/kdrivers/include/wan_mem_debug.h | 2 +- patches/kdrivers/include/wanpipe.h | 19 +- .../kdrivers/include/wanpipe_abstr_types.h | 152 +- patches/kdrivers/include/wanpipe_api_hdr.h | 98 +- patches/kdrivers/include/wanpipe_api_iface.h | 132 +- patches/kdrivers/include/wanpipe_cdev_iface.h | 20 +- patches/kdrivers/include/wanpipe_cfg.h | 109 +- patches/kdrivers/include/wanpipe_cfg_def.h | 5 + patches/kdrivers/include/wanpipe_cfg_sppp.h | 5 - patches/kdrivers/include/wanpipe_common.h | 132 +- patches/kdrivers/include/wanpipe_debug.h | 778 +- patches/kdrivers/include/wanpipe_defines.h | 144 +- patches/kdrivers/include/wanpipe_edac_iface.h | 1 + patches/kdrivers/include/wanpipe_events.h | 11 +- patches/kdrivers/include/wanpipe_fr_iface.h | 4 - patches/kdrivers/include/wanpipe_iface.h | 4 - patches/kdrivers/include/wanpipe_includes.h | 85 +- patches/kdrivers/include/wanpipe_kernel.h | 69 + patches/kdrivers/include/wanpipe_lapb_iface.h | 4 - patches/kdrivers/include/wanpipe_lip.h | 171 +- patches/kdrivers/include/wanpipe_lip_kernel.h | 3 +- patches/kdrivers/include/wanpipe_logger.h | 312 + patches/kdrivers/include/wanpipe_mtp1.h | 60 + patches/kdrivers/include/wanpipe_sppp_iface.h | 4 +- patches/kdrivers/include/wanpipe_tdm_api.h | 4 + patches/kdrivers/include/wanpipe_version.h | 26 +- patches/kdrivers/include/wanrouter.h | 110 +- patches/kdrivers/include/zapcompat.h | 6 + patches/kdrivers/src/lip/wanpipe_lip_bh.c | 14 +- patches/kdrivers/src/lip/wanpipe_lip_iface.c | 107 +- patches/kdrivers/src/lip/wanpipe_lip_ipx.c | 2 + patches/kdrivers/src/lip/wanpipe_lip_netdev.c | 121 +- patches/kdrivers/src/lip/wanpipe_lip_prot.c | 82 +- patches/kdrivers/src/lip/wanpipe_lip_sub.c | 46 +- patches/kdrivers/src/lip/wanpipe_lip_tty.c | 47 +- patches/kdrivers/src/net/Makefile | 7 +- .../kdrivers/src/net/Module.markers | 0 patches/kdrivers/src/net/aft_a104.c | 524 +- patches/kdrivers/src/net/aft_analog.c | 202 +- patches/kdrivers/src/net/aft_bri.c | 34 +- patches/kdrivers/src/net/aft_core.c | 1541 ++-- .../kdrivers/src/net/aft_core_api_events.c | 173 +- patches/kdrivers/src/net/aft_core_prot.c | 11 +- patches/kdrivers/src/net/aft_core_utils.c | 662 +- patches/kdrivers/src/net/sdla_8te1.c | 420 +- patches/kdrivers/src/net/sdla_adsl.c | 134 +- patches/kdrivers/src/net/sdla_adsl_tty.c | 13 +- patches/kdrivers/src/net/sdla_aft_te1_ss7.c | 118 +- patches/kdrivers/src/net/sdla_aft_te3.c | 144 +- patches/kdrivers/src/net/sdla_asyhdlc.c | 24 +- patches/kdrivers/src/net/sdla_atm.c | 45 +- patches/kdrivers/src/net/sdla_bitstrm.c | 80 +- patches/kdrivers/src/net/sdla_bri.c | 124 +- patches/kdrivers/src/net/sdla_bri_tdmv.c | 14 +- patches/kdrivers/src/net/sdla_chdlc.c | 34 +- patches/kdrivers/src/net/sdla_ec.c | 12 +- patches/kdrivers/src/net/sdla_fr.c | 24 +- patches/kdrivers/src/net/sdla_pos.c | 4 +- patches/kdrivers/src/net/sdla_ppp.c | 24 +- patches/kdrivers/src/net/sdla_remora.c | 340 +- patches/kdrivers/src/net/sdla_remora_analog.c | 177 +- patches/kdrivers/src/net/sdla_remora_tdmv.c | 6 +- patches/kdrivers/src/net/sdla_serial.c | 63 +- patches/kdrivers/src/net/sdla_tdmv.c | 223 +- patches/kdrivers/src/net/sdla_te1.c | 24 +- patches/kdrivers/src/net/sdla_te3.c | 40 +- patches/kdrivers/src/net/sdla_usb_remora.c | 8 +- .../kdrivers/src/net/sdla_usb_remora_tdmv.c | 7 +- patches/kdrivers/src/net/sdla_x25.c | 24 +- patches/kdrivers/src/net/sdla_xilinx.c | 4384 ++++++----- patches/kdrivers/src/net/sdladrv.c | 404 +- patches/kdrivers/src/net/sdladrv_fe.c | 210 +- patches/kdrivers/src/net/sdladrv_usb.c | 37 +- patches/kdrivers/src/net/sdladrv_utils.c | 6 +- patches/kdrivers/src/net/sdlamain.c | 4 + patches/kdrivers/src/net/wan_mem_debug.c | 30 +- patches/kdrivers/src/net/wanpipe_abstr.c | 24 +- .../{wanrouter => net}/wanpipe_cdev_linux.c | 95 +- patches/kdrivers/src/net/wanpipe_codec_law.c | 4 +- .../kdrivers/src/net/wanpipe_linux_iface.c | 71 +- patches/kdrivers/src/net/wanpipe_logger.c | 778 ++ patches/kdrivers/src/net/wanpipe_multppp.c | 54 +- patches/kdrivers/src/net/wanpipe_syncppp.c | 8 +- patches/kdrivers/src/net/wanpipe_tdm_api.c | 750 +- patches/kdrivers/src/net/wanpipe_timer_dev.c | 54 +- patches/kdrivers/src/net/wanpipe_usb.c | 70 +- patches/kdrivers/src/net/wanpipe_utils.c | 12 +- patches/kdrivers/src/wanrouter/af_wanpipe.c | 47 +- .../src/wanrouter/af_wanpipe_datascope.c | 14 +- patches/kdrivers/src/wanrouter/wanmain.c | 7 +- patches/kdrivers/src/wanrouter/wanproc.c | 19 +- .../kdrivers/wanec/.tmp_versions/wanec.mod | 4 +- patches/kdrivers/wanec/.wanec.ko.cmd | 1 - patches/kdrivers/wanec/Module.markers | 0 patches/kdrivers/wanec/Module.symvers | 63 - .../oct6100api/oct6100_api/oct6100_channel.c | 5 + patches/kdrivers/wanec/wanec.mod.c | 11 +- patches/kdrivers/wanec/wanec_cmd.c | 25 +- patches/kdrivers/wanec/wanec_dev.c | 41 +- patches/kdrivers/wanec/wanec_iface.c | 160 +- patches/kdrivers/wanec/wanec_iface.h | 17 +- patches/kdrivers/wanec/wanec_tones.h | 106 +- patches/kdrivers/wanec/wanec_utils.c | 4 +- patches/makefile.af_wanpipe.patch | 11 - patches/makefile.patch.2.2.X | 125 - patches/makefile.patch.2.4.0 | 37 - patches/makefile.patch.2.4.4 | 35 - patches/makefile.patch.utils.2.2.X | 31 - patches/makefile.patch.utils.2.4.X | 27 - patches/wanrouter-v2213.gz | Bin 16237 -> 0 bytes patches/wanrouter-v2214.gz | Bin 20149 -> 0 bytes patches/wanrouter-v2215.gz | Bin 20184 -> 0 bytes patches/wanrouter-v2218.gz | Bin 20384 -> 0 bytes patches/wanrouter-v2219.gz | Bin 20339 -> 0 bytes patches/wanrouter-v240.gz | Bin 21704 -> 0 bytes patches/wanrouter-v2416.gz | Bin 6495 -> 0 bytes patches/wanrouter-v244.gz | Bin 6273 -> 0 bytes patches/wanrouter-v249.gz | Bin 6459 -> 0 bytes rpmspec/wanpipe-mod.spec | 79 +- rpmspec/wanpipe-util.spec | 79 +- rpmspec/wanpipe.spec | 79 +- samples/wanrouter | 2 +- scripts/Compile.sh | 2 +- ssmg/sangoma_bri/ChangeLog | 4 + ssmg/sangoma_bri/Makefile | 7 +- ssmg/sangoma_bri/sangoma_brid.i686 | Bin 473608 -> 473660 bytes ssmg/sangoma_bri/sangoma_brid.x86_64 | Bin 586408 -> 586408 bytes ssmg/sangoma_mgd.trunk/.bri | 1 + ssmg/sangoma_mgd.trunk/.pri | 1 + ssmg/sangoma_mgd.trunk/.svn/all-wcprops | 54 +- ssmg/sangoma_mgd.trunk/.svn/entries | 212 +- ...ctrl_common.svn-base => smg_ctrl.svn-base} | 0 .../.svn/text-base/Makefile.svn-base | 63 +- .../.svn/text-base/call_signal.c.svn-base | 39 +- .../.svn/text-base/install.svn-base | 79 +- .../.svn/text-base/safe_sangoma.svn-base | 78 +- .../.svn/text-base/sangoma_mgd.c.svn-base | 1203 ++- .../sangoma_mgd.conf.sample.svn-base | 46 +- .../.svn/text-base/sangoma_mgd.h.svn-base | 241 +- .../sangoma_mgd_ip_bridge.c.svn-base | 376 + .../.svn/text-base/sigboost.h.svn-base | 90 +- ...ctrl_common.svn-base => smg_ctrl.svn-base} | 364 +- .../.svn/text-base/smg_ctrl_bri.svn-base | 15 - .../.svn/text-base/smg_ctrl_pri.svn-base | 15 - .../.svn/text-base/smg_ctrl_ss7.svn-base | 15 - .../.svn/tmp/tempfile.10.tmp | 740 ++ .../.svn/tmp/tempfile.11.tmp | 5970 ++++++++++++++ .../.svn/tmp/tempfile.12.tmp | 414 + .../.svn/tmp/tempfile.13.tmp | 6038 ++++++++++++++ .../.svn/tmp/tempfile.14.tmp | 786 ++ .../.svn/tmp/tempfile.15.tmp | 6489 ++++++++++++++++ .../sangoma_mgd.trunk/.svn/tmp/tempfile.5.tmp | 170 + .../sangoma_mgd.trunk/.svn/tmp/tempfile.6.tmp | 5882 ++++++++++++++ .../sangoma_mgd.trunk/.svn/tmp/tempfile.7.tmp | 740 ++ .../sangoma_mgd.trunk/.svn/tmp/tempfile.8.tmp | 159 + .../sangoma_mgd.trunk/.svn/tmp/tempfile.9.tmp | 5946 ++++++++++++++ ssmg/sangoma_mgd.trunk/Makefile | 63 +- ssmg/sangoma_mgd.trunk/app/.svn/entries | 6 +- ssmg/sangoma_mgd.trunk/call_signal.c | 39 +- .../chan_woomera.trunk/.ast14_check | 2 +- .../chan_woomera.trunk/.ast_src_check | 2 +- .../chan_woomera.trunk/.cw_module_info_check | 1 - .../sangoma_mgd.trunk/chan_woomera.trunk/.log | 634 +- .../chan_woomera.trunk/.pbxdir | 2 +- .../chan_woomera.trunk/.svn/all-wcprops | 10 +- .../chan_woomera.trunk/.svn/entries | 42 +- .../.svn/text-base/Makefile.svn-base | 60 +- .../.svn/text-base/chan_woomera.c.svn-base | 998 ++- .../.svn/text-base/woomera.conf.svn-base | 52 +- .../.svn/tmp/tempfile.2.tmp | 5663 ++++++++++++++ .../.svn/tmp/tempfile.3.tmp | 5676 ++++++++++++++ .../.svn/tmp/tempfile.4.tmp | 5787 ++++++++++++++ .../chan_woomera.trunk/Makefile | 60 +- .../chan_woomera.trunk/chan_woomera.c | 998 ++- .../chan_woomera.trunk/chan_woomera.so | Bin 287233 -> 355347 bytes .../chan_woomera.trunk/svn-commit.tmp | 4 + .../chan_woomera.trunk/woomera.conf | 52 +- ssmg/sangoma_mgd.trunk/conf/.svn/entries | 6 +- ssmg/sangoma_mgd.trunk/conf_bri/.svn/entries | 6 +- ssmg/sangoma_mgd.trunk/install | 79 +- ssmg/sangoma_mgd.trunk/lib/.svn/entries | 2 +- .../lib/libteletone/.svn/entries | 46 +- .../lib/libteletone/src/.svn/entries | 12 +- ssmg/sangoma_mgd.trunk/rc/.svn/all-wcprops | 53 + ssmg/sangoma_mgd.trunk/rc/.svn/entries | 124 + .../sangoma_mgd.trunk/rc}/.svn/format | 0 .../.svn/text-base/safe_sangoma.rc.svn-base | 18 + .../rc/.svn/text-base/smg.rc.bri.svn-base | 10 + .../.svn/text-base/smg.rc.bri_only.svn-base | 11 + .../rc/.svn/text-base/smg.rc.isupd.svn-base | 11 + .../.svn/text-base/smg.rc.isupd_only.svn-base | 11 + .../rc/.svn/text-base/smg.rc.pri.svn-base | 11 + .../.svn/text-base/smg.rc.pri_only.svn-base | 11 + .../.svn/text-base/smg.rc.ss7boost.svn-base | 10 + ssmg/sangoma_mgd.trunk/rc/safe_sangoma.rc | 18 + ssmg/sangoma_mgd.trunk/rc/smg.rc.bri | 10 + ssmg/sangoma_mgd.trunk/rc/smg.rc.bri_only | 11 + ssmg/sangoma_mgd.trunk/rc/smg.rc.isupd | 11 + ssmg/sangoma_mgd.trunk/rc/smg.rc.isupd_only | 11 + ssmg/sangoma_mgd.trunk/rc/smg.rc.pri | 11 + ssmg/sangoma_mgd.trunk/rc/smg.rc.pri_only | 11 + ssmg/sangoma_mgd.trunk/rc/smg.rc.ss7boost | 10 + ssmg/sangoma_mgd.trunk/safe_sangoma | 78 +- ssmg/sangoma_mgd.trunk/sangoma_mgd.c | 1203 ++- .../sangoma_mgd.trunk/sangoma_mgd.conf.sample | 46 +- ssmg/sangoma_mgd.trunk/sangoma_mgd.h | 241 +- .../sangoma_mgd.trunk/sangoma_mgd_ip_bridge.c | 376 + .../scripts/.svn/all-wcprops | 14 +- ssmg/sangoma_mgd.trunk/scripts/.svn/entries | 40 +- .../.svn/prop-base/sig_state.sh.svn-base} | 0 .../.svn/prop-base/stats.sh.svn-base} | 0 .../.svn/text-base/sig_state.sh.svn-base | 70 + .../scripts/.svn/text-base/stats.sh.svn-base | 21 + .../scripts/callgen/.svn/entries | 10 +- .../scripts/init.d/.svn/all-wcprops | 10 +- .../scripts/init.d/.svn/entries | 27 +- .../prop-base/ss7boxd_monitor.sh.svn-base} | 0 .../.svn/text-base/smgss7_init_ctrl.svn-base | 142 +- .../text-base/ss7boxd_monitor.sh.svn-base | 117 + .../scripts/init.d/smgss7_init_ctrl | 142 +- .../scripts/init.d/ss7boxd_monitor.sh | 117 + ssmg/sangoma_mgd.trunk/scripts/sig_state.sh | 70 + .../scripts/ss7/.svn/all-wcprops | 4 +- .../scripts/ss7/.svn/entries | 14 +- .../ss7/.svn/text-base/ckt_report.sh.svn-base | 4 +- .../scripts/ss7/ckt_report.sh | 4 +- ssmg/sangoma_mgd.trunk/scripts/stats.sh | 21 + ssmg/sangoma_mgd.trunk/sigboost.h | 90 +- .../{__smg_ctrl_common => smg_ctrl} | 364 +- ssmg/sangoma_mgd.trunk/smg_ctrl_bri | 15 - ssmg/sangoma_mgd.trunk/smg_ctrl_pri | 15 - ssmg/sangoma_mgd.trunk/smg_ctrl_ss7 | 15 - ssmg/sangoma_mgd.trunk/unit/.svn/entries | 16 +- ssmg/sangoma_mgd.trunk/unit/core/.svn/entries | 6 +- .../sangoma_mgd.trunk/unit/tests/.svn/entries | 2 +- .../unit/tests/1_loop/.svn/entries | 8 +- .../unit/tests/1_loop/smg_unit | Bin 109121 -> 0 bytes .../unit/tests/1_loop/tmp/.svn/entries | 2 +- .../unit/tests/2_loop_call/.svn/entries | 8 +- .../unit/tests/2_loop_call/smg_unit | Bin 109343 -> 0 bytes .../unit/tests/2_loop_call/tmp/.svn/entries | 2 +- util/diff.sh | 4 - util/wan_aftup/A101dm_0040_V36.BIN | Bin 212392 -> 0 bytes util/wan_aftup/A101dm_0040_V37.BIN | Bin 0 -> 212392 bytes util/wan_aftup/A102dm_0040_V36.BIN | Bin 212392 -> 0 bytes util/wan_aftup/A102dm_0040_V37.BIN | Bin 0 -> 212392 bytes util/wan_aftup/A104dm_0100_V36.BIN | Bin 402936 -> 0 bytes util/wan_aftup/A104dm_0100_V37.BIN | Bin 0 -> 402936 bytes util/wan_aftup/A108dm_0100_V38.BIN | Bin 402936 -> 0 bytes util/wan_aftup/A108dm_0100_V41.BIN | Bin 0 -> 402936 bytes util/wan_aftup/A200_0040_V11.BIN | Bin 212392 -> 0 bytes util/wan_aftup/A200_0040_V12.BIN | Bin 0 -> 212392 bytes util/wan_aftup/A200_0040_V13.BIN | Bin 0 -> 212392 bytes util/wan_aftup/B601_0025_V04.BIN | Bin 0 -> 169216 bytes util/wan_aftup/ChangeLog.a101dm | 8 + util/wan_aftup/ChangeLog.a102dm | 8 + util/wan_aftup/ChangeLog.a104dm | 10 + util/wan_aftup/ChangeLog.a108dm | 36 + util/wan_aftup/ChangeLog.a200 | 24 + util/wan_aftup/ChangeLog.b600 | 12 + util/wan_aftup/ChangeLog.b601 | 18 + util/wan_aftup/wan_aft_flash.c | 4 +- util/wan_aftup/wan_aft_flash_a600.c | 18 +- util/wan_aftup/wan_aft_flash_shark.c | 4 +- util/wan_aftup/wan_aft_prg.c | 15 +- util/wan_aftup/wan_aftup.c | 78 +- util/wan_aftup/wan_aftup.h | 3 +- util/wancfg/main.cpp | 4 +- .../menu_net_interface_operation_mode.cpp | 2 +- util/wancfg/menu_tdmv_law.cpp | 2 +- util/wancfg_zaptel/.svn/all-wcprops | 125 - util/wancfg_zaptel/.svn/entries | 281 - .../.svn/prop-base/clean.sh.svn-base | 5 - .../.svn/prop-base/install.sh.svn-base | 5 - .../.svn/prop-base/setup-sangoma.svn-base | 5 - .../.svn/prop-base/uninstall.sh.svn-base | 5 - .../.svn/prop-base/wancfg_dahdi.svn-base | 5 - .../.svn/prop-base/wancfg_fs.svn-base | 5 - .../.svn/prop-base/wancfg_hp_tdmapi.svn-base | 5 - .../.svn/prop-base/wancfg_tdmapi.svn-base | 5 - .../.svn/prop-base/wancfg_zaptel.pl.svn-base | 5 - .../.svn/prop-base/wancfg_zaptel.svn-base | 5 - .../.svn/text-base/A10u.pm.svn-base | 621 -- .../.svn/text-base/A10x.pm.svn-base | 744 -- .../.svn/text-base/A20x.pm.svn-base | 227 - .../.svn/text-base/A50x.pm.svn-base | 198 - .../.svn/text-base/Card.pm.svn-base | 138 - .../.svn/text-base/Makefile.svn-base | 25 - .../.svn/text-base/U10x.pm.svn-base | 212 - .../.svn/text-base/analogspan.pm.svn-base | 50 - .../.svn/text-base/boostspan.pm.svn-base | 56 - .../.svn/text-base/clean.sh.svn-base | 4 - .../.svn/text-base/install.sh.svn-base | 38 - .../.svn/text-base/setup-sangoma.svn-base | 5 - .../.svn/text-base/uninstall.sh.svn-base | 13 - .../.svn/text-base/wancfg_dahdi.svn-base | 51 - .../.svn/text-base/wancfg_hp_tdmapi.svn-base | 52 - .../.svn/text-base/wancfg_smg.svn-base | 51 - .../.svn/text-base/wancfg_tdmapi.svn-base | 52 - .../.svn/text-base/wancfg_zaptel.pl.svn-base | 3547 --------- .../.svn/text-base/wancfg_zaptel.svn-base | 51 - util/wancfg_zaptel/A10u.pm | 104 +- util/wancfg_zaptel/A10x.pm | 147 +- util/wancfg_zaptel/U10x.pm | 2 - util/wancfg_zaptel/boostspan.pm | 31 +- util/wancfg_zaptel/install.sh | 1 + util/wancfg_zaptel/templates/.svn/all-wcprops | 149 - util/wancfg_zaptel/templates/.svn/entries | 327 - util/wancfg_zaptel/templates/.svn/format | 1 - .../.svn/prop-base/smg_ctrl_safe.svn-base | 5 - .../prop-base/wanpipe.tdm_api.a500.svn-base | 5 - .../text-base/dahdi-channels.conf.svn-base | 10 - .../.svn/text-base/dahdi_cfg_script.svn-base | 29 - .../rc_init_template_freebsd.svn-base | 4 - .../.svn/text-base/smg_bri.conf.svn-base | 148 - .../.svn/text-base/smg_ctrl_safe.svn-base | 37 - .../text-base/smgbri_start_script.svn-base | 24 - .../smgbri_start_script_addon.svn-base | 24 - .../text-base/smgbri_stop_script.svn-base | 2 - .../.svn/text-base/wanpipe.tdm.a100.svn-base | 51 - .../.svn/text-base/wanpipe.tdm.a10u.svn-base | 49 - .../.svn/text-base/wanpipe.tdm.a200.svn-base | 47 - .../.svn/text-base/wanpipe.tdm.u100.svn-base | 37 - .../text-base/wanpipe.tdm_api.a100.svn-base | 52 - .../text-base/wanpipe.tdm_api.a500.svn-base | 46 - .../wanrouter.rc.template.FreeBSD.svn-base | 34 - .../text-base/wanrouter.rc.template.svn-base | 41 - .../.svn/text-base/woomera.conf.svn-base | 12 - .../.svn/text-base/zapata-auto.conf.svn-base | 7 - .../.svn/text-base/zapata.conf.svn-base | 28 - .../.svn/text-base/zaptel.conf.svn-base | 6 - .../.svn/text-base/zaptel.conf_test.svn-base | 1 - .../zaptel_cfg_script.FreeBSD.svn-base | 6 - .../.svn/text-base/zaptel_cfg_script.svn-base | 29 - util/wancfg_zaptel/templates/dahdi_cfg_script | 4 +- .../templates/hp_a100/.svn/all-wcprops | 41 - .../templates/hp_a100/.svn/entries | 100 - .../templates/hp_a100/.svn/format | 1 - .../.svn/text-base/wanpipe.hp.1.svn-base | 20 - .../.svn/text-base/wanpipe.hp.2.svn-base | 1 - .../.svn/text-base/wanpipe.hp.3.svn-base | 1 - .../.svn/text-base/wanpipe.hp.4.svn-base | 23 - .../.svn/text-base/wanpipe.hp.5.svn-base | 7 - .../.svn/text-base/wanpipe.hp.6.svn-base | 4 - .../templates/hp_a100/wanpipe.hp.4 | 1 + util/wancfg_zaptel/templates/openzap.conf.xml | 18 + util/wancfg_zaptel/templates/smg.rc.template | 11 + util/wancfg_zaptel/templates/smg_pri.conf | 93 + .../templates/ss7_a100/.svn/all-wcprops | 47 - .../templates/ss7_a100/.svn/entries | 112 - .../templates/ss7_a100/.svn/format | 1 - .../.svn/text-base/wanpipe.ss7.1.svn-base | 20 - .../.svn/text-base/wanpipe.ss7.2.svn-base | 3 - .../.svn/text-base/wanpipe.ss7.3.svn-base | 2 - .../.svn/text-base/wanpipe.ss7.4.svn-base | 21 - .../.svn/text-base/wanpipe.ss7.5.svn-base | 10 - .../.svn/text-base/wanpipe.ss7.6.svn-base | 9 - .../wanpipe.tdmvoiceapi.a100.svn-base | 52 - .../templates/ss7_a100/wanpipe.ss7.4 | 1 + .../ss7_a100/wanpipe.tdmvoiceapi.a100 | 1 + .../templates/ss7_a10u/.svn/all-wcprops | 47 - .../templates/ss7_a10u/.svn/entries | 112 - .../templates/ss7_a10u/.svn/format | 1 - .../.svn/text-base/wanpipe.ss7.1.svn-base | 20 - .../.svn/text-base/wanpipe.ss7.2.svn-base | 2 - .../.svn/text-base/wanpipe.ss7.3.svn-base | 3 - .../.svn/text-base/wanpipe.ss7.4.svn-base | 20 - .../.svn/text-base/wanpipe.ss7.5.svn-base | 9 - .../.svn/text-base/wanpipe.ss7.6.svn-base | 9 - .../wanpipe.tdmvoiceapi.a10u.svn-base | 50 - .../templates/ss7_a10u/wanpipe.ss7.4 | 1 + .../ss7_a10u/wanpipe.tdmvoiceapi.a10u | 1 + util/wancfg_zaptel/templates/wanpipe.tdm.a100 | 4 +- util/wancfg_zaptel/templates/wanpipe.tdm.a10u | 1 + util/wancfg_zaptel/templates/wanpipe.tdm.u100 | 1 - .../wancfg_fs.svn-base => wancfg_openzap} | 2 +- util/wancfg_zaptel/wancfg_zaptel.pl | 577 +- util/wanconfig/Makefile | 2 +- util/wanconfig/wanconfig.c | 1438 +--- util/wanconfig/wanconfig.h | 1344 ++++ util/wanconfig/wanconfig_hwec.c | 351 + util/wanec_apilib/wanec_api.c | 73 +- util/wanec_apilib/wanec_api.h | 56 +- util/wanec_apilib/wanec_api_lib.c | 10 +- util/wanpipemon/aftpipemon.c | 488 +- util/wanpipemon/atmpipemon.c | 6 +- util/wanpipemon/fe_lib.c | 389 +- util/wanpipemon/fe_lib.h | 3 +- util/wanpipemon/prot_trace.c | 9 +- util/wanpipemon/wanpipemon.c | 88 +- util/wanpipemon/wanpipemon.h | 9 +- 558 files changed, 95617 insertions(+), 32152 deletions(-) create mode 100644 .router_version delete mode 100644 api/libsangoma/.svn/prop-base/ltmain.sh.svn-base delete mode 100644 api/libsangoma/.svn/text-base/Makefile.Windows.svn-base delete mode 100644 api/libsangoma/.svn/text-base/config.guess.svn-base delete mode 100644 api/libsangoma/.svn/text-base/config.sub.svn-base delete mode 100644 api/libsangoma/.svn/text-base/libsangoma.vcproj.DAVIDRNEW.User.user.svn-base create mode 100644 api/libsangoma/.svn/text-base/libsangoma_hwec.c.svn-base delete mode 100644 api/libsangoma/.svn/text-base/ltmain.sh.svn-base create mode 100644 api/libsangoma/.svn/tmp/tempfile.17.tmp create mode 100644 api/libsangoma/.svn/tmp/tempfile.18.tmp create mode 100644 api/libsangoma/.svn/tmp/tempfile.19.tmp create mode 100644 api/libsangoma/.svn/tmp/tempfile.20.tmp create mode 100644 api/libsangoma/.svn/tmp/tempfile.21.tmp create mode 100644 api/libsangoma/.svn/tmp/tempfile.22.tmp create mode 100644 api/libsangoma/.svn/tmp/tempfile.23.tmp delete mode 100644 api/libsangoma/Makefile.Windows delete mode 100644 api/libsangoma/libsangoma.vcproj.DAVIDRNEW.User.user create mode 100644 api/libsangoma/libsangoma_hwec.c create mode 100644 api/libsangoma/sample_c/.svn/dir-prop-base delete mode 100644 api/libsangoma/sample_c/.svn/text-base/Makefile.Windows.svn-base delete mode 100644 api/libsangoma/sample_c/Makefile.Windows delete mode 100644 api/libsangoma/sample_cpp/.svn/text-base/Makefile.Windows.svn-base delete mode 100644 api/libsangoma/sample_cpp/Makefile.Windows create mode 100644 api/libstelephony/.svn/dir-prop-base delete mode 100644 api/libstelephony/.svn/text-base/Makefile.Windows.svn-base delete mode 100644 api/libstelephony/.svn/text-base/stelephony.vcproj.DAVIDRNEW.User.user.svn-base delete mode 100644 api/libstelephony/Makefile.Windows create mode 100644 api/libstelephony/stel_tone/.svn/dir-prop-base delete mode 100644 api/libstelephony/stel_tone/.svn/text-base/Makefile.Windows.svn-base rename api/libstelephony/stel_tone/.svn/text-base/{fsk.c.svn-base => wp_fsk.c.svn-base} (99%) rename api/libstelephony/stel_tone/.svn/text-base/{fsk.h.svn-base => wp_fsk.h.svn-base} (99%) rename api/libstelephony/stel_tone/.svn/text-base/{g711.h.svn-base => wp_g711.h.svn-base} (100%) rename api/libstelephony/stel_tone/{libteletone.h => .svn/text-base/wp_libteletone.h.svn-base} (98%) rename api/libstelephony/stel_tone/{libteletone_detect.c => .svn/text-base/wp_libteletone_detect.c.svn-base} (99%) rename api/libstelephony/stel_tone/{libteletone_detect.h => .svn/text-base/wp_libteletone_detect.h.svn-base} (99%) rename api/libstelephony/stel_tone/{libteletone_generate.c => .svn/text-base/wp_libteletone_generate.c.svn-base} (99%) rename api/libstelephony/stel_tone/.svn/text-base/{libteletone_generate.h.svn-base => wp_libteletone_generate.h.svn-base} (99%) rename api/libstelephony/stel_tone/{uart.c => .svn/text-base/wp_uart.c.svn-base} (99%) rename api/libstelephony/stel_tone/.svn/text-base/{uart.h.svn-base => wp_uart.h.svn-base} (100%) delete mode 100644 api/libstelephony/stel_tone/Makefile.Windows rename api/libstelephony/stel_tone/{fsk.c => wp_fsk.c} (99%) rename api/libstelephony/stel_tone/{fsk.h => wp_fsk.h} (99%) rename api/libstelephony/stel_tone/{g711.h => wp_g711.h} (100%) rename api/libstelephony/stel_tone/{.svn/text-base/libteletone.h.svn-base => wp_libteletone.h} (98%) rename api/libstelephony/stel_tone/{.svn/text-base/libteletone_detect.c.svn-base => wp_libteletone_detect.c} (99%) rename api/libstelephony/stel_tone/{.svn/text-base/libteletone_detect.h.svn-base => wp_libteletone_detect.h} (99%) rename api/libstelephony/stel_tone/{.svn/text-base/libteletone_generate.c.svn-base => wp_libteletone_generate.c} (99%) rename api/libstelephony/stel_tone/{libteletone_generate.h => wp_libteletone_generate.h} (99%) rename api/libstelephony/stel_tone/{.svn/text-base/uart.c.svn-base => wp_uart.c} (99%) rename api/libstelephony/stel_tone/{uart.h => wp_uart.h} (100%) delete mode 100644 api/libstelephony/stelephony.vcproj.DAVIDRNEW.User.user delete mode 100644 doc/.README.xdlc_api.swp delete mode 100644 patches/Fix_below_2211.gz delete mode 100644 patches/kdrivers/include/.wanpipe_kernel.h.swp create mode 100644 patches/kdrivers/include/aft_core_bert.h create mode 100644 patches/kdrivers/include/wanpipe_logger.h create mode 100644 patches/kdrivers/include/wanpipe_mtp1.h rename util/wancfg_zaptel/templates/.svn/text-base/openzap.conf.svn-base => patches/kdrivers/src/net/Module.markers (100%) rename patches/kdrivers/src/{wanrouter => net}/wanpipe_cdev_linux.c (86%) create mode 100644 patches/kdrivers/src/net/wanpipe_logger.c delete mode 100644 patches/kdrivers/wanec/.wanec.ko.cmd create mode 100644 patches/kdrivers/wanec/Module.markers delete mode 100644 patches/kdrivers/wanec/Module.symvers delete mode 100644 patches/makefile.af_wanpipe.patch delete mode 100644 patches/makefile.patch.2.2.X delete mode 100644 patches/makefile.patch.2.4.0 delete mode 100644 patches/makefile.patch.2.4.4 delete mode 100644 patches/makefile.patch.utils.2.2.X delete mode 100644 patches/makefile.patch.utils.2.4.X delete mode 100644 patches/wanrouter-v2213.gz delete mode 100644 patches/wanrouter-v2214.gz delete mode 100644 patches/wanrouter-v2215.gz delete mode 100644 patches/wanrouter-v2218.gz delete mode 100644 patches/wanrouter-v2219.gz delete mode 100644 patches/wanrouter-v240.gz delete mode 100644 patches/wanrouter-v2416.gz delete mode 100644 patches/wanrouter-v244.gz delete mode 100644 patches/wanrouter-v249.gz create mode 100644 ssmg/sangoma_mgd.trunk/.bri create mode 100644 ssmg/sangoma_mgd.trunk/.pri rename ssmg/sangoma_mgd.trunk/.svn/prop-base/{__smg_ctrl_common.svn-base => smg_ctrl.svn-base} (100%) create mode 100644 ssmg/sangoma_mgd.trunk/.svn/text-base/sangoma_mgd_ip_bridge.c.svn-base rename ssmg/sangoma_mgd.trunk/.svn/text-base/{__smg_ctrl_common.svn-base => smg_ctrl.svn-base} (62%) delete mode 100644 ssmg/sangoma_mgd.trunk/.svn/text-base/smg_ctrl_bri.svn-base delete mode 100644 ssmg/sangoma_mgd.trunk/.svn/text-base/smg_ctrl_pri.svn-base delete mode 100644 ssmg/sangoma_mgd.trunk/.svn/text-base/smg_ctrl_ss7.svn-base create mode 100644 ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.10.tmp create mode 100644 ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.11.tmp create mode 100644 ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.12.tmp create mode 100644 ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.13.tmp create mode 100644 ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.14.tmp create mode 100644 ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.15.tmp create mode 100644 ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.5.tmp create mode 100644 ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.6.tmp create mode 100644 ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.7.tmp create mode 100644 ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.8.tmp create mode 100644 ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.9.tmp delete mode 100644 ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.cw_module_info_check create mode 100644 ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.svn/tmp/tempfile.2.tmp create mode 100644 ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.svn/tmp/tempfile.3.tmp create mode 100644 ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.svn/tmp/tempfile.4.tmp create mode 100644 ssmg/sangoma_mgd.trunk/chan_woomera.trunk/svn-commit.tmp create mode 100644 ssmg/sangoma_mgd.trunk/rc/.svn/all-wcprops create mode 100644 ssmg/sangoma_mgd.trunk/rc/.svn/entries rename {util/wancfg_zaptel => ssmg/sangoma_mgd.trunk/rc}/.svn/format (100%) create mode 100644 ssmg/sangoma_mgd.trunk/rc/.svn/text-base/safe_sangoma.rc.svn-base create mode 100644 ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.bri.svn-base create mode 100644 ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.bri_only.svn-base create mode 100644 ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.isupd.svn-base create mode 100644 ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.isupd_only.svn-base create mode 100644 ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.pri.svn-base create mode 100644 ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.pri_only.svn-base create mode 100644 ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.ss7boost.svn-base create mode 100644 ssmg/sangoma_mgd.trunk/rc/safe_sangoma.rc create mode 100644 ssmg/sangoma_mgd.trunk/rc/smg.rc.bri create mode 100644 ssmg/sangoma_mgd.trunk/rc/smg.rc.bri_only create mode 100644 ssmg/sangoma_mgd.trunk/rc/smg.rc.isupd create mode 100644 ssmg/sangoma_mgd.trunk/rc/smg.rc.isupd_only create mode 100644 ssmg/sangoma_mgd.trunk/rc/smg.rc.pri create mode 100644 ssmg/sangoma_mgd.trunk/rc/smg.rc.pri_only create mode 100644 ssmg/sangoma_mgd.trunk/rc/smg.rc.ss7boost create mode 100644 ssmg/sangoma_mgd.trunk/sangoma_mgd_ip_bridge.c rename ssmg/sangoma_mgd.trunk/{.svn/prop-base/smg_ctrl_bri.svn-base => scripts/.svn/prop-base/sig_state.sh.svn-base} (100%) rename ssmg/sangoma_mgd.trunk/{.svn/prop-base/smg_ctrl_pri.svn-base => scripts/.svn/prop-base/stats.sh.svn-base} (100%) create mode 100644 ssmg/sangoma_mgd.trunk/scripts/.svn/text-base/sig_state.sh.svn-base create mode 100644 ssmg/sangoma_mgd.trunk/scripts/.svn/text-base/stats.sh.svn-base rename ssmg/sangoma_mgd.trunk/{.svn/prop-base/smg_ctrl_ss7.svn-base => scripts/init.d/.svn/prop-base/ss7boxd_monitor.sh.svn-base} (100%) create mode 100644 ssmg/sangoma_mgd.trunk/scripts/init.d/.svn/text-base/ss7boxd_monitor.sh.svn-base create mode 100755 ssmg/sangoma_mgd.trunk/scripts/init.d/ss7boxd_monitor.sh create mode 100755 ssmg/sangoma_mgd.trunk/scripts/sig_state.sh create mode 100755 ssmg/sangoma_mgd.trunk/scripts/stats.sh rename ssmg/sangoma_mgd.trunk/{__smg_ctrl_common => smg_ctrl} (62%) delete mode 100755 ssmg/sangoma_mgd.trunk/smg_ctrl_bri delete mode 100755 ssmg/sangoma_mgd.trunk/smg_ctrl_pri delete mode 100755 ssmg/sangoma_mgd.trunk/smg_ctrl_ss7 delete mode 100755 ssmg/sangoma_mgd.trunk/unit/tests/1_loop/smg_unit delete mode 100755 ssmg/sangoma_mgd.trunk/unit/tests/2_loop_call/smg_unit delete mode 100755 util/diff.sh delete mode 100644 util/wan_aftup/A101dm_0040_V36.BIN create mode 100644 util/wan_aftup/A101dm_0040_V37.BIN delete mode 100644 util/wan_aftup/A102dm_0040_V36.BIN create mode 100644 util/wan_aftup/A102dm_0040_V37.BIN delete mode 100644 util/wan_aftup/A104dm_0100_V36.BIN create mode 100644 util/wan_aftup/A104dm_0100_V37.BIN delete mode 100644 util/wan_aftup/A108dm_0100_V38.BIN create mode 100644 util/wan_aftup/A108dm_0100_V41.BIN delete mode 100644 util/wan_aftup/A200_0040_V11.BIN create mode 100644 util/wan_aftup/A200_0040_V12.BIN create mode 100644 util/wan_aftup/A200_0040_V13.BIN create mode 100644 util/wan_aftup/B601_0025_V04.BIN create mode 100644 util/wan_aftup/ChangeLog.b600 create mode 100644 util/wan_aftup/ChangeLog.b601 delete mode 100644 util/wancfg_zaptel/.svn/all-wcprops delete mode 100644 util/wancfg_zaptel/.svn/entries delete mode 100644 util/wancfg_zaptel/.svn/prop-base/clean.sh.svn-base delete mode 100644 util/wancfg_zaptel/.svn/prop-base/install.sh.svn-base delete mode 100644 util/wancfg_zaptel/.svn/prop-base/setup-sangoma.svn-base delete mode 100644 util/wancfg_zaptel/.svn/prop-base/uninstall.sh.svn-base delete mode 100644 util/wancfg_zaptel/.svn/prop-base/wancfg_dahdi.svn-base delete mode 100644 util/wancfg_zaptel/.svn/prop-base/wancfg_fs.svn-base delete mode 100644 util/wancfg_zaptel/.svn/prop-base/wancfg_hp_tdmapi.svn-base delete mode 100644 util/wancfg_zaptel/.svn/prop-base/wancfg_tdmapi.svn-base delete mode 100644 util/wancfg_zaptel/.svn/prop-base/wancfg_zaptel.pl.svn-base delete mode 100644 util/wancfg_zaptel/.svn/prop-base/wancfg_zaptel.svn-base delete mode 100644 util/wancfg_zaptel/.svn/text-base/A10u.pm.svn-base delete mode 100644 util/wancfg_zaptel/.svn/text-base/A10x.pm.svn-base delete mode 100644 util/wancfg_zaptel/.svn/text-base/A20x.pm.svn-base delete mode 100644 util/wancfg_zaptel/.svn/text-base/A50x.pm.svn-base delete mode 100644 util/wancfg_zaptel/.svn/text-base/Card.pm.svn-base delete mode 100644 util/wancfg_zaptel/.svn/text-base/Makefile.svn-base delete mode 100644 util/wancfg_zaptel/.svn/text-base/U10x.pm.svn-base delete mode 100644 util/wancfg_zaptel/.svn/text-base/analogspan.pm.svn-base delete mode 100644 util/wancfg_zaptel/.svn/text-base/boostspan.pm.svn-base delete mode 100644 util/wancfg_zaptel/.svn/text-base/clean.sh.svn-base delete mode 100644 util/wancfg_zaptel/.svn/text-base/install.sh.svn-base delete mode 100644 util/wancfg_zaptel/.svn/text-base/setup-sangoma.svn-base delete mode 100644 util/wancfg_zaptel/.svn/text-base/uninstall.sh.svn-base delete mode 100644 util/wancfg_zaptel/.svn/text-base/wancfg_dahdi.svn-base delete mode 100644 util/wancfg_zaptel/.svn/text-base/wancfg_hp_tdmapi.svn-base delete mode 100644 util/wancfg_zaptel/.svn/text-base/wancfg_smg.svn-base delete mode 100644 util/wancfg_zaptel/.svn/text-base/wancfg_tdmapi.svn-base delete mode 100644 util/wancfg_zaptel/.svn/text-base/wancfg_zaptel.pl.svn-base delete mode 100644 util/wancfg_zaptel/.svn/text-base/wancfg_zaptel.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/all-wcprops delete mode 100644 util/wancfg_zaptel/templates/.svn/entries delete mode 100644 util/wancfg_zaptel/templates/.svn/format delete mode 100644 util/wancfg_zaptel/templates/.svn/prop-base/smg_ctrl_safe.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/prop-base/wanpipe.tdm_api.a500.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/dahdi-channels.conf.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/dahdi_cfg_script.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/rc_init_template_freebsd.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/smg_bri.conf.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/smg_ctrl_safe.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/smgbri_start_script.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/smgbri_start_script_addon.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/smgbri_stop_script.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/wanpipe.tdm.a100.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/wanpipe.tdm.a10u.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/wanpipe.tdm.a200.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/wanpipe.tdm.u100.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/wanpipe.tdm_api.a100.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/wanpipe.tdm_api.a500.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/wanrouter.rc.template.FreeBSD.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/wanrouter.rc.template.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/woomera.conf.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/zapata-auto.conf.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/zapata.conf.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/zaptel.conf.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/zaptel.conf_test.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/zaptel_cfg_script.FreeBSD.svn-base delete mode 100644 util/wancfg_zaptel/templates/.svn/text-base/zaptel_cfg_script.svn-base delete mode 100644 util/wancfg_zaptel/templates/hp_a100/.svn/all-wcprops delete mode 100644 util/wancfg_zaptel/templates/hp_a100/.svn/entries delete mode 100644 util/wancfg_zaptel/templates/hp_a100/.svn/format delete mode 100644 util/wancfg_zaptel/templates/hp_a100/.svn/text-base/wanpipe.hp.1.svn-base delete mode 100644 util/wancfg_zaptel/templates/hp_a100/.svn/text-base/wanpipe.hp.2.svn-base delete mode 100644 util/wancfg_zaptel/templates/hp_a100/.svn/text-base/wanpipe.hp.3.svn-base delete mode 100644 util/wancfg_zaptel/templates/hp_a100/.svn/text-base/wanpipe.hp.4.svn-base delete mode 100644 util/wancfg_zaptel/templates/hp_a100/.svn/text-base/wanpipe.hp.5.svn-base delete mode 100644 util/wancfg_zaptel/templates/hp_a100/.svn/text-base/wanpipe.hp.6.svn-base create mode 100644 util/wancfg_zaptel/templates/openzap.conf.xml create mode 100644 util/wancfg_zaptel/templates/smg.rc.template create mode 100644 util/wancfg_zaptel/templates/smg_pri.conf delete mode 100644 util/wancfg_zaptel/templates/ss7_a100/.svn/all-wcprops delete mode 100644 util/wancfg_zaptel/templates/ss7_a100/.svn/entries delete mode 100644 util/wancfg_zaptel/templates/ss7_a100/.svn/format delete mode 100644 util/wancfg_zaptel/templates/ss7_a100/.svn/text-base/wanpipe.ss7.1.svn-base delete mode 100644 util/wancfg_zaptel/templates/ss7_a100/.svn/text-base/wanpipe.ss7.2.svn-base delete mode 100644 util/wancfg_zaptel/templates/ss7_a100/.svn/text-base/wanpipe.ss7.3.svn-base delete mode 100644 util/wancfg_zaptel/templates/ss7_a100/.svn/text-base/wanpipe.ss7.4.svn-base delete mode 100644 util/wancfg_zaptel/templates/ss7_a100/.svn/text-base/wanpipe.ss7.5.svn-base delete mode 100644 util/wancfg_zaptel/templates/ss7_a100/.svn/text-base/wanpipe.ss7.6.svn-base delete mode 100644 util/wancfg_zaptel/templates/ss7_a100/.svn/text-base/wanpipe.tdmvoiceapi.a100.svn-base delete mode 100644 util/wancfg_zaptel/templates/ss7_a10u/.svn/all-wcprops delete mode 100644 util/wancfg_zaptel/templates/ss7_a10u/.svn/entries delete mode 100644 util/wancfg_zaptel/templates/ss7_a10u/.svn/format delete mode 100644 util/wancfg_zaptel/templates/ss7_a10u/.svn/text-base/wanpipe.ss7.1.svn-base delete mode 100644 util/wancfg_zaptel/templates/ss7_a10u/.svn/text-base/wanpipe.ss7.2.svn-base delete mode 100644 util/wancfg_zaptel/templates/ss7_a10u/.svn/text-base/wanpipe.ss7.3.svn-base delete mode 100644 util/wancfg_zaptel/templates/ss7_a10u/.svn/text-base/wanpipe.ss7.4.svn-base delete mode 100644 util/wancfg_zaptel/templates/ss7_a10u/.svn/text-base/wanpipe.ss7.5.svn-base delete mode 100644 util/wancfg_zaptel/templates/ss7_a10u/.svn/text-base/wanpipe.ss7.6.svn-base delete mode 100644 util/wancfg_zaptel/templates/ss7_a10u/.svn/text-base/wanpipe.tdmvoiceapi.a10u.svn-base rename util/wancfg_zaptel/{.svn/text-base/wancfg_fs.svn-base => wancfg_openzap} (93%) mode change 100644 => 100755 create mode 100755 util/wanconfig/wanconfig.h create mode 100755 util/wanconfig/wanconfig_hwec.c diff --git a/.router_version b/.router_version new file mode 100644 index 0000000..38cd4f5 --- /dev/null +++ b/.router_version @@ -0,0 +1 @@ +wanpipe-3.5.11 diff --git a/ChangeLog.3.5 b/ChangeLog.3.5 index 416147f..078d7ad 100644 --- a/ChangeLog.3.5 +++ b/ChangeLog.3.5 @@ -8,12 +8,89 @@ Copyright (c) 1995-2009 Sangoma Technologies Inc. For more info visit: http://wiki.sangoma.com ------------------------------------------------------------------------------ + +* Thu Apr 08 2010 Nenad Corbic - 3.5.11 +=================================================================== + +- Fix for 2.6.31 and higher kernels +- TDM API Analog rx gain feature +- Disabled default NOISE REDUCTION feature in hwec + that was enabled in 3.5.9 release. +- Updates to T1/E1 Loopback and BERT Test + + +* Wed Jan 11 2010 Nenad Corbic - 3.5.10 +=================================================================== + +- Release cleanup script earsed libsangoma.c during release packaging. + I have update release procedure so this does not happen again. + This release has no functionl differences aside from the missing + file from 3.5.9 release. + +* Wed Dec 30 2009 Nenad Corbic - 3.5.9 +=================================================================== + +- New logger dev feature +- Bug fix in tx fifo handler +- Dahdi 2.2 broke wanpipe rbs support. +- Fixed free run interrupt supported on V38 (A108) +- Fixed RBS signalling for E1 channel 31 +- Added Front end Reset Detection + -> Support for new A108 Firmware V40 +- Fixed RTP TAP bug: Caused high system load on RTP TAP usage. +- Added excessive fifo error sanity check. Fixes random pci dma errors. +- HWEC: Increased EC VQE Delay: Fixes random fax failure due to hwec. +- HWEC: Check state before bypass enable. +- HWEC: Disable bypass on release +- HWEC: Enabled Noise Reduction by default +- HWEC: Enabled Auto Gain Control by default +- HWEC: To disable Noise Reduction and Gain control set + -> HWEC_NOISE_REDUCTION_DISABLE=NO in [wanpipe] section of wanpipe1.conf + To check if Noise Reduction or Gain control are set + -> wan_ec_client wanpipe1 stats 1 + + +* Thu Oct 02 2009 Nenad Corbic - 3.5.8 +=================================================================== + +- Bug fix in sangoma_prid PRI stack for FreeSwitch & Asterisk. + There was a slow memory leak. + + +* Thu Sep 04 2009 Nenad Corbic - 3.5.7 +=================================================================== + +- New Telesoft PRI Stack Support for FreeSwitch & Asterisk + For Asterisk: The new stack uses the existing Sangoma Media Gateway + architecture currently used by SS7 and BRI. + -> run: wancfg_dahdi or wancfg_zaptel to configure + for sangoma prid stack. + + For FreeSwitch: The new stack binds to openzap directly just like + current SS7 and BRI. + -> run: wancfg_fs to configure freeswitch for + sangoma prid, brid, ss7. + +- Fixed Tx Tristate + +- Updated yellow alarm handling for Dallas maxim cards + (A101/2/4/8) + +- Autodetect USB support so that driver will compile + correctly on kernel without USB support + +- Added DAHDI Red alarm for Analog + + * Thu Aug 20 2009 Nenad Corbic - 3.5.6 =================================================================== - Update to T1 Yellow Alarm handling. In some cases Yellow alarm did not turn off poperly causing line to stay down an card startup. +- Update configuration utility + wancfg_fs updated for sangoma_prid configuration. Added wancfg_openzap + for OpenZap Configuration * Mon Aug 17 2009 Nenad Corbic - 3.5.5 diff --git a/Makefile b/Makefile index 949d9d3..3eecffc 100644 --- a/Makefile +++ b/Makefile @@ -71,6 +71,12 @@ else EXTRA_CFLAGS+= -DWANPIPE_64BIT_4G_DMA endif +ifndef 64BIT_2G + 64BIT_2G="Disabled" +else + EXTRA_CFLAGS+= -DWANPIPE_64BIT_2G_DMA +endif + #Local wanpipe includes WINCLUDE=patches/kdrivers/include @@ -152,6 +158,8 @@ all: cleanup_local _checkzap _checksrc all_bin_kmod all_util all_src: cleanup_local _checkzap _checksrc all_kmod all_util +all_src_ss7: cleanup_local _checkzap _checksrc all_kmod_ss7 all_util + dahdi: all_src zaptel: all_src @@ -159,6 +167,12 @@ zaptel: all_src openzap: all_src all_lib @touch .all_lib .openzap +freetdm: all_src all_lib + @touch .all_lib .openzap + +openzap_ss7: all_src_ss7 all_lib + @touch .all_lib + tdmapi: all_src all_lib @touch .all_lib @@ -171,6 +185,14 @@ cleanup_local: all_kmod: _checkzap _checksrc _cleanoldwanpipe _check_kver $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C $(KDIR) SUBDIRS=$(WAN_DIR) EXTRA_FLAGS="$(EXTRA_CFLAGS) $(shell cat ./patches/kfeatures)" ZAPDIR=$(ZAPDIR_PRIV) ZAPHDLC=$(ZAPHDLC_PRIV) HOMEDIR=$(PWD) modules +all_kmod_ss7: _checkzap _checksrc _cleanoldwanpipe _check_kver + @if [ -e $(PWD)/ss7_build_dir ]; then \ + rm -rf $(PWD)/ss7_build_dir; \ + fi + @mkdir -p $(PWD)/ss7_build_dir + ./Setup drivers --builddir=$(PWD)/ss7_build_dir --with-linux=$(KDIR) $(ZAP_OPTS) --usr-cc=$(CC) --protocol=AFT_TE1-XMTP2 --no-zaptel-compile --noautostart --arch=$(ARCH) --silent + @eval "./patches/copy_modules.sh $(PWD)/ss7_build_dir $(WAN_DIR)" + all_bin_kmod: _checkzap _checksrc _cleanoldwanpipe _check_kver @if [ -e $(PWD)/ast_build_dir ]; then \ rm -rf $(PWD)/ast_build_dir; \ @@ -186,8 +208,8 @@ clean: cleanup_local clean_util _cleanoldwanpipe $(MAKE) -C api SUBDIRS=$(WAN_DIR) clean @find patches/kdrivers -name '.*.cmd' | xargs rm -f @find . -name 'Module.symver*' | xargs rm -f - @if [ -e .openzap ]; then - rm -f .openzap + @if [ -e .openzap ]; then \ + rm -f .openzap; \ fi @@ -294,6 +316,11 @@ install_kmod: echo "install -m 644 -D $(WAN_DIR)/wanpipe_lip.${MODTYPE} $(INSTALLPREFIX)/$(KINSTDIR)/net/wanrouter/wanpipe_lip.${MODTYPE}"; \ install -m 644 -D $(WAN_DIR)/wanpipe_lip.${MODTYPE} $(INSTALLPREFIX)/$(KINSTDIR)/net/wanrouter/wanpipe_lip.${MODTYPE}; \ fi + @rm -f $(INSTALLPREFIX)/$(KINSTDIR)/drivers/net/wan/xmtp2km.${MODTYPE} + @if [ -f $(WAN_DIR)/xmtp2km.${MODTYPE} ]; then \ + echo "install -m 644 -D $(WAN_DIR)/xmtp2km.${MODTYPE} $(INSTALLPREFIX)/$(KINSTDIR)/drivers/net/wan/xmtp2km.${MODTYPE}"; \ + install -m 644 -D $(WAN_DIR)/xmtp2km.${MODTYPE} $(INSTALLPREFIX)/$(KINSTDIR)/drivers/net/wan/xmtp2km.${MODTYPE}; \ + fi @eval "./patches/rundepmod.sh" endif @@ -326,17 +353,25 @@ install_util: $(MAKE) -C util install SYSINC=$(PWD)/$(WINCLUDE) CC=$(CC) PREFIX=$(INSTALLPREFIX) install_smgbri: - $(MAKE) -C ssmg/sangoma_mgd.trunk/ install SYSINC=$(PWD)/$(WINCLUDE) CC=$(CC) PREFIX=$(INSTALLPREFIX) - install -D -m 755 ssmg/sangoma_bri/smg_ctrl $(INSTALLPREFIX)/usr/sbin/smg_ctrl - install -D -m 755 ssmg/sangoma_bri/sangoma_brid.$(ARCH) $(INSTALLPREFIX)/usr/sbin/sangoma_brid + $(MAKE) -C ssmg/sangoma_mgd.trunk/ install SYSINC=$(PWD)/$(WINCLUDE) CC=$(CC) BRI=YES PREFIX=$(INSTALLPREFIX) $(MAKE) -C ssmg/libsangoma.trunk/ install DESTDIR=$(INSTALLPREFIX) $(MAKE) -C ssmg/sangoma_mgd.trunk/lib/libteletone install DESTDIR=$(INSTALLPREFIX) -install_pri: +install_bri: + $(MAKE) -C ssmg/sangoma_bri/ install SYSINC=$(PWD)/$(WINCLUDE) CC=$(CC) DESTDIR=$(INSTALLPREFIX) + +install_smgpri: + $(MAKE) -C ssmg/sangoma_pri/ install SYSINC=$(PWD)/$(WINCLUDE) CC=$(CC) DESTDIR=$(INSTALLPREFIX) @if [ ! -e .openzap ]; then \ @echo "Installing Sangoma MGD"; \ $(MAKE) -C ssmg/sangoma_mgd.trunk/ install SYSINC=$(PWD)/$(WINCLUDE) CC=$(CC) PRI=YES DESTDIR=$(INSTALLPREFIX) ; \ fi +install_pri: + @eval "cd ssmg; ./get_sangoma_prid.sh; cd .." + $(MAKE) -C ssmg/sangoma_pri/ install SYSINC=$(PWD)/$(WINCLUDE) CC=$(CC) DESTDIR=$(INSTALLPREFIX) + +install_pri_update: + @eval "cd ssmg; ./get_sangoma_prid.sh --update; cd .." $(MAKE) -C ssmg/sangoma_pri/ install SYSINC=$(PWD)/$(WINCLUDE) CC=$(CC) DESTDIR=$(INSTALLPREFIX) #Install etc files diff --git a/Setup b/Setup index 9c2c7cc..68e753c 100755 --- a/Setup +++ b/Setup @@ -9,6 +9,7 @@ # as published by the Free Software Foundation; either version # 2 of the License, or (at your option) any later version. # ---------------------------------------------------------------------------- +# Oct 29, 2009 Yannick Lam Added --no_woomera option (will not install woomera) # Jul 29, 2009 Konrad Hammel Added --with-asterisk option # Jul 27, 2009 Konrad Hammel Update to not compile unneeded utilities in # TDM_VOICE mode. @@ -2552,6 +2553,9 @@ function build_kernel_module() rm -f $modname.ko rm -f $modname.mod.o + echo " make MODULE_NAME=$modname OBJS=\"$ofiles\" CC=$CC SUBDIRS=$PWD KBUILD_VERBOSE=$KBUILD_VERBOSE KDIR=$SOURCEDIR \ + EXTRA_CFLAGS=\"-D__LINUX__ $PROTOCOL_DEFINES $extra_flags \"" >> $CMP_BUILD + make MODULE_NAME=$modname OBJS="$ofiles" CC=$CC SUBDIRS=$PWD KBUILD_VERBOSE=$KBUILD_VERBOSE KDIR=$SOURCEDIR \ EXTRA_CFLAGS="-D__LINUX__ $PROTOCOL_DEFINES $extra_flags " >> $CMP_LOG 2>> $CMP_LOG # echo "ld $LD_ELF -r -o $modname.ko $modname.o $modname.mod.o" >> $CMP_BUILD @@ -3378,7 +3382,7 @@ ENDOFTEXT rm -rf $DRIVER_TMP_DIR fi - eval "find $PROD_HOME -name \"Module.symvers*\" | xargs \rm -f" + eval "find $PROD_HOME/ -name \"Module.symvers*\" | xargs \rm -f" \mkdir "$DRIVER_TMP_DIR" \mkdir "$DRIVER_TMP_DIR/mod" @@ -3934,7 +3938,7 @@ WANPIPE_OBJS= rm -f sdladrv_src.c ln -s sdladrv.c sdladrv_src.c - SDLADRV_OBJS="sdladrv_src sdladrv_fe sdladrv_utils " + SDLADRV_OBJS="sdladrv_src sdladrv_fe sdladrv_utils wanpipe_cdev_linux wanpipe_logger " if [ "$AFT_USB_PROT" = "YES" ]; then SDLADRV_OBJS=$SDLADRV_OBJS"sdladrv_usb " @@ -3947,7 +3951,7 @@ WANPIPE_OBJS= WANROUTER_OBJS="wanmain wanproc waniface " if [ $REL != "3.4" ]; then - WANROUTER_OBJS=$WANROUTER_OBJS" wandev wanpipe_cdev_linux" + WANROUTER_OBJS=$WANROUTER_OBJS" wandev " fi build_kernel_module wanrouter "$WANROUTER_OBJS" @@ -4786,92 +4790,6 @@ ENDOFTEXT return 0 } -function install_chan_woomera() -{ - AST_MODULES=chan_woomera.c - if [ "$AST_DFLT_INSTALL_DIR" = "" ]; then - ast_src_dir=/usr/src/asterisk - else - ast_src_dir=$AST_DFLT_INSTALL_DIR - fi - - cd $PROD_HOME/$SSMG_DIR - cd sangoma_mgd.trunk - - CUR_PWD=`pwd` - - if [ "$ast_src_dir" = "" ]; then - echo - echo "Please Specify Asterisk Source Directory" - echo "Default [/usr/src/asterisk] just press " - echo - echo -n "Asterisk Source: " - read ast_src_dir - - if [ "$ast_src_dir" = "" ]; then - ast_src_dir="/usr/src/asterisk" - fi - fi - - echo - echo - echo "Installing chan_woomera in: $ast_src_dir" - echo - - if [ ! -d $ast_src_dir ]; then - echo - echo "Directory $ast_src_dir not found!" - echo - return 1 - fi - - if [ ! -e $ast_src_dir/contrib/scripts/astxs ]; then - echo - echo "Invalid asterisk source in $ast_src_dir" - echo - return 1 - fi - - - eval "cp -f g711.h $ast_src_dir/" - eval "cp -f $AST_MODULES $ast_src_dir/" - cd $ast_src_dir - - for mod in $AST_MODULES - do - echo "Installing: $mod" - eval "perl ./contrib/scripts/astxs -install $mod > /dev/null" - if [ $? -ne 0 ]; then - echo - echo "Error: Failed to install $mod module!" - install_failures=$install_failures"$mod " - else - echo "Done" - fi - done - - cd $CUR_PWD - - if [ "$install_failures" = "" ]; then - echo - echo "Installation Complete" - echo - else - echo - echo "-------------------------------------" - echo - echo "There was an ERROR in asterisk modules installation" - echo "Following modules failed to install:" - echo -e "\t$install_failures" - echo - echo - - return 1 - fi - - return 0 -} - function install_ssmg_ss7() { @@ -4918,26 +4836,38 @@ function install_ssmg_ss7() pause } - function install_ssmg_pri() { if [ $SSMG_PRI != "YES" ]; then return 0 fi - - if [ !-e $PROD_HOME/$SSMG_DIR/sangoma_pri ]; then - echo "-------------------------------------" - echo "SMG PRID is not part of this release" - echo "Please contact Sangoma Support!" - echo "-------------------------------------" - exit 1; - fi - banner check_sctp_utility find_ast_dirs + cd $PROD_HOME/$SSMG_DIR + eval "./get_sangoma_prid.sh" + if [ $? -ne 0 ]; then + echo "Failed to obtain SMG PRI package" + exit 1 + fi + cd sangoma_pri + echo + echo "Installing SMG PRI Daemon..." + eval "make INSTALLPREFIX=$ROOT install > /dev/null " + + echo + echo "----------------------------------------" + echo "Sangoma PRI Installation Complete" + echo + echo " -> To configure for PRI run:" + echo " -> /usr/bin/wancfg_smg" + echo + echo "----------------------------------------" + echo + pause + eval "install_sangoma_mgd" if [ $? -ne 0 ]; then echo "-------------------------------------" @@ -4967,21 +4897,7 @@ function install_ssmg_pri() fi echo - cd $PROD_HOME/$SSMG_DIR - cd sangoma_pri - echo - echo "Installing SMG PRI Daemon..." - eval "make INSTALLPREFIX=$ROOT install > /dev/null " - - echo - echo "----------------------------------------" - echo "Sangoma PRI Installation Complete" - echo - echo " -> To configure for PRI run:" - echo " -> /usr/bin/wancfg_smg" - echo - echo "----------------------------------------" - echo + install_smg_samples_bri pause } @@ -4998,37 +4914,6 @@ function install_ssmg_bri() check_sctp_utility find_ast_dirs - - eval "install_sangoma_mgd" - if [ $? -ne 0 ]; then - echo "-------------------------------------" - echo "Error: SMG Libraries Failed to install" - echo "Please Contact Sangoma Support!" - echo "-------------------------------------" - exit 1; - fi - echo - - - - cd $PROD_HOME/$SSMG_DIR - cd sangoma_mgd.trunk - - echo "Installing SMG Daemon..." - echo - if [ -z $ROOT ]; then - eval "./install -bri -pbxdir $AST_SOURCE_DIR" - else - eval "./install -bri -rootdir $ROOT -pbxdir $AST_SOURCE_DIR" - fi - if [ $? -ne 0 ]; then - echo "-------------------------------------" - echo "Error: SMG Failed to install" - echo "Please Contact Sangoma Support!" - echo "-------------------------------------" - exit 1; - fi - echo cd $PROD_HOME/$SSMG_DIR cd sangoma_bri @@ -5044,7 +4929,47 @@ function install_ssmg_bri() echo " -> /usr/bin/wancfg_smg" echo echo "----------------------------------------" - echo + echo + + pause + + eval "install_sangoma_mgd" + if [ $? -ne 0 ]; then + echo "-------------------------------------" + echo "Error: SMG Libraries Failed to install" + echo "Please Contact Sangoma Support!" + echo "-------------------------------------" + exit 1; + fi + echo + + cd $PROD_HOME/$SSMG_DIR + cd sangoma_mgd.trunk + + echo "Installing SMG Daemon..." + echo + + if [ "$no_woomera_option" == "TRUE" ];then + smg_installation_type="no_woomera" + else + smg_installation_type="bri" + fi + + if [ -z $ROOT ]; then + eval "./install -$smg_installation_type -pbxdir $AST_SOURCE_DIR" + else + eval "./install -$smg_installation_type -rootdir $ROOT -pbxdir $AST_SOURCE_DIR" + fi + + if [ $? -ne 0 ]; then + echo "-------------------------------------" + echo "Error: SMG Failed to install" + echo "Please Contact Sangoma Support!" + echo "-------------------------------------" + exit 1; + fi + echo + install_smg_samples_bri pause } @@ -6383,13 +6308,18 @@ function find_ast_dirs () if [ "$astdirs" = "" ]; then astdirs=`find /usr/src -maxdepth 2 -name 'asterisk*' | xargs ` if [ -d "/usr/src/asterisk" ]; then - astdirs="/usr/src/asterisk "$astdirs - fi + astdirs_tmp="/usr/src/asterisk "$astdirs + astdirs=$astdirs_tmp + fi + if [ -d "$AST_DFLT_INCLUDE_DIR" ]; then + astdirs_tmp="$AST_DFLT_INCLUDE_DIR "$astdirs + astdirs=$astdirs_tmp + fi astdirs1=`find /usr/src -maxdepth 2 -name 'callweaver*' | xargs ` if [ -d "/usr/src/callweaver" ]; then - astdirs1="/usr/src/callweaver "$astdirs1 - fi + astdirs1="/usr/src/callweaver "$astdirs1 + fi astdirs="$astdirs $astdirs1" fi @@ -6407,18 +6337,19 @@ function find_ast_dirs () continue fi - if [ ! -f $dir/include/asterisk.h ] && [ ! -f $dir/include/callweaver.h ]; then - continue; - fi + if [ $dir != "$AST_DFLT_INCLUDE_DIR" ]; then + if [ ! -f $dir/include/asterisk.h ] && [ ! -f $dir/include/callweaver.h ]; then + continue; + fi - if [ "$dir" = "/usr/src/asterisk" ] && [ $cnt -ne 1 ]; then - continue; - fi - - if [ "$dir" = "/usr/src/callweaver" ] && [ $cnt -ne 1 ]; then - continue; - fi + if [ "$dir" = "/usr/src/asterisk" ] && [ $cnt -gt 2 ]; then + continue; + fi + if [ "$dir" = "/usr/src/callweaver" ] && [ $cnt -ne 1 ]; then + continue; + fi + fi astdir_array[$cnt]=$dir; echo "$cnt : $dir " @@ -6492,16 +6423,18 @@ function find_ast_dirs () fi AST_SOURCE_DIR=$AST_INSTALL_DIR - - if [ ! -f $AST_SOURCE_DIR/include/asterisk.h ] && [ ! -f $AST_SOURCE_DIR/include/callweaver.h ]; then - echo - echo "Error: asterisk.h/callweaver.h not found in $AST_SOURCE_DIR" - echo - echo - astdir_manual=0 - pause 1 - clear - find_ast_dirs "$astdirs" + + if [ $AST_SOURCE_DIR != "$AST_DFLT_INCLUDE_DIR" ]; then + if [ ! -f $AST_SOURCE_DIR/include/asterisk.h ] && [ ! -f $AST_SOURCE_DIR/include/callweaver.h ]; then + echo + echo "Error: asterisk.h/callweaver.h not found in $AST_SOURCE_DIR" + echo + echo + astdir_manual=0 + pause 1 + clear + find_ast_dirs "$astdirs" + fi fi if [ $find_ast_dir_quit -eq 1 ] ; then @@ -6575,6 +6508,13 @@ function enable_protocols () PROT_MATCH=YES fi + echo "$PROTOCOL" | grep "PRI" > /dev/null + if [ $? -eq 0 ]; then + echo "Enabling the AFT PRI SMG Support" + SSMG_PRI="YES" + PROT_MATCH=YES + fi + echo "$PROTOCOL" | grep "SS7" > /dev/null if [ $? -eq 0 ]; then echo "Enabling the AFT SS7 SMG Support" @@ -6821,16 +6761,21 @@ ENDOFTEXT if [ $? -eq 0 ]; then if [ $AFT_TE1_PROT != YES ]; then echo "Enabling the AFT TE1 Support" - PROTOCOL_DEFINES="$PROTOCOL_DEFINES -DCONFIG_PRODUCT_WANPIPE_AFT -DCONFIG_PRODUCT_WANPIPE_AFT_CORE -DCONFIG_PRODUCT_WANPIPE_AFT_TE1 -DCONFIG_PRODUCT_WANPIPE_AFT_56K -DCONFIG_PRODUCT_WANPIPE_AFT_RM -DCONFIG_PRODUCT_WANPIPE_CODEC_SLINEAR_LAW -DCONFIG_PRODUCT_WANPIPE_AFT_BRI -DCONFIG_PRODUCT_WANPIPE_AFT_SERIAL -DCONFIG_PRODUCT_WANPIPE_AFT_A600 " + PROTOCOL_DEFINES="$PROTOCOL_DEFINES -DCONFIG_PRODUCT_WANPIPE_AFT -DCONFIG_PRODUCT_WANPIPE_AFT_CORE -DCONFIG_PRODUCT_WANPIPE_AFT_TE1 -DCONFIG_PRODUCT_WANPIPE_AFT_56K -DCONFIG_PRODUCT_WANPIPE_AFT_RM -DCONFIG_PRODUCT_WANPIPE_CODEC_SLINEAR_LAW -DCONFIG_PRODUCT_WANPIPE_AFT_BRI -DCONFIG_PRODUCT_WANPIPE_AFT_SERIAL -DCONFIG_PRODUCT_WANPIPE_AFT_A600 -DCONFIG_PRODUCT_WANPIPE_AFT_B601 " if [ $REL != "3.4" ]; then - PROTOCOL_DEFINES="$PROTOCOL_DEFINES -DCONFIG_PRODUCT_WANPIPE_USB -DCONFIG_PRODUCT_WANPIPE_AFT_A700 " + PROTOCOL_DEFINES="$PROTOCOL_DEFINES -DCONFIG_PRODUCT_WANPIPE_AFT_A700 " fi fi AFT_TE1_PROT=YES AFT_BRI_PROT=YES AFT_SERIAL_PROT=YES if [ $REL != "3.4" ]; then - AFT_USB_PROT=YES + if [ $AFT_USB_PROT = "NO" ]; then + PROTOCOL_DEFINES="$PROTOCOL_DEFINES -DCONFIG_PRODUCT_WANPIPE_USB " + AFT_USB_PROT=YES + else + echo "USB Support Disabled by user!" + fi fi PROT_MATCH=YES fi @@ -7174,6 +7119,8 @@ AFT_TE3_PROT=NO SSMG_SS7=NO SSMG_PRI=NO SSMG_BRI=NO +no_woomera_option="FALSE" +smg_installation_type="bri" WAN_FRM_UPDATE_DRIVER=YES @@ -7375,6 +7322,8 @@ or Drastically improves performance in TDM Voice See the wiki for more information + --no_woomera : Option to compile the driver without installing + chan_woomera. --protocol : Option to compile in extra protocols that are not installed by default. @@ -7439,7 +7388,7 @@ KERNEL_UNAME=`uname -r` PKG_NAME=wanpipe DISTR_NAME="WANPIPE" PROD=wanrouter -PROD_VER=3.5.6 +PROD_VER=3.5.11 PROD_HOME=`pwd` META_CONF=$PROD_HOME/$PROD.rc WAN_INTR_DIR=$PROD_HOME/interfaces @@ -7534,7 +7483,8 @@ ANNEXG_LOAD=NO SCTP_LOAD=NO ZAPTEL_DFLT_INSTALL_DIR="/usr/src/zaptel" -AST_DFLT_INSTALL_DIR="/usr/src/asterisk" +AST_DFLT_INCLUDE_DIR="/usr/include/asterisk" +AST_DFLT_INSTALL_DIR=$AST_DFLT_INCLUDE_DIR AST_SOURCE_DIR=$AST_DFLT_INSTALL_DIR AST_PATH_OP="NO" ZAPTEL_SOURCE_DIR=$ZAPTEL_DFLT_INSTALL_DIR @@ -7815,6 +7765,10 @@ if [ "$PKG_NAME" != "wanpipe-lite" ]; then PROTOCOL_DEFINES="$PROTOCOL_DEFINES -DWANPIPE_64BIT_4G_DMA " ;; + --64bit_2G*) + PROTOCOL_DEFINES="$PROTOCOL_DEFINES -DWANPIPE_64BIT_2G_DMA " + ;; + --hwec_noise_reduction*) PROTOCOL_DEFINES="$PROTOCOL_DEFINES -DWANEC_ENABLE_NOISE_REDUCTION " ;; @@ -7883,29 +7837,36 @@ if [ "$PKG_NAME" != "wanpipe-lite" ]; then ;; - --with-asterisk*) - AST_DFLT_INSTALL_DIR=`echo $arg | cut -d'=' -f2`; - if [ "$AST_DFLT_INSTALL_DIR" = "" ]; then - echo "Error invalid --with-asterisk option"; - exit 1; - fi - if [ ! -d "$AST_DFLT_INSTALL_DIR" ]; then - echo "Error: Asterisk source directory not found: $AST_DFLT_INSTALL_DIR"; - exit 1; - fi - if [ ! -f "$AST_DFLT_INSTALL_DIR/Makefile" ]; then - echo "Error: Invalid Asterisk source found in $AST_DFLT_INSTALL_DIR"; - exit 1; - fi - AST_PATH_OP="YES"; - echo "Asterisk path defined as: $AST_DFLT_INSTALL_DIR" - - ;; + --with-asterisk*) + AST_DFLT_INSTALL_DIR=`echo $arg | cut -d'=' -f2`; + if [ "$AST_DFLT_INSTALL_DIR" = "" ]; then + echo "Error invalid --with-asterisk option"; + exit 1; + fi + if [ ! -d "$AST_DFLT_INSTALL_DIR" ]; then + echo "Error: Asterisk source directory not found: $AST_DFLT_INSTALL_DIR"; + exit 1; + fi + if [ $AST_DFLT_INSTALL_DIR != "$AST_DFLT_INCLUDE_DIR" ]; then + if [ ! -f "$AST_DFLT_INSTALL_DIR/Makefile" ]; then + echo "Error: Invalid Asterisk source found in $AST_DFLT_INSTALL_DIR"; + exit 1; + fi + fi + AST_PATH_OP="YES"; + echo "Asterisk path defined as: $AST_DFLT_INSTALL_DIR" + AST_SOURCE_DIR=$AST_DFLT_INSTALL_DIR + ;; --no-zaptel-compile*) ZAPTEL_COMPILE_DISABLE="YES"; ;; + --no-usb*) + echo "USB Support Disabled" + AFT_USB_PROT="DISABLE" + ;; + --builddir*) ROOT=`echo $arg | cut -d'=' -f2`; if [ "$ROOT" = "" ]; then @@ -7979,7 +7940,11 @@ if [ "$PKG_NAME" != "wanpipe-lite" ]; then --gz-modules) GZ_MODULES="YES"; ;; - + + --no_woomera) + no_woomera_option="TRUE"; + ;; + --zap-chunk* | --dahdi-chunk*) ZAP_CHUNK=`echo $arg | cut -d'=' -f2`; diff --git a/api/legacy/aft/aft_api b/api/legacy/aft/aft_api index 7f435c67dea2c9dc9914ddbdbd4d72eb4b40fe36..22b4313dc6a6498a04c3503ef43084d8d1d5afb4 100755 GIT binary patch delta 210 zcmZph&)7Dfal;Rb$r~(L#VriX4NMd?f>KiyJQ7P3O!N%(jC4&*CKp(W4+Xi+8dh0PIydGXMYp diff --git a/api/lib/hdlc/wanpipe_hdlc.h b/api/lib/hdlc/wanpipe_hdlc.h index f174778..b497e2f 100644 --- a/api/lib/hdlc/wanpipe_hdlc.h +++ b/api/lib/hdlc/wanpipe_hdlc.h @@ -2,13 +2,25 @@ wanpipe_hdlc.h: WANPIPE HDLC Library */ +#ifndef _WANPIPE_HDLC_H +#define _WANPIPE_HDLC_H + #include #include #include -#include -#include + +#if defined(__WINDOWS__) +# define wan_inline __inline +# ifdef __cplusplus + extern "C" { /* for C++ users */ +# endif +#else +# include +# include +# include +# define wan_inline inline +#endif #include - /*=================================================================== * @@ -126,6 +138,9 @@ typedef struct wanpipe_hdlc_engine int (*hdlc_data) (struct wanpipe_hdlc_engine *hdlc_eng, void *data, int len); + void *context; /* user can store a pointer here, so it can be used when + * hdlc_data() callback runs */ + }wanpipe_hdlc_engine_t; typedef struct hdlc_list @@ -147,7 +162,7 @@ typedef struct hdlc_list #define DEBUG_TX #endif -static inline +static wan_inline void init_hdlc_decoder(wanpipe_hdlc_decoder_t *hdlc_decoder) { hdlc_decoder->hdlc_flag=0; @@ -164,7 +179,7 @@ void init_hdlc_decoder(wanpipe_hdlc_decoder_t *hdlc_decoder) hdlc_decoder->crc_prv=0; } -static inline +static wan_inline void init_hdlc_encoder(wanpipe_hdlc_encoder_t *chan) { chan->tx_crc=-1; @@ -188,3 +203,11 @@ extern int wanpipe_get_tx_hdlc_errors (wanpipe_hdlc_engine_t *hdlc_eng); extern int wanpipe_get_rx_hdlc_packets (wanpipe_hdlc_engine_t *hdlc_eng); extern int wanpipe_get_tx_hdlc_packets (wanpipe_hdlc_engine_t *hdlc_eng); extern int wanpipe_hdlc_dump_ring(wanpipe_hdlc_engine_t *hdlc_eng); + +#if defined(__WINDOWS__) +#ifdef __cplusplus +} /* for C++ users */ +#endif /* __cplusplus */ +#endif + +#endif /* _WANPIPE_HDLC_H */ diff --git a/api/libsangoma/.svn/all-wcprops b/api/libsangoma/.svn/all-wcprops index ca2cd1f..53fbcb1 100644 --- a/api/libsangoma/.svn/all-wcprops +++ b/api/libsangoma/.svn/all-wcprops @@ -1,7 +1,7 @@ K 25 svn:wc:ra_dav:version-url V 34 -/svn/libsangoma/!svn/ver/222/trunk +/svn/libsangoma/!svn/ver/273/trunk END init-automake.sh K 25 @@ -13,7 +13,7 @@ configure K 25 svn:wc:ra_dav:version-url V 44 -/svn/libsangoma/!svn/ver/222/trunk/configure +/svn/libsangoma/!svn/ver/224/trunk/configure END Makefile.in K 25 @@ -25,7 +25,7 @@ sources K 25 svn:wc:ra_dav:version-url V 42 -/svn/libsangoma/!svn/ver/221/trunk/sources +/svn/libsangoma/!svn/ver/269/trunk/sources END AUTHORS K 25 @@ -57,18 +57,6 @@ svn:wc:ra_dav:version-url V 54 /svn/libsangoma/!svn/ver/222/trunk/libhpsangoma_priv.h END -libsangoma.vcproj.DAVIDRNEW.User.user -K 25 -svn:wc:ra_dav:version-url -V 72 -/svn/libsangoma/!svn/ver/136/trunk/libsangoma.vcproj.DAVIDRNEW.User.user -END -config.guess -K 25 -svn:wc:ra_dav:version-url -V 47 -/svn/libsangoma/!svn/ver/202/trunk/config.guess -END libsangoma-doxygen.config K 25 svn:wc:ra_dav:version-url @@ -79,7 +67,7 @@ libsangoma.c K 25 svn:wc:ra_dav:version-url V 47 -/svn/libsangoma/!svn/ver/222/trunk/libsangoma.c +/svn/libsangoma/!svn/ver/272/trunk/libsangoma.c END compile.bat K 25 @@ -87,29 +75,23 @@ svn:wc:ra_dav:version-url V 45 /svn/libsangoma/!svn/ver/32/trunk/compile.bat END -ltmain.sh -K 25 -svn:wc:ra_dav:version-url -V 44 -/svn/libsangoma/!svn/ver/222/trunk/ltmain.sh -END -config.sub -K 25 -svn:wc:ra_dav:version-url -V 45 -/svn/libsangoma/!svn/ver/202/trunk/config.sub -END cleanup.sh K 25 svn:wc:ra_dav:version-url V 45 -/svn/libsangoma/!svn/ver/222/trunk/cleanup.sh +/svn/libsangoma/!svn/ver/244/trunk/cleanup.sh END libsangoma.h K 25 svn:wc:ra_dav:version-url V 47 -/svn/libsangoma/!svn/ver/222/trunk/libsangoma.h +/svn/libsangoma/!svn/ver/273/trunk/libsangoma.h +END +libsangoma_hwec.c +K 25 +svn:wc:ra_dav:version-url +V 52 +/svn/libsangoma/!svn/ver/271/trunk/libsangoma_hwec.c END g711.h K 25 @@ -123,18 +105,18 @@ svn:wc:ra_dav:version-url V 40 /svn/libsangoma/!svn/ver/1/trunk/INSTALL END -COPYING -K 25 -svn:wc:ra_dav:version-url -V 40 -/svn/libsangoma/!svn/ver/1/trunk/COPYING -END build.sh K 25 svn:wc:ra_dav:version-url V 43 /svn/libsangoma/!svn/ver/222/trunk/build.sh END +COPYING +K 25 +svn:wc:ra_dav:version-url +V 40 +/svn/libsangoma/!svn/ver/1/trunk/COPYING +END NEWS K 25 svn:wc:ra_dav:version-url @@ -153,29 +135,29 @@ svn:wc:ra_dav:version-url V 48 /svn/libsangoma/!svn/ver/146/trunk/libsangoma.rc END -libsangoma.so.conf -K 25 -svn:wc:ra_dav:version-url -V 51 -/svn/libsangoma/!svn/ver/1/trunk/libsangoma.so.conf -END sangoma_pri.c K 25 svn:wc:ra_dav:version-url V 48 /svn/libsangoma/!svn/ver/222/trunk/sangoma_pri.c END +libsangoma.so.conf +K 25 +svn:wc:ra_dav:version-url +V 51 +/svn/libsangoma/!svn/ver/1/trunk/libsangoma.so.conf +END libhpsangoma-doxygen.config K 25 svn:wc:ra_dav:version-url V 61 /svn/libsangoma/!svn/ver/50/trunk/libhpsangoma-doxygen.config END -libhpsangoma.c +sangoma_pri.h K 25 svn:wc:ra_dav:version-url -V 49 -/svn/libsangoma/!svn/ver/222/trunk/libhpsangoma.c +V 48 +/svn/libsangoma/!svn/ver/222/trunk/sangoma_pri.h END version K 25 @@ -183,17 +165,17 @@ svn:wc:ra_dav:version-url V 40 /svn/libsangoma/!svn/ver/1/trunk/version END -sangoma_pri.h +libhpsangoma.c K 25 svn:wc:ra_dav:version-url -V 48 -/svn/libsangoma/!svn/ver/222/trunk/sangoma_pri.h +V 49 +/svn/libsangoma/!svn/ver/222/trunk/libhpsangoma.c END configure.in K 25 svn:wc:ra_dav:version-url V 47 -/svn/libsangoma/!svn/ver/216/trunk/configure.in +/svn/libsangoma/!svn/ver/224/trunk/configure.in END ChangeLog K 25 @@ -219,17 +201,11 @@ svn:wc:ra_dav:version-url V 46 /svn/libsangoma/!svn/ver/222/trunk/config.h.in END -Makefile.Windows -K 25 -svn:wc:ra_dav:version-url -V 50 -/svn/libsangoma/!svn/ver/27/trunk/Makefile.Windows -END libsangoma.sln K 25 svn:wc:ra_dav:version-url V 49 -/svn/libsangoma/!svn/ver/136/trunk/libsangoma.sln +/svn/libsangoma/!svn/ver/235/trunk/libsangoma.sln END Makefile.am K 25 @@ -247,7 +223,7 @@ libsangoma.def K 25 svn:wc:ra_dav:version-url V 49 -/svn/libsangoma/!svn/ver/221/trunk/libsangoma.def +/svn/libsangoma/!svn/ver/269/trunk/libsangoma.def END libsangoma-pvt.h K 25 @@ -265,5 +241,5 @@ libsangoma.vcproj K 25 svn:wc:ra_dav:version-url V 52 -/svn/libsangoma/!svn/ver/219/trunk/libsangoma.vcproj +/svn/libsangoma/!svn/ver/269/trunk/libsangoma.vcproj END diff --git a/api/libsangoma/.svn/dir-prop-base b/api/libsangoma/.svn/dir-prop-base index 2a57b29..12b41d1 100644 --- a/api/libsangoma/.svn/dir-prop-base +++ b/api/libsangoma/.svn/dir-prop-base @@ -1,6 +1,7 @@ K 10 svn:ignore -V 15 +V 32 +Makefile.Windows libsangoma.suo END diff --git a/api/libsangoma/.svn/entries b/api/libsangoma/.svn/entries index 08c2f9f..9040225 100644 --- a/api/libsangoma/.svn/entries +++ b/api/libsangoma/.svn/entries @@ -1,14 +1,14 @@ 8 dir -222 +273 https://www.sangomapbx.com/svn/libsangoma/trunk https://www.sangomapbx.com/svn/libsangoma -2009-08-12T18:36:20.606028Z -222 +2010-04-09T17:11:09.447360Z +273 ncorbic has-props @@ -32,7 +32,7 @@ file -2009-08-12T17:56:21.000000Z +2009-08-25T20:44:41.000000Z 3accc8de43673675a4464508f1b14a6a 2009-08-12T18:36:20.606028Z 222 @@ -45,10 +45,10 @@ file -2009-08-12T17:49:44.000000Z -60fce03cb57361b8798108c17785bfa5 -2009-08-12T18:36:20.606028Z -222 +2010-04-09T15:03:43.000000Z +ca4757d166b30d6f13233e912bce42d3 +2009-10-13T17:16:38.441677Z +224 ncorbic has-props @@ -58,7 +58,7 @@ file -2009-08-12T17:49:43.000000Z +2010-04-09T15:03:42.000000Z 782866a28766db490d5e05eb04cb657f 2009-08-12T18:36:20.606028Z 222 @@ -73,10 +73,10 @@ file -2009-08-12T17:51:19.000000Z -e7f606a94d75804b47ee5a35165c6f90 -2009-07-31T21:19:58.950307Z -221 +2010-03-23T16:14:34.000000Z +261dbd98b6a866003ce00fddeb1867cf +2010-03-15T20:12:36.300714Z +269 davidr AUTHORS @@ -85,7 +85,7 @@ file -2009-06-26T20:41:46.000000Z +2009-08-25T20:44:41.000000Z d41d8cd98f00b204e9800998ecf8427e 2008-02-28T18:51:53.196120Z 1 @@ -97,7 +97,7 @@ file -2009-08-12T17:56:21.000000Z +2009-08-25T20:44:41.000000Z 6311c0e89812fa88c9a1f2ef7784a3f3 2009-08-12T18:36:20.606028Z 222 @@ -110,7 +110,7 @@ file -2009-06-26T20:41:46.000000Z +2009-08-25T20:44:41.000000Z e181e2c8720c60522c4c4c981108e367 2008-02-28T18:51:53.196120Z 1 @@ -123,7 +123,7 @@ file -2009-06-26T20:41:46.000000Z +2009-08-25T20:44:41.000000Z a7ecc032b527a0d578545f19d3418073 2008-02-28T18:51:53.196120Z 1 @@ -139,109 +139,60 @@ file -2009-08-12T17:56:21.000000Z +2009-08-25T20:44:41.000000Z 53f977038b3fb8b3a679e9fbd43e620e 2009-08-12T18:36:20.606028Z 222 ncorbic has-props -libsangoma.vcproj.DAVIDRNEW.User.user -file - - - - -2009-06-26T20:41:46.000000Z -0ae611e17b3f6524851c84ea66d4a7f3 -2009-04-02T17:14:58.164005Z -136 -davidr - -config.guess -file - - - - -2009-08-17T21:21:49.000000Z -7fc030a85a8f3d4864a65db5fb70c794 -2009-06-26T20:12:34.655724Z -202 -moises - libsangoma-doxygen.config file -2009-06-26T20:41:46.000000Z +2009-08-25T20:44:41.000000Z fd2f056fa00d98b918d141f218a000f8 2009-04-01T21:38:17.305658Z 135 ncorbic -libsangoma.c -file - - - - -2009-08-12T17:56:21.000000Z -68d0365da6648ad9460c89f5845269c3 -2009-08-12T18:36:20.606028Z -222 -ncorbic -has-props - compile.bat file -2009-06-26T20:41:46.000000Z +2009-08-25T20:44:41.000000Z 5b82f62f87e4700af9b7e48c658b2cfc 2008-11-28T23:21:04.815499Z 32 davidr -ltmain.sh +libsangoma.c file -2009-08-17T21:21:49.000000Z -ce785af2c9c33dad261e255cd91a9a89 -2009-08-12T18:36:20.606028Z -222 -ncorbic +2010-04-09T15:03:14.000000Z +a0ef759d6887cf41a79e164cde54b98c +2010-04-01T17:47:10.550488Z +272 +davidr has-props -config.sub -file - - - - -2009-08-17T21:21:49.000000Z -a1ad4822ccc53519fe519a9d353407d0 -2009-06-26T20:12:34.655724Z -202 -moises - cleanup.sh file -2009-08-12T17:56:21.000000Z -54016e14ca0954d3a7a04f3301381467 -2009-08-12T18:36:20.606028Z -222 +2010-01-11T23:26:54.000000Z +692904ce7758fc035273cbe52fb10127 +2010-01-12T01:27:55.842408Z +244 ncorbic has-props @@ -251,20 +202,32 @@ file -2009-08-12T17:56:21.000000Z -e988bd9c20a491c7ca1c643c9ff5906a -2009-08-12T18:36:20.606028Z -222 +2010-04-09T15:03:14.000000Z +d3f29adcf654b49568d4759e8dcb4c22 +2010-04-09T17:11:09.447360Z +273 ncorbic has-props +libsangoma_hwec.c +file + + + + +2010-03-23T16:14:34.000000Z +912e0fe804db3423d7b7ddc34fedd8e4 +2010-03-17T22:36:18.066541Z +271 +davidr + g711.h file -2009-08-12T17:56:21.000000Z +2009-08-25T20:44:41.000000Z 0f725f95ced42af15dcaef21f3a1722b 2009-08-12T18:36:20.606028Z 222 @@ -277,44 +240,44 @@ file -2009-06-26T20:41:46.000000Z +2009-08-25T20:44:41.000000Z 40539bd3eff06e4b82f380103145415b 2008-02-28T18:51:53.196120Z 1 root +COPYING +file + + + + +2009-08-25T20:44:41.000000Z +d41d8cd98f00b204e9800998ecf8427e +2008-02-28T18:51:53.196120Z +1 +root + build.sh file -2009-08-12T17:56:21.000000Z +2009-08-25T20:44:41.000000Z f9109b55d1a9940209c4430121564f71 2009-08-12T18:36:20.606028Z 222 ncorbic has-props -COPYING -file - - - - -2009-06-26T20:41:46.000000Z -d41d8cd98f00b204e9800998ecf8427e -2008-02-28T18:51:53.196120Z -1 -root - NEWS file -2009-06-26T20:41:46.000000Z +2009-08-25T20:44:41.000000Z d41d8cd98f00b204e9800998ecf8427e 2008-02-28T18:51:53.196120Z 1 @@ -326,7 +289,7 @@ file -2009-06-26T20:41:46.000000Z +2009-08-25T20:44:41.000000Z 9fb7603b06c96163344636bf36410d64 2009-06-26T19:39:04.828119Z 199 @@ -338,59 +301,59 @@ file -2009-06-26T20:41:46.000000Z +2009-08-25T20:44:41.000000Z 5f46b82087b742e186dea4a23a18fc88 2009-04-22T20:41:57.677943Z 146 davidr -sangoma_pri.c -file - - - - -2009-08-12T17:56:21.000000Z -59210eaa3523ca8fdc605f5651fe2533 -2009-08-12T18:36:20.606028Z -222 -ncorbic -has-props - libsangoma.so.conf file -2009-06-26T20:41:46.000000Z +2009-08-25T20:44:41.000000Z bcdcb23c5d5fb460cee2ce315ef7bd32 2008-02-28T18:51:53.196120Z 1 root +sangoma_pri.c +file + + + + +2009-08-25T20:44:41.000000Z +59210eaa3523ca8fdc605f5651fe2533 +2009-08-12T18:36:20.606028Z +222 +ncorbic +has-props + +sample_c +dir + libhpsangoma-doxygen.config file -2009-06-26T20:41:46.000000Z +2009-08-25T20:44:41.000000Z 85429a0c83986ff69262848d5a5d453f 2008-12-10T01:33:45.806759Z 50 ncorbic -sample_c -dir - version file -2009-06-26T20:41:46.000000Z +2009-08-25T20:44:41.000000Z 35f672d1fb01b542f667952c9dbb26fe 2008-02-28T18:51:53.196120Z 1 @@ -402,7 +365,7 @@ file -2009-08-12T17:56:21.000000Z +2009-08-25T20:44:41.000000Z 7e23f30d22f5d2542866f5b9a81bcc80 2009-08-12T18:36:20.606028Z 222 @@ -415,7 +378,7 @@ file -2009-08-12T17:56:21.000000Z +2009-08-25T20:44:41.000000Z f487a2c347c2ff3e6633ea7022b649e3 2009-08-12T18:36:20.606028Z 222 @@ -428,10 +391,10 @@ file -2009-07-09T21:18:35.000000Z -ab95388591bf0b4bd335350fcfa875d5 -2009-07-09T21:42:07.606652Z -216 +2009-10-13T16:07:54.000000Z +a731eb1641e7b86d5c8e2ab15cb5e49c +2009-10-13T17:16:38.441677Z +224 ncorbic ChangeLog @@ -440,7 +403,7 @@ file -2009-06-26T20:41:46.000000Z +2009-08-25T20:44:41.000000Z d41d8cd98f00b204e9800998ecf8427e 2008-02-28T18:51:53.196120Z 1 @@ -452,7 +415,7 @@ file -2009-08-12T17:56:21.000000Z +2009-08-25T20:44:41.000000Z 9e95e25bbf705718d18c5c00cc7c8efe 2009-08-12T18:36:20.606028Z 222 @@ -465,7 +428,7 @@ file -2009-06-26T20:41:46.000000Z +2009-08-25T20:44:41.000000Z e8a0abce86211364eb60bbe6e07d4b05 2008-02-28T18:51:53.196120Z 1 @@ -477,34 +440,22 @@ file -2009-08-12T17:49:49.000000Z +2010-04-09T15:03:48.000000Z 1a4d975497bfcde75e3cbf4a54f1d72e 2009-08-12T18:36:20.606028Z 222 ncorbic -Makefile.Windows -file - - - - -2009-06-26T20:41:46.000000Z -cebc214b51868b9cdca9356c0d87d074 -2008-11-27T23:16:36.297108Z -27 -ncorbic - libsangoma.sln file -2009-06-26T20:41:46.000000Z -66e0fdbc15bba74dca556e72235e09a9 -2009-04-02T17:14:58.164005Z -136 +2009-12-04T21:21:35.000000Z +fe647b1212e15d68672bbc21737980b8 +2009-12-01T20:38:57.462038Z +235 davidr missing @@ -513,7 +464,7 @@ file -2009-06-26T20:41:46.000000Z +2009-08-25T20:44:41.000000Z fd5dd60aa8cefab9462677280ea74a61 2008-02-28T18:51:53.196120Z 1 @@ -526,7 +477,7 @@ file -2009-07-02T19:33:08.000000Z +2009-08-25T20:44:41.000000Z e91d44c5f7c7b260b98044016baacad9 2009-06-26T19:19:31.981035Z 197 @@ -538,10 +489,10 @@ file -2009-08-12T17:51:19.000000Z -924e6bfc7feda57ecd2128c06fa93381 -2009-07-31T21:19:58.950307Z -221 +2010-03-23T16:14:34.000000Z +39d2237e03532fcd8cdd7d7e1006fc07 +2010-03-15T20:12:36.300714Z +269 davidr libsangoma-pvt.h @@ -550,7 +501,7 @@ file -2009-08-12T17:56:21.000000Z +2009-08-25T20:44:41.000000Z f804894d319dde10f91daf4542b9003b 2009-08-12T18:36:20.606028Z 222 @@ -566,7 +517,7 @@ file -2009-06-26T20:41:46.000000Z +2009-08-25T20:44:41.000000Z 92067666ddce97609bd0ec67d36460cd 2008-02-28T18:51:53.196120Z 1 @@ -579,9 +530,9 @@ file -2009-08-12T17:51:19.000000Z -bdd1f5992f4ca0ea38ee8211a9da93d3 -2009-07-23T18:21:50.864680Z -219 +2010-03-23T16:14:34.000000Z +1758fcb8a7c95432556823b5cb0355f1 +2010-03-15T20:12:36.300714Z +269 davidr diff --git a/api/libsangoma/.svn/prop-base/ltmain.sh.svn-base b/api/libsangoma/.svn/prop-base/ltmain.sh.svn-base deleted file mode 100644 index abd5821..0000000 --- a/api/libsangoma/.svn/prop-base/ltmain.sh.svn-base +++ /dev/null @@ -1,5 +0,0 @@ -K 13 -svn:eol-style -V 2 -LF -END diff --git a/api/libsangoma/.svn/text-base/Makefile.Windows.svn-base b/api/libsangoma/.svn/text-base/Makefile.Windows.svn-base deleted file mode 100644 index 66f1c8e..0000000 --- a/api/libsangoma/.svn/text-base/Makefile.Windows.svn-base +++ /dev/null @@ -1,8 +0,0 @@ -# -# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source -# file to this component. This file merely indirects to the real make file -# that is shared by all the driver components of the Windows NT DDK -# - -!INCLUDE $(NTMAKEENV)\makefile.def - diff --git a/api/libsangoma/.svn/text-base/cleanup.sh.svn-base b/api/libsangoma/.svn/text-base/cleanup.sh.svn-base index 53556c3..1c8f41c 100644 --- a/api/libsangoma/.svn/text-base/cleanup.sh.svn-base +++ b/api/libsangoma/.svn/text-base/cleanup.sh.svn-base @@ -1,4 +1,4 @@ #!/bin/sh -svn status | xargs rm -rf +svn status | grep -v "libsangoma.*" | xargs rm -rf rm -rf sample_c/regression diff --git a/api/libsangoma/.svn/text-base/config.guess.svn-base b/api/libsangoma/.svn/text-base/config.guess.svn-base deleted file mode 100644 index 2fc3acc..0000000 --- a/api/libsangoma/.svn/text-base/config.guess.svn-base +++ /dev/null @@ -1,1411 +0,0 @@ -#! /bin/sh -# Attempt to guess a canonical system name. -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003 Free Software Foundation, Inc. - -timestamp='2003-06-17' - -# This file 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. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -# Originally written by Per Bothner . -# Please send patches to . Submit a context -# diff and a properly formatted ChangeLog entry. -# -# This script attempts to guess a canonical system name similar to -# config.sub. If it succeeds, it prints the system name on stdout, and -# exits with 0. Otherwise, it exits with 1. -# -# The plan is that this can be called by configure scripts if you -# don't specify an explicit build system type. - -me=`echo "$0" | sed -e 's,.*/,,'` - -usage="\ -Usage: $0 [OPTION] - -Output the configuration name of the system \`$me' is run on. - -Operation modes: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.guess ($timestamp) - -Originally written by Per Bothner. -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 -Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit 0 ;; - --version | -v ) - echo "$version" ; exit 0 ;; - --help | --h* | -h ) - echo "$usage"; exit 0 ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" >&2 - exit 1 ;; - * ) - break ;; - esac -done - -if test $# != 0; then - echo "$me: too many arguments$help" >&2 - exit 1 -fi - -trap 'exit 1' 1 2 15 - -# CC_FOR_BUILD -- compiler used by this script. Note that the use of a -# compiler to aid in system detection is discouraged as it requires -# temporary files to be created and, as you can see below, it is a -# headache to deal with in a portable fashion. - -# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still -# use `HOST_CC' if defined, but it is deprecated. - -# Portable tmp directory creation inspired by the Autoconf team. - -set_cc_for_build=' -trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; -trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; -: ${TMPDIR=/tmp} ; - { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || - { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || - { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || - { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; -dummy=$tmp/dummy ; -tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; -case $CC_FOR_BUILD,$HOST_CC,$CC in - ,,) echo "int x;" > $dummy.c ; - for c in cc gcc c89 c99 ; do - if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then - CC_FOR_BUILD="$c"; break ; - fi ; - done ; - if test x"$CC_FOR_BUILD" = x ; then - CC_FOR_BUILD=no_compiler_found ; - fi - ;; - ,,*) CC_FOR_BUILD=$CC ;; - ,*,*) CC_FOR_BUILD=$HOST_CC ;; -esac ;' - -# This is needed to find uname on a Pyramid OSx when run in the BSD universe. -# (ghazi@noc.rutgers.edu 1994-08-24) -if (test -f /.attbin/uname) >/dev/null 2>&1 ; then - PATH=$PATH:/.attbin ; export PATH -fi - -UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown -UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown -UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown -UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown - -## for Red Hat Linux -if test -f /etc/redhat-release ; then - VENDOR=redhat ; -else - VENDOR= ; -fi - -# Note: order is significant - the case branches are not exclusive. - -case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in - *:NetBSD:*:*) - # NetBSD (nbsd) targets should (where applicable) match one or - # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, - # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently - # switched to ELF, *-*-netbsd* would select the old - # object file format. This provides both forward - # compatibility and a consistent mechanism for selecting the - # object file format. - # - # Note: NetBSD doesn't particularly care about the vendor - # portion of the name. We always set it to "unknown". - sysctl="sysctl -n hw.machine_arch" - UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ - /usr/sbin/$sysctl 2>/dev/null || echo unknown)` - case "${UNAME_MACHINE_ARCH}" in - armeb) machine=armeb-unknown ;; - arm*) machine=arm-unknown ;; - sh3el) machine=shl-unknown ;; - sh3eb) machine=sh-unknown ;; - *) machine=${UNAME_MACHINE_ARCH}-unknown ;; - esac - # The Operating System including object format, if it has switched - # to ELF recently, or will in the future. - case "${UNAME_MACHINE_ARCH}" in - arm*|i386|m68k|ns32k|sh3*|sparc|vax) - eval $set_cc_for_build - if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep __ELF__ >/dev/null - then - # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). - # Return netbsd for either. FIX? - os=netbsd - else - os=netbsdelf - fi - ;; - *) - os=netbsd - ;; - esac - # The OS release - # Debian GNU/NetBSD machines have a different userland, and - # thus, need a distinct triplet. However, they do not need - # kernel version information, so it can be replaced with a - # suitable tag, in the style of linux-gnu. - case "${UNAME_VERSION}" in - Debian*) - release='-gnu' - ;; - *) - release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` - ;; - esac - # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: - # contains redundant information, the shorter form: - # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "${machine}-${os}${release}" - exit 0 ;; - amiga:OpenBSD:*:*) - echo m68k-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - arc:OpenBSD:*:*) - echo mipsel-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - hp300:OpenBSD:*:*) - echo m68k-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - mac68k:OpenBSD:*:*) - echo m68k-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - macppc:OpenBSD:*:*) - echo powerpc-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - mvme68k:OpenBSD:*:*) - echo m68k-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - mvme88k:OpenBSD:*:*) - echo m88k-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - mvmeppc:OpenBSD:*:*) - echo powerpc-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - pmax:OpenBSD:*:*) - echo mipsel-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - sgi:OpenBSD:*:*) - echo mipseb-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - sun3:OpenBSD:*:*) - echo m68k-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - wgrisc:OpenBSD:*:*) - echo mipsel-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - *:OpenBSD:*:*) - echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE} - exit 0 ;; - alpha:OSF1:*:*) - if test $UNAME_RELEASE = "V4.0"; then - UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` - fi - # According to Compaq, /usr/sbin/psrinfo has been available on - # OSF/1 and Tru64 systems produced since 1995. I hope that - # covers most systems running today. This code pipes the CPU - # types through head -n 1, so we only detect the type of CPU 0. - ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` - case "$ALPHA_CPU_TYPE" in - "EV4 (21064)") - UNAME_MACHINE="alpha" ;; - "EV4.5 (21064)") - UNAME_MACHINE="alpha" ;; - "LCA4 (21066/21068)") - UNAME_MACHINE="alpha" ;; - "EV5 (21164)") - UNAME_MACHINE="alphaev5" ;; - "EV5.6 (21164A)") - UNAME_MACHINE="alphaev56" ;; - "EV5.6 (21164PC)") - UNAME_MACHINE="alphapca56" ;; - "EV5.7 (21164PC)") - UNAME_MACHINE="alphapca57" ;; - "EV6 (21264)") - UNAME_MACHINE="alphaev6" ;; - "EV6.7 (21264A)") - UNAME_MACHINE="alphaev67" ;; - "EV6.8CB (21264C)") - UNAME_MACHINE="alphaev68" ;; - "EV6.8AL (21264B)") - UNAME_MACHINE="alphaev68" ;; - "EV6.8CX (21264D)") - UNAME_MACHINE="alphaev68" ;; - "EV6.9A (21264/EV69A)") - UNAME_MACHINE="alphaev69" ;; - "EV7 (21364)") - UNAME_MACHINE="alphaev7" ;; - "EV7.9 (21364A)") - UNAME_MACHINE="alphaev79" ;; - esac - # A Vn.n version is a released version. - # A Tn.n version is a released field test version. - # A Xn.n version is an unreleased experimental baselevel. - # 1.2 uses "1.2" for uname -r. - echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - exit 0 ;; - Alpha*:OpenVMS:*:*) - echo alpha-hp-vms - exit 0 ;; - Alpha\ *:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # Should we change UNAME_MACHINE based on the output of uname instead - # of the specific Alpha model? - echo alpha-pc-interix - exit 0 ;; - 21064:Windows_NT:50:3) - echo alpha-dec-winnt3.5 - exit 0 ;; - Amiga*:UNIX_System_V:4.0:*) - echo m68k-unknown-sysv4 - exit 0;; - *:[Aa]miga[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-amigaos - exit 0 ;; - *:[Mm]orph[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-morphos - exit 0 ;; - *:OS/390:*:*) - echo i370-ibm-openedition - exit 0 ;; - arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) - echo arm-acorn-riscix${UNAME_RELEASE} - exit 0;; - SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) - echo hppa1.1-hitachi-hiuxmpp - exit 0;; - Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) - # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. - if test "`(/bin/universe) 2>/dev/null`" = att ; then - echo pyramid-pyramid-sysv3 - else - echo pyramid-pyramid-bsd - fi - exit 0 ;; - NILE*:*:*:dcosx) - echo pyramid-pyramid-svr4 - exit 0 ;; - DRS?6000:unix:4.0:6*) - echo sparc-icl-nx6 - exit 0 ;; - DRS?6000:UNIX_SV:4.2*:7*) - case `/usr/bin/uname -p` in - sparc) echo sparc-icl-nx7 && exit 0 ;; - esac ;; - sun4H:SunOS:5.*:*) - echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit 0 ;; - sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) - echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit 0 ;; - i86pc:SunOS:5.*:*) - echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit 0 ;; - sun4*:SunOS:6*:*) - # According to config.sub, this is the proper way to canonicalize - # SunOS6. Hard to guess exactly what SunOS6 will be like, but - # it's likely to be more like Solaris than SunOS4. - echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit 0 ;; - sun4*:SunOS:*:*) - case "`/usr/bin/arch -k`" in - Series*|S4*) - UNAME_RELEASE=`uname -v` - ;; - esac - # Japanese Language versions have a version number like `4.1.3-JL'. - echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` - exit 0 ;; - sun3*:SunOS:*:*) - echo m68k-sun-sunos${UNAME_RELEASE} - exit 0 ;; - sun*:*:4.2BSD:*) - UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` - test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 - case "`/bin/arch`" in - sun3) - echo m68k-sun-sunos${UNAME_RELEASE} - ;; - sun4) - echo sparc-sun-sunos${UNAME_RELEASE} - ;; - esac - exit 0 ;; - aushp:SunOS:*:*) - echo sparc-auspex-sunos${UNAME_RELEASE} - exit 0 ;; - # The situation for MiNT is a little confusing. The machine name - # can be virtually everything (everything which is not - # "atarist" or "atariste" at least should have a processor - # > m68000). The system name ranges from "MiNT" over "FreeMiNT" - # to the lowercase version "mint" (or "freemint"). Finally - # the system name "TOS" denotes a system which is actually not - # MiNT. But MiNT is downward compatible to TOS, so this should - # be no problem. - atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit 0 ;; - atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit 0 ;; - *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit 0 ;; - milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) - echo m68k-milan-mint${UNAME_RELEASE} - exit 0 ;; - hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) - echo m68k-hades-mint${UNAME_RELEASE} - exit 0 ;; - *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) - echo m68k-unknown-mint${UNAME_RELEASE} - exit 0 ;; - powerpc:machten:*:*) - echo powerpc-apple-machten${UNAME_RELEASE} - exit 0 ;; - RISC*:Mach:*:*) - echo mips-dec-mach_bsd4.3 - exit 0 ;; - RISC*:ULTRIX:*:*) - echo mips-dec-ultrix${UNAME_RELEASE} - exit 0 ;; - VAX*:ULTRIX*:*:*) - echo vax-dec-ultrix${UNAME_RELEASE} - exit 0 ;; - 2020:CLIX:*:* | 2430:CLIX:*:*) - echo clipper-intergraph-clix${UNAME_RELEASE} - exit 0 ;; - mips:*:*:UMIPS | mips:*:*:RISCos) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c -#ifdef __cplusplus -#include /* for printf() prototype */ - int main (int argc, char *argv[]) { -#else - int main (argc, argv) int argc; char *argv[]; { -#endif - #if defined (host_mips) && defined (MIPSEB) - #if defined (SYSTYPE_SYSV) - printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_SVR4) - printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) - printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); - #endif - #endif - exit (-1); - } -EOF - $CC_FOR_BUILD -o $dummy $dummy.c \ - && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ - && exit 0 - echo mips-mips-riscos${UNAME_RELEASE} - exit 0 ;; - Motorola:PowerMAX_OS:*:*) - echo powerpc-motorola-powermax - exit 0 ;; - Motorola:*:4.3:PL8-*) - echo powerpc-harris-powermax - exit 0 ;; - Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) - echo powerpc-harris-powermax - exit 0 ;; - Night_Hawk:Power_UNIX:*:*) - echo powerpc-harris-powerunix - exit 0 ;; - m88k:CX/UX:7*:*) - echo m88k-harris-cxux7 - exit 0 ;; - m88k:*:4*:R4*) - echo m88k-motorola-sysv4 - exit 0 ;; - m88k:*:3*:R3*) - echo m88k-motorola-sysv3 - exit 0 ;; - AViiON:dgux:*:*) - # DG/UX returns AViiON for all architectures - UNAME_PROCESSOR=`/usr/bin/uname -p` - if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] - then - if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ - [ ${TARGET_BINARY_INTERFACE}x = x ] - then - echo m88k-dg-dgux${UNAME_RELEASE} - else - echo m88k-dg-dguxbcs${UNAME_RELEASE} - fi - else - echo i586-dg-dgux${UNAME_RELEASE} - fi - exit 0 ;; - M88*:DolphinOS:*:*) # DolphinOS (SVR3) - echo m88k-dolphin-sysv3 - exit 0 ;; - M88*:*:R3*:*) - # Delta 88k system running SVR3 - echo m88k-motorola-sysv3 - exit 0 ;; - XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) - echo m88k-tektronix-sysv3 - exit 0 ;; - Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) - echo m68k-tektronix-bsd - exit 0 ;; - *:IRIX*:*:*) - echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` - exit 0 ;; - ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. - echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id - exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' - i*86:AIX:*:*) - echo i386-ibm-aix - exit 0 ;; - ia64:AIX:*:*) - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` - else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} - fi - echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} - exit 0 ;; - *:AIX:2:3) - if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #include - - main() - { - if (!__power_pc()) - exit(1); - puts("powerpc-ibm-aix3.2.5"); - exit(0); - } -EOF - $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 - echo rs6000-ibm-aix3.2.5 - elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then - echo rs6000-ibm-aix3.2.4 - else - echo rs6000-ibm-aix3.2 - fi - exit 0 ;; - *:AIX:*:[45]) - IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` - if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then - IBM_ARCH=rs6000 - else - IBM_ARCH=powerpc - fi - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` - else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} - fi - echo ${IBM_ARCH}-ibm-aix${IBM_REV} - exit 0 ;; - *:AIX:*:*) - echo rs6000-ibm-aix - exit 0 ;; - ibmrt:4.4BSD:*|romp-ibm:BSD:*) - echo romp-ibm-bsd4.4 - exit 0 ;; - ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and - echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to - exit 0 ;; # report: romp-ibm BSD 4.3 - *:BOSX:*:*) - echo rs6000-bull-bosx - exit 0 ;; - DPX/2?00:B.O.S.:*:*) - echo m68k-bull-sysv3 - exit 0 ;; - 9000/[34]??:4.3bsd:1.*:*) - echo m68k-hp-bsd - exit 0 ;; - hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) - echo m68k-hp-bsd4.4 - exit 0 ;; - 9000/[34678]??:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - case "${UNAME_MACHINE}" in - 9000/31? ) HP_ARCH=m68000 ;; - 9000/[34]?? ) HP_ARCH=m68k ;; - 9000/[678][0-9][0-9]) - if [ -x /usr/bin/getconf ]; then - sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` - sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` - case "${sc_cpu_version}" in - 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 - 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 - 532) # CPU_PA_RISC2_0 - case "${sc_kernel_bits}" in - 32) HP_ARCH="hppa2.0n" ;; - 64) HP_ARCH="hppa2.0w" ;; - '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 - esac ;; - esac - fi - if [ "${HP_ARCH}" = "" ]; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - - #define _HPUX_SOURCE - #include - #include - - int main () - { - #if defined(_SC_KERNEL_BITS) - long bits = sysconf(_SC_KERNEL_BITS); - #endif - long cpu = sysconf (_SC_CPU_VERSION); - - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1"); break; - case CPU_PA_RISC2_0: - #if defined(_SC_KERNEL_BITS) - switch (bits) - { - case 64: puts ("hppa2.0w"); break; - case 32: puts ("hppa2.0n"); break; - default: puts ("hppa2.0"); break; - } break; - #else /* !defined(_SC_KERNEL_BITS) */ - puts ("hppa2.0"); break; - #endif - default: puts ("hppa1.0"); break; - } - exit (0); - } -EOF - (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` - test -z "$HP_ARCH" && HP_ARCH=hppa - fi ;; - esac - if [ ${HP_ARCH} = "hppa2.0w" ] - then - # avoid double evaluation of $set_cc_for_build - test -n "$CC_FOR_BUILD" || eval $set_cc_for_build - if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null - then - HP_ARCH="hppa2.0w" - else - HP_ARCH="hppa64" - fi - fi - echo ${HP_ARCH}-hp-hpux${HPUX_REV} - exit 0 ;; - ia64:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - echo ia64-hp-hpux${HPUX_REV} - exit 0 ;; - 3050*:HI-UX:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #include - int - main () - { - long cpu = sysconf (_SC_CPU_VERSION); - /* The order matters, because CPU_IS_HP_MC68K erroneously returns - true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct - results, however. */ - if (CPU_IS_PA_RISC (cpu)) - { - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; - case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; - default: puts ("hppa-hitachi-hiuxwe2"); break; - } - } - else if (CPU_IS_HP_MC68K (cpu)) - puts ("m68k-hitachi-hiuxwe2"); - else puts ("unknown-hitachi-hiuxwe2"); - exit (0); - } -EOF - $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 - echo unknown-hitachi-hiuxwe2 - exit 0 ;; - 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) - echo hppa1.1-hp-bsd - exit 0 ;; - 9000/8??:4.3bsd:*:*) - echo hppa1.0-hp-bsd - exit 0 ;; - *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) - echo hppa1.0-hp-mpeix - exit 0 ;; - hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) - echo hppa1.1-hp-osf - exit 0 ;; - hp8??:OSF1:*:*) - echo hppa1.0-hp-osf - exit 0 ;; - i*86:OSF1:*:*) - if [ -x /usr/sbin/sysversion ] ; then - echo ${UNAME_MACHINE}-unknown-osf1mk - else - echo ${UNAME_MACHINE}-unknown-osf1 - fi - exit 0 ;; - parisc*:Lites*:*:*) - echo hppa1.1-hp-lites - exit 0 ;; - C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) - echo c1-convex-bsd - exit 0 ;; - C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit 0 ;; - C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) - echo c34-convex-bsd - exit 0 ;; - C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) - echo c38-convex-bsd - exit 0 ;; - C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) - echo c4-convex-bsd - exit 0 ;; - CRAY*Y-MP:*:*:*) - echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit 0 ;; - CRAY*[A-Z]90:*:*:*) - echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ - | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ - -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ - -e 's/\.[^.]*$/.X/' - exit 0 ;; - CRAY*TS:*:*:*) - echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit 0 ;; - CRAY*T3E:*:*:*) - echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit 0 ;; - CRAY*SV1:*:*:*) - echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit 0 ;; - *:UNICOS/mp:*:*) - echo nv1-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit 0 ;; - F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) - FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` - echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit 0 ;; - i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) - echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} - exit 0 ;; - sparc*:BSD/OS:*:*) - echo sparc-unknown-bsdi${UNAME_RELEASE} - exit 0 ;; - *:BSD/OS:*:*) - echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} - exit 0 ;; - *:FreeBSD:*:*|*:GNU/FreeBSD:*:*) - # Determine whether the default compiler uses glibc. - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #include - #if __GLIBC__ >= 2 - LIBC=gnu - #else - LIBC= - #endif -EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` - echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`${LIBC:+-$LIBC} - exit 0 ;; - i*:CYGWIN*:*) - echo ${UNAME_MACHINE}-pc-cygwin - exit 0 ;; - i*:MINGW*:*) - echo ${UNAME_MACHINE}-pc-mingw32 - exit 0 ;; - i*:PW*:*) - echo ${UNAME_MACHINE}-pc-pw32 - exit 0 ;; - x86:Interix*:[34]*) - echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//' - exit 0 ;; - [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) - echo i${UNAME_MACHINE}-pc-mks - exit 0 ;; - i*:Windows_NT*:* | Pentium*:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we - # UNAME_MACHINE based on the output of uname instead of i386? - echo i586-pc-interix - exit 0 ;; - i*:UWIN*:*) - echo ${UNAME_MACHINE}-pc-uwin - exit 0 ;; - p*:CYGWIN*:*) - echo powerpcle-unknown-cygwin - exit 0 ;; - prep*:SunOS:5.*:*) - echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit 0 ;; - *:GNU:*:*) - echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` - exit 0 ;; - i*86:Minix:*:*) - echo ${UNAME_MACHINE}-pc-minix - exit 0 ;; - arm*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit 0 ;; - cris:Linux:*:*) - echo cris-axis-linux-gnu - exit 0 ;; - ia64:Linux:*:*) - echo ${UNAME_MACHINE}-${VENDOR:-unknown}-linux-gnu - exit 0 ;; - m68*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit 0 ;; - mips:Linux:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #undef CPU - #undef mips - #undef mipsel - #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=mipsel - #else - #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=mips - #else - CPU= - #endif - #endif -EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` - test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 - ;; - mips64:Linux:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #undef CPU - #undef mips64 - #undef mips64el - #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=mips64el - #else - #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=mips64 - #else - CPU= - #endif - #endif -EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` - test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 - ;; - ppc:Linux:*:*) - echo powerpc-${VENDOR:-unknown}-linux-gnu - exit 0 ;; - ppc64:Linux:*:*) - echo powerpc64-${VENDOR:-unknown}-linux-gnu - exit 0 ;; - alpha:Linux:*:*) - case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in - EV5) UNAME_MACHINE=alphaev5 ;; - EV56) UNAME_MACHINE=alphaev56 ;; - PCA56) UNAME_MACHINE=alphapca56 ;; - PCA57) UNAME_MACHINE=alphapca56 ;; - EV6) UNAME_MACHINE=alphaev6 ;; - EV67) UNAME_MACHINE=alphaev67 ;; - EV68*) UNAME_MACHINE=alphaev68 ;; - esac - objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null - if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi - echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} - exit 0 ;; - parisc:Linux:*:* | hppa:Linux:*:*) - # Look for CPU level - case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in - PA7*) echo hppa1.1-unknown-linux-gnu ;; - PA8*) echo hppa2.0-unknown-linux-gnu ;; - *) echo hppa-unknown-linux-gnu ;; - esac - exit 0 ;; - parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-gnu - exit 0 ;; - s390:Linux:*:* | s390x:Linux:*:*) - echo ${UNAME_MACHINE}-${VENDOR:-ibm}-linux-gnu - exit 0 ;; - sh64*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit 0 ;; - sh*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit 0 ;; - sparc:Linux:*:* | sparc64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit 0 ;; - x86_64:Linux:*:*) - echo x86_64-${VENDOR:-unknown}-linux-gnu - exit 0 ;; - i*86:Linux:*:*) - # The BFD linker knows what the default object file format is, so - # first see if it will tell us. cd to the root directory to prevent - # problems with other programs or directories called `ld' in the path. - # Set LC_ALL=C to ensure ld outputs messages in English. - ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ - | sed -ne '/supported targets:/!d - s/[ ][ ]*/ /g - s/.*supported targets: *// - s/ .*// - p'` - case "$ld_supported_targets" in - elf32-i386) - TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" - ;; - a.out-i386-linux) - echo "${UNAME_MACHINE}-pc-linux-gnuaout" - exit 0 ;; - coff-i386) - echo "${UNAME_MACHINE}-pc-linux-gnucoff" - exit 0 ;; - "") - # Either a pre-BFD a.out linker (linux-gnuoldld) or - # one that does not give us useful --help. - echo "${UNAME_MACHINE}-pc-linux-gnuoldld" - exit 0 ;; - esac - # Determine whether the default compiler is a.out or elf - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #include - #ifdef __ELF__ - # ifdef __GLIBC__ - # if __GLIBC__ >= 2 - LIBC=gnu - # else - LIBC=gnulibc1 - # endif - # else - LIBC=gnulibc1 - # endif - #else - #ifdef __INTEL_COMPILER - LIBC=gnu - #else - LIBC=gnuaout - #endif - #endif -EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` - test x"${LIBC}" != x && echo "${UNAME_MACHINE}-${VENDOR:-pc}-linux-${LIBC}" && exit 0 - test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0 - ;; - i*86:DYNIX/ptx:4*:*) - # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. - # earlier versions are messed up and put the nodename in both - # sysname and nodename. - echo i386-sequent-sysv4 - exit 0 ;; - i*86:UNIX_SV:4.2MP:2.*) - # Unixware is an offshoot of SVR4, but it has its own version - # number series starting with 2... - # I am not positive that other SVR4 systems won't match this, - # I just have to hope. -- rms. - # Use sysv4.2uw... so that sysv4* matches it. - echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} - exit 0 ;; - i*86:OS/2:*:*) - # If we were able to find `uname', then EMX Unix compatibility - # is probably installed. - echo ${UNAME_MACHINE}-pc-os2-emx - exit 0 ;; - i*86:XTS-300:*:STOP) - echo ${UNAME_MACHINE}-unknown-stop - exit 0 ;; - i*86:atheos:*:*) - echo ${UNAME_MACHINE}-unknown-atheos - exit 0 ;; - i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) - echo i386-unknown-lynxos${UNAME_RELEASE} - exit 0 ;; - i*86:*DOS:*:*) - echo ${UNAME_MACHINE}-pc-msdosdjgpp - exit 0 ;; - i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) - UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` - if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then - echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} - else - echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} - fi - exit 0 ;; - i*86:*:5:[78]*) - case `/bin/uname -X | grep "^Machine"` in - *486*) UNAME_MACHINE=i486 ;; - *Pentium) UNAME_MACHINE=i586 ;; - *Pent*|*Celeron) UNAME_MACHINE=i686 ;; - esac - echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} - exit 0 ;; - i*86:*:3.2:*) - if test -f /usr/options/cb.name; then - UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then - UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` - (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 - (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ - && UNAME_MACHINE=i586 - (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ - && UNAME_MACHINE=i686 - (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ - && UNAME_MACHINE=i686 - echo ${UNAME_MACHINE}-pc-sco$UNAME_REL - else - echo ${UNAME_MACHINE}-pc-sysv32 - fi - exit 0 ;; - pc:*:*:*) - # Left here for compatibility: - # uname -m prints for DJGPP always 'pc', but it prints nothing about - # the processor, so we play safe by assuming i386. - echo i386-pc-msdosdjgpp - exit 0 ;; - Intel:Mach:3*:*) - echo i386-pc-mach3 - exit 0 ;; - paragon:*:*:*) - echo i860-intel-osf1 - exit 0 ;; - i860:*:4.*:*) # i860-SVR4 - if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then - echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 - else # Add other i860-SVR4 vendors below as they are discovered. - echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 - fi - exit 0 ;; - mini*:CTIX:SYS*5:*) - # "miniframe" - echo m68010-convergent-sysv - exit 0 ;; - mc68k:UNIX:SYSTEM5:3.51m) - echo m68k-convergent-sysv - exit 0 ;; - M680?0:D-NIX:5.3:*) - echo m68k-diab-dnix - exit 0 ;; - M68*:*:R3V[567]*:*) - test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; - 3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0) - OS_REL='' - test -r /etc/.relid \ - && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && echo i486-ncr-sysv4.3${OS_REL} && exit 0 - /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; - 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && echo i486-ncr-sysv4 && exit 0 ;; - m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) - echo m68k-unknown-lynxos${UNAME_RELEASE} - exit 0 ;; - mc68030:UNIX_System_V:4.*:*) - echo m68k-atari-sysv4 - exit 0 ;; - TSUNAMI:LynxOS:2.*:*) - echo sparc-unknown-lynxos${UNAME_RELEASE} - exit 0 ;; - rs6000:LynxOS:2.*:*) - echo rs6000-unknown-lynxos${UNAME_RELEASE} - exit 0 ;; - PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) - echo powerpc-unknown-lynxos${UNAME_RELEASE} - exit 0 ;; - SM[BE]S:UNIX_SV:*:*) - echo mips-dde-sysv${UNAME_RELEASE} - exit 0 ;; - RM*:ReliantUNIX-*:*:*) - echo mips-sni-sysv4 - exit 0 ;; - RM*:SINIX-*:*:*) - echo mips-sni-sysv4 - exit 0 ;; - *:SINIX-*:*:*) - if uname -p 2>/dev/null >/dev/null ; then - UNAME_MACHINE=`(uname -p) 2>/dev/null` - echo ${UNAME_MACHINE}-sni-sysv4 - else - echo ns32k-sni-sysv - fi - exit 0 ;; - PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort - # says - echo i586-unisys-sysv4 - exit 0 ;; - *:UNIX_System_V:4*:FTX*) - # From Gerald Hewes . - # How about differentiating between stratus architectures? -djm - echo hppa1.1-stratus-sysv4 - exit 0 ;; - *:*:*:FTX*) - # From seanf@swdc.stratus.com. - echo i860-stratus-sysv4 - exit 0 ;; - *:VOS:*:*) - # From Paul.Green@stratus.com. - echo hppa1.1-stratus-vos - exit 0 ;; - mc68*:A/UX:*:*) - echo m68k-apple-aux${UNAME_RELEASE} - exit 0 ;; - news*:NEWS-OS:6*:*) - echo mips-sony-newsos6 - exit 0 ;; - R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) - if [ -d /usr/nec ]; then - echo mips-nec-sysv${UNAME_RELEASE} - else - echo mips-unknown-sysv${UNAME_RELEASE} - fi - exit 0 ;; - BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. - echo powerpc-be-beos - exit 0 ;; - BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. - echo powerpc-apple-beos - exit 0 ;; - BePC:BeOS:*:*) # BeOS running on Intel PC compatible. - echo i586-pc-beos - exit 0 ;; - SX-4:SUPER-UX:*:*) - echo sx4-nec-superux${UNAME_RELEASE} - exit 0 ;; - SX-5:SUPER-UX:*:*) - echo sx5-nec-superux${UNAME_RELEASE} - exit 0 ;; - SX-6:SUPER-UX:*:*) - echo sx6-nec-superux${UNAME_RELEASE} - exit 0 ;; - Power*:Rhapsody:*:*) - echo powerpc-apple-rhapsody${UNAME_RELEASE} - exit 0 ;; - *:Rhapsody:*:*) - echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} - exit 0 ;; - *:Darwin:*:*) - case `uname -p` in - *86) UNAME_PROCESSOR=i686 ;; - powerpc) UNAME_PROCESSOR=powerpc ;; - esac - echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} - exit 0 ;; - *:procnto*:*:* | *:QNX:[0123456789]*:*) - UNAME_PROCESSOR=`uname -p` - if test "$UNAME_PROCESSOR" = "x86"; then - UNAME_PROCESSOR=i386 - UNAME_MACHINE=pc - fi - echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} - exit 0 ;; - *:QNX:*:4*) - echo i386-pc-qnx - exit 0 ;; - NSR-[DGKLNPTVW]:NONSTOP_KERNEL:*:*) - echo nsr-tandem-nsk${UNAME_RELEASE} - exit 0 ;; - *:NonStop-UX:*:*) - echo mips-compaq-nonstopux - exit 0 ;; - BS2000:POSIX*:*:*) - echo bs2000-siemens-sysv - exit 0 ;; - DS/*:UNIX_System_V:*:*) - echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} - exit 0 ;; - *:Plan9:*:*) - # "uname -m" is not consistent, so use $cputype instead. 386 - # is converted to i386 for consistency with other x86 - # operating systems. - if test "$cputype" = "386"; then - UNAME_MACHINE=i386 - else - UNAME_MACHINE="$cputype" - fi - echo ${UNAME_MACHINE}-unknown-plan9 - exit 0 ;; - *:TOPS-10:*:*) - echo pdp10-unknown-tops10 - exit 0 ;; - *:TENEX:*:*) - echo pdp10-unknown-tenex - exit 0 ;; - KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) - echo pdp10-dec-tops20 - exit 0 ;; - XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) - echo pdp10-xkl-tops20 - exit 0 ;; - *:TOPS-20:*:*) - echo pdp10-unknown-tops20 - exit 0 ;; - *:ITS:*:*) - echo pdp10-unknown-its - exit 0 ;; - SEI:*:*:SEIUX) - echo mips-sei-seiux${UNAME_RELEASE} - exit 0 ;; -esac - -#echo '(No uname command or uname output not recognized.)' 1>&2 -#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 - -eval $set_cc_for_build -cat >$dummy.c < -# include -#endif -main () -{ -#if defined (sony) -#if defined (MIPSEB) - /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, - I don't know.... */ - printf ("mips-sony-bsd\n"); exit (0); -#else -#include - printf ("m68k-sony-newsos%s\n", -#ifdef NEWSOS4 - "4" -#else - "" -#endif - ); exit (0); -#endif -#endif - -#if defined (__arm) && defined (__acorn) && defined (__unix) - printf ("arm-acorn-riscix"); exit (0); -#endif - -#if defined (hp300) && !defined (hpux) - printf ("m68k-hp-bsd\n"); exit (0); -#endif - -#if defined (NeXT) -#if !defined (__ARCHITECTURE__) -#define __ARCHITECTURE__ "m68k" -#endif - int version; - version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; - if (version < 4) - printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); - else - printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); - exit (0); -#endif - -#if defined (MULTIMAX) || defined (n16) -#if defined (UMAXV) - printf ("ns32k-encore-sysv\n"); exit (0); -#else -#if defined (CMU) - printf ("ns32k-encore-mach\n"); exit (0); -#else - printf ("ns32k-encore-bsd\n"); exit (0); -#endif -#endif -#endif - -#if defined (__386BSD__) - printf ("i386-pc-bsd\n"); exit (0); -#endif - -#if defined (sequent) -#if defined (i386) - printf ("i386-sequent-dynix\n"); exit (0); -#endif -#if defined (ns32000) - printf ("ns32k-sequent-dynix\n"); exit (0); -#endif -#endif - -#if defined (_SEQUENT_) - struct utsname un; - - uname(&un); - - if (strncmp(un.version, "V2", 2) == 0) { - printf ("i386-sequent-ptx2\n"); exit (0); - } - if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ - printf ("i386-sequent-ptx1\n"); exit (0); - } - printf ("i386-sequent-ptx\n"); exit (0); - -#endif - -#if defined (vax) -# if !defined (ultrix) -# include -# if defined (BSD) -# if BSD == 43 - printf ("vax-dec-bsd4.3\n"); exit (0); -# else -# if BSD == 199006 - printf ("vax-dec-bsd4.3reno\n"); exit (0); -# else - printf ("vax-dec-bsd\n"); exit (0); -# endif -# endif -# else - printf ("vax-dec-bsd\n"); exit (0); -# endif -# else - printf ("vax-dec-ultrix\n"); exit (0); -# endif -#endif - -#if defined (alliant) && defined (i860) - printf ("i860-alliant-bsd\n"); exit (0); -#endif - - exit (1); -} -EOF - -$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0 - -# Apollos put the system type in the environment. - -test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } - -# Convex versions that predate uname can use getsysinfo(1) - -if [ -x /usr/convex/getsysinfo ] -then - case `getsysinfo -f cpu_type` in - c1*) - echo c1-convex-bsd - exit 0 ;; - c2*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit 0 ;; - c34*) - echo c34-convex-bsd - exit 0 ;; - c38*) - echo c38-convex-bsd - exit 0 ;; - c4*) - echo c4-convex-bsd - exit 0 ;; - esac -fi - -cat >&2 < in order to provide the needed -information to handle your system. - -config.guess timestamp = $timestamp - -uname -m = `(uname -m) 2>/dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` - -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null` - -hostinfo = `(hostinfo) 2>/dev/null` -/bin/universe = `(/bin/universe) 2>/dev/null` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` -/bin/arch = `(/bin/arch) 2>/dev/null` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` - -UNAME_MACHINE = ${UNAME_MACHINE} -UNAME_RELEASE = ${UNAME_RELEASE} -UNAME_SYSTEM = ${UNAME_SYSTEM} -UNAME_VERSION = ${UNAME_VERSION} -EOF - -exit 1 - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: diff --git a/api/libsangoma/.svn/text-base/config.sub.svn-base b/api/libsangoma/.svn/text-base/config.sub.svn-base deleted file mode 100644 index 6b2ff9f..0000000 --- a/api/libsangoma/.svn/text-base/config.sub.svn-base +++ /dev/null @@ -1,1500 +0,0 @@ -#! /bin/sh -# Configuration validation subroutine script. -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003 Free Software Foundation, Inc. - -timestamp='2003-06-18' - -# This file is (in principle) common to ALL GNU software. -# The presence of a machine in this file suggests that SOME GNU software -# can handle that machine. It does not imply ALL GNU software can. -# -# This file 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. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. - -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -# Please send patches to . Submit a context -# diff and a properly formatted ChangeLog entry. -# -# Configuration subroutine to validate and canonicalize a configuration type. -# Supply the specified configuration type as an argument. -# If it is invalid, we print an error message on stderr and exit with code 1. -# Otherwise, we print the canonical config type on stdout and succeed. - -# This file is supposed to be the same for all GNU packages -# and recognize all the CPU types, system types and aliases -# that are meaningful with *any* GNU software. -# Each package is responsible for reporting which valid configurations -# it does not support. The user should be able to distinguish -# a failure to support a valid configuration from a meaningless -# configuration. - -# The goal of this file is to map all the various variations of a given -# machine specification into a single specification in the form: -# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM -# or in some cases, the newer four-part form: -# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM -# It is wrong to echo any other type of specification. - -me=`echo "$0" | sed -e 's,.*/,,'` - -usage="\ -Usage: $0 [OPTION] CPU-MFR-OPSYS - $0 [OPTION] ALIAS - -Canonicalize a configuration name. - -Operation modes: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.sub ($timestamp) - -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 -Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit 0 ;; - --version | -v ) - echo "$version" ; exit 0 ;; - --help | --h* | -h ) - echo "$usage"; exit 0 ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" - exit 1 ;; - - *local*) - # First pass through any local machine types. - echo $1 - exit 0;; - - * ) - break ;; - esac -done - -case $# in - 0) echo "$me: missing argument$help" >&2 - exit 1;; - 1) ;; - *) echo "$me: too many arguments$help" >&2 - exit 1;; -esac - -# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). -# Here we must recognize all the valid KERNEL-OS combinations. -maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` -case $maybe_os in - nto-qnx* | linux-gnu* | freebsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*) - os=-$maybe_os - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` - ;; - *) - basic_machine=`echo $1 | sed 's/-[^-]*$//'` - if [ $basic_machine != $1 ] - then os=`echo $1 | sed 's/.*-/-/'` - else os=; fi - ;; -esac - -### Let's recognize common machines as not being operating systems so -### that things like config.sub decstation-3100 work. We also -### recognize some manufacturers as not being operating systems, so we -### can provide default operating systems below. -case $os in - -sun*os*) - # Prevent following clause from handling this invalid input. - ;; - -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ - -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ - -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ - -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ - -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ - -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ - -apple | -axis) - os= - basic_machine=$1 - ;; - -sim | -cisco | -oki | -wec | -winbond) - os= - basic_machine=$1 - ;; - -scout) - ;; - -wrs) - os=-vxworks - basic_machine=$1 - ;; - -chorusos*) - os=-chorusos - basic_machine=$1 - ;; - -chorusrdb) - os=-chorusrdb - basic_machine=$1 - ;; - -hiux*) - os=-hiuxwe2 - ;; - -sco5) - os=-sco3.2v5 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco4) - os=-sco3.2v4 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2.[4-9]*) - os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2v[4-9]*) - # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco*) - os=-sco3.2v2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -udk*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -isc) - os=-isc2.2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -clix*) - basic_machine=clipper-intergraph - ;; - -isc*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -lynx*) - os=-lynxos - ;; - -ptx*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` - ;; - -windowsnt*) - os=`echo $os | sed -e 's/windowsnt/winnt/'` - ;; - -psos*) - os=-psos - ;; - -mint | -mint[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; -esac - -# Decode aliases for certain CPU-COMPANY combinations. -case $basic_machine in - # Recognize the basic CPU types without company name. - # Some are omitted here because they have special meanings below. - 1750a | 580 \ - | a29k \ - | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ - | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ - | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ - | c4x | clipper \ - | d10v | d30v | dlx | dsp16xx \ - | fr30 | frv \ - | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ - | i370 | i860 | i960 | ia64 \ - | ip2k \ - | m32r | m68000 | m68k | m88k | mcore \ - | mips | mipsbe | mipseb | mipsel | mipsle \ - | mips16 \ - | mips64 | mips64el \ - | mips64vr | mips64vrel \ - | mips64orion | mips64orionel \ - | mips64vr4100 | mips64vr4100el \ - | mips64vr4300 | mips64vr4300el \ - | mips64vr5000 | mips64vr5000el \ - | mipsisa32 | mipsisa32el \ - | mipsisa32r2 | mipsisa32r2el \ - | mipsisa64 | mipsisa64el \ - | mipsisa64sb1 | mipsisa64sb1el \ - | mipsisa64sr71k | mipsisa64sr71kel \ - | mipstx39 | mipstx39el \ - | mn10200 | mn10300 \ - | msp430 \ - | ns16k | ns32k \ - | openrisc | or32 \ - | pdp10 | pdp11 | pj | pjl \ - | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ - | pyramid \ - | s390 | s390x \ - | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \ - | sh64 | sh64le \ - | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv8 | sparcv9 | sparcv9b \ - | strongarm \ - | tahoe | thumb | tic4x | tic80 | tron \ - | v850 | v850e \ - | we32k \ - | x86 | xscale | xstormy16 | xtensa \ - | z8k) - basic_machine=$basic_machine-unknown - ;; - m6811 | m68hc11 | m6812 | m68hc12) - # Motorola 68HC11/12. - basic_machine=$basic_machine-unknown - os=-none - ;; - m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) - ;; - - # We use `pc' rather than `unknown' - # because (1) that's what they normally are, and - # (2) the word "unknown" tends to confuse beginning users. - i*86 | x86_64) - basic_machine=$basic_machine-pc - ;; - # Object if more than one company name word. - *-*-*) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 - ;; - # Recognize the basic CPU types with company name. - 580-* \ - | a29k-* \ - | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ - | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ - | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ - | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ - | avr-* \ - | bs2000-* \ - | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ - | clipper-* | cydra-* \ - | d10v-* | d30v-* | dlx-* \ - | elxsi-* \ - | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \ - | h8300-* | h8500-* \ - | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ - | i*86-* | i860-* | i960-* | ia64-* \ - | ip2k-* \ - | m32r-* \ - | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ - | m88110-* | m88k-* | mcore-* \ - | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ - | mips16-* \ - | mips64-* | mips64el-* \ - | mips64vr-* | mips64vrel-* \ - | mips64orion-* | mips64orionel-* \ - | mips64vr4100-* | mips64vr4100el-* \ - | mips64vr4300-* | mips64vr4300el-* \ - | mips64vr5000-* | mips64vr5000el-* \ - | mipsisa32-* | mipsisa32el-* \ - | mipsisa32r2-* | mipsisa32r2el-* \ - | mipsisa64-* | mipsisa64el-* \ - | mipsisa64sb1-* | mipsisa64sb1el-* \ - | mipsisa64sr71k-* | mipsisa64sr71kel-* \ - | mipstx39-* | mipstx39el-* \ - | msp430-* \ - | none-* | np1-* | nv1-* | ns16k-* | ns32k-* \ - | orion-* \ - | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ - | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ - | pyramid-* \ - | romp-* | rs6000-* \ - | s390-* | s390x-* \ - | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \ - | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ - | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \ - | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \ - | tahoe-* | thumb-* \ - | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ - | tron-* \ - | v850-* | v850e-* | vax-* \ - | we32k-* \ - | x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \ - | xtensa-* \ - | ymp-* \ - | z8k-*) - ;; - # Recognize the various machine names and aliases which stand - # for a CPU type and a company and sometimes even an OS. - 386bsd) - basic_machine=i386-unknown - os=-bsd - ;; - 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) - basic_machine=m68000-att - ;; - 3b*) - basic_machine=we32k-att - ;; - a29khif) - basic_machine=a29k-amd - os=-udi - ;; - adobe68k) - basic_machine=m68010-adobe - os=-scout - ;; - alliant | fx80) - basic_machine=fx80-alliant - ;; - altos | altos3068) - basic_machine=m68k-altos - ;; - am29k) - basic_machine=a29k-none - os=-bsd - ;; - amd64) - basic_machine=x86_64-pc - ;; - amdahl) - basic_machine=580-amdahl - os=-sysv - ;; - amiga | amiga-*) - basic_machine=m68k-unknown - ;; - amigaos | amigados) - basic_machine=m68k-unknown - os=-amigaos - ;; - amigaunix | amix) - basic_machine=m68k-unknown - os=-sysv4 - ;; - apollo68) - basic_machine=m68k-apollo - os=-sysv - ;; - apollo68bsd) - basic_machine=m68k-apollo - os=-bsd - ;; - aux) - basic_machine=m68k-apple - os=-aux - ;; - balance) - basic_machine=ns32k-sequent - os=-dynix - ;; - c90) - basic_machine=c90-cray - os=-unicos - ;; - convex-c1) - basic_machine=c1-convex - os=-bsd - ;; - convex-c2) - basic_machine=c2-convex - os=-bsd - ;; - convex-c32) - basic_machine=c32-convex - os=-bsd - ;; - convex-c34) - basic_machine=c34-convex - os=-bsd - ;; - convex-c38) - basic_machine=c38-convex - os=-bsd - ;; - cray | j90) - basic_machine=j90-cray - os=-unicos - ;; - crds | unos) - basic_machine=m68k-crds - ;; - cris | cris-* | etrax*) - basic_machine=cris-axis - ;; - da30 | da30-*) - basic_machine=m68k-da30 - ;; - decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) - basic_machine=mips-dec - ;; - decsystem10* | dec10*) - basic_machine=pdp10-dec - os=-tops10 - ;; - decsystem20* | dec20*) - basic_machine=pdp10-dec - os=-tops20 - ;; - delta | 3300 | motorola-3300 | motorola-delta \ - | 3300-motorola | delta-motorola) - basic_machine=m68k-motorola - ;; - delta88) - basic_machine=m88k-motorola - os=-sysv3 - ;; - dpx20 | dpx20-*) - basic_machine=rs6000-bull - os=-bosx - ;; - dpx2* | dpx2*-bull) - basic_machine=m68k-bull - os=-sysv3 - ;; - ebmon29k) - basic_machine=a29k-amd - os=-ebmon - ;; - elxsi) - basic_machine=elxsi-elxsi - os=-bsd - ;; - encore | umax | mmax) - basic_machine=ns32k-encore - ;; - es1800 | OSE68k | ose68k | ose | OSE) - basic_machine=m68k-ericsson - os=-ose - ;; - fx2800) - basic_machine=i860-alliant - ;; - genix) - basic_machine=ns32k-ns - ;; - gmicro) - basic_machine=tron-gmicro - os=-sysv - ;; - go32) - basic_machine=i386-pc - os=-go32 - ;; - h3050r* | hiux*) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - h8300hms) - basic_machine=h8300-hitachi - os=-hms - ;; - h8300xray) - basic_machine=h8300-hitachi - os=-xray - ;; - h8500hms) - basic_machine=h8500-hitachi - os=-hms - ;; - harris) - basic_machine=m88k-harris - os=-sysv3 - ;; - hp300-*) - basic_machine=m68k-hp - ;; - hp300bsd) - basic_machine=m68k-hp - os=-bsd - ;; - hp300hpux) - basic_machine=m68k-hp - os=-hpux - ;; - hp3k9[0-9][0-9] | hp9[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hp9k2[0-9][0-9] | hp9k31[0-9]) - basic_machine=m68000-hp - ;; - hp9k3[2-9][0-9]) - basic_machine=m68k-hp - ;; - hp9k6[0-9][0-9] | hp6[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hp9k7[0-79][0-9] | hp7[0-79][0-9]) - basic_machine=hppa1.1-hp - ;; - hp9k78[0-9] | hp78[0-9]) - # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp - ;; - hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) - # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp - ;; - hp9k8[0-9][13679] | hp8[0-9][13679]) - basic_machine=hppa1.1-hp - ;; - hp9k8[0-9][0-9] | hp8[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hppa-next) - os=-nextstep3 - ;; - hppaosf) - basic_machine=hppa1.1-hp - os=-osf - ;; - hppro) - basic_machine=hppa1.1-hp - os=-proelf - ;; - i370-ibm* | ibm*) - basic_machine=i370-ibm - ;; -# I'm not sure what "Sysv32" means. Should this be sysv3.2? - i*86v32) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv32 - ;; - i*86v4*) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv4 - ;; - i*86v) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv - ;; - i*86sol2) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-solaris2 - ;; - i386mach) - basic_machine=i386-mach - os=-mach - ;; - i386-vsta | vsta) - basic_machine=i386-unknown - os=-vsta - ;; - iris | iris4d) - basic_machine=mips-sgi - case $os in - -irix*) - ;; - *) - os=-irix4 - ;; - esac - ;; - isi68 | isi) - basic_machine=m68k-isi - os=-sysv - ;; - m88k-omron*) - basic_machine=m88k-omron - ;; - magnum | m3230) - basic_machine=mips-mips - os=-sysv - ;; - merlin) - basic_machine=ns32k-utek - os=-sysv - ;; - mingw32) - basic_machine=i386-pc - os=-mingw32 - ;; - miniframe) - basic_machine=m68000-convergent - ;; - *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; - mips3*-*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` - ;; - mips3*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown - ;; - mmix*) - basic_machine=mmix-knuth - os=-mmixware - ;; - monitor) - basic_machine=m68k-rom68k - os=-coff - ;; - morphos) - basic_machine=powerpc-unknown - os=-morphos - ;; - msdos) - basic_machine=i386-pc - os=-msdos - ;; - mvs) - basic_machine=i370-ibm - os=-mvs - ;; - ncr3000) - basic_machine=i486-ncr - os=-sysv4 - ;; - netbsd386) - basic_machine=i386-unknown - os=-netbsd - ;; - netwinder) - basic_machine=armv4l-rebel - os=-linux - ;; - news | news700 | news800 | news900) - basic_machine=m68k-sony - os=-newsos - ;; - news1000) - basic_machine=m68030-sony - os=-newsos - ;; - news-3600 | risc-news) - basic_machine=mips-sony - os=-newsos - ;; - necv70) - basic_machine=v70-nec - os=-sysv - ;; - next | m*-next ) - basic_machine=m68k-next - case $os in - -nextstep* ) - ;; - -ns2*) - os=-nextstep2 - ;; - *) - os=-nextstep3 - ;; - esac - ;; - nh3000) - basic_machine=m68k-harris - os=-cxux - ;; - nh[45]000) - basic_machine=m88k-harris - os=-cxux - ;; - nindy960) - basic_machine=i960-intel - os=-nindy - ;; - mon960) - basic_machine=i960-intel - os=-mon960 - ;; - nonstopux) - basic_machine=mips-compaq - os=-nonstopux - ;; - np1) - basic_machine=np1-gould - ;; - nv1) - basic_machine=nv1-cray - os=-unicosmp - ;; - nsr-tandem) - basic_machine=nsr-tandem - ;; - op50n-* | op60c-*) - basic_machine=hppa1.1-oki - os=-proelf - ;; - or32 | or32-*) - basic_machine=or32-unknown - os=-coff - ;; - OSE68000 | ose68000) - basic_machine=m68000-ericsson - os=-ose - ;; - os68k) - basic_machine=m68k-none - os=-os68k - ;; - pa-hitachi) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - paragon) - basic_machine=i860-intel - os=-osf - ;; - pbd) - basic_machine=sparc-tti - ;; - pbb) - basic_machine=m68k-tti - ;; - pc532 | pc532-*) - basic_machine=ns32k-pc532 - ;; - pentium | p5 | k5 | k6 | nexgen | viac3) - basic_machine=i586-pc - ;; - pentiumpro | p6 | 6x86 | athlon | athlon_*) - basic_machine=i686-pc - ;; - pentiumii | pentium2 | pentiumiii | pentium3) - basic_machine=i686-pc - ;; - pentium4) - basic_machine=i786-pc - ;; - pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) - basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumpro-* | p6-* | 6x86-* | athlon-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentium4-*) - basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pn) - basic_machine=pn-gould - ;; - power) basic_machine=power-ibm - ;; - ppc) basic_machine=powerpc-unknown - ;; - ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppcle | powerpclittle | ppc-le | powerpc-little) - basic_machine=powerpcle-unknown - ;; - ppcle-* | powerpclittle-*) - basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppc64) basic_machine=powerpc64-unknown - ;; - ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppc64le | powerpc64little | ppc64-le | powerpc64-little) - basic_machine=powerpc64le-unknown - ;; - ppc64le-* | powerpc64little-*) - basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ps2) - basic_machine=i386-ibm - ;; - pw32) - basic_machine=i586-unknown - os=-pw32 - ;; - rom68k) - basic_machine=m68k-rom68k - os=-coff - ;; - rm[46]00) - basic_machine=mips-siemens - ;; - rtpc | rtpc-*) - basic_machine=romp-ibm - ;; - sa29200) - basic_machine=a29k-amd - os=-udi - ;; - sb1) - basic_machine=mipsisa64sb1-unknown - ;; - sb1el) - basic_machine=mipsisa64sb1el-unknown - ;; - sei) - basic_machine=mips-sei - os=-seiux - ;; - sequent) - basic_machine=i386-sequent - ;; - sh) - basic_machine=sh-hitachi - os=-hms - ;; - sh64) - basic_machine=sh64-unknown - ;; - sparclite-wrs | simso-wrs) - basic_machine=sparclite-wrs - os=-vxworks - ;; - sps7) - basic_machine=m68k-bull - os=-sysv2 - ;; - spur) - basic_machine=spur-unknown - ;; - st2000) - basic_machine=m68k-tandem - ;; - stratus) - basic_machine=i860-stratus - os=-sysv4 - ;; - sun2) - basic_machine=m68000-sun - ;; - sun2os3) - basic_machine=m68000-sun - os=-sunos3 - ;; - sun2os4) - basic_machine=m68000-sun - os=-sunos4 - ;; - sun3os3) - basic_machine=m68k-sun - os=-sunos3 - ;; - sun3os4) - basic_machine=m68k-sun - os=-sunos4 - ;; - sun4os3) - basic_machine=sparc-sun - os=-sunos3 - ;; - sun4os4) - basic_machine=sparc-sun - os=-sunos4 - ;; - sun4sol2) - basic_machine=sparc-sun - os=-solaris2 - ;; - sun3 | sun3-*) - basic_machine=m68k-sun - ;; - sun4) - basic_machine=sparc-sun - ;; - sun386 | sun386i | roadrunner) - basic_machine=i386-sun - ;; - sv1) - basic_machine=sv1-cray - os=-unicos - ;; - symmetry) - basic_machine=i386-sequent - os=-dynix - ;; - t3e) - basic_machine=alphaev5-cray - os=-unicos - ;; - t90) - basic_machine=t90-cray - os=-unicos - ;; - tic54x | c54x*) - basic_machine=tic54x-unknown - os=-coff - ;; - tic55x | c55x*) - basic_machine=tic55x-unknown - os=-coff - ;; - tic6x | c6x*) - basic_machine=tic6x-unknown - os=-coff - ;; - tx39) - basic_machine=mipstx39-unknown - ;; - tx39el) - basic_machine=mipstx39el-unknown - ;; - toad1) - basic_machine=pdp10-xkl - os=-tops20 - ;; - tower | tower-32) - basic_machine=m68k-ncr - ;; - udi29k) - basic_machine=a29k-amd - os=-udi - ;; - ultra3) - basic_machine=a29k-nyu - os=-sym1 - ;; - v810 | necv810) - basic_machine=v810-nec - os=-none - ;; - vaxv) - basic_machine=vax-dec - os=-sysv - ;; - vms) - basic_machine=vax-dec - os=-vms - ;; - vpp*|vx|vx-*) - basic_machine=f301-fujitsu - ;; - vxworks960) - basic_machine=i960-wrs - os=-vxworks - ;; - vxworks68) - basic_machine=m68k-wrs - os=-vxworks - ;; - vxworks29k) - basic_machine=a29k-wrs - os=-vxworks - ;; - w65*) - basic_machine=w65-wdc - os=-none - ;; - w89k-*) - basic_machine=hppa1.1-winbond - os=-proelf - ;; - xps | xps100) - basic_machine=xps100-honeywell - ;; - ymp) - basic_machine=ymp-cray - os=-unicos - ;; - z8k-*-coff) - basic_machine=z8k-unknown - os=-sim - ;; - none) - basic_machine=none-none - os=-none - ;; - -# Here we handle the default manufacturer of certain CPU types. It is in -# some cases the only manufacturer, in others, it is the most popular. - w89k) - basic_machine=hppa1.1-winbond - ;; - op50n) - basic_machine=hppa1.1-oki - ;; - op60c) - basic_machine=hppa1.1-oki - ;; - romp) - basic_machine=romp-ibm - ;; - rs6000) - basic_machine=rs6000-ibm - ;; - vax) - basic_machine=vax-dec - ;; - pdp10) - # there are many clones, so DEC is not a safe bet - basic_machine=pdp10-unknown - ;; - pdp11) - basic_machine=pdp11-dec - ;; - we32k) - basic_machine=we32k-att - ;; - sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele) - basic_machine=sh-unknown - ;; - sh64) - basic_machine=sh64-unknown - ;; - sparc | sparcv8 | sparcv9 | sparcv9b) - basic_machine=sparc-sun - ;; - cydra) - basic_machine=cydra-cydrome - ;; - orion) - basic_machine=orion-highlevel - ;; - orion105) - basic_machine=clipper-highlevel - ;; - mac | mpw | mac-mpw) - basic_machine=m68k-apple - ;; - pmac | pmac-mpw) - basic_machine=powerpc-apple - ;; - *-unknown) - # Make sure to match an already-canonicalized machine name. - ;; - *) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 - ;; -esac - -# Here we canonicalize certain aliases for manufacturers. -case $basic_machine in - *-digital*) - basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` - ;; - *-commodore*) - basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` - ;; - *) - ;; -esac - -# Decode manufacturer-specific aliases for certain operating systems. - -if [ x"$os" != x"" ] -then -case $os in - # First match some system type aliases - # that might get confused with valid system types. - # -solaris* is a basic system type, with this one exception. - -solaris1 | -solaris1.*) - os=`echo $os | sed -e 's|solaris1|sunos4|'` - ;; - -solaris) - os=-solaris2 - ;; - -svr4*) - os=-sysv4 - ;; - -unixware*) - os=-sysv4.2uw - ;; - -gnu/linux*) - os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` - ;; - # First accept the basic system types. - # The portable systems comes first. - # Each alternative MUST END IN A *, to match a version number. - # -sysv* is not here because it comes later, after sysvr4. - -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ - | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ - | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ - | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ - | -aos* \ - | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ - | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ - | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \ - | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ - | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ - | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ - | -chorusos* | -chorusrdb* \ - | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ - | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \ - | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ - | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ - | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ - | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ - | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ - | -powermax* | -dnix* | -nx6 | -nx7 | -sei*) - # Remember, each alternative MUST END IN *, to match a version number. - ;; - -qnx*) - case $basic_machine in - x86-* | i*86-*) - ;; - *) - os=-nto$os - ;; - esac - ;; - -nto-qnx*) - ;; - -nto*) - os=`echo $os | sed -e 's|nto|nto-qnx|'` - ;; - -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ - | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ - | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) - ;; - -mac*) - os=`echo $os | sed -e 's|mac|macos|'` - ;; - -linux*) - os=`echo $os | sed -e 's|linux|linux-gnu|'` - ;; - -sunos5*) - os=`echo $os | sed -e 's|sunos5|solaris2|'` - ;; - -sunos6*) - os=`echo $os | sed -e 's|sunos6|solaris3|'` - ;; - -opened*) - os=-openedition - ;; - -wince*) - os=-wince - ;; - -osfrose*) - os=-osfrose - ;; - -osf*) - os=-osf - ;; - -utek*) - os=-bsd - ;; - -dynix*) - os=-bsd - ;; - -acis*) - os=-aos - ;; - -atheos*) - os=-atheos - ;; - -386bsd) - os=-bsd - ;; - -ctix* | -uts*) - os=-sysv - ;; - -nova*) - os=-rtmk-nova - ;; - -ns2 ) - os=-nextstep2 - ;; - -nsk*) - os=-nsk - ;; - # Preserve the version number of sinix5. - -sinix5.*) - os=`echo $os | sed -e 's|sinix|sysv|'` - ;; - -sinix*) - os=-sysv4 - ;; - -triton*) - os=-sysv3 - ;; - -oss*) - os=-sysv3 - ;; - -svr4) - os=-sysv4 - ;; - -svr3) - os=-sysv3 - ;; - -sysvr4) - os=-sysv4 - ;; - # This must come after -sysvr4. - -sysv*) - ;; - -ose*) - os=-ose - ;; - -es1800*) - os=-ose - ;; - -xenix) - os=-xenix - ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) - os=-mint - ;; - -aros*) - os=-aros - ;; - -kaos*) - os=-kaos - ;; - -none) - ;; - *) - # Get rid of the `-' at the beginning of $os. - os=`echo $os | sed 's/[^-]*-//'` - echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 - exit 1 - ;; -esac -else - -# Here we handle the default operating systems that come with various machines. -# The value should be what the vendor currently ships out the door with their -# machine or put another way, the most popular os provided with the machine. - -# Note that if you're going to try to match "-MANUFACTURER" here (say, -# "-sun"), then you have to tell the case statement up towards the top -# that MANUFACTURER isn't an operating system. Otherwise, code above -# will signal an error saying that MANUFACTURER isn't an operating -# system, and we'll never get to this point. - -case $basic_machine in - *-acorn) - os=-riscix1.2 - ;; - arm*-rebel) - os=-linux - ;; - arm*-semi) - os=-aout - ;; - c4x-* | tic4x-*) - os=-coff - ;; - # This must come before the *-dec entry. - pdp10-*) - os=-tops20 - ;; - pdp11-*) - os=-none - ;; - *-dec | vax-*) - os=-ultrix4.2 - ;; - m68*-apollo) - os=-domain - ;; - i386-sun) - os=-sunos4.0.2 - ;; - m68000-sun) - os=-sunos3 - # This also exists in the configure program, but was not the - # default. - # os=-sunos4 - ;; - m68*-cisco) - os=-aout - ;; - mips*-cisco) - os=-elf - ;; - mips*-*) - os=-elf - ;; - or32-*) - os=-coff - ;; - *-tti) # must be before sparc entry or we get the wrong os. - os=-sysv3 - ;; - sparc-* | *-sun) - os=-sunos4.1.1 - ;; - *-be) - os=-beos - ;; - *-ibm) - os=-aix - ;; - *-wec) - os=-proelf - ;; - *-winbond) - os=-proelf - ;; - *-oki) - os=-proelf - ;; - *-hp) - os=-hpux - ;; - *-hitachi) - os=-hiux - ;; - i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) - os=-sysv - ;; - *-cbm) - os=-amigaos - ;; - *-dg) - os=-dgux - ;; - *-dolphin) - os=-sysv3 - ;; - m68k-ccur) - os=-rtu - ;; - m88k-omron*) - os=-luna - ;; - *-next ) - os=-nextstep - ;; - *-sequent) - os=-ptx - ;; - *-crds) - os=-unos - ;; - *-ns) - os=-genix - ;; - i370-*) - os=-mvs - ;; - *-next) - os=-nextstep3 - ;; - *-gould) - os=-sysv - ;; - *-highlevel) - os=-bsd - ;; - *-encore) - os=-bsd - ;; - *-sgi) - os=-irix - ;; - *-siemens) - os=-sysv4 - ;; - *-masscomp) - os=-rtu - ;; - f30[01]-fujitsu | f700-fujitsu) - os=-uxpv - ;; - *-rom68k) - os=-coff - ;; - *-*bug) - os=-coff - ;; - *-apple) - os=-macos - ;; - *-atari*) - os=-mint - ;; - *) - os=-none - ;; -esac -fi - -# Here we handle the case where we know the os, and the CPU type, but not the -# manufacturer. We pick the logical manufacturer. -vendor=unknown -case $basic_machine in - *-unknown) - case $os in - -riscix*) - vendor=acorn - ;; - -sunos*) - vendor=sun - ;; - -aix*) - vendor=ibm - ;; - -beos*) - vendor=be - ;; - -hpux*) - vendor=hp - ;; - -mpeix*) - vendor=hp - ;; - -hiux*) - vendor=hitachi - ;; - -unos*) - vendor=crds - ;; - -dgux*) - vendor=dg - ;; - -luna*) - vendor=omron - ;; - -genix*) - vendor=ns - ;; - -mvs* | -opened*) - vendor=ibm - ;; - -ptx*) - vendor=sequent - ;; - -vxsim* | -vxworks* | -windiss*) - vendor=wrs - ;; - -aux*) - vendor=apple - ;; - -hms*) - vendor=hitachi - ;; - -mpw* | -macos*) - vendor=apple - ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) - vendor=atari - ;; - -vos*) - vendor=stratus - ;; - esac - basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` - ;; -esac - -echo $basic_machine$os -exit 0 - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: diff --git a/api/libsangoma/.svn/text-base/configure.in.svn-base b/api/libsangoma/.svn/text-base/configure.in.svn-base index ad3e732..3da2963 100644 --- a/api/libsangoma/.svn/text-base/configure.in.svn-base +++ b/api/libsangoma/.svn/text-base/configure.in.svn-base @@ -15,12 +15,12 @@ # for example, if we ever fix a bug in priserver.c and we want to make a release we can do so # without incrementing libsangoma LT version -AC_INIT([libsangoma],[3.2.0],[ncorbic@sangoma.com]) +AC_INIT([libsangoma],[3.3.0],[ncorbic@sangoma.com]) AC_CONFIG_HEADERS(config.h) AM_INIT_AUTOMAKE LIBSANGOMA_LT_CURRENT=3 # interface 3 -LIBSANGOMA_LT_REVISION=2 # first revision of this interface +LIBSANGOMA_LT_REVISION=3 # first revision of this interface LIBSANGOMA_LT_AGE=0 #not backwards compatible (0 previous interfaces are compatible) #AC_CONFIG_MACRO_DIR([m4]) diff --git a/api/libsangoma/.svn/text-base/configure.svn-base b/api/libsangoma/.svn/text-base/configure.svn-base index d50bda6..a3ac102 100644 --- a/api/libsangoma/.svn/text-base/configure.svn-base +++ b/api/libsangoma/.svn/text-base/configure.svn-base @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.59 for libsangoma 3.2.0. +# Generated by GNU Autoconf 2.59 for libsangoma 3.3.0. # # Report bugs to . # @@ -423,8 +423,8 @@ SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='libsangoma' PACKAGE_TARNAME='libsangoma' -PACKAGE_VERSION='3.2.0' -PACKAGE_STRING='libsangoma 3.2.0' +PACKAGE_VERSION='3.3.0' +PACKAGE_STRING='libsangoma 3.3.0' PACKAGE_BUGREPORT='ncorbic@sangoma.com' # Factoring default headers for most tests. @@ -953,7 +953,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures libsangoma 3.2.0 to adapt to many kinds of systems. +\`configure' configures libsangoma 3.3.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1019,7 +1019,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of libsangoma 3.2.0:";; + short | recursive ) echo "Configuration of libsangoma 3.3.0:";; esac cat <<\_ACEOF @@ -1159,7 +1159,7 @@ fi test -n "$ac_init_help" && exit 0 if $ac_init_version; then cat <<\_ACEOF -libsangoma configure 3.2.0 +libsangoma configure 3.3.0 generated by GNU Autoconf 2.59 Copyright (C) 2003 Free Software Foundation, Inc. @@ -1173,7 +1173,7 @@ cat >&5 <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by libsangoma $as_me 3.2.0, which was +It was created by libsangoma $as_me 3.3.0, which was generated by GNU Autoconf 2.59. Invocation command line was $ $0 $@ @@ -1818,7 +1818,7 @@ fi # Define the identity of the package. PACKAGE='libsangoma' - VERSION='3.2.0' + VERSION='3.3.0' cat >>confdefs.h <<_ACEOF @@ -1950,7 +1950,7 @@ am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -' LIBSANGOMA_LT_CURRENT=3 # interface 3 -LIBSANGOMA_LT_REVISION=2 # first revision of this interface +LIBSANGOMA_LT_REVISION=3 # first revision of this interface LIBSANGOMA_LT_AGE=0 #not backwards compatible (0 previous interfaces are compatible) #AC_CONFIG_MACRO_DIR([m4]) @@ -19648,7 +19648,7 @@ _ASBOX } >&5 cat >&5 <<_CSEOF -This file was extended by libsangoma $as_me 3.2.0, which was +This file was extended by libsangoma $as_me 3.3.0, which was generated by GNU Autoconf 2.59. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -19711,7 +19711,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ -libsangoma config.status 3.2.0 +libsangoma config.status 3.3.0 configured by $0, generated by GNU Autoconf 2.59, with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" diff --git a/api/libsangoma/.svn/text-base/libsangoma.c.svn-base b/api/libsangoma/.svn/text-base/libsangoma.c.svn-base index e53ce0d..b4cd35f 100644 --- a/api/libsangoma/.svn/text-base/libsangoma.c.svn-base +++ b/api/libsangoma/.svn/text-base/libsangoma.c.svn-base @@ -44,6 +44,10 @@ # define MAX_COMP_DESC 2096 # define MAX_FRIENDLY 2096 # define TMP_BUFFER_LEN 256 + +/*!+! jpboily used to tell get_out_flags no objects were signaled */ +# define INVALID_INDEX (uint32_t) -1 +# define WP_ALL_BITS_SET ((uint32_t)-1) #endif static void libsng_dbg(const char * fmt, ...) @@ -63,14 +67,15 @@ static void libsng_dbg(const char * fmt, ...) /*********************************************************************//** * WINDOWS Only Section *************************************************************************/ +static int libsng_dbg_level = 0; -#define DBG_POLL if(0)libsng_dbg -#define DBG_EVNT if(0)libsng_dbg -#define DBG_ERR if(0)libsng_dbg("Error: %s() line: %d : ", __FUNCTION__, __LINE__);if(0)libsng_dbg -#define DBG_INIT if(0)libsng_dbg +#define DBG_POLL if(libsng_dbg_level)libsng_dbg +#define DBG_EVNT if(libsng_dbg_level)libsng_dbg +#define DBG_ERR if(libsng_dbg_level)libsng_dbg("Error: %s() line: %d : ", __FUNCTION__, __LINE__);if(libsng_dbg_level)libsng_dbg +#define DBG_INIT if(libsng_dbg_level)libsng_dbg #if defined(__WINDOWS__) -#define DBG_REGISTRY if(0)libsng_dbg +#define DBG_REGISTRY if(libsng_dbg_level)libsng_dbg /* \fn static void DecodeLastError(LPSTR lpszFunction) @@ -79,7 +84,7 @@ static void libsng_dbg(const char * fmt, ...) Private Windows Only Function */ -static void DecodeLastError(LPSTR lpszFunction) +static void LibSangomaDecodeLastError(LPSTR lpszFunction) { LPVOID lpMsgBuf; DWORD dwLastErr = GetLastError(); @@ -95,7 +100,7 @@ static void DecodeLastError(LPSTR lpszFunction) NULL ); /* Display the string. */ - DBG_POLL("Last Error in %s(): %s (%d)\n", lpszFunction, lpMsgBuf, dwLastErr); + DBG_POLL("Last Error in %s(): %s (%d)\n", lpszFunction, (char*)lpMsgBuf, dwLastErr); /* Free the buffer. */ LocalFree( lpMsgBuf ); } @@ -111,11 +116,10 @@ static u16 handle_device_ioctl_result(int bResult, char *caller_name) { if(bResult == 0){ /*error*/ - DecodeLastError(caller_name); - return 1; - + LibSangomaDecodeLastError(caller_name); + return SANG_STATUS_IO_ERROR; }else{ - return 0; + return SANG_STATUS_SUCCESS; } } @@ -151,14 +155,14 @@ static int UdpManagementCommand(sng_fd_t fd, wan_udp_hdr_t* wan_udp) } /* - \fn static int TdmvApiCommand(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) - \brief Executes Driver TDM API Command + \fn static int tdmv_api_ioctl(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) + \brief Executes Driver TDM API Command Wrapper Function \param fd device file descriptor \param api_cmd tdm_api managemet cmd structure Private Windows Function */ -static int TdmvApiCommand(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) +static int tdmv_api_ioctl(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) { DWORD ln, bIoResult; @@ -176,23 +180,6 @@ static int TdmvApiCommand(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) return handle_device_ioctl_result(bIoResult, __FUNCTION__); } -/* - \fn static int tdmv_api_ioctl(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) - \brief Executes Driver TDM API Command Wrapper Function - \param fd device file descriptor - \param api_cmd tdm_api managemet cmd structure - - Private Windows Function - */ -static int tdmv_api_ioctl(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) -{ - if(TdmvApiCommand(fd, api_cmd)){ - return SANG_STATUS_GENERAL_ERROR; - } - - return api_cmd->result; -} - /* \fn static USHORT DoReadCommand(sng_fd_t fd, RX_DATA_STRUCT * pRx) \brief API READ Function @@ -276,7 +263,33 @@ static USHORT sangoma_poll_fd(sng_fd_t fd, API_POLL_STRUCT *api_poll_ptr) return handle_device_ioctl_result(bIoResult, __FUNCTION__); } -static int CdevCtrlCommand(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) +/* + \fn static int logger_api_ioctl(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Executes Logger API IOCTL + \param fd device file descriptor + \param logger_cmd Logger API command structure + + Private Windows Function + */ +static sangoma_status_t logger_api_ioctl(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + DWORD ln, bIoResult; + + bIoResult = DeviceIoControl( + fd, + IoctlLoggerApiCommand, + (LPVOID)logger_cmd, + sizeof(wp_logger_cmd_t), + (LPVOID)logger_cmd, + sizeof(wp_logger_cmd_t), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL ); + + return handle_device_ioctl_result(bIoResult, __FUNCTION__); +} + +/* This function is exported only for debugging purposes and NOT a part of API. */ +sangoma_status_t _LIBSNG_CALL sangoma_cdev_ctrl_cmd(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) { DWORD ln, bIoResult; @@ -294,15 +307,6 @@ static int CdevCtrlCommand(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) return handle_device_ioctl_result(bIoResult, __FUNCTION__); } -/* This function is exported only for debugging purposes and NOT a part of API. */ -sangoma_status_t _SAPI_CALL sangoma_cdev_ctrl_cmd(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) -{ - if(CdevCtrlCommand(fd, api_cmd)){ - return SANG_STATUS_GENERAL_ERROR; - } - return api_cmd->result; -} - static sangoma_status_t init_sangoma_event_object(sangoma_wait_obj_t *sng_wait_obj, int flags_in, sng_fd_t fd) { int event_index = -1; @@ -356,14 +360,14 @@ static sangoma_status_t init_sangoma_event_object(sangoma_wait_obj_t *sng_wait_o sng_wait_obj->sng_event_objects[event_index] = OpenEvent(EVENT_ALL_ACCESS, TRUE, event_name); if(NULL == sng_wait_obj->sng_event_objects[event_index]){ /* error */ - DecodeLastError(__FUNCTION__); + LibSangomaDecodeLastError(__FUNCTION__); return SANG_STATUS_GENERAL_ERROR; } return SANG_STATUS_SUCCESS; } -static sangoma_status_t _SAPI_CALL sangoma_wait_obj_poll(sangoma_wait_obj_t *sangoma_wait_object, int flags_in, int *flags_out) +static sangoma_status_t sangoma_wait_obj_poll(sangoma_wait_obj_t *sangoma_wait_object, int flags_in, int *flags_out) { int err; sangoma_wait_obj_t *sng_wait_obj = sangoma_wait_object; @@ -390,7 +394,7 @@ static sangoma_status_t _SAPI_CALL sangoma_wait_obj_poll(sangoma_wait_obj_t *san } if(*flags_out == 0){ - DBG_POLL("======%s(): *flags_out: 0x%X, flags_in: 0x%X\n", __FUNCTION__, *flags_out, flags_in); + /*DBG_POLL("======%s(): *flags_out: 0x%X, flags_in: 0x%X\n", __FUNCTION__, *flags_out, flags_in);*/ } return err; } @@ -406,6 +410,7 @@ static int check_number_of_wait_objects(uint32_t number_of_objects, const char * } static sangoma_status_t get_out_flags(IN sangoma_wait_obj_t *sng_wait_objects[], + IN uint32_t first_signaled_obj_index, IN uint32_t in_flags[], OUT uint32_t out_flags[], IN uint32_t number_of_sangoma_wait_objects, OUT BOOL *at_least_one_poll_set_flags_out) @@ -419,7 +424,18 @@ static sangoma_status_t get_out_flags(IN sangoma_wait_obj_t *sng_wait_objects[], for(i = 0; i < number_of_sangoma_wait_objects; i++) { sangoma_wait_obj_t *sangoma_wait_object = sng_wait_objects[i]; + if (!SANGOMA_OBJ_HAS_DEVICE(sangoma_wait_object)) { + + /* This object does not has a device, but, may have been signaled via sangoma_wait_obj_signal() + * test if the object is signaled, if it is, set SANG_WAIT_OBJ_IS_SIGNALED */ + + if((i == first_signaled_obj_index) || (WaitForSingleObject(sangoma_wait_object->generic_event_object, 0) == WAIT_OBJECT_0)) { + /* !+! jpboily : Since WaitForMultipleObjects cleared the status + * of the first signaled object, we make sure that the out_flag + * for this object is set */ + out_flags[i] |= SANG_WAIT_OBJ_IS_SIGNALED; + } continue; } @@ -438,8 +454,14 @@ static sangoma_status_t get_out_flags(IN sangoma_wait_obj_t *sng_wait_objects[], *at_least_one_poll_set_flags_out = TRUE; } } - } - } +#if 0 + /* Check if a device-related object was signalled, if it is, set SANG_WAIT_OBJ_IS_SIGNALED. */ + if (WaitForSingleObject(sangoma_wait_object->sng_event_objects[j], 0) == WAIT_OBJECT_0) { + out_flags[i] |= SANG_WAIT_OBJ_IS_SIGNALED; + } +#endif + }/* for(j = 0; j < LIBSNG_NUMBER_OF_EVENT_OBJECTS; j++) */ + }/* for(i = 0; i < number_of_sangoma_wait_objects; i++) */ return SANG_STATUS_SUCCESS; } @@ -447,48 +469,69 @@ static sangoma_status_t get_out_flags(IN sangoma_wait_obj_t *sng_wait_objects[], /*! \brief Brief description */ -static void registry_get_string_value(HKEY hkey, LPTSTR szKeyname, OUT LPSTR szValue, OUT DWORD *pdwSize) +static LONG registry_get_string_value(HKEY hkey, LPTSTR szKeyname, OUT LPSTR szValue, OUT DWORD *pdwSize) { + LONG iReturnCode; + /* reading twice to set pdwSize to needed value */ RegQueryValueEx(hkey, szKeyname, NULL, NULL, (unsigned char *)szValue, pdwSize); - RegQueryValueEx(hkey, szKeyname, NULL, NULL, (unsigned char *)szValue, pdwSize); - DBG_REGISTRY("%s(): %s: %s\n", __FUNCTION__, szKeyname, szValue); + + iReturnCode = RegQueryValueEx(hkey, szKeyname, NULL, NULL, (unsigned char *)szValue, pdwSize); + if(ERROR_SUCCESS == iReturnCode){ + iReturnCode = 0; + } + DBG_REGISTRY("%s(): %s: %s: iReturnCode: %d\n", __FUNCTION__, szKeyname, szValue, iReturnCode); + return iReturnCode; } /*! \brief Brief description */ -static void registry_set_string_value(HKEY hkey, LPTSTR szKeyname, IN LPSTR szValue) +static LONG registry_set_string_value(HKEY hkey, LPTSTR szKeyname, IN LPSTR szValue) { DWORD dwSize; + LONG iReturnCode; - dwSize = strlen(szValue) + 1; - RegSetValueEx(hkey, szKeyname, 0, REG_SZ, (unsigned char *)szValue, dwSize); + dwSize = (DWORD)strlen(szValue) + 1; + + iReturnCode = RegSetValueEx(hkey, szKeyname, 0, REG_SZ, (unsigned char *)szValue, dwSize); DBG_REGISTRY("%s(): %s: %s\n", __FUNCTION__, szKeyname, szValue); + + if(ERROR_SUCCESS == iReturnCode){ + iReturnCode = 0; + } + return iReturnCode; } /*! \brief Convert an integer (iValue) to string and write it to registry */ -static void registry_set_integer_value(HKEY hkey, LPTSTR szKeyname, IN int iValue) +static LONG registry_set_integer_value(HKEY hkey, LPTSTR szKeyname, IN int iValue) { DWORD dwSize; char szTemp[TMP_BUFFER_LEN]; + LONG iReturnCode; sprintf(szTemp, "%u", iValue); - dwSize = strlen(szTemp) + 1; - RegSetValueEx(hkey, szKeyname, 0, REG_SZ, (unsigned char *)szTemp, dwSize); - DBG_REGISTRY("%s(): %s: %d\n", __FUNCTION__, szKeyname, iValue); + dwSize = (DWORD)strlen(szTemp) + 1; + iReturnCode = RegSetValueEx(hkey, szKeyname, 0, REG_SZ, (unsigned char *)szTemp, dwSize); + + if(ERROR_SUCCESS == iReturnCode){ + iReturnCode = 0; + } + + DBG_REGISTRY("%s(): %s: %d: iReturnCode: %d\n", __FUNCTION__, szKeyname, iValue, iReturnCode); + return iReturnCode; } /*! * \brief Go through the list of ALL "Sangoma Hardware Abstraction Driver" ports installed on the system. - * Read Bus/Slot/Port information for a port and copare with what is searched for. + * Read Bus/Slot/Port information for a port and compare with what is searched for. */ static HKEY registry_open_port_key(hardware_info_t *hardware_info) { - int i; + int i, iRegistryReturnCode; SP_DEVINFO_DATA deid={sizeof(SP_DEVINFO_DATA)}; HDEVINFO hdi = SetupDiGetClassDevs((struct _GUID *)&GUID_DEVCLASS_SANGOMA_ADAPTER, NULL,NULL, DIGCF_PRESENT); char DeviceName[TMP_BUFFER_LEN], PCI_Bus[TMP_BUFFER_LEN], PCI_Slot[TMP_BUFFER_LEN], Port_Number[TMP_BUFFER_LEN]; @@ -496,12 +539,10 @@ static HKEY registry_open_port_key(hardware_info_t *hardware_info) HKEY hKeyTmp = (struct HKEY__ *)INVALID_HANDLE_VALUE; HKEY hPortRegistryKey = (struct HKEY__ *)INVALID_HANDLE_VALUE; - TCHAR name[MAX_FRIENDLY]; char szCompInstanceId[MAX_COMP_INSTID]; TCHAR szCompDescription[MAX_COMP_DESC]; DWORD dwRegType; - - /* Possible Port Names (refer to sdladrv.inf): + /* Possible Sangoma Port Names (refer to sdladrv.inf): Sangoma Hardware Abstraction Driver (Port 1) Sangoma Hardware Abstraction Driver (Port 2) Sangoma Hardware Abstraction Driver (Port 3) @@ -512,23 +553,22 @@ static HKEY registry_open_port_key(hardware_info_t *hardware_info) Sangoma Hardware Abstraction Driver (Port 8) Sangoma Hardware Abstraction Driver (Analog) Sangoma Hardware Abstraction Driver (ISDN BRI) */ + const TCHAR *search_SubStr = "Sangoma Hardware Abstraction Driver"; /* search for all (AFT) ports: */ - sprintf(name," Sangoma Hardware Abstraction Driver"); - for (i = 0; SetupDiEnumDeviceInfo(hdi, i, &deid); i++){ - BOOL fSuccess = SetupDiGetDeviceInstanceId(hdi, &deid, (PSTR)szCompInstanceId,MAX_COMP_INSTID, NULL); + BOOL fSuccess = SetupDiGetDeviceInstanceId(hdi, &deid, (PSTR)szCompInstanceId, MAX_COMP_INSTID, NULL); if (!fSuccess){ continue; } - fSuccess = SetupDiGetDeviceRegistryProperty(hdi, &deid,SPDRP_DEVICEDESC,&dwRegType, (BYTE*)szCompDescription, MAX_COMP_DESC, NULL); + fSuccess = SetupDiGetDeviceRegistryProperty(hdi, &deid, SPDRP_DEVICEDESC, &dwRegType, (BYTE*)szCompDescription, MAX_COMP_DESC, NULL); if (!fSuccess){ continue; } - if (strncmp(szCompDescription, name, strlen(name)) != 0) { /* Windows can add #2 etc - do NOT consider */ + if (!strstr(szCompDescription, search_SubStr)) { /* Windows can add #2 etc - do NOT consider */ /* This is a "Sangoma Card" device, we are interested only in "Sangoma Port" devices. */ continue; } @@ -542,13 +582,22 @@ static HKEY registry_open_port_key(hardware_info_t *hardware_info) } PCI_Bus[0] = '\0'; - registry_get_string_value(hKeyTmp, "PCI_Bus", PCI_Bus, &dwTemp); + iRegistryReturnCode = registry_get_string_value(hKeyTmp, "PCI_Bus", PCI_Bus, &dwTemp); + if(iRegistryReturnCode){ + continue; + } PCI_Slot[0] = '\0'; - registry_get_string_value(hKeyTmp, "PCI_Slot", PCI_Slot, &dwTemp); + iRegistryReturnCode = registry_get_string_value(hKeyTmp, "PCI_Slot", PCI_Slot, &dwTemp); + if(iRegistryReturnCode){ + continue; + } Port_Number[0] = '\0'; - registry_get_string_value(hKeyTmp, "Port_Number", Port_Number, &dwTemp); + iRegistryReturnCode = registry_get_string_value(hKeyTmp, "Port_Number", Port_Number, &dwTemp); + if(iRegistryReturnCode){ + continue; + } if( atoi(PCI_Bus) == hardware_info->pci_bus_number && atoi(PCI_Slot) == hardware_info->pci_slot_number && @@ -556,6 +605,7 @@ static HKEY registry_open_port_key(hardware_info_t *hardware_info) hPortRegistryKey = hKeyTmp; + /* read device name for debugging only */ DeviceName[0] = '\0'; registry_get_string_value(hPortRegistryKey, "DeviceName", DeviceName, &dwTemp); @@ -572,72 +622,16 @@ static HKEY registry_open_port_key(hardware_info_t *hardware_info) return hPortRegistryKey; } -static int registry_write_front_end_cfg(HKEY hPortRegistryKey, port_cfg_t *port_cfg) -{ - wandev_conf_t *wandev_conf = &port_cfg->wandev_conf; - sdla_fe_cfg_t *sdla_fe_cfg = &wandev_conf->fe_cfg; - sdla_te_cfg_t *te_cfg = &sdla_fe_cfg->cfg.te_cfg; - sdla_remora_cfg_t *analog_cfg = &sdla_fe_cfg->cfg.remora; - sdla_bri_cfg_t *bri_cfg = &sdla_fe_cfg->cfg.bri; - - /* set Media specific values. */ - switch(sdla_fe_cfg->media) - { - case WAN_MEDIA_T1: - case WAN_MEDIA_J1: /* the same as T1 */ - case WAN_MEDIA_E1: - /* only T1/E1 Port can change the Media, all other ports ignore this parameter. */ - registry_set_integer_value(hPortRegistryKey, "Media", sdla_fe_cfg->media /*WAN_MEDIA_T1*/); - registry_set_integer_value(hPortRegistryKey, "LDecoding", sdla_fe_cfg->lcode /*WAN_LCODE_B8ZS*/); - registry_set_integer_value(hPortRegistryKey, "Framing", sdla_fe_cfg->frame /*WAN_FR_ESF*/); - - registry_set_integer_value(hPortRegistryKey, "ClkRefPort", te_cfg->te_ref_clock); - registry_set_integer_value(hPortRegistryKey, "TE_IGNORE_YEL", te_cfg->ignore_yel_alarm); - registry_set_integer_value(hPortRegistryKey, "ACTIVE_CH", ENABLE_ALL_CHANNELS /*must be hardcoded*/); - registry_set_integer_value(hPortRegistryKey, "TE_RBS_CH", te_cfg->te_rbs_ch); /*not used by DS chip code, only by PMC */ - - registry_set_integer_value(hPortRegistryKey, "LBO", te_cfg->lbo /*WAN_T1_LBO_0_DB*/); - registry_set_integer_value(hPortRegistryKey, "ClkMode", te_cfg->te_clock /*WAN_NORMAL_CLK*/); - registry_set_integer_value(hPortRegistryKey, "HighImpedanceMode",te_cfg->high_impedance_mode /*WANOPT_NO*/); - registry_set_integer_value(hPortRegistryKey, "TE_RX_SLEVEL", te_cfg->rx_slevel /*WAN_TE1_RX_SLEVEL_12_DB*/); - /* write E1 signalling for both T1 and E1 */ - registry_set_integer_value(hPortRegistryKey, "E1Signalling", te_cfg->sig_mode /*WAN_TE1_SIG_CCS*/); - break; - case WAN_MEDIA_56K: - /* do nothing */ - break; - case WAN_MEDIA_FXOFXS: - /* Analog global (per-card) settings */ - registry_set_string_value(hPortRegistryKey, "remora_fxo_operation_mode_name", analog_cfg->opermode_name); - registry_set_integer_value(hPortRegistryKey, "RM_BATTTHRESH", analog_cfg->battthresh); - registry_set_integer_value(hPortRegistryKey, "RM_BATTDEBOUNCE", analog_cfg->battdebounce); - registry_set_integer_value(hPortRegistryKey, "RM_FXO_TAPPING", analog_cfg->rm_mode); - registry_set_integer_value(hPortRegistryKey, "RM_FXO_TAPPING_OFF_HOOK_THRESHOLD",analog_cfg->ohthresh); - break; - case WAN_MEDIA_BRI: - registry_set_integer_value(hPortRegistryKey, "aft_bri_clock_mode", bri_cfg->clock_mode); - break; - case WAN_MEDIA_SERIAL: - registry_set_integer_value(hPortRegistryKey, "clock_source", wandev_conf->clocking); - registry_set_integer_value(hPortRegistryKey, "BaudRate", wandev_conf->bps); - registry_set_integer_value(hPortRegistryKey, "serial_connection_type", wandev_conf->connection); - registry_set_integer_value(hPortRegistryKey, "serial_line_coding", wandev_conf->line_coding); - registry_set_integer_value(hPortRegistryKey, "serial_line_idle", wandev_conf->line_idle); - break; - default: - DBG_ERR("Invalid Media Type %d!\n", sdla_fe_cfg->media); - return 1; - } - - return 0; -} - static char* timeslot_bitmap_to_string(int bitmap) { int i = 0, range_counter = 0; int start_channel = -1, stop_channel = -1; static char tmp_string[256]; + if( WP_ALL_BITS_SET == bitmap ){ + return "ALL"; /* If all bits are set, use the "ALL" keyword. */ + } + tmp_string[0] = '\0'; /* all ranges between two zeros is what we will look for */ @@ -646,7 +640,7 @@ static char* timeslot_bitmap_to_string(int bitmap) if(start_channel < 0){ /* found a starting one of a range */ if(bitmap & (1 << i)){ - start_channel = i; + start_channel = i + 1; } } @@ -654,14 +648,14 @@ static char* timeslot_bitmap_to_string(int bitmap) if((bitmap & (1 << i)) == 0){ /* we hit a zero, that means the previous channel is one */ - stop_channel = i - 1; + stop_channel = i; }else if(i == (sizeof(bitmap) * 8 - 1)){ /* The most significant bit is set - there will be no delimiting zero. * It will also take care of "all channels" which is a special * case - there is a start but no stop channel because all bits - * are set. result will be 0-31 */ - stop_channel = (sizeof(bitmap) * 8 - 1); + * are set. result will be 1-32 */ + stop_channel = (sizeof(bitmap) * 8); } } @@ -690,34 +684,549 @@ static char* timeslot_bitmap_to_string(int bitmap) return tmp_string; } +static int registry_write_front_end_cfg(HKEY hPortRegistryKey, port_cfg_t *port_cfg) +{ + wandev_conf_t *wandev_conf = &port_cfg->wandev_conf; + sdla_fe_cfg_t *sdla_fe_cfg = &wandev_conf->fe_cfg; + sdla_te_cfg_t *te_cfg = &sdla_fe_cfg->cfg.te_cfg; + sdla_remora_cfg_t *analog_cfg = &sdla_fe_cfg->cfg.remora; + sdla_bri_cfg_t *bri_cfg = &sdla_fe_cfg->cfg.bri; + int iReturnCode = 0; + + /* Set Card/Port-wide values, which are independent of Media type, and + * stored in sdla_fe_cfg_t. */ + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "tdmv_law", FE_TDMV_LAW(sdla_fe_cfg) /* WAN_TDMV_MULAW/WAN_TDMV_ALAW */); + if(iReturnCode){ + return iReturnCode; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "ExternalSynchronization", FE_NETWORK_SYNC(sdla_fe_cfg) /* WANOPT_NO/WANOPT_YES */); + if(iReturnCode){ + return iReturnCode; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "FE_TXTRISTATE", FE_TXTRISTATE(sdla_fe_cfg) /* WANOPT_NO/WANOPT_YES */); + if(iReturnCode){ + return iReturnCode; + } + + /* set Media specific values. */ + switch(sdla_fe_cfg->media) + { + case WAN_MEDIA_T1: + case WAN_MEDIA_J1: /* the same as T1 */ + case WAN_MEDIA_E1: + /* only T1/E1 Port can change the Media, all other ports ignore this parameter. */ + iReturnCode = registry_set_integer_value(hPortRegistryKey, "Media", sdla_fe_cfg->media /*WAN_MEDIA_T1*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "LDecoding", sdla_fe_cfg->lcode /*WAN_LCODE_B8ZS*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "Framing", sdla_fe_cfg->frame /*WAN_FR_ESF*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "ClkRefPort", te_cfg->te_ref_clock); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "TE_IGNORE_YEL", te_cfg->ignore_yel_alarm); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "ACTIVE_CH", ENABLE_ALL_CHANNELS /*must be hardcoded*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "TE_RBS_CH", te_cfg->te_rbs_ch); /*not used by DS chip code, only by PMC */ + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "LBO", te_cfg->lbo /*WAN_T1_LBO_0_DB*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "ClkMode", te_cfg->te_clock /*WAN_NORMAL_CLK*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "HighImpedanceMode",te_cfg->high_impedance_mode /*WANOPT_NO*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "TE_RX_SLEVEL", te_cfg->rx_slevel /*WAN_TE1_RX_SLEVEL_12_DB*/); + if(iReturnCode){ + break; + } + + /* write E1 signalling for both T1 and E1 */ + iReturnCode = registry_set_integer_value(hPortRegistryKey, "E1Signalling", te_cfg->sig_mode /*WAN_TE1_SIG_CCS*/); + if(iReturnCode){ + break; + } + break; + + case WAN_MEDIA_56K: + /* do nothing */ + iReturnCode = 0; + break; + + case WAN_MEDIA_FXOFXS: + /* Analog global (per-card) settings */ + iReturnCode = registry_set_string_value(hPortRegistryKey, "remora_fxo_operation_mode_name", analog_cfg->opermode_name); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_BATTTHRESH", analog_cfg->battthresh); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_BATTDEBOUNCE", analog_cfg->battdebounce); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_FXO_TAPPING", analog_cfg->rm_mode); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_FXO_TAPPING_OFF_HOOK_THRESHOLD",analog_cfg->ohthresh); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_LCM", analog_cfg->rm_lcm); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_FXSTXGAIN", analog_cfg->fxs_txgain); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_FXSRXGAIN", analog_cfg->fxs_rxgain); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_FXOTXGAIN", analog_cfg->fxo_txgain); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_FXORXGAIN", analog_cfg->fxo_rxgain); + if(iReturnCode){ + break; + } + break; + + case WAN_MEDIA_BRI: + iReturnCode = registry_set_integer_value(hPortRegistryKey, "aft_bri_clock_mode", bri_cfg->clock_mode); + if(iReturnCode){ + break; + } + break; + + case WAN_MEDIA_SERIAL: + iReturnCode = registry_set_integer_value(hPortRegistryKey, "clock_source", wandev_conf->clocking); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "BaudRate", wandev_conf->bps); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "serial_connection_type", wandev_conf->connection); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "serial_line_coding", wandev_conf->line_coding); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "serial_line_idle", wandev_conf->line_idle); + if(iReturnCode){ + break; + } + break; + + default: + DBG_ERR("Invalid Media Type %d!\n", sdla_fe_cfg->media); + iReturnCode = 1; + } + + return iReturnCode; +} + +static int registry_write_wan_tdmv_conf(HKEY hPortRegistryKey, port_cfg_t *port_cfg) +{ + wandev_conf_t *wandev_conf = &port_cfg->wandev_conf; + sdla_fe_cfg_t *sdla_fe_cfg = &wandev_conf->fe_cfg; + wan_tdmv_conf_t *tdmv_conf = &wandev_conf->tdmv_conf; + int iReturnCode = 1; + + /* set Media specific values. */ + switch(sdla_fe_cfg->media) + { + case WAN_MEDIA_T1: + case WAN_MEDIA_J1: /* the same as T1 */ + case WAN_MEDIA_E1: + /* write dchannel bitmap as a string (the same way as active channels) */ + iReturnCode = registry_set_string_value(hPortRegistryKey, "TDMV_DCHAN", timeslot_bitmap_to_string(tdmv_conf->dchan)); + break; + + case WAN_MEDIA_56K: + case WAN_MEDIA_FXOFXS: + case WAN_MEDIA_BRI: + case WAN_MEDIA_SERIAL: + /* do nothing */ + iReturnCode = 0; + break; + + default: + DBG_ERR("Invalid Media Type %d!\n", sdla_fe_cfg->media); + iReturnCode = 2; + break; + } + + return iReturnCode; +} + static int registry_write_channel_group_cfg(HKEY hPortRegistryKey, port_cfg_t *port_cfg, int interface_index, wanif_conf_t wanif_conf) { char szTemp[TMP_BUFFER_LEN]; + int iReturnCode = 0; - // Line mode - _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_line_mode", interface_index); - registry_set_string_value(hPortRegistryKey, szTemp, - (wanif_conf.hdlc_streaming == WANOPT_YES ? MODE_OPTION_HDLC : MODE_OPTION_BITSTRM)); + do{ + // Line mode + _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_line_mode", interface_index); + iReturnCode = registry_set_string_value(hPortRegistryKey, szTemp, + (wanif_conf.hdlc_streaming == WANOPT_YES ? MODE_OPTION_HDLC : MODE_OPTION_BITSTRM)); + if(iReturnCode){ + break; + } - // MTU - _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_mtu", interface_index); - registry_set_integer_value(hPortRegistryKey, szTemp, wanif_conf.mtu); + // MTU + _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_mtu", interface_index); + iReturnCode = registry_set_integer_value(hPortRegistryKey, szTemp, wanif_conf.mtu); + if(iReturnCode){ + break; + } - // Operational mode - _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_operational_mode", interface_index); - registry_set_string_value(hPortRegistryKey, szTemp, wanif_conf.usedby); + // Operational mode + _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_operational_mode", interface_index); + iReturnCode = registry_set_string_value(hPortRegistryKey, szTemp, wanif_conf.usedby); + if(iReturnCode){ + break; + } - // Active Timeslots for the Group - _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_active_ch", interface_index); - registry_set_string_value(hPortRegistryKey, szTemp, timeslot_bitmap_to_string(wanif_conf.active_ch)); + // Active Timeslots for the Group + _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_active_ch", interface_index); + iReturnCode = registry_set_string_value(hPortRegistryKey, szTemp, timeslot_bitmap_to_string(wanif_conf.active_ch)); + if(iReturnCode){ + break; + } - // Idle char. - _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_idle_char", interface_index); - registry_set_integer_value(hPortRegistryKey, szTemp, wanif_conf.u.aft.idle_flag); + // Idle char. + _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_idle_char", interface_index); + iReturnCode = registry_set_integer_value(hPortRegistryKey, szTemp, wanif_conf.u.aft.idle_flag); + if(iReturnCode){ + break; + } - return 0; + }while(0); + + return iReturnCode; } +/*! + * \brief Go through the list of ALL Sangoma PCI Adapters (Cards) on the system. + * Enable or Disable each one of them (state change will be visible in the Device Manager). + */ +static sangoma_status_t sangoma_control_cards(DWORD StateChange) +{ + sangoma_status_t rc; + int i; + SP_DEVINFO_DATA deid = {sizeof(SP_DEVINFO_DATA)}; + HDEVINFO hdi; + TCHAR szInstanceId[MAX_COMP_INSTID]; + + const TCHAR *szAftSearchSubStr = "PCI\\VEN_1923"; /* Sangoma PCI Vendor ID is 1923 */ + /*const TCHAR *szS518AdslSearchSubStr = "PCI\\VEN_14BC&DEV_D002";*/ /* Note: S518 is end-of-life */ + + SP_PROPCHANGE_PARAMS pcp; + + hdi = SetupDiGetClassDevs((struct _GUID *)&GUID_DEVCLASS_SANGOMA_ADAPTER, NULL, NULL, DIGCF_PRESENT); + if(INVALID_HANDLE_VALUE == hdi){ + /* no sangoma cards installed on the system */ + return SANG_STATUS_INVALID_DEVICE; + } + + /* search for all AFT Cards: */ + for (i = 0; SetupDiEnumDeviceInfo(hdi, i, &deid); i++){ + + BOOL fSuccess = SetupDiGetDeviceInstanceId(hdi, &deid, (PSTR)szInstanceId, sizeof(szInstanceId), NULL); + if (!fSuccess){ + rc = SANG_STATUS_REGISTRY_ERROR; + break; + } + if(!strstr(szInstanceId, szAftSearchSubStr)){ + /* Found Sangoma Port, but we are interested only Cards. */ + continue; + } + + memset(&pcp, 0x00, sizeof(pcp)); + //Set the PropChangeParams structure. + pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER); + pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE; + pcp.Scope = DICS_FLAG_GLOBAL; /* DICS_FLAG_CONFIGSPECIFIC will enable ALL cards, including + * those explicetly DISABLED by the user in the Device Manager, + * and we don't want to force anything on the user, hence we + * use DICS_FLAG_GLOBAL. */ + pcp.StateChange = StateChange; + + if(!SetupDiSetClassInstallParams(hdi, &deid, (SP_CLASSINSTALL_HEADER *)&pcp, sizeof(pcp))){ + rc = SANG_STATUS_REGISTRY_ERROR; + break; + } + + if(!SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, hdi, &deid)){ + rc = SANG_STATUS_REGISTRY_ERROR; + break; + } + + }/* for() */ + + SetupDiDestroyDeviceInfoList(hdi); + + return SANG_STATUS_SUCCESS; +} + +sangoma_status_t _LIBSNG_CALL sangoma_unload_driver() +{ + return sangoma_control_cards(DICS_DISABLE); +} + +sangoma_status_t _LIBSNG_CALL sangoma_load_driver() +{ + return sangoma_control_cards(DICS_ENABLE); +} + +/*! + * \brief Go through the list of ALL "Sangoma Hardware Abstraction Driver" ports installed on the system, + * and assign sequential numbers to the Ports, starting from 1. This will override the Automatic Wanpipe + * Span numbering. + */ +void _LIBSNG_CALL sangoma_reset_port_numbers() +{ + int i, iRegistryReturnCode, iPortCounter = 0; + SP_DEVINFO_DATA deid={sizeof(SP_DEVINFO_DATA)}; + HDEVINFO hdi = SetupDiGetClassDevs((struct _GUID *)&GUID_DEVCLASS_SANGOMA_ADAPTER, NULL,NULL, DIGCF_PRESENT); + DWORD dwTemp; + HKEY hKeyTmp = (struct HKEY__ *)INVALID_HANDLE_VALUE; + + char szCompInstanceId[MAX_COMP_INSTID]; + TCHAR szCompDescription[MAX_COMP_DESC]; + DWORD dwRegType; + /* Possible Sangoma Port Names (refer to sdladrv.inf): + Sangoma Hardware Abstraction Driver (Port 1) + Sangoma Hardware Abstraction Driver (Port 2) + Sangoma Hardware Abstraction Driver (Port 3) + Sangoma Hardware Abstraction Driver (Port 4) + Sangoma Hardware Abstraction Driver (Port 5) + Sangoma Hardware Abstraction Driver (Port 6) + Sangoma Hardware Abstraction Driver (Port 7) + Sangoma Hardware Abstraction Driver (Port 8) + Sangoma Hardware Abstraction Driver (Analog) + Sangoma Hardware Abstraction Driver (ISDN BRI) */ + const TCHAR *search_SubStr = "Sangoma Hardware Abstraction Driver"; + + /* search for all (AFT) ports: */ + for (i = 0; SetupDiEnumDeviceInfo(hdi, i, &deid); i++){ + + BOOL fSuccess = SetupDiGetDeviceInstanceId(hdi, &deid, (PSTR)szCompInstanceId, MAX_COMP_INSTID, NULL); + if (!fSuccess){ + continue; + } + + fSuccess = SetupDiGetDeviceRegistryProperty(hdi, &deid, SPDRP_DEVICEDESC, &dwRegType, (BYTE*)szCompDescription, MAX_COMP_DESC, NULL); + if (!fSuccess){ + continue; + } + + if (!strstr(szCompDescription, search_SubStr)) { /* Windows can add #2 etc - do NOT consider */ + /* This is a "Sangoma Card" device, we are interested only in "Sangoma Port" devices. */ + continue; + } + + hKeyTmp = SetupDiOpenDevRegKey(hdi, &deid, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ | KEY_SET_VALUE ); + if(hKeyTmp == INVALID_HANDLE_VALUE){ + DBG_REGISTRY("Error: Failed to open Registry key!!\n"); + continue; + } + + iPortCounter++; + + printf("* %s -> Setting Span/Port number: %d\n", szCompDescription, iPortCounter); + + /* TODO: 1. search for installed Sangoma Cards + 2. for each Card, search Ports (Bus/Slot must match) + 3. based on number of installed Ports, set SerialNumbersRange of the Card + + This way the "UserWanpipeNumber" can be set to zero and Span numbers will be + seqencial automatically. + */ + iRegistryReturnCode = registry_set_integer_value(hKeyTmp, "UserWanpipeNumber", iPortCounter); + if(iRegistryReturnCode){ + continue; + } + + RegCloseKey(hKeyTmp); + + }/* for() */ + + SetupDiDestroyDeviceInfoList(hdi); + + return; +} + + +/*! + * \brief Get version of driver from the Windows Registry, as it was written by .inf parser. + * Note that there is a possibility that currently loaded driver has a different + * version (if the original binary file was overwritten). + */ +sangoma_status_t _LIBSNG_CALL sangoma_get_driver_version_from_registry(char *out_buffer, int out_buffer_length) +{ + int i, iRegistryReturnCode; + SP_DEVINFO_DATA deid = {sizeof(SP_DEVINFO_DATA)}; + HDEVINFO hdi = SetupDiGetClassDevs((struct _GUID *)&GUID_DEVCLASS_SANGOMA_ADAPTER, NULL, NULL, DIGCF_PRESENT); + DWORD dwTemp; + HKEY hKeyTmp = (struct HKEY__ *)INVALID_HANDLE_VALUE; + + TCHAR szTmp[MAX_COMP_DESC]; + BOOL fSuccess = FALSE; + + /* search for ANY Sangoma device of class GUID_DEVCLASS_SANGOMA_ADAPTER */ + for (i = 0; SetupDiEnumDeviceInfo(hdi, i, &deid); i++){ + + fSuccess = SetupDiGetDeviceInstanceId(hdi, &deid, (PSTR)szTmp, sizeof(szTmp), NULL); + if (!fSuccess){ + break; + } + DBG_REGISTRY("%s(): Device Instance Id: %s\n", __FUNCTION__, szTmp); + /* Device Instance Id: COMMSADAPTER\SANGOMAADAPTER_AFT_LINE8\5&32E1B75F&0&13 + Device Instance Id: PCI\VEN_1923&DEV_0100&SUBSYS_4113A114&REV_00\4&31B6CD7&0&10F0 */ + + hKeyTmp = SetupDiOpenDevRegKey( hdi, &deid, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ | KEY_SET_VALUE ); + if(hKeyTmp == INVALID_HANDLE_VALUE){ + DBG_REGISTRY("Error: Failed to open Registry key!!\n"); + fSuccess = FALSE; + break; + } + + iRegistryReturnCode = registry_get_string_value(hKeyTmp, "DriverVersion", szTmp, &dwTemp); + if(iRegistryReturnCode){ + fSuccess = FALSE; + break; + } + + wp_snprintf(out_buffer, out_buffer_length, szTmp); + + RegCloseKey(hKeyTmp); + + /* it is enough to read the version only once, so break here */ + break; + }/* for() */ + + SetupDiDestroyDeviceInfoList(hdi); + + if (!fSuccess){ + return SANG_STATUS_REGISTRY_ERROR; + }else{ + return SANG_STATUS_SUCCESS; + } +} + +/*! + * \brief Set the Driver Mode of all Hardware (not virtual) devices. + * The modes are: Normal + * Firmware Update + */ +sangoma_status_t _LIBSNG_CALL sangoma_set_driver_mode_of_all_hw_devices(int driver_mode) +{ + sangoma_status_t rc; + int i, iRegistryReturnCode; + SP_DEVINFO_DATA deid = {sizeof(SP_DEVINFO_DATA)}; + HDEVINFO hdi; + TCHAR szInstanceId[MAX_COMP_INSTID]; + HKEY hKeyTmp = (struct HKEY__ *)INVALID_HANDLE_VALUE; + + const TCHAR *szAftSearchSubStr = "PCI\\VEN_1923"; /* Sangoma PCI Vendor ID is 1923 */ + + hdi = SetupDiGetClassDevs((struct _GUID *)&GUID_DEVCLASS_SANGOMA_ADAPTER, NULL, NULL, DIGCF_PRESENT); + if(INVALID_HANDLE_VALUE == hdi){ + /* no sangoma cards installed on the system */ + return SANG_STATUS_INVALID_DEVICE; + } + + /* search for all AFT Cards: */ + for (i = 0; SetupDiEnumDeviceInfo(hdi, i, &deid); i++){ + + BOOL fSuccess = SetupDiGetDeviceInstanceId(hdi, &deid, (PSTR)szInstanceId, sizeof(szInstanceId), NULL); + if (!fSuccess){ + rc = SANG_STATUS_REGISTRY_ERROR; + break; + } + if(!strstr(szInstanceId, szAftSearchSubStr)){ + /* Found Sangoma Port, but we are interested only Cards. */ + continue; + } + + hKeyTmp = SetupDiOpenDevRegKey(hdi, &deid, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ | KEY_SET_VALUE ); + if(hKeyTmp == INVALID_HANDLE_VALUE){ + rc = SANG_STATUS_REGISTRY_ERROR; + break; + } + + iRegistryReturnCode = registry_set_integer_value(hKeyTmp, "driver_mode", driver_mode); + if(iRegistryReturnCode){ + rc = SANG_STATUS_REGISTRY_ERROR; + break; + } + + RegCloseKey(hKeyTmp); + + }/* for() */ + + SetupDiDestroyDeviceInfoList(hdi); + + return SANG_STATUS_SUCCESS; +} + + #endif /* __WINDOWS__ */ @@ -740,10 +1249,10 @@ static int registry_write_channel_group_cfg(HKEY hPortRegistryKey, port_cfg_t *p \see sangoma_wait_obj_type_t \return SANG_STATUS_SUCCESS: success, or error status */ -sangoma_status_t _SAPI_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma_wait_object, sng_fd_t fd, sangoma_wait_obj_type_t object_type) +sangoma_status_t _LIBSNG_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma_wait_object, sng_fd_t fd, sangoma_wait_obj_type_t object_type) { int err = 0; - sangoma_wait_obj_t *sng_wait_obj; + sangoma_wait_obj_t *sng_wait_obj = NULL; if (!sangoma_wait_object) { return SANG_STATUS_INVALID_DEVICE; @@ -769,7 +1278,8 @@ sangoma_status_t _SAPI_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma if (SANGOMA_OBJ_IS_SIGNALABLE(sng_wait_obj)) { sng_wait_obj->generic_event_object = CreateEvent( NULL, FALSE, FALSE, NULL); if(!sng_wait_obj->generic_event_object){ - return SANG_STATUS_GENERAL_ERROR; + err = SANG_STATUS_GENERAL_ERROR; + goto failed; } } @@ -781,17 +1291,17 @@ sangoma_status_t _SAPI_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma err = init_sangoma_event_object(sng_wait_obj, POLLIN, fd); if(SANG_STATUS_SUCCESS != err){ - return err; + goto failed; } err = init_sangoma_event_object(sng_wait_obj, POLLOUT, fd); if(SANG_STATUS_SUCCESS != err){ - return err; + goto failed; } err = init_sangoma_event_object(sng_wait_obj, POLLPRI, fd); if(SANG_STATUS_SUCCESS != err) { - return err; + goto failed; } DBG_INIT("%s(): returning: %d", __FUNCTION__, err); @@ -802,7 +1312,8 @@ sangoma_status_t _SAPI_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma sng_wait_obj->signal_write_fd = INVALID_HANDLE_VALUE; /* if we want cross-process event notification we can use a named pipe with mkfifo() */ if (pipe(filedes)) { - return -1; + err = SANG_STATUS_GENERAL_ERROR; + goto failed; } sng_wait_obj->signal_read_fd = filedes[0]; sng_wait_obj->signal_write_fd = filedes[1]; @@ -810,6 +1321,12 @@ sangoma_status_t _SAPI_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma #endif *sangoma_wait_object = sng_wait_obj; return err; + +failed: + if (sng_wait_obj) { + sangoma_wait_obj_delete(&sng_wait_obj); + } + return err; } /*! @@ -818,7 +1335,7 @@ sangoma_status_t _SAPI_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma \param sangoma_wait_object pointer to a pointer to a single device object \return SANG_STATUS_SUCCESS on success or some sangoma status error */ -sangoma_status_t _SAPI_CALL sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object) +sangoma_status_t _LIBSNG_CALL sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object) { sangoma_wait_obj_t *sng_wait_obj = *sangoma_wait_object; #if defined(__WINDOWS__) @@ -847,6 +1364,7 @@ sangoma_status_t _SAPI_CALL sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma #endif sng_wait_obj->init_flag = 0; sng_wait_obj->object_type = UNKNOWN_WAIT_OBJ; + free(sng_wait_obj); *sangoma_wait_object = NULL; return SANG_STATUS_SUCCESS; } @@ -857,7 +1375,7 @@ sangoma_status_t _SAPI_CALL sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma \param sangoma_wait_object pointer a single device object \return 0 on success, non-zero on error */ -int _SAPI_CALL sangoma_wait_obj_signal(sangoma_wait_obj_t *sng_wait_obj) +int _LIBSNG_CALL sangoma_wait_obj_signal(sangoma_wait_obj_t *sng_wait_obj) { if (!SANGOMA_OBJ_IS_SIGNALABLE(sng_wait_obj)) { /* even when Windows objects are always signalable for the sake of providing @@ -887,7 +1405,7 @@ int _SAPI_CALL sangoma_wait_obj_signal(sangoma_wait_obj_t *sng_wait_obj) \param sangoma_wait_object pointer a single device object \return fd - device file descriptor */ -sng_fd_t _SAPI_CALL sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sng_wait_obj) +sng_fd_t _LIBSNG_CALL sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sng_wait_obj) { return sng_wait_obj->fd; } @@ -900,24 +1418,24 @@ sng_fd_t _SAPI_CALL sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sng_wait_obj) \param context void pointer to user context \return void */ -void _SAPI_CALL sangoma_wait_obj_set_context(sangoma_wait_obj_t *sng_wait_obj, void *context) +void _LIBSNG_CALL sangoma_wait_obj_set_context(sangoma_wait_obj_t *sng_wait_obj, void *context) { sng_wait_obj->context = context; } /*! - \fn void *sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object) + \fn PVOID sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object) \brief Retrieve the user context (if any) that was set via sangoma_wait_obj_set_context. \param sangoma_wait_object pointer a single device object \return void* */ -void* _SAPI_CALL sangoma_wait_obj_get_context(sangoma_wait_obj_t *sng_wait_obj) +PVOID _LIBSNG_CALL sangoma_wait_obj_get_context(sangoma_wait_obj_t *sng_wait_obj) { return sng_wait_obj->context; } /*! - \fn sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], int32_t in_flags[], int32_t out_flags[] uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout); + \fn sangoma_status_t _LIBSNG_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], int32_t in_flags[], int32_t out_flags[] uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout); \brief Wait for multiple sangoma waitable objects \param sangoma_wait_objects pointer to array of wait objects previously created with sangoma_wait_obj_create \param in_flags array of flags corresponding to the flags the user is interested on for each waitable object @@ -926,11 +1444,13 @@ void* _SAPI_CALL sangoma_wait_obj_get_context(sangoma_wait_obj_t *sng_wait_obj) \param system_wait_timeout timeout in miliseconds in case of no event \return negative: SANG_STATUS_APIPOLL_TIMEOUT: timeout, SANG_STATUS_SUCCESS: event occured check in sangoma_wait_objects, any other return code is some error */ -sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sng_wait_objects[], uint32_t in_flags[], uint32_t out_flags[], +sangoma_status_t _LIBSNG_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sng_wait_objects[], uint32_t in_flags[], uint32_t out_flags[], uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout) { #if defined(__WINDOWS__) HANDLE hEvents[MAXIMUM_WAIT_OBJECTS]; + uint32_t uiMapEventIndexToWaitObjectIndex[MAXIMUM_WAIT_OBJECTS]; + DWORD dwResult; int at_least_one_poll_set_flags_out, number_of_internal_signaling_objects, err; #endif uint32_t i = 0, j = 0; @@ -949,6 +1469,7 @@ sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sng_wait_ob return SANG_STATUS_NO_FREE_BUFFERS; } hEvents[number_of_internal_signaling_objects] = sangoma_wait_object->generic_event_object; + uiMapEventIndexToWaitObjectIndex[number_of_internal_signaling_objects] = i; number_of_internal_signaling_objects++; } @@ -958,11 +1479,12 @@ sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sng_wait_ob ((j == LIBSNG_EVENT_INDEX_POLLOUT) && (in_flags[i] & POLLOUT)) || ((j == LIBSNG_EVENT_INDEX_POLLPRI) && (in_flags[i] & POLLPRI)) ){ - if(check_number_of_wait_objects(number_of_internal_signaling_objects, __FUNCTION__, __LINE__)){ - return SANG_STATUS_NO_FREE_BUFFERS; - } - hEvents[number_of_internal_signaling_objects] = sangoma_wait_object->sng_event_objects[j]; - number_of_internal_signaling_objects++; + if(check_number_of_wait_objects(number_of_internal_signaling_objects, __FUNCTION__, __LINE__)){ + return SANG_STATUS_NO_FREE_BUFFERS; + } + hEvents[number_of_internal_signaling_objects] = sangoma_wait_object->sng_event_objects[j]; + uiMapEventIndexToWaitObjectIndex[number_of_internal_signaling_objects] = i; + number_of_internal_signaling_objects++; } }/* if () */ }/* for() */ @@ -979,7 +1501,7 @@ sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sng_wait_ob /* It is important to get 'out flags' BEFORE the WaitForMultipleObjects() * because it allows to keep API driver's transmit queue full. */ - err = get_out_flags(sng_wait_objects, in_flags, out_flags, number_of_sangoma_wait_objects, &at_least_one_poll_set_flags_out); + err = get_out_flags(sng_wait_objects, INVALID_INDEX, in_flags, out_flags, number_of_sangoma_wait_objects, &at_least_one_poll_set_flags_out); if(SANG_ERROR(err)){ return err; } @@ -989,12 +1511,20 @@ sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sng_wait_ob } /* wait untill at least one of the events is signaled OR a 'system_wait_timeout' occured */ - if (WAIT_TIMEOUT == WaitForMultipleObjects(number_of_internal_signaling_objects, &hEvents[0], FALSE, system_wait_timeout)){ + dwResult = WaitForMultipleObjects(number_of_internal_signaling_objects, &hEvents[0], FALSE, system_wait_timeout); + if (WAIT_TIMEOUT == dwResult){ return SANG_STATUS_APIPOLL_TIMEOUT; } + if( dwResult >= (DWORD)number_of_internal_signaling_objects ) { + return SANG_STATUS_GENERAL_ERROR; + } + /* WaitForMultipleObjects() was waken by a Sangoma or by a non-Sangoma wait object. */ - err = get_out_flags(sng_wait_objects, in_flags, out_flags, number_of_sangoma_wait_objects, NULL); + err = get_out_flags(sng_wait_objects, + uiMapEventIndexToWaitObjectIndex[dwResult],/* This is the index of first signaled object in + * the original sng_wait_objects[] array, not in hEvents[] array. */ + in_flags, out_flags, number_of_sangoma_wait_objects, NULL); if(SANG_ERROR(err)){ return err; } @@ -1028,18 +1558,14 @@ sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sng_wait_ob if (res > 0) { for(i = 0; i < number_of_sangoma_wait_objects; i++){ out_flags[i] = pfds[i].revents; + /* POLLIN, POLLOUT, POLLPRI have same values as SANG_WAIT_OBJ_ HAS_INPUT, CAN_OUTPUT and HAS_EVENTS, no need to set them */ } for(i = 0; i < j; i++){ - /* TODO: we must do something extra for signalled objects like setting a flag. - * current user of the SANGOMA_OBJ_IS_SIGNALABLE feature is Netborder and they dont - * need to know which object was signaled. If we want to know which object was signaled - * we need a new libsangoma API sangoma_wait_obj_is_signaled() where in Windows we can - * use WaitForSingleObject to test the signaled state and in Linux we can set a flag in - * sng_wait_obj - * */ if (pfds[number_of_sangoma_wait_objects+i].revents & POLLIN) { - /* read and discard the signal byte */ + /* read and discard the signal byte */ read(pfds[number_of_sangoma_wait_objects+i].fd, &dummy_buf, 1); + /* set the proper flag so users may know this object was signaled */ + out_flags[i] |= SANG_WAIT_OBJ_IS_SIGNALED; } } } else if (res < 0 && errno == EINTR) { @@ -1058,13 +1584,13 @@ sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sng_wait_ob /*! - \fn sangoma_status_t _SAPI_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, int32_t inflags, int32_t *outflags, int32_t timeout) + \fn sangoma_status_t _LIBSNG_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, int32_t inflags, int32_t *outflags, int32_t timeout) \brief Wait for a single waitable object - \param sangoma_wait_obj pointer to array of file descriptors to wait for + \param sangoma_wait_obj pointer to a wait object previously created with sangoma_wait_obj_create \param timeout timeout in miliseconds in case of no event \return SANG_STATUS_APIPOLL_TIMEOUT: timeout, SANG_STATUS_SUCCESS: event occured use sangoma_wait_obj_get_out_flags to check or error status */ -sangoma_status_t _SAPI_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, uint32_t inflags, uint32_t *outflags, int32_t timeout) +sangoma_status_t _LIBSNG_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, uint32_t inflags, uint32_t *outflags, int32_t timeout) { return sangoma_waitfor_many(&sangoma_wait_obj, &inflags, outflags, 1, timeout); } @@ -1075,18 +1601,18 @@ sangoma_status_t _SAPI_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj ***************************************************************/ -int _SAPI_CALL sangoma_span_chan_toif(int span, int chan, char *interface_name) +int _LIBSNG_CALL sangoma_span_chan_toif(int span, int chan, char *interface_name) { #if defined(__WINDOWS__) -/* FIXME: Not implemented */ - return -1; + /* Form the Interface Name from span and chan number (i.e. wanpipe1_if1). */ + sprintf(interface_name, WP_INTERFACE_NAME_FORM, span, chan); #else sprintf(interface_name,"s%ic%i",span,chan); #endif return 0; } -int _SAPI_CALL sangoma_interface_toi(char *interface_name, int *span, int *chan) +int _LIBSNG_CALL sangoma_interface_toi(char *interface_name, int *span, int *chan) { char *p=NULL, *sp = NULL, *ch = NULL; int ret = 0; @@ -1117,7 +1643,7 @@ int _SAPI_CALL sangoma_interface_toi(char *interface_name, int *span, int *chan) return ret; } -int _SAPI_CALL sangoma_interface_wait_up(int span, int chan, int sectimeout) +int _LIBSNG_CALL sangoma_interface_wait_up(int span, int chan, int sectimeout) { #if defined(__WINDOWS__) /* Windows does not need to wait for interfaces to come up */ @@ -1149,7 +1675,7 @@ int _SAPI_CALL sangoma_interface_wait_up(int span, int chan, int sectimeout) #endif } -int _SAPI_CALL sangoma_span_chan_fromif(char *interface_name, int *span, int *chan) +int _LIBSNG_CALL sangoma_span_chan_fromif(char *interface_name, int *span, int *chan) { char *p = NULL, *sp = NULL, *ch = NULL; int ret = 0; @@ -1180,7 +1706,7 @@ int _SAPI_CALL sangoma_span_chan_fromif(char *interface_name, int *span, int *ch return ret; } -sng_fd_t _SAPI_CALL sangoma_open_api_span_chan(int span, int chan) +sng_fd_t _LIBSNG_CALL sangoma_open_api_span_chan(int span, int chan) { sng_fd_t fd = INVALID_HANDLE_VALUE; wanpipe_api_t tdm_api; @@ -1215,40 +1741,12 @@ sng_fd_t _SAPI_CALL sangoma_open_api_span_chan(int span, int chan) return fd; } -/* no checks done for multiple open */ -sng_fd_t _SAPI_CALL __sangoma_open_api_span_chan(int span, int chan) +static sng_fd_t sangoma_open_dev_by_name(const char *dev_name) { - char fname[FNAME_LEN], tmp_fname[FNAME_LEN]; - - /* Form the Interface Name from span and chan number (i.e. wanpipe1_if1). */ - _snprintf(tmp_fname, DEV_NAME_LEN, WP_INTERFACE_NAME_FORM, span, chan); + char fname[FNAME_LEN]; #if defined(__WINDOWS__) - _snprintf(fname , FNAME_LEN, "\\\\.\\%s", tmp_fname); - return CreateFile( fname, - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - (LPSECURITY_ATTRIBUTES)NULL, - OPEN_EXISTING, - FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, - (HANDLE)NULL - ); -#else - sprintf(fname,"/dev/%s", tmp_fname); - - return open(fname, O_RDWR); -#endif -} - -sng_fd_t _SAPI_CALL sangoma_open_api_ctrl(void) -{ - char fname[FNAME_LEN], tmp_fname[FNAME_LEN]; - - /* Form the Ctrl Device Name. */ - _snprintf(tmp_fname, DEV_NAME_LEN, WP_CTRL_DEV_NAME); - -#if defined(__WINDOWS__) - _snprintf(fname , FNAME_LEN, "\\\\.\\%s", tmp_fname); + _snprintf(fname , FNAME_LEN, "\\\\.\\%s", dev_name); return CreateFile( fname, GENERIC_READ | GENERIC_WRITE, @@ -1259,16 +1757,40 @@ sng_fd_t _SAPI_CALL sangoma_open_api_ctrl(void) (HANDLE)NULL ); #else - sprintf(fname,"/dev/%s", tmp_fname); + sprintf(fname,"/dev/%s", dev_name); return open(fname, O_RDWR); #endif } +/* no checks done for multiple open */ +sng_fd_t _LIBSNG_CALL __sangoma_open_api_span_chan(int span, int chan) +{ + char tmp_fname[FNAME_LEN]; -int _SAPI_CALL sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api) + /* Form the Interface Name from span and chan number (i.e. wanpipe1_if1). */ + _snprintf(tmp_fname, DEV_NAME_LEN, WP_INTERFACE_NAME_FORM, span, chan); + + return sangoma_open_dev_by_name(tmp_fname); +} + +sng_fd_t _LIBSNG_CALL sangoma_open_api_ctrl(void) +{ + return sangoma_open_dev_by_name(WP_CTRL_DEV_NAME); +} + +#ifdef WP_API_FEATURE_LOGGER +sng_fd_t _LIBSNG_CALL sangoma_logger_open(void) +{ + return sangoma_open_dev_by_name(WP_LOGGER_DEV_NAME); +} +#endif + +int _LIBSNG_CALL sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api) { int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_OPEN_CNT; err=sangoma_cmd_exec(fd,tdm_api); @@ -1279,7 +1801,7 @@ int _SAPI_CALL sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api) return tdm_api->wp_cmd.open_cnt; } -sng_fd_t _SAPI_CALL sangoma_create_socket_by_name(char *device, char *card) +sng_fd_t _LIBSNG_CALL sangoma_create_socket_by_name(char *device, char *card) { int span,chan; sangoma_interface_toi(device,&span,&chan); @@ -1288,7 +1810,7 @@ sng_fd_t _SAPI_CALL sangoma_create_socket_by_name(char *device, char *card) } -sng_fd_t _SAPI_CALL sangoma_open_api_span(int span) +sng_fd_t _LIBSNG_CALL sangoma_open_api_span(int span) { int i=0; sng_fd_t fd = INVALID_HANDLE_VALUE; @@ -1319,18 +1841,21 @@ sng_fd_t _SAPI_CALL sangoma_open_api_span(int span) \param fd device file descriptor \return void */ -void _SAPI_CALL sangoma_close(sng_fd_t *fd) +void _LIBSNG_CALL sangoma_close(sng_fd_t *fd) { + if (!fd) { + return; + } #if defined(__WINDOWS__) - if( *fd != INVALID_HANDLE_VALUE){ + if (*fd != INVALID_HANDLE_VALUE){ CloseHandle(*fd); *fd = INVALID_HANDLE_VALUE; } #else - if (*fd >= 0) { + if (*fd >= 0) { close(*fd); *fd = -1; - } + } #endif } @@ -1340,7 +1865,7 @@ void _SAPI_CALL sangoma_close(sng_fd_t *fd) * Device READ / WRITE Functions ***************************************************************/ -int _SAPI_CALL sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, int datalen, int flag) +int _LIBSNG_CALL sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, int datalen, int flag) { int rx_len=0; @@ -1374,7 +1899,7 @@ int _SAPI_CALL sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *data break; default: /* note that SANG_STATUS_NO_DATA_AVAILABLE is NOT an error! */ - if(0)DBG_ERR("Operation Status: %s(%d)\n", + if(libsng_dbg_level)DBG_ERR("Operation Status: %s(%d)\n", SDLA_DECODE_SANG_STATUS(rx_hdr->operation_status), rx_hdr->operation_status); return -5; } @@ -1407,7 +1932,7 @@ int _SAPI_CALL sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *data return rx_len; } -int _SAPI_CALL sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, unsigned short datalen, int flag) +int _LIBSNG_CALL sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, unsigned short datalen, int flag) { int bsent=-1; wp_api_hdr_t *wp_api_hdr = hdrbuf; @@ -1487,7 +2012,7 @@ int _SAPI_CALL sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *dat * Execute TDM command * */ -int _SAPI_CALL sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api) { int err; @@ -1509,10 +2034,11 @@ int _SAPI_CALL sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api) * Get Full TDM API configuration per channel * */ -int _SAPI_CALL sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_FULL_CFG; err=sangoma_cmd_exec(fd,tdm_api); @@ -1563,16 +2089,12 @@ int _SAPI_CALL sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api) * } * */ -int _SAPI_CALL sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int codec) +int _LIBSNG_CALL sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int codec) { - int err; - + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_CODEC; tdm_api->wp_cmd.tdm_codec = codec; - - err=sangoma_cmd_exec(fd,tdm_api); - - return err; + return sangoma_cmd_exec(fd,tdm_api); } /*======================================================== @@ -1587,10 +2109,11 @@ int _SAPI_CALL sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int co * } * */ -int _SAPI_CALL sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_CODEC; err=sangoma_cmd_exec(fd,tdm_api); @@ -1608,16 +2131,12 @@ int _SAPI_CALL sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api) * 10,20,30,40,50 ms * */ -int _SAPI_CALL sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, int period) +int _LIBSNG_CALL sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, int period) { - int err; - + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_USR_PERIOD; tdm_api->wp_cmd.usr_period = period; - - err=sangoma_cmd_exec(fd,tdm_api); - - return err; + return sangoma_cmd_exec(fd,tdm_api); } /*======================================================== @@ -1627,10 +2146,11 @@ int _SAPI_CALL sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, i * 10,20,30,40,50 ms * */ -int _SAPI_CALL sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_USR_PERIOD; err=sangoma_cmd_exec(fd,tdm_api); @@ -1647,15 +2167,17 @@ int _SAPI_CALL sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api) * Coding Format will be ULAW/ALAW based on T1/E1 */ -int _SAPI_CALL sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api) { - int err; - tdm_api->wp_cmd.cmd = WP_API_CMD_GET_HW_CODING; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - return tdm_api->wp_cmd.hw_tdm_coding; + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_HW_CODING; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + return tdm_api->wp_cmd.hw_tdm_coding; } #ifdef WP_API_FEATURE_DTMF_EVENTS @@ -1665,9 +2187,11 @@ int _SAPI_CALL sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api) * Will return true if HW DTMF is enabled on Octasic */ -int _SAPI_CALL sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api) { int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_HW_DTMF; err=sangoma_cmd_exec(fd,tdm_api); if (err){ @@ -1682,9 +2206,11 @@ int _SAPI_CALL sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api) * Will return true if HW EC is enabled */ -int _SAPI_CALL sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api) { int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_HW_EC; err=sangoma_cmd_exec(fd,tdm_api); if (err){ @@ -1700,10 +2226,11 @@ int _SAPI_CALL sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api) * The USER MTU/MRU values will change each time a PERIOD * or CODEC is adjusted. */ -int _SAPI_CALL sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_USR_MTU_MRU; err=sangoma_cmd_exec(fd,tdm_api); @@ -1720,16 +2247,12 @@ int _SAPI_CALL sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api) * This option is not implemented yet * */ -int _SAPI_CALL sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, int power) +int _LIBSNG_CALL sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, int power) { - int err; - + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_POWER_LEVEL; tdm_api->wp_cmd.power_level = power; - - err=sangoma_cmd_exec(fd,tdm_api); - - return err; + return sangoma_cmd_exec(fd,tdm_api); } /*======================================================== @@ -1738,10 +2261,11 @@ int _SAPI_CALL sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, * This option is not implemented yet * */ -int _SAPI_CALL sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_POWER_LEVEL; err=sangoma_cmd_exec(fd,tdm_api); @@ -1752,71 +2276,62 @@ int _SAPI_CALL sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api) return tdm_api->wp_cmd.power_level; } -int _SAPI_CALL sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) { - int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_FLUSH_BUFFERS; - - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_enable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api, int poll_in_sec) { - - int err; - - tdm_api->wp_cmd.cmd = WP_API_CMD_ENABLE_RBS_EVENTS; - tdm_api->wp_cmd.rbs_poll=poll_in_sec; - - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; -} - - -int _SAPI_CALL sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - tdm_api->wp_cmd.cmd = WP_API_CMD_DISABLE_RBS_EVENTS; - - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; -} - -int _SAPI_CALL sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char rbs) +int _LIBSNG_CALL sangoma_flush_rx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - tdm_api->wp_cmd.cmd = WP_API_CMD_WRITE_RBS_BITS; - tdm_api->wp_cmd.chan = (unsigned char)channel; - tdm_api->wp_cmd.rbs_tx_bits=rbs; - - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_FLUSH_RX_BUFFERS; + return sangoma_cmd_exec(fd,tdm_api); +} - return 0; +int _LIBSNG_CALL sangoma_flush_tx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_FLUSH_TX_BUFFERS; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _LIBSNG_CALL sangoma_flush_event_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_FLUSH_EVENT_BUFFERS; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _LIBSNG_CALL sangoma_tdm_enable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api, int poll_in_sec) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_ENABLE_RBS_EVENTS; + tdm_api->wp_cmd.rbs_poll = poll_in_sec; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _LIBSNG_CALL sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_DISABLE_RBS_EVENTS; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _LIBSNG_CALL sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char rbs) +{ + WANPIPE_API_INIT_CHAN(tdm_api, channel); + tdm_api->wp_cmd.cmd = WP_API_CMD_WRITE_RBS_BITS; + tdm_api->wp_cmd.rbs_tx_bits=rbs; + return sangoma_cmd_exec(fd,tdm_api); } - -int _SAPI_CALL sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char *rbs) +int _LIBSNG_CALL sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char *rbs) { - int err; + WANPIPE_API_INIT_CHAN(tdm_api, channel); tdm_api->wp_cmd.cmd = WP_API_CMD_READ_RBS_BITS; - tdm_api->wp_cmd.chan = (unsigned char)channel; tdm_api->wp_cmd.rbs_tx_bits=0; err=sangoma_cmd_exec(fd,tdm_api); @@ -1825,17 +2340,17 @@ int _SAPI_CALL sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int cha } *rbs=(unsigned char)tdm_api->wp_cmd.rbs_rx_bits; - return 0; } -int _SAPI_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api) { #ifdef WP_API_FEATURE_EVENTS wp_api_event_t *rx_event; int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_READ_EVENT; err=sangoma_cmd_exec(fd,tdm_api); @@ -1845,7 +2360,6 @@ int _SAPI_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api) rx_event = &tdm_api->wp_cmd.event; - /* The use of callbacks here is purely optional and is left here for backward compatibility purposes. By default user @@ -1859,7 +2373,6 @@ int _SAPI_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api) if (tdm_api->wp_callback.wp_rbs_event) { tdm_api->wp_callback.wp_rbs_event(fd,rx_event->wp_api_event_rbs_bits); } - break; #ifdef WP_API_FEATURE_DTMF_EVENTS @@ -1920,9 +2433,9 @@ int _SAPI_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api) #endif default: #ifdef __WINDOWS__ - printf("libsangoma: %s fd=0x%p: Unknown TDM event!", __FUNCTION__,fd); + if(0)printf("Warning: libsangoma: %s fd=0x%p: Unknown TDM event!", __FUNCTION__,fd); #else - printf("libsangoma: %s fd=%d: Unknown TDM event!", __FUNCTION__, fd); + if(0)printf("Warning: libsangoma: %s fd=%d: Unknown TDM event!", __FUNCTION__, fd); #endif break; } @@ -1932,314 +2445,283 @@ int _SAPI_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api) printf("Error: Read Event not supported!\n"); return -1; #endif -} +} + + +#ifdef WP_API_FEATURE_LOGGER +/*======================================================== + * Execute Wanpipe Logger command + */ +sangoma_status_t _LIBSNG_CALL sangoma_logger_cmd_exec(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ +#if defined(__WINDOWS__) + return logger_api_ioctl(fd, logger_cmd); +#else + int err = ioctl(fd,WANPIPE_IOCTL_LOGGER_CMD,logger_cmd); + if (err < 0){ + char tmp[50]; + sprintf(tmp,"Logger CMD: %i\n",logger_cmd->cmd); + perror(tmp); + return -1; + } + return err; +#endif +} + +sangoma_status_t _LIBSNG_CALL sangoma_logger_read_event(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_READ_EVENT; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _LIBSNG_CALL sangoma_logger_flush_buffers(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_FLUSH_BUFFERS; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _LIBSNG_CALL sangoma_logger_get_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_GET_STATS; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _LIBSNG_CALL sangoma_logger_reset_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_RESET_STATS; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _LIBSNG_CALL sangoma_logger_get_open_handle_counter(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_OPEN_CNT; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _LIBSNG_CALL sangoma_logger_get_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_GET_LOGGER_LEVEL; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _LIBSNG_CALL sangoma_logger_set_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_SET_LOGGER_LEVEL; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +#endif /* WP_API_FEATURE_LOGGER */ + +#ifdef WP_API_FEATURE_FAX_EVENTS +int _LIBSNG_CALL sangoma_tdm_enable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_FAX_DETECT; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _LIBSNG_CALL sangoma_tdm_disable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_FAX_DETECT; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _LIBSNG_CALL sangoma_tdm_get_hw_fax(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_HW_FAX_DETECT; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + return tdm_api->wp_cmd.hw_fax; +} +#endif #ifdef WP_API_FEATURE_DTMF_EVENTS -int _SAPI_CALL sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - int err; - + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_DTMF; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - int err; - + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_DTMF; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } - -int _SAPI_CALL sangoma_tdm_enable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_enable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - int err; - + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RM_DTMF; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - int err; - + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RM_DTMF; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - int err; - + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RXHOOK; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - int err; - + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RXHOOK; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; - - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } - -int _SAPI_CALL sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; - - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_enable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_enable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING_DETECT; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return err; + return sangoma_cmd_exec(fd,tdm_api); } - -int _SAPI_CALL sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING_DETECT; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING_TRIP_DETECT; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return tdm_api->wp_cmd.rbs_poll; + return sangoma_cmd_exec(fd,tdm_api); } - -int _SAPI_CALL sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; - tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING_DETECT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING_TRIP_DETECT; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TXSIG_KEWL; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TXSIG_START; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TXSIG_ONHOOK; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TXSIG_OFFHOOK; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } - -int _SAPI_CALL sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api, uint16_t tone_id) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api, uint16_t tone_id) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TONE; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; tdm_api->wp_cmd.event.wp_api_event_tone_type = tone_id; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return tdm_api->wp_cmd.rbs_poll; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TONE; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; tdm_api->wp_cmd.event.wp_api_event_tone_type = 0x00; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return tdm_api->wp_cmd.rbs_poll; + return sangoma_cmd_exec(fd,tdm_api); } - #endif -int _SAPI_CALL sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) { - int err; - - tdm_api->wp_cmd.cmd = WP_API_CMD_ENABLE_HWEC; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_ENABLE_HWEC; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) { - int err; - - tdm_api->wp_cmd.cmd = WP_API_CMD_DISABLE_HWEC; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_DISABLE_HWEC; + return sangoma_cmd_exec(fd,tdm_api); } @@ -2248,10 +2730,11 @@ int _SAPI_CALL sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) * */ #ifdef WP_API_FEATURE_FE_ALARM -int _SAPI_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms) +int _LIBSNG_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_FE_ALARMS; err=sangoma_cmd_exec(fd,tdm_api); @@ -2265,10 +2748,11 @@ int _SAPI_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, un } /* get current Line Connection state - Connected/Disconnected */ -int _SAPI_CALL sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status) +int _LIBSNG_CALL sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_FE_STATUS; err = sangoma_cmd_exec(fd, tdm_api); *current_status = tdm_api->wp_cmd.fe_status; @@ -2279,10 +2763,11 @@ int _SAPI_CALL sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsign /* get current Line Connection state - Connected/Disconnected */ #ifdef WP_API_FEATURE_LINK_STATUS -int _SAPI_CALL sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status) +int _LIBSNG_CALL sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_FE_STATUS; err = sangoma_cmd_exec(fd, tdm_api); *current_status = tdm_api->wp_cmd.fe_status; @@ -2291,17 +2776,18 @@ int _SAPI_CALL sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsi } /* set current Line Connection state - Connected/Disconnected. valid only for ISDN BRI */ -int _SAPI_CALL sangoma_set_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char new_status) +int _LIBSNG_CALL sangoma_set_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char new_status) { + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_FE_STATUS; tdm_api->wp_cmd.fe_status = new_status; - return sangoma_cmd_exec(fd, tdm_api); } #endif -int _SAPI_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) +int _LIBSNG_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) { + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.channel = (unsigned char)channel; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_BRI_CHAN_LOOPBACK; @@ -2309,8 +2795,9 @@ int _SAPI_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *td return sangoma_cmd_exec(fd, tdm_api); } -int _SAPI_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) +int _LIBSNG_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) { + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.channel = (unsigned char)channel; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_BRI_CHAN_LOOPBACK; @@ -2318,11 +2805,11 @@ int _SAPI_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm return sangoma_cmd_exec(fd, tdm_api); } - -int _SAPI_CALL sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_TX_Q_SIZE; tdm_api->wp_cmd.tx_queue_sz = 0; @@ -2334,22 +2821,22 @@ int _SAPI_CALL sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) return tdm_api->wp_cmd.tx_queue_sz; } -int _SAPI_CALL sangoma_set_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) +int _LIBSNG_CALL sangoma_set_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) { if (size < 0) { return -1; } - + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_TX_Q_SIZE; tdm_api->wp_cmd.tx_queue_sz = size; - return sangoma_cmd_exec(fd, tdm_api); } -int _SAPI_CALL sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_RX_Q_SIZE; tdm_api->wp_cmd.rx_queue_sz = 0; @@ -2362,23 +2849,23 @@ int _SAPI_CALL sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) } -int _SAPI_CALL sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) +int _LIBSNG_CALL sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) { if (size < 0) { return -1; } + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_RX_Q_SIZE; tdm_api->wp_cmd.rx_queue_sz = size; - return sangoma_cmd_exec(fd, tdm_api); - } -int _SAPI_CALL sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, wan_driver_version_t *drv_ver) +int _LIBSNG_CALL sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, wan_driver_version_t *drv_ver) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_DRIVER_VERSION; err = sangoma_cmd_exec(fd, tdm_api); @@ -2395,10 +2882,11 @@ int _SAPI_CALL sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, w return err; } -int _SAPI_CALL sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) +int _LIBSNG_CALL sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_FIRMWARE_VERSION; err = sangoma_cmd_exec(fd, tdm_api); @@ -2413,11 +2901,11 @@ int _SAPI_CALL sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, return err; } - -int _SAPI_CALL sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) +int _LIBSNG_CALL sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_CPLD_VERSION; err = sangoma_cmd_exec(fd, tdm_api); @@ -2432,41 +2920,109 @@ int _SAPI_CALL sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, uns return err; } -int _SAPI_CALL sangoma_get_stats(sng_fd_t fd, wanpipe_api_t *tdm_api, wanpipe_chan_stats_t *stats) +int _LIBSNG_CALL sangoma_get_stats(sng_fd_t fd, wanpipe_api_t *tdm_api, wanpipe_chan_stats_t *stats) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_STATS; err = sangoma_cmd_exec(fd, tdm_api); if (err == 0) { if (stats) { - memcpy(stats,&tdm_api->wp_cmd.stats,sizeof(wanpipe_chan_stats_t)); + memcpy(stats, &tdm_api->wp_cmd.stats, sizeof(wanpipe_chan_stats_t)); } } return err; } -int _SAPI_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api) { + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_RESET_STATS; return sangoma_cmd_exec(fd, tdm_api); } -int _SAPI_CALL sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, int rxflashtime) +int _LIBSNG_CALL sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, int rxflashtime) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_RM_RXFLASHTIME; + tdm_api->wp_cmd.rxflashtime=rxflashtime; + return sangoma_cmd_exec(fd, tdm_api); +} + +#ifdef WP_API_FEATURE_RM_GAIN +int _LIBSNG_CALL sangoma_set_rm_tx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_SET_RM_TX_GAIN; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + tdm_api->wp_cmd.event.wp_api_event_gain_value = value; + return sangoma_cmd_exec(fd, tdm_api); +} + +int _LIBSNG_CALL sangoma_set_rm_rx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_SET_RM_RX_GAIN; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + tdm_api->wp_cmd.event.wp_api_event_gain_value = value; + return sangoma_cmd_exec(fd, tdm_api); +} +#endif /* WP_API_FEATURE_RM_GAIN */ + +int _LIBSNG_CALL sangoma_tdm_set_polarity(sng_fd_t fd, wanpipe_api_t *tdm_api, int polarity) { int err; - tdm_api->wp_cmd.cmd = WP_API_CMD_SET_RM_RXFLASHTIME; - tdm_api->wp_cmd.rxflashtime=rxflashtime; + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_SETPOLARITY; + tdm_api->wp_cmd.event.wp_api_event_polarity = polarity; err = sangoma_cmd_exec(fd, tdm_api); return err; + } -#ifndef LIBSANGOMA_LIGHT +int _LIBSNG_CALL sangoma_tdm_txsig_onhooktransfer(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_ONHOOKTRANSFER; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + return sangoma_cmd_exec(fd,tdm_api); +} +#ifdef WP_API_FEATURE_LOOP + +int _LIBSNG_CALL sangoma_tdm_enable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_ENABLE_LOOP; + err = sangoma_cmd_exec(fd, tdm_api); + return err; +} + +int _LIBSNG_CALL sangoma_tdm_disable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_DISABLE_LOOP; + err = sangoma_cmd_exec(fd, tdm_api); + return err; +} + +#endif + + +#ifndef LIBSANGOMA_LIGHT /************************************************************//** * Device PORT Control Functions @@ -2487,7 +3043,7 @@ static int sangoma_port_mgmnt_ioctl(sng_fd_t fd, port_management_struct_t *port_ (LPDWORD)(&ln), (LPOVERLAPPED)NULL ) == FALSE){ - /* Call Call OS specific code to find cause of the error and check messages log. */ + /* Call OS specific code to find cause of the error and check messages log. */ DBG_ERR("%s():Error: IoctlPortManagementCommand failed!!\n", __FUNCTION__); err = -1; } @@ -2519,7 +3075,7 @@ static int sangoma_port_cfg_ioctl(sng_fd_t fd, port_cfg_t *port_cfg) (LPDWORD)(&ln), (LPOVERLAPPED)NULL ) == FALSE){ - /* Call Call OS specific code to find cause of the error and check messages log. */ + /* Call OS specific code to find cause of the error and check messages log. */ DBG_ERR("%s():Error: IoctlPortConfigurationCommand failed!!\n", __FUNCTION__); err = -1; } @@ -2537,60 +3093,50 @@ static int sangoma_port_cfg_ioctl(sng_fd_t fd, port_cfg_t *port_cfg) } /* open wanpipe configuration device */ -sng_fd_t _SAPI_CALL sangoma_open_driver_ctrl(int port_no) +sng_fd_t _LIBSNG_CALL sangoma_open_driver_ctrl(int port_no) { - char fname[FNAME_LEN], tmp_fname[FNAME_LEN]; + char tmp_fname[FNAME_LEN]; #if defined(__WINDOWS__) /* Form the Config Device Name (i.e. wanpipe1, wanpipe2,...). */ _snprintf(tmp_fname, DEV_NAME_LEN, WP_PORT_NAME_FORM, port_no); - _snprintf(fname, FNAME_LEN, "\\\\.\\%s", tmp_fname); - - return CreateFile( fname, - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - (LPSECURITY_ATTRIBUTES)NULL, - OPEN_EXISTING, - FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, - (HANDLE)NULL - ); #else /* Form the Config Device Name. ("/dev/wanpipe") */ _snprintf(tmp_fname, DEV_NAME_LEN, WP_CONFIG_DEV_NAME); - - sprintf(fname,"/dev/%s", tmp_fname); - - return open(fname, O_RDWR); #endif + return sangoma_open_dev_by_name(tmp_fname); } - -int _SAPI_CALL sangoma_mgmt_cmd(sng_fd_t fd, wan_udp_hdr_t* wan_udp) +int _LIBSNG_CALL sangoma_mgmt_cmd(sng_fd_t fd, wan_udp_hdr_t* wan_udp) { + int err=0; #if defined(__WINDOWS__) if(UdpManagementCommand(fd, wan_udp)){ - return 1; + err = 1; } #else unsigned char id = 0; - int err=0; + wan_udp->wan_udphdr_request_reply = 0x01; wan_udp->wan_udphdr_id = id; wan_udp->wan_udphdr_return_code = WAN_UDP_TIMEOUT_CMD; err=ioctl(fd,WANPIPE_IOCTL_PIPEMON,wan_udp); if (err < 0) { - return 1; + err = 1; } #endif - - if(wan_udp->wan_udphdr_return_code != WAN_CMD_OK){ - return 2; + if(err){ + /* The ioctl failed. */ + return err; } + + /* The ioctl was successfull. The caller must check + * value of wan_udp->wan_udphdr_return_code. */ return 0; } -int _SAPI_CALL sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) +int _LIBSNG_CALL sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) { int err; port_mgmnt->command_code = START_PORT_VOLATILE_CONFIG; @@ -2605,7 +3151,7 @@ int _SAPI_CALL sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t * return port_mgmnt->operation_status; } -int _SAPI_CALL sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) +int _LIBSNG_CALL sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) { int err; port_mgmnt->command_code = STOP_PORT; @@ -2633,7 +3179,7 @@ int _SAPI_CALL sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *p return err; } -int _SAPI_CALL sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) +int _LIBSNG_CALL sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) { int err; port_mgmnt->command_code = GET_HARDWARE_INFO; @@ -2647,7 +3193,7 @@ int _SAPI_CALL sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t return port_mgmnt->operation_status; } -int _SAPI_CALL sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no) +int _LIBSNG_CALL sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no) { port_cfg->command_code = SET_PORT_VOLATILE_CONFIG; port_cfg->port_no = port_no; @@ -2655,22 +3201,20 @@ int _SAPI_CALL sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, return sangoma_port_cfg_ioctl(fd, port_cfg); } -int _SAPI_CALL sangoma_driver_port_get_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no) +int _LIBSNG_CALL sangoma_driver_port_get_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no) { - port_cfg->command_code = GET_PORT_VOLATILE_CONFIG; - port_cfg->port_no = port_no; - + port_cfg->port_no = port_no; return sangoma_port_cfg_ioctl(fd, port_cfg); } -int _SAPI_CALL sangoma_write_port_config_on_persistent_storage(hardware_info_t *hardware_info, port_cfg_t *port_cfg, unsigned short port_no) +int _LIBSNG_CALL sangoma_write_port_config_on_persistent_storage(hardware_info_t *hardware_info, port_cfg_t *port_cfg, unsigned short port_no) { int err = 0; #if defined(__WINDOWS__) HKEY hPortRegistryKey = registry_open_port_key(hardware_info); - wandev_conf_t *wandev_conf = &port_cfg->wandev_conf; - sdla_fe_cfg_t *sdla_fe_cfg = &wandev_conf->fe_cfg; +/* wandev_conf_t *wandev_conf = &port_cfg->wandev_conf; + sdla_fe_cfg_t *sdla_fe_cfg = &wandev_conf->fe_cfg;*/ unsigned int ind; if(hPortRegistryKey == INVALID_HANDLE_VALUE){ @@ -2682,9 +3226,17 @@ int _SAPI_CALL sangoma_write_port_config_on_persistent_storage(hardware_info_t * return 2; } - /* write number of groups */ - registry_set_integer_value(hPortRegistryKey, "aft_number_of_logic_channels", port_cfg->num_of_ifs); + /* write TDM Voice configuration */ + if(registry_write_wan_tdmv_conf(hPortRegistryKey, port_cfg)){ + return 3; + } + /* write number of groups */ + err = registry_set_integer_value(hPortRegistryKey, "aft_number_of_logic_channels", port_cfg->num_of_ifs); + if(err){ + return err; + } + /* write configuration of each group */ for(ind = 0; ind < port_cfg->num_of_ifs; ind++){ registry_write_channel_group_cfg(hPortRegistryKey, port_cfg, ind, port_cfg->if_cfg[ind]); @@ -2696,7 +3248,6 @@ int _SAPI_CALL sangoma_write_port_config_on_persistent_storage(hardware_info_t * #endif return err; } - #endif /* #ifndef LIBSANGOMA_LIGHT */ #endif /* WANPIPE_TDM_API */ diff --git a/api/libsangoma/.svn/text-base/libsangoma.def.svn-base b/api/libsangoma/.svn/text-base/libsangoma.def.svn-base index 88a3fe3..213664f 100644 --- a/api/libsangoma/.svn/text-base/libsangoma.def.svn-base +++ b/api/libsangoma/.svn/text-base/libsangoma.def.svn-base @@ -1,83 +1,2 @@ LIBRARY libsangoma - EXPORTS - sangoma_open_api_span - sangoma_span_chan_toif - sangoma_span_chan_fromif - sangoma_interface_toi - sangoma_create_socket_by_name - sangoma_open_api_span_chan - sangoma_open_api_span - sangoma_writemsg - sangoma_readmsg - sangoma_close - sangoma_get_full_cfg - sangoma_tdm_set_codec - sangoma_tdm_get_codec - sangoma_tdm_set_usr_period - sangoma_tdm_get_usr_period - sangoma_tdm_get_usr_mtu_mru - sangoma_tdm_set_power_level - sangoma_tdm_get_power_level - sangoma_flush_bufs - sangoma_tdm_enable_rbs_events - sangoma_tdm_disable_rbs_events - sangoma_tdm_write_rbs - sangoma_read_event - sangoma_tdm_enable_dtmf_events - sangoma_tdm_disable_dtmf_events - sangoma_tdm_enable_rm_dtmf_events - sangoma_tdm_disable_rm_dtmf_events - sangoma_tdm_enable_rxhook_events - sangoma_tdm_disable_rxhook_events - sangoma_tdm_enable_ring_events - sangoma_tdm_disable_ring_events - sangoma_tdm_enable_ring_detect_events - sangoma_tdm_disable_ring_detect_events - sangoma_tdm_enable_ring_trip_detect_events - sangoma_tdm_disable_ring_trip_detect_events - sangoma_tdm_enable_tone_events - sangoma_tdm_disable_tone_events - sangoma_tdm_txsig_onhook - sangoma_tdm_txsig_offhook - sangoma_tdm_txsig_start - sangoma_tdm_txsig_kewl - sangoma_get_hw_coding - sangoma_waitfor - sangoma_waitfor_many - sangoma_cmd_exec - sangoma_mgmt_cmd - __sangoma_open_api_span_chan - sangoma_get_tx_queue_sz - sangoma_get_rx_queue_sz - sangoma_get_driver_version - sangoma_get_firmware_version - sangoma_get_cpld_version - sangoma_get_stats - sangoma_flush_stats - sangoma_set_tx_queue_sz - sangoma_set_rx_queue_sz - sangoma_set_rm_rxflashtime - sangoma_open_api_ctrl - sangoma_open_driver_ctrl - sangoma_driver_port_start - sangoma_driver_port_stop - sangoma_driver_port_set_config - sangoma_driver_port_get_config - sangoma_driver_get_hw_info - sangoma_tdm_read_rbs - sangoma_get_open_cnt - sangoma_enable_bri_bchan_loopback - sangoma_disable_bri_bchan_loopback - sangoma_set_fe_status - sangoma_get_fe_status - sangoma_tdm_get_hw_dtmf - sangoma_tdm_get_fe_alarms - sangoma_wait_obj_create - sangoma_wait_obj_delete - sangoma_wait_obj_signal - sangoma_wait_obj_get_fd - sangoma_wait_obj_set_context - sangoma_wait_obj_get_context - sangoma_cdev_ctrl_cmd - sangoma_write_port_config_on_persistent_storage diff --git a/api/libsangoma/.svn/text-base/libsangoma.h.svn-base b/api/libsangoma/.svn/text-base/libsangoma.h.svn-base index c477710..58fecf9 100644 --- a/api/libsangoma/.svn/text-base/libsangoma.h.svn-base +++ b/api/libsangoma/.svn/text-base/libsangoma.h.svn-base @@ -3,10 +3,10 @@ * \brief Wanpipe API Library header for Sangoma AFT T1/E1/Analog/BRI/Serial Hardware - * \brief Provides User a Unified/OS Agnostic API to Wanpipe/Sangoma Drivers and Hardware * - * Author(s): Nenad Corbic + * Author(s): Nenad Corbic * David Rokhvarg * Michael Jerris - * Anthony Minessale II + * Anthony Minessale II * * Copyright: (c) 2005-2008 Nenad Corbic * @@ -40,6 +40,13 @@ #ifndef _LIBSNAGOMA_H #define _LIBSNAGOMA_H +#ifdef __linux__ +#ifndef __LINUX__ +/* most wanpipe driver headers require __LINUX__ to be defined */ +#define __LINUX__ +#endif +#endif + #ifdef __cplusplus extern "C" { /* for C++ users */ #endif @@ -65,20 +72,30 @@ extern "C" { /* for C++ users */ \def LIBSANGOMA_VERSION_CODE \brief LibSangoma Current Version Number to be checked against the LIBSANGOMA_VERSION Macro */ -#define LIBSANGOMA_VERSION_CODE LIBSANGOMA_VERSION(3,2,0) +#define LIBSANGOMA_VERSION_CODE LIBSANGOMA_VERSION(3,3,0) /*! \def LIBSANGOMA_VERSION_STR \brief LibSangoma Version in string format */ -#define LIBSANGOMA_VERSION_STR "3.2.0" +#define LIBSANGOMA_VERSION_STR "3.3.0" + +struct sangoma_wait_obj; +#define sangoma_wait_obj_t struct sangoma_wait_obj + + +/*! + \def SANGOMA_DECLARE_TDM_API_CMD + \brief Instantiate/Declare a tdm api cmd strucure + \def SANGOMA_INIT_TDM_API_CMD + \brief Initialize the tdm api cmd structure. Set to 0. + \def SANGOMA_DECLARE_INIT_TDM_API_CMD + \brief Declare and initialize the tdm api cmd structure. +*/ +#define SANGOMA_DECLARE_TDM_API_CMD(_name_) wanpipe_api_t _name_ +#define SANGOMA_INIT_TDM_API_CMD(_name_) memset(&_name_,0,sizeof(_name_)) +#define SANGOMA_DECLARE_INIT_TDM_API_CMD(_name_) wanpipe_tdm_api_t _name_; SANGOMA_INIT_TDM_API_CMD(_name_) -#ifdef __COMPILING_LIBSANGOMA__ - struct sangoma_wait_obj; - #define sangoma_wait_obj_t struct sangoma_wait_obj -#else - typedef void sangoma_wait_obj_t; -#endif #if defined(WIN32) || defined(WIN64) #ifndef __WINDOWS__ @@ -91,10 +108,14 @@ extern "C" { /* for C++ users */ #include /*! - \def _SAPI_CALL + \def _LIBSNG_CALL \brief libsangoma.dll functions exported as __cdecl calling convention */ -#define _SAPI_CALL __cdecl +#ifdef __COMPILING_LIBSANGOMA__ +# define _LIBSNG_CALL __declspec(dllexport) __cdecl +#else +# define _LIBSNG_CALL __declspec(dllimport) __cdecl +#endif /*! \def SANGOMA_INFINITE_API_POLL_WAIT (deprecated, use SANGOMA_WAIT_INFINITE instead) @@ -109,6 +130,13 @@ extern "C" { /* for C++ users */ */ #define sangoma_msleep(x) Sleep(x) +/*! + \def sangoma_ctime(time_val) + \brief Convert a time value to a string + \param time_val pointer to 64-bit time value +*/ +#define sangoma_ctime(time) _ctime64(time) + #else /* L I N U X */ #include @@ -135,10 +163,10 @@ extern "C" { /* for C++ users */ #include /*! - \def _SAPI_CALL + \def _LIBSNG_CALL \brief Not used in Linux */ -#define _SAPI_CALL +#define _LIBSNG_CALL /*! \def INVALID_HANDLE_VALUE @@ -184,6 +212,8 @@ extern "C" { /* for C++ users */ \brief LPSTR type mapped to unsigned char *, Ported from Windows \typedef PUCHAR \brief PUCHAR type mapped to unsigned char *, Ported from Windows + \typedef PVOID + \brief PVOID type mapped to void *, Ported from Windows \typedef LPTHREAD_START_ROUTINE \brief LPTHREAD_START_ROUTINE type mapped to unsigned char *, Ported from Windows \def _stricmp @@ -218,6 +248,7 @@ typedef unsigned long ULONG; typedef unsigned short USHORT; typedef unsigned char * LPSTR; typedef unsigned char * PUCHAR; +typedef void * PVOID; typedef void * LPTHREAD_START_ROUTINE; typedef pthread_mutex_t CRITICAL_SECTION; @@ -227,7 +258,14 @@ typedef pthread_mutex_t CRITICAL_SECTION; typedef struct tm SYSTEMTIME; typedef char * LPCTSTR; - + +/*! + \def sangoma_ctime(time_val) + \brief Convert a time value to a string + \param time_val pointer to time value +*/ +#define sangoma_ctime(time) ctime((time_t*)time) + #endif/* WIN32 */ @@ -277,8 +315,8 @@ typedef int32_t sangoma_status_t; \brief debug print function */ #define FNAME_LEN 100 -#define FUNC_DBG(x) if(0)printf("%s():%d\n", x, __LINE__) -#define DBG_PRINT if(1)printf +#define LIBSNG_FUNC_DBG() if(0)printf("%s(): line:%d\n", __FUNCTION__, __LINE__) +#define LIBSNG_DBG_PRINT if(0)printf /*! \typedef sangoma_api_hdr_t @@ -310,6 +348,19 @@ typedef enum _sangoma_wait_obj_type type == SANGOMA_DEVICE_WAIT_OBJ_SIG ? "SANGOMA_DEVICE_WAIT_OBJ_SIG" :\ "Invalid Wait Object type!" +/* + * Possible flags for sangoma waitable objects + * this definitions MUST match POLLIN, POLLOUT and POLLPRI + * SANG_WAIT_OBJ_IS_SIGNALED has no posix equivalent though + * Users are encouraged to use this flags instead of the system ones + */ +typedef enum _sangoma_wait_obj_flags { + SANG_WAIT_OBJ_HAS_INPUT = POLLIN, + SANG_WAIT_OBJ_HAS_OUTPUT = POLLOUT, + SANG_WAIT_OBJ_HAS_EVENTS = POLLPRI, + SANG_WAIT_OBJ_IS_SIGNALED = 0x400 /* matches GNU extension POLLMSG */ +} sangoma_wait_obj_flags_t; + /************************************************************//** * Device OPEN / CLOSE Functions ***************************************************************/ @@ -323,7 +374,7 @@ typedef enum _sangoma_wait_obj_type Restriced open, device will allowed to be open only once. */ -sng_fd_t _SAPI_CALL sangoma_open_api_span_chan(int span, int chan); +sng_fd_t _LIBSNG_CALL sangoma_open_api_span_chan(int span, int chan); /*! @@ -335,7 +386,7 @@ sng_fd_t _SAPI_CALL sangoma_open_api_span_chan(int span, int chan); Unrestriced open, allows mutiple open calls on a single device */ -sng_fd_t _SAPI_CALL __sangoma_open_api_span_chan(int span, int chan); +sng_fd_t _LIBSNG_CALL __sangoma_open_api_span_chan(int span, int chan); #define __sangoma_open_tdmapi_span_chan __sangoma_open_api_span_chan /*! @@ -346,7 +397,7 @@ sng_fd_t _SAPI_CALL __sangoma_open_api_span_chan(int span, int chan); Unrestriced open, allows mutiple open calls on a single device */ -sng_fd_t _SAPI_CALL sangoma_open_api_span(int span); +sng_fd_t _LIBSNG_CALL sangoma_open_api_span(int span); /*! @@ -372,8 +423,17 @@ sng_fd_t _SAPI_CALL sangoma_open_api_span(int span); The global control device receives events for all devices configured. */ -sng_fd_t _SAPI_CALL sangoma_open_api_ctrl(void); +sng_fd_t _LIBSNG_CALL sangoma_open_api_ctrl(void); +/*! + \fn sng_fd_t sangoma_logger_open(void) + \brief Open a Global Logger Device + \return File Descriptor - negative=error 0 or greater = fd + + The global Logger device receives Logger Events for all devices + configured. +*/ +sng_fd_t _LIBSNG_CALL sangoma_logger_open(void); /*! \fn sng_fd_t sangoma_open_driver_ctrl(int port_no) @@ -383,7 +443,7 @@ sng_fd_t _SAPI_CALL sangoma_open_api_ctrl(void); The global control device receives events for all devices configured. */ -sng_fd_t _SAPI_CALL sangoma_open_driver_ctrl(int port_no); +sng_fd_t _LIBSNG_CALL sangoma_open_driver_ctrl(int port_no); @@ -394,7 +454,7 @@ sng_fd_t _SAPI_CALL sangoma_open_driver_ctrl(int port_no); \return void */ -void _SAPI_CALL sangoma_close(sng_fd_t *fd); +void _LIBSNG_CALL sangoma_close(sng_fd_t *fd); @@ -406,7 +466,7 @@ void _SAPI_CALL sangoma_close(sng_fd_t *fd); \return negative or 0: error, greater than 1 : open count */ -int _SAPI_CALL sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api); /************************************************************//** @@ -428,7 +488,7 @@ int _SAPI_CALL sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api); variable to identify the reason of an error. Please refer to the error codes. */ -int _SAPI_CALL sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, +int _LIBSNG_CALL sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, unsigned short datalen, int flag); @@ -446,7 +506,7 @@ int _SAPI_CALL sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, In case of error return code, one must check the header operation_status variable to identify the reason of error. Please refer to the error codes. */ -int _SAPI_CALL sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, +int _LIBSNG_CALL sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, int datalen, int flag); @@ -457,16 +517,16 @@ int _SAPI_CALL sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, ***************************************************************/ /*! - \fn sangoma_status_t _SAPI_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, int32_t inflags, int32_t *outflags, int32_t timeout) + \fn sangoma_status_t _LIBSNG_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, int32_t inflags, int32_t *outflags, int32_t timeout) \brief Wait for a single waitable object - \param sangoma_wait_obj pointer to array of file descriptors to wait for + \param sangoma_wait_obj pointer to a wait object previously created with sangoma_wait_obj_create \param timeout timeout in miliseconds in case of no event \return SANG_STATUS_APIPOLL_TIMEOUT: timeout, SANG_STATUS_SUCCESS: event occured use sangoma_wait_obj_get_out_flags to check or error status */ -sangoma_status_t _SAPI_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, uint32_t inflags, uint32_t *outflags, int32_t timeout); +sangoma_status_t _LIBSNG_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, uint32_t inflags, uint32_t *outflags, int32_t timeout); /*! - \fn sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], int32_t in_flags[], int32_t out_flags[] uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout); + \fn sangoma_status_t _LIBSNG_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], int32_t in_flags[], int32_t out_flags[] uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout); \brief Wait for multiple sangoma waitable objects \param sangoma_wait_objects pointer to array of wait objects previously created with sangoma_wait_obj_create \param in_flags array of flags corresponding to the flags the user is interested on for each waitable object @@ -475,7 +535,7 @@ sangoma_status_t _SAPI_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj \param system_wait_timeout timeout in miliseconds in case of no event \return negative: SANG_STATUS_APIPOLL_TIMEOUT: timeout, SANG_STATUS_SUCCESS: event occured check in sangoma_wait_objects, any other return code is some error */ -sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], uint32_t in_flags[], uint32_t out_flags[], +sangoma_status_t _LIBSNG_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], uint32_t in_flags[], uint32_t out_flags[], uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout); /*! @@ -487,7 +547,7 @@ sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wai \see sangoma_wait_obj_type_t \return SANG_STATUS_SUCCESS: success, or error status */ -sangoma_status_t _SAPI_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma_wait_object, sng_fd_t fd, sangoma_wait_obj_type_t object_type); +sangoma_status_t _LIBSNG_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma_wait_object, sng_fd_t fd, sangoma_wait_obj_type_t object_type); /*! \fn sangoma_status_t sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object) @@ -495,7 +555,7 @@ sangoma_status_t _SAPI_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma \param sangoma_wait_object pointer to a pointer to a single device object \return SANG_STATUS_SUCCESS on success or some sangoma status error */ -sangoma_status_t _SAPI_CALL sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object); +sangoma_status_t _LIBSNG_CALL sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object); /*! \fn void sangoma_wait_obj_signal(sangoma_wait_obj_t *sangoma_wait_object) @@ -503,7 +563,7 @@ sangoma_status_t _SAPI_CALL sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma \param sangoma_wait_object pointer a single device object that can be signaled \return sangoma_status_t */ -sangoma_status_t _SAPI_CALL sangoma_wait_obj_signal(sangoma_wait_obj_t *sangoma_wait_object); +sangoma_status_t _LIBSNG_CALL sangoma_wait_obj_signal(sangoma_wait_obj_t *sangoma_wait_object); /*! \fn sng_fd_t sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sangoma_wait_object) @@ -511,7 +571,7 @@ sangoma_status_t _SAPI_CALL sangoma_wait_obj_signal(sangoma_wait_obj_t *sangoma_ \param sangoma_wait_object pointer a single device object \return sng_fd_t - device file descriptor */ -sng_fd_t _SAPI_CALL sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sangoma_wait_object); +sng_fd_t _LIBSNG_CALL sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sangoma_wait_object); /*! \fn void sangoma_wait_obj_set_context(sangoma_wait_obj_t *sangoma_wait_object) @@ -521,15 +581,16 @@ sng_fd_t _SAPI_CALL sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sangoma_wait_obj \param context void pointer to user context \return void */ -void _SAPI_CALL sangoma_wait_obj_set_context(sangoma_wait_obj_t *sangoma_wait_object, void *context); +void _LIBSNG_CALL sangoma_wait_obj_set_context(sangoma_wait_obj_t *sangoma_wait_object, void *context); /*! - \fn void *sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object) + \fn PVOID sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object) \brief Retrieve the user context (if any) that was set via sangoma_wait_obj_set_context. + \brief Windows note: must use return type PVOID instead of void* to satisfy WDK compiler. \param sangoma_wait_object pointer a single device object \return void* */ -void* _SAPI_CALL sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object); +PVOID _LIBSNG_CALL sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object); /************************************************************//** @@ -543,7 +604,7 @@ void* _SAPI_CALL sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_o \param tdm_api tdm api command structure \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! @@ -553,7 +614,7 @@ int _SAPI_CALL sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api); \param tdm_api tdm api command structure \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, int period) @@ -565,7 +626,7 @@ int _SAPI_CALL sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api); Only valid in CHAN Operation Mode */ -int _SAPI_CALL sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, int period); +int _LIBSNG_CALL sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, int period); /*! \fn int sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -574,7 +635,7 @@ int _SAPI_CALL sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, i \param tdm_api tdm api command structure \return negative: error or configured period value */ -int _SAPI_CALL sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -583,18 +644,47 @@ int _SAPI_CALL sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api); \param tdm_api tdm api command structure \return negative: error or configured mtu/mru in bytes */ -int _SAPI_CALL sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) - \brief Flush buffers from current channel + \brief Flush all (tx/rx/event) buffers from current channel \param fd device file descriptor \param tdm_api tdm api command structure \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_flush_rx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush only rx buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _LIBSNG_CALL sangoma_flush_rx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); +/*! + \fn int sangoma_flush_tx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush only tx buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _LIBSNG_CALL sangoma_flush_tx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_flush_event_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush only event buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _LIBSNG_CALL sangoma_flush_event_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! @@ -605,7 +695,7 @@ int _SAPI_CALL sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); \param poll_in_sec driver poll period for rbs events \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_tdm_enable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api, int poll_in_sec); +int _LIBSNG_CALL sangoma_tdm_enable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api, int poll_in_sec); /*! \fn int sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -614,7 +704,7 @@ int _SAPI_CALL sangoma_tdm_enable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api \param tdm_api tdm api command structure \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char rbs) @@ -625,7 +715,7 @@ int _SAPI_CALL sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_ap \param rbs rbs bits (ABCD) \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char rbs); +int _LIBSNG_CALL sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char rbs); /*! \fn int sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char *rbs) @@ -637,7 +727,7 @@ int _SAPI_CALL sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int ch \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char *rbs); +int _LIBSNG_CALL sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char *rbs); /*! \fn int sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -648,7 +738,7 @@ int _SAPI_CALL sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int cha Supported only on cards that have HWEC */ -int _SAPI_CALL sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -659,7 +749,44 @@ int _SAPI_CALL sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_ap Supported only on cards that have HWEC */ -int _SAPI_CALL sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +#ifdef WP_API_FEATURE_FAX_EVENTS +/*! + \fn int sangoma_tdm_enable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable FAX Detection on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _LIBSNG_CALL sangoma_tdm_enable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable FAX Detection on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _LIBSNG_CALL sangoma_tdm_disable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_get_hw_fax(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get HW FAX Detection State (Enable or Disabled) on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: Disabled, 1: Enabled + + Supported only on cards that have HWEC +*/ +int _LIBSNG_CALL sangoma_tdm_get_hw_fax(sng_fd_t fd, wanpipe_api_t *tdm_api); + +#endif /*! @@ -668,10 +795,10 @@ int _SAPI_CALL sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_a \param fd device file descriptor \param tdm_api tdm api command structure \return non-zero: error, 0: ok - + Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_enable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_enable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -682,7 +809,7 @@ int _SAPI_CALL sangoma_tdm_enable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -693,7 +820,7 @@ int _SAPI_CALL sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *td Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -704,7 +831,7 @@ int _SAPI_CALL sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_ Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -715,7 +842,7 @@ int _SAPI_CALL sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -726,7 +853,7 @@ int _SAPI_CALL sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_ap Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! @@ -738,7 +865,7 @@ int _SAPI_CALL sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_a Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_enable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_enable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -749,7 +876,7 @@ int _SAPI_CALL sangoma_tdm_enable_ring_detect_events(sng_fd_t fd, wanpipe_api_t Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -760,7 +887,7 @@ int _SAPI_CALL sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -771,7 +898,7 @@ int _SAPI_CALL sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_a Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api, uint16_t tone_id) @@ -783,7 +910,7 @@ int _SAPI_CALL sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_ Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api, uint16_t tone_id); +int _LIBSNG_CALL sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api, uint16_t tone_id); /*! \fn int sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -794,7 +921,7 @@ int _SAPI_CALL sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_ap Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -805,7 +932,7 @@ int _SAPI_CALL sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_a Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -816,7 +943,7 @@ int _SAPI_CALL sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api); Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -827,7 +954,7 @@ int _SAPI_CALL sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api); Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -838,7 +965,7 @@ int _SAPI_CALL sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api); Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -849,7 +976,7 @@ int _SAPI_CALL sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api); Supported only on cards that have HWEC */ -int _SAPI_CALL sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -860,10 +987,10 @@ int _SAPI_CALL sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api); Supported only on cards that have HWEC */ -int _SAPI_CALL sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! - \fn int _SAPI_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms); + \fn int _LIBSNG_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms); \brief Get Front End Alarms (T1/E1 Only) \param fd device file descriptor \param tdm_api tdm api command structure @@ -872,7 +999,7 @@ int _SAPI_CALL sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api); Supported only on T1/E1 Cards */ -int _SAPI_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms); +int _LIBSNG_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms); @@ -894,7 +1021,7 @@ int _SAPI_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, un \return non-zero: error, 0: ok -> check current_status */ -int _SAPI_CALL sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status); +int _LIBSNG_CALL sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status); #endif @@ -915,11 +1042,11 @@ int _SAPI_CALL sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsi \param new_status new status 0=Link UP 1=Link Down \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_set_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char new_status); +int _LIBSNG_CALL sangoma_set_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char new_status); /*! - \fn int _SAPI_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) + \fn int _LIBSNG_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) \brief Enable BRI Bchannel loopback - used when debugging bri device \param fd device file descriptor \param tdm_api tdm api command structure @@ -927,10 +1054,10 @@ int _SAPI_CALL sangoma_set_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsign \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel); +int _LIBSNG_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel); /*! - \fn int _SAPI_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) + \fn int _LIBSNG_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) \brief Disable BRI Bchannel loopback - used when debugging bri device \param fd device file descriptor \param tdm_api tdm api command structure @@ -938,7 +1065,7 @@ int _SAPI_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel); +int _LIBSNG_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel); /*! @@ -948,7 +1075,7 @@ int _SAPI_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *td \param tdm_api tdm api command structure \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! @@ -959,7 +1086,7 @@ int _SAPI_CALL sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api); \param size tx queue size (minimum value of 1) \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_set_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size); +int _LIBSNG_CALL sangoma_set_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size); /*! \fn int sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -968,7 +1095,7 @@ int _SAPI_CALL sangoma_set_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int \param tdm_api tdm api command structure \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) @@ -978,7 +1105,7 @@ int _SAPI_CALL sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api); \param size rx queue size (minimum value of 1) \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size); +int _LIBSNG_CALL sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size); #ifndef LIBSANGOMA_GET_HWCODING @@ -999,7 +1126,7 @@ int _SAPI_CALL sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int This function will return the low level voice coding depending on configuration. (ulaw or alaw) */ -int _SAPI_CALL sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api); @@ -1019,7 +1146,7 @@ int _SAPI_CALL sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api); This function will check if hw supports HW DTMF. */ -int _SAPI_CALL sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -1030,7 +1157,7 @@ int _SAPI_CALL sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api); This function will check if hw supports HW EC. */ -int _SAPI_CALL sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_span_chan_toif(int span, int chan, char *interface_name) @@ -1040,7 +1167,7 @@ int _SAPI_CALL sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api); \param interface_name pointer to string where interface name will be written \return non-zero = error, 0 = ok */ -int _SAPI_CALL sangoma_span_chan_toif(int span, int chan, char *interface_name); +int _LIBSNG_CALL sangoma_span_chan_toif(int span, int chan, char *interface_name); /*! \fn int sangoma_span_chan_fromif(char *interface_name, int *span, int *chan) @@ -1050,7 +1177,7 @@ int _SAPI_CALL sangoma_span_chan_toif(int span, int chan, char *interface_name); \param chan integer pointer where to write chan value \return non-zero = error, 0 = ok */ -int _SAPI_CALL sangoma_span_chan_fromif(char *interface_name, int *span, int *chan); +int _LIBSNG_CALL sangoma_span_chan_fromif(char *interface_name, int *span, int *chan); /*! @@ -1061,7 +1188,7 @@ int _SAPI_CALL sangoma_span_chan_fromif(char *interface_name, int *span, int *ch \param sectimeout how many seconds to wait for the device to come up, -1 to wait forever \return non-zero = error, 0 = ok */ -int _SAPI_CALL sangoma_interface_wait_up(int span, int chan, int sectimeout); +int _LIBSNG_CALL sangoma_interface_wait_up(int span, int chan, int sectimeout); /*! \fn int sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, wan_driver_version_t *drv_ver) @@ -1071,7 +1198,7 @@ int _SAPI_CALL sangoma_interface_wait_up(int span, int chan, int sectimeout); \param drv_ver driver version structure that will contain the driver version \return non-zero = error, 0 = ok */ -int _SAPI_CALL sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, wan_driver_version_t *drv_ver); +int _LIBSNG_CALL sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, wan_driver_version_t *drv_ver); /*! \fn int sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) @@ -1081,7 +1208,7 @@ int _SAPI_CALL sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, w \param ver hardware/firmware version number \return non-zero = error, 0 = ok */ -int _SAPI_CALL sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver); +int _LIBSNG_CALL sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver); /*! \fn int sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) @@ -1091,7 +1218,7 @@ int _SAPI_CALL sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, \param ver hardware/cpld version number \return non-zero = error, 0 = ok */ -int _SAPI_CALL sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver); +int _LIBSNG_CALL sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver); /*! @@ -1102,16 +1229,16 @@ int _SAPI_CALL sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, uns \param stats stats structure will be filled with device stats. (Optional, can be left NULL) \return non-zero = error, 0 = ok */ -int _SAPI_CALL sangoma_get_stats(sng_fd_t fd, wanpipe_api_t *tdm_api, wanpipe_chan_stats_t *stats); +int _LIBSNG_CALL sangoma_get_stats(sng_fd_t fd, wanpipe_api_t *tdm_api, wanpipe_chan_stats_t *stats); /*! - \fn int _SAPI_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api) + \fn int _LIBSNG_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api) \brief Flush/Reset device statistics \param fd device file descriptor \param tdm_api tdm api command structure \return non-zero = error, 0 = ok */ -int _SAPI_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, int rxflashtime) @@ -1121,9 +1248,73 @@ int _SAPI_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api); \param rxflashtime time value \return non-zero = error, 0 = ok */ -int _SAPI_CALL sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, int rxflashtime); +int _LIBSNG_CALL sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, int rxflashtime); + +#ifdef WP_API_FEATURE_RM_GAIN +/*! + \fn int sangoma_set_rm_tx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) + \brief set tx gain for FXO/FXS module + \param fd device file descriptor + \param tdm_api tdm api command structure + \param value txgain (FXO - txgain value ranges from -150 to 120 , FXS - txgain value 35,-35) + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_set_rm_tx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value); +/*! + \fn int sangoma_set_rm_rx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) + \brief set rx gain for FXO/FXS module + \param fd device file descriptor + \param tdm_api tdm api command structure + \param value rxgain (FXO - rxgain value ranges from -150 to 120 , FXS -rxgain value 35,-35) + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_set_rm_rx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value); + +#endif /* WP RM GAIN feature */ + +/*! + \fn int sangoma_set_polarity(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) + \brief set polarity on FXS (Analog only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param value 0 fwd, 1 rev + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_tdm_set_polarity(sng_fd_t fd, wanpipe_api_t *tdm_api, int polarity); + +/*! + \fn int sangoma_tdm_txsig_onhooktranfer(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG ON HOOK Tranfer (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_txsig_onhooktransfer(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +#ifdef WP_API_FEATURE_LOOP +/*! + \fn int sangoma_tdm_enable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable channel loop: All rx data will be transmitted back out. + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_tdm_enable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable channel loop + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_tdm_disable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api); +#endif /************************************************************//** * Device EVENT Function @@ -1141,9 +1332,80 @@ int _SAPI_CALL sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, i This function usually used after wait() function indicated that event has occured. */ -int _SAPI_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api); +#ifdef WP_API_FEATURE_LOGGER +sangoma_status_t _LIBSNG_CALL sangoma_logger_cmd_exec(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_read_event(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Read Wanpipe Logger Events + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error + + The Logger API structure will be populated with a Logger Event. + This function usually used after wait() function indicated that + an event has occured. +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_read_event(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_flush_buffers(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Flush Wanpipe Logger internal buffers + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_flush_buffers(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_get_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Get Wanpipe Logger statistics + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_get_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_reset_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Reset Wanpipe Logger statistics + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_reset_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_get_open_handle_counter(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Get Counter of open Handles/File Descriptors of Wanpipe Logger + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_get_open_handle_counter(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_get_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Get current level (types of events) of Wanpipe Logger + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_get_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_set_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Set current level (types of events) of Wanpipe Logger + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_set_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +#endif /* WP LOGGER FEATURE */ #ifndef LIBSANGOMA_LIGHT @@ -1165,7 +1427,7 @@ int _SAPI_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api); Windows example: combination of GetLastError()/FormatMessage() zero: no system error. Check port_mgmt->operation_status. */ -int _SAPI_CALL sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); +int _LIBSNG_CALL sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); /*! @@ -1182,7 +1444,7 @@ int _SAPI_CALL sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t * Windows example: combination of GetLastError()/FormatMessage() zero: no system error. Check port_mgmt->operation_status. */ -int _SAPI_CALL sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); +int _LIBSNG_CALL sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); /*! @@ -1201,7 +1463,7 @@ int _SAPI_CALL sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *p Windows example: combination of GetLastError()/FormatMessage() zero: no system error. Check port_cfg->operation_status. */ -int _SAPI_CALL sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no); +int _LIBSNG_CALL sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no); /*! @@ -1217,7 +1479,7 @@ int _SAPI_CALL sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, Windows example: combination of GetLastError()/FormatMessage() zero: no system error. Check port_cfg->operation_status. */ -int _SAPI_CALL sangoma_driver_port_get_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no); +int _LIBSNG_CALL sangoma_driver_port_get_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no); /*! @@ -1232,7 +1494,7 @@ int _SAPI_CALL sangoma_driver_port_get_config(sng_fd_t fd, port_cfg_t *port_cfg, Windows example: combination of GetLastError()/FormatMessage() zero: no system error. Check port_mgmt->operation_status. */ -int _SAPI_CALL sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); +int _LIBSNG_CALL sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); /*! @@ -1251,7 +1513,7 @@ int _SAPI_CALL sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t \param[in] port_no please see comment of sangoma_driver_port_set_config() \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_write_port_config_on_persistent_storage(hardware_info_t *hardware_info, port_cfg_t *port_cfg, unsigned short port_no); +int _LIBSNG_CALL sangoma_write_port_config_on_persistent_storage(hardware_info_t *hardware_info, port_cfg_t *port_cfg, unsigned short port_no); /************************************************************//** @@ -1265,7 +1527,7 @@ int _SAPI_CALL sangoma_write_port_config_on_persistent_storage(hardware_info_t * \param wan_udp management command structure \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_mgmt_cmd(sng_fd_t fd, wan_udp_hdr_t* wan_udp); +int _LIBSNG_CALL sangoma_mgmt_cmd(sng_fd_t fd, wan_udp_hdr_t* wan_udp); #endif /* LIBSANGOMA_LIGHT */ @@ -1295,7 +1557,7 @@ int _SAPI_CALL sangoma_mgmt_cmd(sng_fd_t fd, wan_udp_hdr_t* wan_udp); Deprecated - replaced by sangoma_tdm_get_link_status function */ -int _SAPI_CALL sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status); +int _LIBSNG_CALL sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status); @@ -1310,7 +1572,7 @@ int _SAPI_CALL sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsign Deprecated Function - Here for backward compatibility Only valid in CHAN Operation Mode */ -int _SAPI_CALL sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int codec); +int _LIBSNG_CALL sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int codec); /*! \fn int sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -1322,7 +1584,7 @@ int _SAPI_CALL sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int co Deprecated Function - Here for backward compatibility Only valid in CHAN Operation Mode */ -int _SAPI_CALL sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! @@ -1334,7 +1596,7 @@ int _SAPI_CALL sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api); Deprecated - here for backward compatibility */ -sng_fd_t _SAPI_CALL sangoma_create_socket_by_name(char *device, char *card); +sng_fd_t _LIBSNG_CALL sangoma_create_socket_by_name(char *device, char *card); /*! \fn int sangoma_interface_toi(char *interface_name, int *span, int *chan) @@ -1345,7 +1607,7 @@ sng_fd_t _SAPI_CALL sangoma_create_socket_by_name(char *device, char *card); \return non-zero = error, 0 = ok Deprecated - here for backward compatibility */ -int _SAPI_CALL sangoma_interface_toi(char *interface_name, int *span, int *chan); +int _LIBSNG_CALL sangoma_interface_toi(char *interface_name, int *span, int *chan); /*! @@ -1358,7 +1620,7 @@ int _SAPI_CALL sangoma_interface_toi(char *interface_name, int *span, int *chan) Deprecated - not used/implemented */ -int _SAPI_CALL sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, int power); +int _LIBSNG_CALL sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, int power); /*! \fn int sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -1369,15 +1631,318 @@ int _SAPI_CALL sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, Deprecated - not used/implemented */ -int _SAPI_CALL sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +#ifdef WP_API_FEATURE_LIBSNG_HWEC +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_init(char *device_name) + + \brief Load Firmware image onto EC chip. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_init(char *device_name); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_release(char *device_name) + + \brief Reset internal state of HWEC API. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_release(char *device_name); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_operation_mode(char *device_name, int mode, unsigned int fe_chan_map) + + \brief Modify channel operation mode. + + \param mode One of WANEC_API_OPMODE_? values defined in wanpipe_api_iface.h: + WANEC_API_OPMODE_NORMAL, + WANEC_API_OPMODE_HT_FREEZE, + WANEC_API_OPMODE_HT_RESET, + WANEC_API_OPMODE_POWER_DOWN, + WANEC_API_OPMODE_NO_ECHO, + WANEC_API_OPMODE_SPEECH_RECOGNITION. + + \param fe_chan_map Bitmap of channels (timeslots for Digital, + lines for Analog) where the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_operation_mode(char *device_name, int mode, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_on (char *device_name, unsigned int fe_chan_map) + + \brief Set the channel state in the echo canceller to NORMAL/POWER ON. + This enables echo cancelation logic inside the chip. + The action is internal to EC chip itself, not related to AFT FPGA. + This call is slow and should be used only on startup. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, + lines for Analog) where the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_on(char *device_name, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_off (char *device_name, unsigned int fe_chan_map) + + \brief Set the channel state in the echo canceller to POWER OFF. + This disables echo cancellatio logic inside the chip and + data passes unmodified through the ec chip. + The action is internal to EC chip itself, not related + to AFT FPGA. This call is slow and should be used only on startup. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, + lines for Analog) where the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_off(char *device_name, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_enable(char *device_name, unsigned int fe_chan_map) + + \brief Redirect audio stream from AFT FPGA to EC chip. + This command effectively enables echo cancellation since + data is now forced through the EC chip by the FPGA. + Data will be modified by the echo canceller. + This command is recommened for fast enabling of Echo Cancellation. + Note 1: Chip must be configured and in POWER ON state for echo + Chancellation to take place. + Note 2: sangoma_tdm_enable_hwec() function can be use to achive + the same funcitnality based on file descriptor versus + channel map. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_enable(char *device_name, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_disable(char *device_name, unsigned int fe_chan_map) + + \brief Force AFT FPGA to bypass the echo canceller. + This command effectively disables echo cancellation since + data will not flowing through the ec chip. + Data will not be modified by the echo canceller. + This command is recommened for fast disabling of Echo Cancelation. + Note: sangoma_tdm_disable_hwec() function can be use to achive + the same functionality based on file descriptor versus + channel map. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_disable(char *device_name, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_channel_parameters(char *device_name, char *parameter, char *parameter_value, unsigned int channel_map) + + \brief Modify channel configuration parameters. + This is list of Echo Cancellation channel parameters: + + Channel parameter Channel parameter value + ================= ======================= + WANEC_EnableNlp TRUE | FALSE + WANEC_EnableTailDisplacement TRUE | FALSE + WANEC_TailDisplacement 0-896 + WANEC_SoutLevelControl TRUE | FALSE + WANEC_RinAutomaticLevelControl TRUE | FALSE + WANEC_SoutAutomaticLevelControl TRUE | FALSE + WANEC_SoutAdaptiveNoiseReduction TRUE | FALSE + WANEC_RoutNoiseReduction TRUE | FALSE + WANEC_ComfortNoiseMode COMFORT_NOISE_NORMAL + COMFORT_NOISE_FAST_LATCH + COMFORT_NOISE_EXTENDED + COMFORT_NOISE_OFF + WANEC_DtmfToneRemoval TRUE | FALSE + WANEC_AcousticEcho TRUE | FALSE + WANEC_NonLinearityBehaviorA 0-13 + WANEC_NonLinearityBehaviorB 0-8 + WANEC_DoubleTalkBehavior DT_BEH_NORMAL + DT_BEH_LESS_AGGRESSIVE + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_channel_parameters(char *device_name, char *parameter, char *parameter_value, unsigned int channel_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_tone_detection(char *device_name, int tone_id, int enable, unsigned int fe_chan_map, unsigned char port_map) + + \brief Enable/Disable tone detection (such as DTMF) of channels from channel map. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param tone_id See wanpipe_api_iface.h for list of valid tones + + \param enable A flag, if 1 - the specified tone will be detected, + if 0 - specified tone will not be detected. + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \param port_map Port\Direction of tone detection - Rx, Tx. See wanpipe_events.h for + list of valid ports (WAN_EC_CHANNEL_PORT_SOUT...). + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_tone_detection(char *device_name, int tone_id, int enable, unsigned int fe_chan_map, unsigned char port_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_print_statistics(char *device_name, int full, unsigned int fe_chan_map) + + \brief Read and print Chip/Channel statistics from EC chip. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param full Flag to read full statistics, if set to 1. + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will read statistics. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_print_statistics(char *device_name, int full, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_load(char *device_name, char *filename, char pcmlaw, int *out_buffer_id) + + \brief Load audio buffer to EC chip. The buffer can be played out using the sangoma_hwec_audio_buffer_playout() function. + + \param filename name of the audio file (without the extension). + Actual file must have .pcm extension. + Location: + Windows: %SystemRoot%\sang_ec_files (ex: c:\WINDOWS\sang_ec_files) + Linux: /etc/wanpipe/buffers + + \param out_buffer_id when the buffer is loaded on the chip, it is assigned an ID. This ID should + be used when requesting to play out the buffer. + + \return SANG_STATUS_SUCCESS: success, or error status + + */ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_load(char *device_name, char *filename, char pcmlaw, int *out_buffer_id); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_bufferunload(char *device_name, int in_buffer_id) + + \brief Unload/remove an audio buffer from the HWEC chip. + + \param device_name Sangoma wanpipe device name. (ex: wanpipe1 - Linux; wanpipe1_if1 - Windows). + + \param in_buffer_id ID of the buffer which will be unloaded. The ID must be initialized by sangoma_hwec_audio_bufferload(). + + \return SANG_STATUS_SUCCESS - buffer was successfully unloaded/removed, SANG_STATUS_GENERAL_ERROR - error occured +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_unload(char *device_name, int in_buffer_id); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_playout(char *device_name, unsigned int fe_chan_map, + unsigned char port_map, int buffer_id, int start, int repeat_cnt, int duration) + + \brief Start\Stop playing out an audio buffer previously loaded by sangoma_hwec_audio_buffer_load(). + + \param fe_chan_map Bitmap of channels (timeslots for Digital, + lines for Analog) where the call will take effect. + + \param port_map Port\Direction where the buffer will be played out. + This is the channel port on which the buffer will be + played (WAN_EC_CHANNEL_PORT_SOUT or WAN_EC_CHANNEL_PORT_ROUT) + + \param in_buffer_id ID of the buffer which will be unloaded. The ID must be initialized by sangoma_hwec_audio_bufferload(). + + \param start If 1 - start the play out, 0 - stop the play out + + \param repeat_cnt Number of times to play out the same buffer + + \param duration Maximum duration of the playout, in milliseconds. If it takes less then 'duration' to + play out the whole buffer this paramter is ignored. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_playout(char *device_name, unsigned int fe_chan_map, + unsigned char port, int in_buffer_id, int start, + int repeat_cnt, int duration); + +/*! + \fn void _LIBSNG_CALL sangoma_hwec_config_verbosity(int verbosity_level) + + \brief Set Verbosity level of EC API. The level controls amount of data + printed to stdout and wanpipelog.txt for diagnostic purposes. + + \param verbosity_level Valid values are from 0 to 3. + + \return SANG_STATUS_SUCCESS: success - the level was changed to 'verbosity_level', + SANG_STATUS_INVALID_PARAMETER: error - the level was not changed because new level is invalid +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_verbosity(int verbosity_level); + +#endif /* WP_API_FEATURE_LIBSNG_HWEC */ #ifdef __cplusplus } +#endif/* __WINDOWS__ */ + + +#if defined(__WINDOWS__) +/*! Windows specific API */ +#ifdef __cplusplus +extern "C" { /* for C++ users */ #endif +sangoma_status_t _LIBSNG_CALL sangoma_unload_driver(); +sangoma_status_t _LIBSNG_CALL sangoma_load_driver(); +void _LIBSNG_CALL sangoma_reset_port_numbers(); +sangoma_status_t _LIBSNG_CALL sangoma_get_driver_version_from_registry(char *out_buffer, int out_buffer_length); +sangoma_status_t _LIBSNG_CALL sangoma_set_driver_mode_of_all_hw_devices(int driver_mode); + +#ifdef __cplusplus +} +#endif/* __WINDOWS__ */ + +#else /*! Backward compabile defines */ -#if !defined(__WINDOWS__) #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 diff --git a/api/libsangoma/.svn/text-base/libsangoma.sln.svn-base b/api/libsangoma/.svn/text-base/libsangoma.sln.svn-base index 26f7387..34d3d31 100644 --- a/api/libsangoma/.svn/text-base/libsangoma.sln.svn-base +++ b/api/libsangoma/.svn/text-base/libsangoma.sln.svn-base @@ -1,6 +1,6 @@  -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libsangoma", "libsangoma.vcproj", "{7E60CC2D-E818-4712-8F16-C0E6EC64982F}" EndProject Global diff --git a/api/libsangoma/.svn/text-base/libsangoma.vcproj.DAVIDRNEW.User.user.svn-base b/api/libsangoma/.svn/text-base/libsangoma.vcproj.DAVIDRNEW.User.user.svn-base deleted file mode 100644 index 9c2ce8b..0000000 --- a/api/libsangoma/.svn/text-base/libsangoma.vcproj.DAVIDRNEW.User.user.svn-base +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - diff --git a/api/libsangoma/.svn/text-base/libsangoma.vcproj.svn-base b/api/libsangoma/.svn/text-base/libsangoma.vcproj.svn-base index aac9048..c1e2001 100644 --- a/api/libsangoma/.svn/text-base/libsangoma.vcproj.svn-base +++ b/api/libsangoma/.svn/text-base/libsangoma.vcproj.svn-base @@ -1,10 +1,11 @@ @@ -215,10 +216,6 @@ RelativePath=".\libsangoma.h" > - - + * + * Copyright: (c) 2005-2010 Sangoma Technologies Corporation + * + * * 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 Sangoma Technologies 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 Sangoma Technologies ``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 Sangoma Technologies 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 + ******************************************************************************* + */ + +#include "libsangoma.h" +#include "libsangoma-pvt.h" +#include "wanpipe_includes.h" + +#ifdef WP_API_FEATURE_LIBSNG_HWEC + +#include "wanpipe_events.h" +#include "wanec_api.h" + +#if defined (__WINDOWS__) +# include "wanpipe_time.h" /* wp_sleep() */ +# pragma comment( lib, "waneclib" ) /* import functions from waneclib.dll */ +#endif/* __WINDOWS__) */ + +/* Fast sequence of commands to HWEC may cause the chip + * enter fatal error state, the workaround is to have + * a guaranteed delay after eache command. */ +#define HWEC_CMD_DELAY() wp_usleep(20000) /* 20ms */ + +static int libsng_hwec_verbosity_level = 0; + +/************************************************************//** + * Private Functions. (Not exported) + ***************************************************************/ + +static sangoma_status_t sangoma_hwec_bypass(char *device_name, int enable, unsigned int fe_chan_map) +{ + sangoma_status_t rc; + wanec_api_hwec_t hwec; + + memset(&hwec, 0, sizeof(wanec_api_hwec_t)); + + hwec.enable = enable; + hwec.fe_chan_map = fe_chan_map; + + /* WAN_EC_API_CMD_HWEC_ENABLE/WAN_EC_API_CMD_HWEC_DISABLE - Controls the "bypass" mode.)*/ + rc = wanec_api_hwec(device_name, libsng_hwec_verbosity_level, &hwec); + if( rc ) { + return rc; + } + HWEC_CMD_DELAY(); + return SANG_STATUS_SUCCESS; +} + + +/************************************************************//** + * Public Functions. (Exported) + ***************************************************************/ + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_init(char *device_name) + + \brief Load Firmware image onto EC chip. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_init(char *device_name) +{ + sangoma_status_t rc; + wan_custom_param_t custom_parms; + wanec_api_config_t config; + + memset(&config, 0x00, sizeof(config)); + memset(&custom_parms, 0x0, sizeof(custom_parms)); + +#if 1 + /* enable acoustic echo cancellation by default */ + strcpy( custom_parms.name, "WANEC_EnableAcousticEcho" ); + strcpy( custom_parms.sValue, "TRUE" ); + + config.conf.param_no = 1; + config.conf.params = &custom_parms; +#endif + + /* Load firmware on EC chip */ + rc = wanec_api_config( device_name, libsng_hwec_verbosity_level, &config ); + if( rc ) { + return rc; + } + HWEC_CMD_DELAY(); + return SANG_STATUS_SUCCESS; +} + + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_release(char *device_name) + + \brief Reset internal state of HWEC API. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_release(char *device_name) +{ + sangoma_status_t rc; + wanec_api_release_t release; + + memset(&release, 0, sizeof(wanec_api_release_t)); + + rc = wanec_api_release( device_name, libsng_hwec_verbosity_level, &release ); + if( rc ) { + return rc; + } + HWEC_CMD_DELAY(); + return SANG_STATUS_SUCCESS; +} + + +/*! + Modify channel operation mode. +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_operation_mode(char *device_name, int mode, unsigned int fe_chan_map) +{ + sangoma_status_t rc; + wanec_api_opmode_t opmode; + + memset(&opmode, 0, sizeof(wanec_api_opmode_t)); + + opmode.mode = mode; + opmode.fe_chan_map = fe_chan_map; + + /* modes are: + WANEC_API_OPMODE_NORMAL, + WANEC_API_OPMODE_HT_FREEZE, + WANEC_API_OPMODE_HT_RESET, + WANEC_API_OPMODE_POWER_DOWN, + WANEC_API_OPMODE_NO_ECHO, + WANEC_API_OPMODE_SPEECH_RECOGNITION. + */ + rc = wanec_api_opmode(device_name, libsng_hwec_verbosity_level, &opmode); + if( rc ) { + return rc; + } + HWEC_CMD_DELAY(); + return SANG_STATUS_SUCCESS; +} + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_on (char *device_name, unsigned int fe_chan_map) + + \brief Set the channel state in the echo canceller to NORMAL/POWER ON. + This enables echo cancelation logic inside the chip. + The action is internal to EC chip itself, not related to AFT FPGA. + This call is slow and should be used only on startup. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, + lines for Analog) where the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_on(char *device_name, unsigned int fe_chan_map) +{ + return sangoma_hwec_config_operation_mode(device_name, WANEC_API_OPMODE_NORMAL, fe_chan_map); +} + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_off (char *device_name, unsigned int fe_chan_map) + + \brief Set the channel state in the echo canceller to POWER OFF. + This disables echo cancellatio logic inside the chip and + data passes unmodified through the ec chip. + The action is internal to EC chip itself, not related + to AFT FPGA. This call is slow and should be used only on startup. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, + lines for Analog) where the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_off(char *device_name, unsigned int fe_chan_map) +{ + return sangoma_hwec_config_operation_mode(device_name, WANEC_API_OPMODE_POWER_DOWN, fe_chan_map); +} + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_enable(char *device_name, unsigned int fe_chan_map) + + \brief Redirect audio stream from AFT FPGA to EC chip. + This command effectively enables echo cancellation since + data is now forced through the EC chip by the FPGA. + Data will be modified by the echo canceller. + This command is recommened for fast enabling of Echo Cancellation. + Note 1: Chip must be configured and in POWER ON state for echo + Chancellation to take place. + Note 2: sangoma_tdm_enable_hwec() function can be use to achive + the same funcitnality based on file descriptor versus + channel map. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_enable(char *device_name, unsigned int fe_chan_map) +{ + return sangoma_hwec_bypass(device_name, 1 /* enable */, fe_chan_map); +} + + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_disable(char *device_name, unsigned int fe_chan_map) + + \brief Force AFT FPGA to bypass the echo canceller. + This command effectively disables echo cancellation since + data will not flowing through the ec chip. + Data will not be modified by the echo canceller. + This command is recommened for fast disabling of Echo Cancelation. + Note: sangoma_tdm_disable_hwec() function can be use to achive + the same functionality based on file descriptor versus + channel map. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_disable(char *device_name, unsigned int fe_chan_map) +{ + return sangoma_hwec_bypass(device_name, 0 /* disable */, fe_chan_map);; +} + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_channel_parameters(char *device_name, char *parameter, char *parameter_value, unsigned int channel_map) + + \brief Modify channel configuration parameters. + This is list of Echo Cancellation channel parameters: + + Channel parameter Channel parameter value + ================= ======================= + WANEC_EnableNlp TRUE | FALSE + WANEC_EnableTailDisplacement TRUE | FALSE + WANEC_TailDisplacement 0-896 + WANEC_SoutLevelControl TRUE | FALSE + WANEC_RinAutomaticLevelControl TRUE | FALSE + WANEC_SoutAutomaticLevelControl TRUE | FALSE + WANEC_SoutAdaptiveNoiseReduction TRUE | FALSE + WANEC_RoutNoiseReduction TRUE | FALSE + WANEC_ComfortNoiseMode COMFORT_NOISE_NORMAL + COMFORT_NOISE_FAST_LATCH + COMFORT_NOISE_EXTENDED + COMFORT_NOISE_OFF + WANEC_DtmfToneRemoval TRUE | FALSE + WANEC_AcousticEcho TRUE | FALSE + WANEC_NonLinearityBehaviorA 0-13 + WANEC_NonLinearityBehaviorB 0-8 + WANEC_DoubleTalkBehavior DT_BEH_NORMAL + DT_BEH_LESS_AGGRESSIVE + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_channel_parameters(char *device_name, char *parameter, char *parameter_value, unsigned int channel_map) +{ + sangoma_status_t rc; + wanec_api_modify_t channelModify; + wan_custom_param_t aParms; + + memset(&channelModify, 0x00, sizeof(channelModify)); + memset(&aParms, 0x00, sizeof(aParms)); + + channelModify.fe_chan_map = channel_map; + channelModify.conf.param_no = 1; + channelModify.conf.params = &aParms; + + strcpy( aParms.name, parameter); + strcpy( aParms.sValue, parameter_value); + + rc = wanec_api_modify( device_name, libsng_hwec_verbosity_level, &channelModify ); + if( rc ){ + return rc; + } + HWEC_CMD_DELAY(); + return SANG_STATUS_SUCCESS; +} + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_tone_detection(char *device_name, int tone_id, int enable, unsigned int fe_chan_map, unsigned char port_map) + + \brief Enable/Disable tone detection (such as DTMF) of channels from channel map. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param tone_id See wanpipe_api_iface.h for list of valid tones + + \param enable A flag, if 1 - the specified tone will be detected, + if 0 - specified tone will not be detected. + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \param port_map Port\Direction of tone detection - Rx, Tx. See wanpipe_events.h for + list of valid ports (WAN_EC_CHANNEL_PORT_SOUT...). + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_tone_detection(char *device_name, int tone_id, int enable, unsigned int fe_chan_map, unsigned char port_map) +{ + sangoma_status_t rc; + wanec_api_tone_t tone; + + memset(&tone, 0, sizeof(wanec_api_tone_t)); + + tone.id = tone_id; + tone.enable = enable; + tone.fe_chan_map = fe_chan_map; + tone.port_map = port_map; + tone.type_map = WAN_EC_TONE_PRESENT | WAN_EC_TONE_STOP; + + rc = wanec_api_tone( device_name, libsng_hwec_verbosity_level, &tone ); + if( rc ) { + return rc; + } + HWEC_CMD_DELAY(); + return SANG_STATUS_SUCCESS; +} + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_print_statistics(char *device_name, int full, unsigned int fe_chan_map) + + \brief Read and print Chip/Channel statistics from EC chip. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param full Flag to read full statistics, if set to 1. + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will read statistics. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_print_statistics(char *device_name, int full, unsigned int fe_chan_map) +{ + sangoma_status_t rc; + wanec_api_stats_t stats; + + memset(&stats, 0, sizeof(wanec_api_stats_t)); + + stats.full = full; + stats.fe_chan = fe_chan_map; + stats.reset = 0; /* do not reset */ + + rc = wanec_api_stats( device_name, libsng_hwec_verbosity_level, &stats ); + if( rc ) { + return rc; + } + HWEC_CMD_DELAY(); + return SANG_STATUS_SUCCESS; +} + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_load(char *device_name, char *filename, char pcmlaw, int *out_buffer_id) + + \brief Load audio buffer to EC chip. The buffer can be played out using the sangoma_hwec_audio_buffer_playout() function. + + \param filename name of the audio file (without the extension). + Actual file must have .pcm extension. + Location: + Windows: %SystemRoot%\sang_ec_files (ex: c:\WINDOWS\sang_ec_files) + Linux: /etc/wanpipe/buffers + + \param out_buffer_id when the buffer is loaded on the chip, it is assigned an ID. This ID should + be used when requesting to play out the buffer. + + \return SANG_STATUS_SUCCESS: success, or error status + + */ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_load(char *device_name, char *filename, char pcmlaw, int *out_buffer_id) +{ + sangoma_status_t rc; + wanec_api_bufferload_t bufferload; + + memset(&bufferload, 0, sizeof(wanec_api_bufferload_t)); + *out_buffer_id = -1; + + bufferload.buffer = filename; + bufferload.pcmlaw = pcmlaw; + + rc = wanec_api_buffer_load( device_name, libsng_hwec_verbosity_level, &bufferload ); + if( rc ) { + return rc; + } + + *out_buffer_id = bufferload.buffer_id; + + HWEC_CMD_DELAY(); + return SANG_STATUS_SUCCESS; +} + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_bufferunload(char *device_name, int in_buffer_id) + + \brief Unload/remove an audio buffer from the HWEC chip. + + \param device_name Sangoma wanpipe device name. (ex: wanpipe1 - Linux; wanpipe1_if1 - Windows). + + \param in_buffer_id ID of the buffer which will be unloaded. The ID must be initialized by sangoma_hwec_audio_bufferload(). + + \return SANG_STATUS_SUCCESS - buffer was successfully unloaded/removed, SANG_STATUS_GENERAL_ERROR - error occured +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_unload(char *device_name, int in_buffer_id) +{ + sangoma_status_t rc; + wanec_api_bufferunload_t bufferunload; + + memset(&bufferunload, 0, sizeof(wanec_api_bufferunload_t)); + + bufferunload.buffer_id = (unsigned int)in_buffer_id; + + rc = wanec_api_buffer_unload( device_name, libsng_hwec_verbosity_level, &bufferunload); + if( rc ) { + return rc; + } + HWEC_CMD_DELAY(); + return SANG_STATUS_SUCCESS; +} + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_playout(char *device_name, unsigned int fe_chan_map, + unsigned char port_map, int buffer_id, int start, int repeat_cnt, int duration) + + \brief Start\Stop playing out an audio buffer previously loaded by sangoma_hwec_audio_buffer_load(). + + \param fe_chan_map Bitmap of channels (timeslots for Digital, + lines for Analog) where the call will take effect. + + \param port_map Port\Direction where the buffer will be played out. + This is the channel port on which the buffer will be + played (WAN_EC_CHANNEL_PORT_SOUT or WAN_EC_CHANNEL_PORT_ROUT) + + \param in_buffer_id ID of the buffer which will be unloaded. The ID must be initialized by sangoma_hwec_audio_bufferload(). + + \param start If 1 - start the play out, 0 - stop the play out + + \param repeat_cnt Number of times to play out the same buffer + + \param duration Maximum duration of the playout, in milliseconds. If it takes less then 'duration' to + play out the whole buffer this paramter is ignored. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_playout(char *device_name, unsigned int fe_chan_map, + unsigned char port, int in_buffer_id, int start, + int repeat_cnt, int duration) +{ + sangoma_status_t rc; + wanec_api_playout_t playout; + + memset(&playout, 0, sizeof(wanec_api_playout_t)); + + playout.start = start; + playout.fe_chan = fe_chan_map; + playout.buffer_id = in_buffer_id; + playout.port = port; + playout.notifyonstop = 1; + playout.user_event_id = 0xA5; /* dummy value */ + playout.repeat_cnt = repeat_cnt; + playout.duration = (duration) ? duration : 5000; /* default is 5s */ + + rc = wanec_api_playout( device_name, libsng_hwec_verbosity_level, &playout); + if( rc ) { + return rc; + } + HWEC_CMD_DELAY(); + return SANG_STATUS_SUCCESS; +} + +/*! + \fn void _LIBSNG_CALL sangoma_hwec_config_verbosity(int verbosity_level) + + \brief Set Verbosity level of EC API. The level controls amount of data + printed to stdout and wanpipelog.txt for diagnostic purposes. + + \param verbosity_level Valid values are from 0 to 3. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_verbosity(int verbosity_level) +{ + if (verbosity_level >= 0 || verbosity_level <= 3) { + libsng_hwec_verbosity_level = verbosity_level; + return SANG_STATUS_SUCCESS; + } + return SANG_STATUS_INVALID_PARAMETER; +} + +#endif /* WP_API_FEATURE_LIBSNG_HWEC */ diff --git a/api/libsangoma/.svn/text-base/ltmain.sh.svn-base b/api/libsangoma/.svn/text-base/ltmain.sh.svn-base deleted file mode 100644 index 0223495..0000000 --- a/api/libsangoma/.svn/text-base/ltmain.sh.svn-base +++ /dev/null @@ -1,6911 +0,0 @@ -# ltmain.sh - Provide generalized library-building support services. -# NOTE: Changing this file will not affect anything until you rerun configure. -# -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005 -# Free Software Foundation, Inc. -# Originally by Gordon Matzigkeit , 1996 -# -# 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. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -basename="s,^.*/,,g" - -# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh -# is ksh but when the shell is invoked as "sh" and the current value of -# the _XPG environment variable is not equal to 1 (one), the special -# positional parameter $0, within a function call, is the name of the -# function. -progpath="$0" - -# The name of this program: -progname=`echo "$progpath" | $SED $basename` -modename="$progname" - -# Global variables: -EXIT_SUCCESS=0 -EXIT_FAILURE=1 - -PROGRAM=ltmain.sh -PACKAGE=libtool -VERSION=1.5.22 -TIMESTAMP=" (1.1220.2.365 2005/12/18 22:14:06)" - -# Be Bourne compatible (taken from Autoconf:_AS_BOURNE_COMPATIBLE). -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then - emulate sh - NULLCMD=: - # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac -fi - -# Check that we have a working $echo. -if test "X$1" = X--no-reexec; then - # Discard the --no-reexec flag, and continue. - shift -elif test "X$1" = X--fallback-echo; then - # Avoid inline document here, it may be left over - : -elif test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then - # Yippee, $echo works! - : -else - # Restart under the correct shell, and then maybe $echo will work. - exec $SHELL "$progpath" --no-reexec ${1+"$@"} -fi - -if test "X$1" = X--fallback-echo; then - # used as fallback echo - shift - cat <&2 - $echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 - exit $EXIT_FAILURE -fi - -# Global variables. -mode=$default_mode -nonopt= -prev= -prevopt= -run= -show="$echo" -show_help= -execute_dlfiles= -duplicate_deps=no -preserve_args= -lo2o="s/\\.lo\$/.${objext}/" -o2lo="s/\\.${objext}\$/.lo/" -extracted_archives= -extracted_serial=0 - -##################################### -# Shell function definitions: -# This seems to be the best place for them - -# func_mktempdir [string] -# Make a temporary directory that won't clash with other running -# libtool processes, and avoids race conditions if possible. If -# given, STRING is the basename for that directory. -func_mktempdir () -{ - my_template="${TMPDIR-/tmp}/${1-$progname}" - - if test "$run" = ":"; then - # Return a directory name, but don't create it in dry-run mode - my_tmpdir="${my_template}-$$" - else - - # If mktemp works, use that first and foremost - my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` - - if test ! -d "$my_tmpdir"; then - # Failing that, at least try and use $RANDOM to avoid a race - my_tmpdir="${my_template}-${RANDOM-0}$$" - - save_mktempdir_umask=`umask` - umask 0077 - $mkdir "$my_tmpdir" - umask $save_mktempdir_umask - fi - - # If we're not in dry-run mode, bomb out on failure - test -d "$my_tmpdir" || { - $echo "cannot create temporary directory \`$my_tmpdir'" 1>&2 - exit $EXIT_FAILURE - } - fi - - $echo "X$my_tmpdir" | $Xsed -} - - -# func_win32_libid arg -# return the library type of file 'arg' -# -# Need a lot of goo to handle *both* DLLs and import libs -# Has to be a shell function in order to 'eat' the argument -# that is supplied when $file_magic_command is called. -func_win32_libid () -{ - win32_libid_type="unknown" - win32_fileres=`file -L $1 2>/dev/null` - case $win32_fileres in - *ar\ archive\ import\ library*) # definitely import - win32_libid_type="x86 archive import" - ;; - *ar\ archive*) # could be an import, or static - if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | \ - $EGREP -e 'file format pe-i386(.*architecture: i386)?' >/dev/null ; then - win32_nmres=`eval $NM -f posix -A $1 | \ - $SED -n -e '1,100{/ I /{s,.*,import,;p;q;};}'` - case $win32_nmres in - import*) win32_libid_type="x86 archive import";; - *) win32_libid_type="x86 archive static";; - esac - fi - ;; - *DLL*) - win32_libid_type="x86 DLL" - ;; - *executable*) # but shell scripts are "executable" too... - case $win32_fileres in - *MS\ Windows\ PE\ Intel*) - win32_libid_type="x86 DLL" - ;; - esac - ;; - esac - $echo $win32_libid_type -} - - -# func_infer_tag arg -# Infer tagged configuration to use if any are available and -# if one wasn't chosen via the "--tag" command line option. -# Only attempt this if the compiler in the base compile -# command doesn't match the default compiler. -# arg is usually of the form 'gcc ...' -func_infer_tag () -{ - if test -n "$available_tags" && test -z "$tagname"; then - CC_quoted= - for arg in $CC; do - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - CC_quoted="$CC_quoted $arg" - done - case $@ in - # Blanks in the command may have been stripped by the calling shell, - # but not from the CC environment variable when configure was run. - " $CC "* | "$CC "* | " `$echo $CC` "* | "`$echo $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$echo $CC_quoted` "* | "`$echo $CC_quoted` "*) ;; - # Blanks at the start of $base_compile will cause this to fail - # if we don't check for them as well. - *) - for z in $available_tags; do - if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then - # Evaluate the configuration. - eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" - CC_quoted= - for arg in $CC; do - # Double-quote args containing other shell metacharacters. - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - CC_quoted="$CC_quoted $arg" - done - case "$@ " in - " $CC "* | "$CC "* | " `$echo $CC` "* | "`$echo $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$echo $CC_quoted` "* | "`$echo $CC_quoted` "*) - # The compiler in the base compile command matches - # the one in the tagged configuration. - # Assume this is the tagged configuration we want. - tagname=$z - break - ;; - esac - fi - done - # If $tagname still isn't set, then no tagged configuration - # was found and let the user know that the "--tag" command - # line option must be used. - if test -z "$tagname"; then - $echo "$modename: unable to infer tagged configuration" - $echo "$modename: specify a tag with \`--tag'" 1>&2 - exit $EXIT_FAILURE -# else -# $echo "$modename: using $tagname tagged configuration" - fi - ;; - esac - fi -} - - -# func_extract_an_archive dir oldlib -func_extract_an_archive () -{ - f_ex_an_ar_dir="$1"; shift - f_ex_an_ar_oldlib="$1" - - $show "(cd $f_ex_an_ar_dir && $AR x $f_ex_an_ar_oldlib)" - $run eval "(cd \$f_ex_an_ar_dir && $AR x \$f_ex_an_ar_oldlib)" || exit $? - if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then - : - else - $echo "$modename: ERROR: object name conflicts: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" 1>&2 - exit $EXIT_FAILURE - fi -} - -# func_extract_archives gentop oldlib ... -func_extract_archives () -{ - my_gentop="$1"; shift - my_oldlibs=${1+"$@"} - my_oldobjs="" - my_xlib="" - my_xabs="" - my_xdir="" - my_status="" - - $show "${rm}r $my_gentop" - $run ${rm}r "$my_gentop" - $show "$mkdir $my_gentop" - $run $mkdir "$my_gentop" - my_status=$? - if test "$my_status" -ne 0 && test ! -d "$my_gentop"; then - exit $my_status - fi - - for my_xlib in $my_oldlibs; do - # Extract the objects. - case $my_xlib in - [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; - *) my_xabs=`pwd`"/$my_xlib" ;; - esac - my_xlib=`$echo "X$my_xlib" | $Xsed -e 's%^.*/%%'` - my_xlib_u=$my_xlib - while :; do - case " $extracted_archives " in - *" $my_xlib_u "*) - extracted_serial=`expr $extracted_serial + 1` - my_xlib_u=lt$extracted_serial-$my_xlib ;; - *) break ;; - esac - done - extracted_archives="$extracted_archives $my_xlib_u" - my_xdir="$my_gentop/$my_xlib_u" - - $show "${rm}r $my_xdir" - $run ${rm}r "$my_xdir" - $show "$mkdir $my_xdir" - $run $mkdir "$my_xdir" - exit_status=$? - if test "$exit_status" -ne 0 && test ! -d "$my_xdir"; then - exit $exit_status - fi - case $host in - *-darwin*) - $show "Extracting $my_xabs" - # Do not bother doing anything if just a dry run - if test -z "$run"; then - darwin_orig_dir=`pwd` - cd $my_xdir || exit $? - darwin_archive=$my_xabs - darwin_curdir=`pwd` - darwin_base_archive=`$echo "X$darwin_archive" | $Xsed -e 's%^.*/%%'` - darwin_arches=`lipo -info "$darwin_archive" 2>/dev/null | $EGREP Architectures 2>/dev/null` - if test -n "$darwin_arches"; then - darwin_arches=`echo "$darwin_arches" | $SED -e 's/.*are://'` - darwin_arch= - $show "$darwin_base_archive has multiple architectures $darwin_arches" - for darwin_arch in $darwin_arches ; do - mkdir -p "unfat-$$/${darwin_base_archive}-${darwin_arch}" - lipo -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" - cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" - func_extract_an_archive "`pwd`" "${darwin_base_archive}" - cd "$darwin_curdir" - $rm "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" - done # $darwin_arches - ## Okay now we have a bunch of thin objects, gotta fatten them up :) - darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print| xargs basename | sort -u | $NL2SP` - darwin_file= - darwin_files= - for darwin_file in $darwin_filelist; do - darwin_files=`find unfat-$$ -name $darwin_file -print | $NL2SP` - lipo -create -output "$darwin_file" $darwin_files - done # $darwin_filelist - ${rm}r unfat-$$ - cd "$darwin_orig_dir" - else - cd "$darwin_orig_dir" - func_extract_an_archive "$my_xdir" "$my_xabs" - fi # $darwin_arches - fi # $run - ;; - *) - func_extract_an_archive "$my_xdir" "$my_xabs" - ;; - esac - my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP` - done - func_extract_archives_result="$my_oldobjs" -} -# End of Shell function definitions -##################################### - -# Darwin sucks -eval std_shrext=\"$shrext_cmds\" - -disable_libs=no - -# Parse our command line options once, thoroughly. -while test "$#" -gt 0 -do - arg="$1" - shift - - case $arg in - -*=*) optarg=`$echo "X$arg" | $Xsed -e 's/[-_a-zA-Z0-9]*=//'` ;; - *) optarg= ;; - esac - - # If the previous option needs an argument, assign it. - if test -n "$prev"; then - case $prev in - execute_dlfiles) - execute_dlfiles="$execute_dlfiles $arg" - ;; - tag) - tagname="$arg" - preserve_args="${preserve_args}=$arg" - - # Check whether tagname contains only valid characters - case $tagname in - *[!-_A-Za-z0-9,/]*) - $echo "$progname: invalid tag name: $tagname" 1>&2 - exit $EXIT_FAILURE - ;; - esac - - case $tagname in - CC) - # Don't test for the "default" C tag, as we know, it's there, but - # not specially marked. - ;; - *) - if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$" < "$progpath" > /dev/null; then - taglist="$taglist $tagname" - # Evaluate the configuration. - eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$tagname'$/,/^# ### END LIBTOOL TAG CONFIG: '$tagname'$/p' < $progpath`" - else - $echo "$progname: ignoring unknown tag $tagname" 1>&2 - fi - ;; - esac - ;; - *) - eval "$prev=\$arg" - ;; - esac - - prev= - prevopt= - continue - fi - - # Have we seen a non-optional argument yet? - case $arg in - --help) - show_help=yes - ;; - - --version) - $echo "$PROGRAM (GNU $PACKAGE) $VERSION$TIMESTAMP" - $echo - $echo "Copyright (C) 2005 Free Software Foundation, Inc." - $echo "This is free software; see the source for copying conditions. There is NO" - $echo "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - exit $? - ;; - - --config) - ${SED} -e '1,/^# ### BEGIN LIBTOOL CONFIG/d' -e '/^# ### END LIBTOOL CONFIG/,$d' $progpath - # Now print the configurations for the tags. - for tagname in $taglist; do - ${SED} -n -e "/^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$/,/^# ### END LIBTOOL TAG CONFIG: $tagname$/p" < "$progpath" - done - exit $? - ;; - - --debug) - $echo "$progname: enabling shell trace mode" - set -x - preserve_args="$preserve_args $arg" - ;; - - --dry-run | -n) - run=: - ;; - - --features) - $echo "host: $host" - if test "$build_libtool_libs" = yes; then - $echo "enable shared libraries" - else - $echo "disable shared libraries" - fi - if test "$build_old_libs" = yes; then - $echo "enable static libraries" - else - $echo "disable static libraries" - fi - exit $? - ;; - - --finish) mode="finish" ;; - - --mode) prevopt="--mode" prev=mode ;; - --mode=*) mode="$optarg" ;; - - --preserve-dup-deps) duplicate_deps="yes" ;; - - --quiet | --silent) - show=: - preserve_args="$preserve_args $arg" - ;; - - --tag) - prevopt="--tag" - prev=tag - preserve_args="$preserve_args --tag" - ;; - --tag=*) - set tag "$optarg" ${1+"$@"} - shift - prev=tag - preserve_args="$preserve_args --tag" - ;; - - -dlopen) - prevopt="-dlopen" - prev=execute_dlfiles - ;; - - -*) - $echo "$modename: unrecognized option \`$arg'" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - ;; - - *) - nonopt="$arg" - break - ;; - esac -done - -if test -n "$prevopt"; then - $echo "$modename: option \`$prevopt' requires an argument" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE -fi - -case $disable_libs in -no) - ;; -shared) - build_libtool_libs=no - build_old_libs=yes - ;; -static) - build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` - ;; -esac - -# If this variable is set in any of the actions, the command in it -# will be execed at the end. This prevents here-documents from being -# left over by shells. -exec_cmd= - -if test -z "$show_help"; then - - # Infer the operation mode. - if test -z "$mode"; then - $echo "*** Warning: inferring the mode of operation is deprecated." 1>&2 - $echo "*** Future versions of Libtool will require --mode=MODE be specified." 1>&2 - case $nonopt in - *cc | cc* | *++ | gcc* | *-gcc* | g++* | xlc*) - mode=link - for arg - do - case $arg in - -c) - mode=compile - break - ;; - esac - done - ;; - *db | *dbx | *strace | *truss) - mode=execute - ;; - *install*|cp|mv) - mode=install - ;; - *rm) - mode=uninstall - ;; - *) - # If we have no mode, but dlfiles were specified, then do execute mode. - test -n "$execute_dlfiles" && mode=execute - - # Just use the default operation mode. - if test -z "$mode"; then - if test -n "$nonopt"; then - $echo "$modename: warning: cannot infer operation mode from \`$nonopt'" 1>&2 - else - $echo "$modename: warning: cannot infer operation mode without MODE-ARGS" 1>&2 - fi - fi - ;; - esac - fi - - # Only execute mode is allowed to have -dlopen flags. - if test -n "$execute_dlfiles" && test "$mode" != execute; then - $echo "$modename: unrecognized option \`-dlopen'" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - - # Change the help message to a mode-specific one. - generic_help="$help" - help="Try \`$modename --help --mode=$mode' for more information." - - # These modes are in order of execution frequency so that they run quickly. - case $mode in - # libtool compile mode - compile) - modename="$modename: compile" - # Get the compilation command and the source file. - base_compile= - srcfile="$nonopt" # always keep a non-empty value in "srcfile" - suppress_opt=yes - suppress_output= - arg_mode=normal - libobj= - later= - - for arg - do - case $arg_mode in - arg ) - # do not "continue". Instead, add this to base_compile - lastarg="$arg" - arg_mode=normal - ;; - - target ) - libobj="$arg" - arg_mode=normal - continue - ;; - - normal ) - # Accept any command-line options. - case $arg in - -o) - if test -n "$libobj" ; then - $echo "$modename: you cannot specify \`-o' more than once" 1>&2 - exit $EXIT_FAILURE - fi - arg_mode=target - continue - ;; - - -static | -prefer-pic | -prefer-non-pic) - later="$later $arg" - continue - ;; - - -no-suppress) - suppress_opt=no - continue - ;; - - -Xcompiler) - arg_mode=arg # the next one goes into the "base_compile" arg list - continue # The current "srcfile" will either be retained or - ;; # replaced later. I would guess that would be a bug. - - -Wc,*) - args=`$echo "X$arg" | $Xsed -e "s/^-Wc,//"` - lastarg= - save_ifs="$IFS"; IFS=',' - for arg in $args; do - IFS="$save_ifs" - - # Double-quote args containing other shell metacharacters. - # Many Bourne shells cannot handle close brackets correctly - # in scan sets, so we specify it separately. - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - lastarg="$lastarg $arg" - done - IFS="$save_ifs" - lastarg=`$echo "X$lastarg" | $Xsed -e "s/^ //"` - - # Add the arguments to base_compile. - base_compile="$base_compile $lastarg" - continue - ;; - - * ) - # Accept the current argument as the source file. - # The previous "srcfile" becomes the current argument. - # - lastarg="$srcfile" - srcfile="$arg" - ;; - esac # case $arg - ;; - esac # case $arg_mode - - # Aesthetically quote the previous argument. - lastarg=`$echo "X$lastarg" | $Xsed -e "$sed_quote_subst"` - - case $lastarg in - # Double-quote args containing other shell metacharacters. - # Many Bourne shells cannot handle close brackets correctly - # in scan sets, and some SunOS ksh mistreat backslash-escaping - # in scan sets (worked around with variable expansion), - # and furthermore cannot handle '|' '&' '(' ')' in scan sets - # at all, so we specify them separately. - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - lastarg="\"$lastarg\"" - ;; - esac - - base_compile="$base_compile $lastarg" - done # for arg - - case $arg_mode in - arg) - $echo "$modename: you must specify an argument for -Xcompile" - exit $EXIT_FAILURE - ;; - target) - $echo "$modename: you must specify a target with \`-o'" 1>&2 - exit $EXIT_FAILURE - ;; - *) - # Get the name of the library object. - [ -z "$libobj" ] && libobj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%'` - ;; - esac - - # Recognize several different file suffixes. - # If the user specifies -o file.o, it is replaced with file.lo - xform='[cCFSifmso]' - case $libobj in - *.ada) xform=ada ;; - *.adb) xform=adb ;; - *.ads) xform=ads ;; - *.asm) xform=asm ;; - *.c++) xform=c++ ;; - *.cc) xform=cc ;; - *.ii) xform=ii ;; - *.class) xform=class ;; - *.cpp) xform=cpp ;; - *.cxx) xform=cxx ;; - *.f90) xform=f90 ;; - *.for) xform=for ;; - *.java) xform=java ;; - *.obj) xform=obj ;; - esac - - libobj=`$echo "X$libobj" | $Xsed -e "s/\.$xform$/.lo/"` - - case $libobj in - *.lo) obj=`$echo "X$libobj" | $Xsed -e "$lo2o"` ;; - *) - $echo "$modename: cannot determine name of library object from \`$libobj'" 1>&2 - exit $EXIT_FAILURE - ;; - esac - - func_infer_tag $base_compile - - for arg in $later; do - case $arg in - -static) - build_old_libs=yes - continue - ;; - - -prefer-pic) - pic_mode=yes - continue - ;; - - -prefer-non-pic) - pic_mode=no - continue - ;; - esac - done - - qlibobj=`$echo "X$libobj" | $Xsed -e "$sed_quote_subst"` - case $qlibobj in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - qlibobj="\"$qlibobj\"" ;; - esac - test "X$libobj" != "X$qlibobj" \ - && $echo "X$libobj" | grep '[]~#^*{};<>?"'"'"' &()|`$[]' \ - && $echo "$modename: libobj name \`$libobj' may not contain shell special characters." - objname=`$echo "X$obj" | $Xsed -e 's%^.*/%%'` - xdir=`$echo "X$obj" | $Xsed -e 's%/[^/]*$%%'` - if test "X$xdir" = "X$obj"; then - xdir= - else - xdir=$xdir/ - fi - lobj=${xdir}$objdir/$objname - - if test -z "$base_compile"; then - $echo "$modename: you must specify a compilation command" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - - # Delete any leftover library objects. - if test "$build_old_libs" = yes; then - removelist="$obj $lobj $libobj ${libobj}T" - else - removelist="$lobj $libobj ${libobj}T" - fi - - $run $rm $removelist - trap "$run $rm $removelist; exit $EXIT_FAILURE" 1 2 15 - - # On Cygwin there's no "real" PIC flag so we must build both object types - case $host_os in - cygwin* | mingw* | pw32* | os2*) - pic_mode=default - ;; - esac - if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then - # non-PIC code in shared libraries is not supported - pic_mode=default - fi - - # Calculate the filename of the output object if compiler does - # not support -o with -c - if test "$compiler_c_o" = no; then - output_obj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.${objext} - lockfile="$output_obj.lock" - removelist="$removelist $output_obj $lockfile" - trap "$run $rm $removelist; exit $EXIT_FAILURE" 1 2 15 - else - output_obj= - need_locks=no - lockfile= - fi - - # Lock this critical section if it is needed - # We use this script file to make the link, it avoids creating a new file - if test "$need_locks" = yes; then - until $run ln "$progpath" "$lockfile" 2>/dev/null; do - $show "Waiting for $lockfile to be removed" - sleep 2 - done - elif test "$need_locks" = warn; then - if test -f "$lockfile"; then - $echo "\ -*** ERROR, $lockfile exists and contains: -`cat $lockfile 2>/dev/null` - -This indicates that another process is trying to use the same -temporary object file, and libtool could not work around it because -your compiler does not support \`-c' and \`-o' together. If you -repeat this compilation, it may succeed, by chance, but you had better -avoid parallel builds (make -j) in this platform, or get a better -compiler." - - $run $rm $removelist - exit $EXIT_FAILURE - fi - $echo "$srcfile" > "$lockfile" - fi - - if test -n "$fix_srcfile_path"; then - eval srcfile=\"$fix_srcfile_path\" - fi - qsrcfile=`$echo "X$srcfile" | $Xsed -e "$sed_quote_subst"` - case $qsrcfile in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - qsrcfile="\"$qsrcfile\"" ;; - esac - - $run $rm "$libobj" "${libobj}T" - - # Create a libtool object file (analogous to a ".la" file), - # but don't create it if we're doing a dry run. - test -z "$run" && cat > ${libobj}T </dev/null`" != "X$srcfile"; then - $echo "\ -*** ERROR, $lockfile contains: -`cat $lockfile 2>/dev/null` - -but it should contain: -$srcfile - -This indicates that another process is trying to use the same -temporary object file, and libtool could not work around it because -your compiler does not support \`-c' and \`-o' together. If you -repeat this compilation, it may succeed, by chance, but you had better -avoid parallel builds (make -j) in this platform, or get a better -compiler." - - $run $rm $removelist - exit $EXIT_FAILURE - fi - - # Just move the object if needed, then go on to compile the next one - if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then - $show "$mv $output_obj $lobj" - if $run $mv $output_obj $lobj; then : - else - error=$? - $run $rm $removelist - exit $error - fi - fi - - # Append the name of the PIC object to the libtool object file. - test -z "$run" && cat >> ${libobj}T <> ${libobj}T </dev/null`" != "X$srcfile"; then - $echo "\ -*** ERROR, $lockfile contains: -`cat $lockfile 2>/dev/null` - -but it should contain: -$srcfile - -This indicates that another process is trying to use the same -temporary object file, and libtool could not work around it because -your compiler does not support \`-c' and \`-o' together. If you -repeat this compilation, it may succeed, by chance, but you had better -avoid parallel builds (make -j) in this platform, or get a better -compiler." - - $run $rm $removelist - exit $EXIT_FAILURE - fi - - # Just move the object if needed - if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then - $show "$mv $output_obj $obj" - if $run $mv $output_obj $obj; then : - else - error=$? - $run $rm $removelist - exit $error - fi - fi - - # Append the name of the non-PIC object the libtool object file. - # Only append if the libtool object file exists. - test -z "$run" && cat >> ${libobj}T <> ${libobj}T <&2 - fi - if test -n "$link_static_flag"; then - dlopen_self=$dlopen_self_static - fi - prefer_static_libs=yes - ;; - -static) - if test -z "$pic_flag" && test -n "$link_static_flag"; then - dlopen_self=$dlopen_self_static - fi - prefer_static_libs=built - ;; - -static-libtool-libs) - if test -z "$pic_flag" && test -n "$link_static_flag"; then - dlopen_self=$dlopen_self_static - fi - prefer_static_libs=yes - ;; - esac - build_libtool_libs=no - build_old_libs=yes - break - ;; - esac - done - - # See if our shared archives depend on static archives. - test -n "$old_archive_from_new_cmds" && build_old_libs=yes - - # Go through the arguments, transforming them on the way. - while test "$#" -gt 0; do - arg="$1" - shift - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - qarg=\"`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`\" ### testsuite: skip nested quoting test - ;; - *) qarg=$arg ;; - esac - libtool_args="$libtool_args $qarg" - - # If the previous option needs an argument, assign it. - if test -n "$prev"; then - case $prev in - output) - compile_command="$compile_command @OUTPUT@" - finalize_command="$finalize_command @OUTPUT@" - ;; - esac - - case $prev in - dlfiles|dlprefiles) - if test "$preload" = no; then - # Add the symbol object into the linking commands. - compile_command="$compile_command @SYMFILE@" - finalize_command="$finalize_command @SYMFILE@" - preload=yes - fi - case $arg in - *.la | *.lo) ;; # We handle these cases below. - force) - if test "$dlself" = no; then - dlself=needless - export_dynamic=yes - fi - prev= - continue - ;; - self) - if test "$prev" = dlprefiles; then - dlself=yes - elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then - dlself=yes - else - dlself=needless - export_dynamic=yes - fi - prev= - continue - ;; - *) - if test "$prev" = dlfiles; then - dlfiles="$dlfiles $arg" - else - dlprefiles="$dlprefiles $arg" - fi - prev= - continue - ;; - esac - ;; - expsyms) - export_symbols="$arg" - if test ! -f "$arg"; then - $echo "$modename: symbol file \`$arg' does not exist" - exit $EXIT_FAILURE - fi - prev= - continue - ;; - expsyms_regex) - export_symbols_regex="$arg" - prev= - continue - ;; - inst_prefix) - inst_prefix_dir="$arg" - prev= - continue - ;; - precious_regex) - precious_files_regex="$arg" - prev= - continue - ;; - release) - release="-$arg" - prev= - continue - ;; - objectlist) - if test -f "$arg"; then - save_arg=$arg - moreargs= - for fil in `cat $save_arg` - do -# moreargs="$moreargs $fil" - arg=$fil - # A libtool-controlled object. - - # Check to see that this really is a libtool object. - if (${SED} -e '2q' $arg | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then - pic_object= - non_pic_object= - - # Read the .lo file - # If there is no directory component, then add one. - case $arg in - */* | *\\*) . $arg ;; - *) . ./$arg ;; - esac - - if test -z "$pic_object" || \ - test -z "$non_pic_object" || - test "$pic_object" = none && \ - test "$non_pic_object" = none; then - $echo "$modename: cannot find name of object for \`$arg'" 1>&2 - exit $EXIT_FAILURE - fi - - # Extract subdirectory from the argument. - xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` - if test "X$xdir" = "X$arg"; then - xdir= - else - xdir="$xdir/" - fi - - if test "$pic_object" != none; then - # Prepend the subdirectory the object is found in. - pic_object="$xdir$pic_object" - - if test "$prev" = dlfiles; then - if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then - dlfiles="$dlfiles $pic_object" - prev= - continue - else - # If libtool objects are unsupported, then we need to preload. - prev=dlprefiles - fi - fi - - # CHECK ME: I think I busted this. -Ossama - if test "$prev" = dlprefiles; then - # Preload the old-style object. - dlprefiles="$dlprefiles $pic_object" - prev= - fi - - # A PIC object. - libobjs="$libobjs $pic_object" - arg="$pic_object" - fi - - # Non-PIC object. - if test "$non_pic_object" != none; then - # Prepend the subdirectory the object is found in. - non_pic_object="$xdir$non_pic_object" - - # A standard non-PIC object - non_pic_objects="$non_pic_objects $non_pic_object" - if test -z "$pic_object" || test "$pic_object" = none ; then - arg="$non_pic_object" - fi - else - # If the PIC object exists, use it instead. - # $xdir was prepended to $pic_object above. - non_pic_object="$pic_object" - non_pic_objects="$non_pic_objects $non_pic_object" - fi - else - # Only an error if not doing a dry-run. - if test -z "$run"; then - $echo "$modename: \`$arg' is not a valid libtool object" 1>&2 - exit $EXIT_FAILURE - else - # Dry-run case. - - # Extract subdirectory from the argument. - xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` - if test "X$xdir" = "X$arg"; then - xdir= - else - xdir="$xdir/" - fi - - pic_object=`$echo "X${xdir}${objdir}/${arg}" | $Xsed -e "$lo2o"` - non_pic_object=`$echo "X${xdir}${arg}" | $Xsed -e "$lo2o"` - libobjs="$libobjs $pic_object" - non_pic_objects="$non_pic_objects $non_pic_object" - fi - fi - done - else - $echo "$modename: link input file \`$save_arg' does not exist" - exit $EXIT_FAILURE - fi - arg=$save_arg - prev= - continue - ;; - rpath | xrpath) - # We need an absolute path. - case $arg in - [\\/]* | [A-Za-z]:[\\/]*) ;; - *) - $echo "$modename: only absolute run-paths are allowed" 1>&2 - exit $EXIT_FAILURE - ;; - esac - if test "$prev" = rpath; then - case "$rpath " in - *" $arg "*) ;; - *) rpath="$rpath $arg" ;; - esac - else - case "$xrpath " in - *" $arg "*) ;; - *) xrpath="$xrpath $arg" ;; - esac - fi - prev= - continue - ;; - xcompiler) - compiler_flags="$compiler_flags $qarg" - prev= - compile_command="$compile_command $qarg" - finalize_command="$finalize_command $qarg" - continue - ;; - xlinker) - linker_flags="$linker_flags $qarg" - compiler_flags="$compiler_flags $wl$qarg" - prev= - compile_command="$compile_command $wl$qarg" - finalize_command="$finalize_command $wl$qarg" - continue - ;; - xcclinker) - linker_flags="$linker_flags $qarg" - compiler_flags="$compiler_flags $qarg" - prev= - compile_command="$compile_command $qarg" - finalize_command="$finalize_command $qarg" - continue - ;; - shrext) - shrext_cmds="$arg" - prev= - continue - ;; - darwin_framework|darwin_framework_skip) - test "$prev" = "darwin_framework" && compiler_flags="$compiler_flags $arg" - compile_command="$compile_command $arg" - finalize_command="$finalize_command $arg" - prev= - continue - ;; - *) - eval "$prev=\"\$arg\"" - prev= - continue - ;; - esac - fi # test -n "$prev" - - prevarg="$arg" - - case $arg in - -all-static) - if test -n "$link_static_flag"; then - compile_command="$compile_command $link_static_flag" - finalize_command="$finalize_command $link_static_flag" - fi - continue - ;; - - -allow-undefined) - # FIXME: remove this flag sometime in the future. - $echo "$modename: \`-allow-undefined' is deprecated because it is the default" 1>&2 - continue - ;; - - -avoid-version) - avoid_version=yes - continue - ;; - - -dlopen) - prev=dlfiles - continue - ;; - - -dlpreopen) - prev=dlprefiles - continue - ;; - - -export-dynamic) - export_dynamic=yes - continue - ;; - - -export-symbols | -export-symbols-regex) - if test -n "$export_symbols" || test -n "$export_symbols_regex"; then - $echo "$modename: more than one -exported-symbols argument is not allowed" - exit $EXIT_FAILURE - fi - if test "X$arg" = "X-export-symbols"; then - prev=expsyms - else - prev=expsyms_regex - fi - continue - ;; - - -framework|-arch|-isysroot) - case " $CC " in - *" ${arg} ${1} "* | *" ${arg} ${1} "*) - prev=darwin_framework_skip ;; - *) compiler_flags="$compiler_flags $arg" - prev=darwin_framework ;; - esac - compile_command="$compile_command $arg" - finalize_command="$finalize_command $arg" - continue - ;; - - -inst-prefix-dir) - prev=inst_prefix - continue - ;; - - # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* - # so, if we see these flags be careful not to treat them like -L - -L[A-Z][A-Z]*:*) - case $with_gcc/$host in - no/*-*-irix* | /*-*-irix*) - compile_command="$compile_command $arg" - finalize_command="$finalize_command $arg" - ;; - esac - continue - ;; - - -L*) - dir=`$echo "X$arg" | $Xsed -e 's/^-L//'` - # We need an absolute path. - case $dir in - [\\/]* | [A-Za-z]:[\\/]*) ;; - *) - absdir=`cd "$dir" && pwd` - if test -z "$absdir"; then - $echo "$modename: cannot determine absolute directory name of \`$dir'" 1>&2 - absdir="$dir" - notinst_path="$notinst_path $dir" - fi - dir="$absdir" - ;; - esac - case "$deplibs " in - *" -L$dir "*) ;; - *) - deplibs="$deplibs -L$dir" - lib_search_path="$lib_search_path $dir" - ;; - esac - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) - testbindir=`$echo "X$dir" | $Xsed -e 's*/lib$*/bin*'` - case :$dllsearchpath: in - *":$dir:"*) ;; - *) dllsearchpath="$dllsearchpath:$dir";; - esac - case :$dllsearchpath: in - *":$testbindir:"*) ;; - *) dllsearchpath="$dllsearchpath:$testbindir";; - esac - ;; - esac - continue - ;; - - -l*) - if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos*) - # These systems don't actually have a C or math library (as such) - continue - ;; - *-*-os2*) - # These systems don't actually have a C library (as such) - test "X$arg" = "X-lc" && continue - ;; - *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) - # Do not include libc due to us having libc/libc_r. - test "X$arg" = "X-lc" && continue - ;; - *-*-rhapsody* | *-*-darwin1.[012]) - # Rhapsody C and math libraries are in the System framework - deplibs="$deplibs -framework System" - continue - ;; - *-*-sco3.2v5* | *-*-sco5v6*) - # Causes problems with __ctype - test "X$arg" = "X-lc" && continue - ;; - *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) - # Compiler inserts libc in the correct place for threads to work - test "X$arg" = "X-lc" && continue - ;; - esac - elif test "X$arg" = "X-lc_r"; then - case $host in - *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) - # Do not include libc_r directly, use -pthread flag. - continue - ;; - esac - fi - deplibs="$deplibs $arg" - continue - ;; - - # Tru64 UNIX uses -model [arg] to determine the layout of C++ - # classes, name mangling, and exception handling. - -model) - compile_command="$compile_command $arg" - compiler_flags="$compiler_flags $arg" - finalize_command="$finalize_command $arg" - prev=xcompiler - continue - ;; - - -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe) - compiler_flags="$compiler_flags $arg" - compile_command="$compile_command $arg" - finalize_command="$finalize_command $arg" - continue - ;; - - -module) - module=yes - continue - ;; - - # -64, -mips[0-9] enable 64-bit mode on the SGI compiler - # -r[0-9][0-9]* specifies the processor on the SGI compiler - # -xarch=*, -xtarget=* enable 64-bit mode on the Sun compiler - # +DA*, +DD* enable 64-bit mode on the HP compiler - # -q* pass through compiler args for the IBM compiler - # -m* pass through architecture-specific compiler args for GCC - # -m*, -t[45]*, -txscale* pass through architecture-specific - # compiler args for GCC - # -pg pass through profiling flag for GCC - # @file GCC response files - -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*|-pg| \ - -t[45]*|-txscale*|@*) - - # Unknown arguments in both finalize_command and compile_command need - # to be aesthetically quoted because they are evaled later. - arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - compile_command="$compile_command $arg" - finalize_command="$finalize_command $arg" - compiler_flags="$compiler_flags $arg" - continue - ;; - - -shrext) - prev=shrext - continue - ;; - - -no-fast-install) - fast_install=no - continue - ;; - - -no-install) - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) - # The PATH hackery in wrapper scripts is required on Windows - # in order for the loader to find any dlls it needs. - $echo "$modename: warning: \`-no-install' is ignored for $host" 1>&2 - $echo "$modename: warning: assuming \`-no-fast-install' instead" 1>&2 - fast_install=no - ;; - *) no_install=yes ;; - esac - continue - ;; - - -no-undefined) - allow_undefined=no - continue - ;; - - -objectlist) - prev=objectlist - continue - ;; - - -o) prev=output ;; - - -precious-files-regex) - prev=precious_regex - continue - ;; - - -release) - prev=release - continue - ;; - - -rpath) - prev=rpath - continue - ;; - - -R) - prev=xrpath - continue - ;; - - -R*) - dir=`$echo "X$arg" | $Xsed -e 's/^-R//'` - # We need an absolute path. - case $dir in - [\\/]* | [A-Za-z]:[\\/]*) ;; - *) - $echo "$modename: only absolute run-paths are allowed" 1>&2 - exit $EXIT_FAILURE - ;; - esac - case "$xrpath " in - *" $dir "*) ;; - *) xrpath="$xrpath $dir" ;; - esac - continue - ;; - - -static | -static-libtool-libs) - # The effects of -static are defined in a previous loop. - # We used to do the same as -all-static on platforms that - # didn't have a PIC flag, but the assumption that the effects - # would be equivalent was wrong. It would break on at least - # Digital Unix and AIX. - continue - ;; - - -thread-safe) - thread_safe=yes - continue - ;; - - -version-info) - prev=vinfo - continue - ;; - -version-number) - prev=vinfo - vinfo_number=yes - continue - ;; - - -Wc,*) - args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wc,//'` - arg= - save_ifs="$IFS"; IFS=',' - for flag in $args; do - IFS="$save_ifs" - case $flag in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - flag="\"$flag\"" - ;; - esac - arg="$arg $wl$flag" - compiler_flags="$compiler_flags $flag" - done - IFS="$save_ifs" - arg=`$echo "X$arg" | $Xsed -e "s/^ //"` - ;; - - -Wl,*) - args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wl,//'` - arg= - save_ifs="$IFS"; IFS=',' - for flag in $args; do - IFS="$save_ifs" - case $flag in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - flag="\"$flag\"" - ;; - esac - arg="$arg $wl$flag" - compiler_flags="$compiler_flags $wl$flag" - linker_flags="$linker_flags $flag" - done - IFS="$save_ifs" - arg=`$echo "X$arg" | $Xsed -e "s/^ //"` - ;; - - -Xcompiler) - prev=xcompiler - continue - ;; - - -Xlinker) - prev=xlinker - continue - ;; - - -XCClinker) - prev=xcclinker - continue - ;; - - # Some other compiler flag. - -* | +*) - # Unknown arguments in both finalize_command and compile_command need - # to be aesthetically quoted because they are evaled later. - arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - ;; - - *.$objext) - # A standard object. - objs="$objs $arg" - ;; - - *.lo) - # A libtool-controlled object. - - # Check to see that this really is a libtool object. - if (${SED} -e '2q' $arg | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then - pic_object= - non_pic_object= - - # Read the .lo file - # If there is no directory component, then add one. - case $arg in - */* | *\\*) . $arg ;; - *) . ./$arg ;; - esac - - if test -z "$pic_object" || \ - test -z "$non_pic_object" || - test "$pic_object" = none && \ - test "$non_pic_object" = none; then - $echo "$modename: cannot find name of object for \`$arg'" 1>&2 - exit $EXIT_FAILURE - fi - - # Extract subdirectory from the argument. - xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` - if test "X$xdir" = "X$arg"; then - xdir= - else - xdir="$xdir/" - fi - - if test "$pic_object" != none; then - # Prepend the subdirectory the object is found in. - pic_object="$xdir$pic_object" - - if test "$prev" = dlfiles; then - if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then - dlfiles="$dlfiles $pic_object" - prev= - continue - else - # If libtool objects are unsupported, then we need to preload. - prev=dlprefiles - fi - fi - - # CHECK ME: I think I busted this. -Ossama - if test "$prev" = dlprefiles; then - # Preload the old-style object. - dlprefiles="$dlprefiles $pic_object" - prev= - fi - - # A PIC object. - libobjs="$libobjs $pic_object" - arg="$pic_object" - fi - - # Non-PIC object. - if test "$non_pic_object" != none; then - # Prepend the subdirectory the object is found in. - non_pic_object="$xdir$non_pic_object" - - # A standard non-PIC object - non_pic_objects="$non_pic_objects $non_pic_object" - if test -z "$pic_object" || test "$pic_object" = none ; then - arg="$non_pic_object" - fi - else - # If the PIC object exists, use it instead. - # $xdir was prepended to $pic_object above. - non_pic_object="$pic_object" - non_pic_objects="$non_pic_objects $non_pic_object" - fi - else - # Only an error if not doing a dry-run. - if test -z "$run"; then - $echo "$modename: \`$arg' is not a valid libtool object" 1>&2 - exit $EXIT_FAILURE - else - # Dry-run case. - - # Extract subdirectory from the argument. - xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` - if test "X$xdir" = "X$arg"; then - xdir= - else - xdir="$xdir/" - fi - - pic_object=`$echo "X${xdir}${objdir}/${arg}" | $Xsed -e "$lo2o"` - non_pic_object=`$echo "X${xdir}${arg}" | $Xsed -e "$lo2o"` - libobjs="$libobjs $pic_object" - non_pic_objects="$non_pic_objects $non_pic_object" - fi - fi - ;; - - *.$libext) - # An archive. - deplibs="$deplibs $arg" - old_deplibs="$old_deplibs $arg" - continue - ;; - - *.la) - # A libtool-controlled library. - - if test "$prev" = dlfiles; then - # This library was specified with -dlopen. - dlfiles="$dlfiles $arg" - prev= - elif test "$prev" = dlprefiles; then - # The library was specified with -dlpreopen. - dlprefiles="$dlprefiles $arg" - prev= - else - deplibs="$deplibs $arg" - fi - continue - ;; - - # Some other compiler argument. - *) - # Unknown arguments in both finalize_command and compile_command need - # to be aesthetically quoted because they are evaled later. - arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - ;; - esac # arg - - # Now actually substitute the argument into the commands. - if test -n "$arg"; then - compile_command="$compile_command $arg" - finalize_command="$finalize_command $arg" - fi - done # argument parsing loop - - if test -n "$prev"; then - $echo "$modename: the \`$prevarg' option requires an argument" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - - if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then - eval arg=\"$export_dynamic_flag_spec\" - compile_command="$compile_command $arg" - finalize_command="$finalize_command $arg" - fi - - oldlibs= - # calculate the name of the file, without its directory - outputname=`$echo "X$output" | $Xsed -e 's%^.*/%%'` - libobjs_save="$libobjs" - - if test -n "$shlibpath_var"; then - # get the directories listed in $shlibpath_var - eval shlib_search_path=\`\$echo \"X\${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\` - else - shlib_search_path= - fi - eval sys_lib_search_path=\"$sys_lib_search_path_spec\" - eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" - - output_objdir=`$echo "X$output" | $Xsed -e 's%/[^/]*$%%'` - if test "X$output_objdir" = "X$output"; then - output_objdir="$objdir" - else - output_objdir="$output_objdir/$objdir" - fi - # Create the object directory. - if test ! -d "$output_objdir"; then - $show "$mkdir $output_objdir" - $run $mkdir $output_objdir - exit_status=$? - if test "$exit_status" -ne 0 && test ! -d "$output_objdir"; then - exit $exit_status - fi - fi - - # Determine the type of output - case $output in - "") - $echo "$modename: you must specify an output file" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - ;; - *.$libext) linkmode=oldlib ;; - *.lo | *.$objext) linkmode=obj ;; - *.la) linkmode=lib ;; - *) linkmode=prog ;; # Anything else should be a program. - esac - - case $host in - *cygwin* | *mingw* | *pw32*) - # don't eliminate duplications in $postdeps and $predeps - duplicate_compiler_generated_deps=yes - ;; - *) - duplicate_compiler_generated_deps=$duplicate_deps - ;; - esac - specialdeplibs= - - libs= - # Find all interdependent deplibs by searching for libraries - # that are linked more than once (e.g. -la -lb -la) - for deplib in $deplibs; do - if test "X$duplicate_deps" = "Xyes" ; then - case "$libs " in - *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; - esac - fi - libs="$libs $deplib" - done - - if test "$linkmode" = lib; then - libs="$predeps $libs $compiler_lib_search_path $postdeps" - - # Compute libraries that are listed more than once in $predeps - # $postdeps and mark them as special (i.e., whose duplicates are - # not to be eliminated). - pre_post_deps= - if test "X$duplicate_compiler_generated_deps" = "Xyes" ; then - for pre_post_dep in $predeps $postdeps; do - case "$pre_post_deps " in - *" $pre_post_dep "*) specialdeplibs="$specialdeplibs $pre_post_deps" ;; - esac - pre_post_deps="$pre_post_deps $pre_post_dep" - done - fi - pre_post_deps= - fi - - deplibs= - newdependency_libs= - newlib_search_path= - need_relink=no # whether we're linking any uninstalled libtool libraries - notinst_deplibs= # not-installed libtool libraries - case $linkmode in - lib) - passes="conv link" - for file in $dlfiles $dlprefiles; do - case $file in - *.la) ;; - *) - $echo "$modename: libraries can \`-dlopen' only libtool libraries: $file" 1>&2 - exit $EXIT_FAILURE - ;; - esac - done - ;; - prog) - compile_deplibs= - finalize_deplibs= - alldeplibs=no - newdlfiles= - newdlprefiles= - passes="conv scan dlopen dlpreopen link" - ;; - *) passes="conv" - ;; - esac - for pass in $passes; do - if test "$linkmode,$pass" = "lib,link" || - test "$linkmode,$pass" = "prog,scan"; then - libs="$deplibs" - deplibs= - fi - if test "$linkmode" = prog; then - case $pass in - dlopen) libs="$dlfiles" ;; - dlpreopen) libs="$dlprefiles" ;; - link) libs="$deplibs %DEPLIBS% $dependency_libs" ;; - esac - fi - if test "$pass" = dlopen; then - # Collect dlpreopened libraries - save_deplibs="$deplibs" - deplibs= - fi - for deplib in $libs; do - lib= - found=no - case $deplib in - -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe) - if test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - compiler_flags="$compiler_flags $deplib" - fi - continue - ;; - -l*) - if test "$linkmode" != lib && test "$linkmode" != prog; then - $echo "$modename: warning: \`-l' is ignored for archives/objects" 1>&2 - continue - fi - name=`$echo "X$deplib" | $Xsed -e 's/^-l//'` - for searchdir in $newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path; do - for search_ext in .la $std_shrext .so .a; do - # Search the libtool library - lib="$searchdir/lib${name}${search_ext}" - if test -f "$lib"; then - if test "$search_ext" = ".la"; then - found=yes - else - found=no - fi - break 2 - fi - done - done - if test "$found" != yes; then - # deplib doesn't seem to be a libtool library - if test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - deplibs="$deplib $deplibs" - test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" - fi - continue - else # deplib is a libtool library - # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, - # We need to do some special things here, and not later. - if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then - case " $predeps $postdeps " in - *" $deplib "*) - if (${SED} -e '2q' $lib | - grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then - library_names= - old_library= - case $lib in - */* | *\\*) . $lib ;; - *) . ./$lib ;; - esac - for l in $old_library $library_names; do - ll="$l" - done - if test "X$ll" = "X$old_library" ; then # only static version available - found=no - ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'` - test "X$ladir" = "X$lib" && ladir="." - lib=$ladir/$old_library - if test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - deplibs="$deplib $deplibs" - test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" - fi - continue - fi - fi - ;; - *) ;; - esac - fi - fi - ;; # -l - -L*) - case $linkmode in - lib) - deplibs="$deplib $deplibs" - test "$pass" = conv && continue - newdependency_libs="$deplib $newdependency_libs" - newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'` - ;; - prog) - if test "$pass" = conv; then - deplibs="$deplib $deplibs" - continue - fi - if test "$pass" = scan; then - deplibs="$deplib $deplibs" - else - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - fi - newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'` - ;; - *) - $echo "$modename: warning: \`-L' is ignored for archives/objects" 1>&2 - ;; - esac # linkmode - continue - ;; # -L - -R*) - if test "$pass" = link; then - dir=`$echo "X$deplib" | $Xsed -e 's/^-R//'` - # Make sure the xrpath contains only unique directories. - case "$xrpath " in - *" $dir "*) ;; - *) xrpath="$xrpath $dir" ;; - esac - fi - deplibs="$deplib $deplibs" - continue - ;; - *.la) lib="$deplib" ;; - *.$libext) - if test "$pass" = conv; then - deplibs="$deplib $deplibs" - continue - fi - case $linkmode in - lib) - valid_a_lib=no - case $deplibs_check_method in - match_pattern*) - set dummy $deplibs_check_method - match_pattern_regex=`expr "$deplibs_check_method" : "$2 \(.*\)"` - if eval $echo \"$deplib\" 2>/dev/null \ - | $SED 10q \ - | $EGREP "$match_pattern_regex" > /dev/null; then - valid_a_lib=yes - fi - ;; - pass_all) - valid_a_lib=yes - ;; - esac - if test "$valid_a_lib" != yes; then - $echo - $echo "*** Warning: Trying to link with static lib archive $deplib." - $echo "*** I have the capability to make that library automatically link in when" - $echo "*** you link to this library. But I can only do this if you have a" - $echo "*** shared version of the library, which you do not appear to have" - $echo "*** because the file extensions .$libext of this argument makes me believe" - $echo "*** that it is just a static archive that I should not used here." - else - $echo - $echo "*** Warning: Linking the shared library $output against the" - $echo "*** static library $deplib is not portable!" - deplibs="$deplib $deplibs" - fi - continue - ;; - prog) - if test "$pass" != link; then - deplibs="$deplib $deplibs" - else - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - fi - continue - ;; - esac # linkmode - ;; # *.$libext - *.lo | *.$objext) - if test "$pass" = conv; then - deplibs="$deplib $deplibs" - elif test "$linkmode" = prog; then - if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then - # If there is no dlopen support or we're linking statically, - # we need to preload. - newdlprefiles="$newdlprefiles $deplib" - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - newdlfiles="$newdlfiles $deplib" - fi - fi - continue - ;; - %DEPLIBS%) - alldeplibs=yes - continue - ;; - esac # case $deplib - if test "$found" = yes || test -f "$lib"; then : - else - $echo "$modename: cannot find the library \`$lib' or unhandled argument \`$deplib'" 1>&2 - exit $EXIT_FAILURE - fi - - # Check to see that this really is a libtool archive. - if (${SED} -e '2q' $lib | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : - else - $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 - exit $EXIT_FAILURE - fi - - ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'` - test "X$ladir" = "X$lib" && ladir="." - - dlname= - dlopen= - dlpreopen= - libdir= - library_names= - old_library= - # If the library was installed with an old release of libtool, - # it will not redefine variables installed, or shouldnotlink - installed=yes - shouldnotlink=no - avoidtemprpath= - - - # Read the .la file - case $lib in - */* | *\\*) . $lib ;; - *) . ./$lib ;; - esac - - if test "$linkmode,$pass" = "lib,link" || - test "$linkmode,$pass" = "prog,scan" || - { test "$linkmode" != prog && test "$linkmode" != lib; }; then - test -n "$dlopen" && dlfiles="$dlfiles $dlopen" - test -n "$dlpreopen" && dlprefiles="$dlprefiles $dlpreopen" - fi - - if test "$pass" = conv; then - # Only check for convenience libraries - deplibs="$lib $deplibs" - if test -z "$libdir"; then - if test -z "$old_library"; then - $echo "$modename: cannot find name of link library for \`$lib'" 1>&2 - exit $EXIT_FAILURE - fi - # It is a libtool convenience library, so add in its objects. - convenience="$convenience $ladir/$objdir/$old_library" - old_convenience="$old_convenience $ladir/$objdir/$old_library" - tmp_libs= - for deplib in $dependency_libs; do - deplibs="$deplib $deplibs" - if test "X$duplicate_deps" = "Xyes" ; then - case "$tmp_libs " in - *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; - esac - fi - tmp_libs="$tmp_libs $deplib" - done - elif test "$linkmode" != prog && test "$linkmode" != lib; then - $echo "$modename: \`$lib' is not a convenience library" 1>&2 - exit $EXIT_FAILURE - fi - continue - fi # $pass = conv - - - # Get the name of the library we link against. - linklib= - for l in $old_library $library_names; do - linklib="$l" - done - if test -z "$linklib"; then - $echo "$modename: cannot find name of link library for \`$lib'" 1>&2 - exit $EXIT_FAILURE - fi - - # This library was specified with -dlopen. - if test "$pass" = dlopen; then - if test -z "$libdir"; then - $echo "$modename: cannot -dlopen a convenience library: \`$lib'" 1>&2 - exit $EXIT_FAILURE - fi - if test -z "$dlname" || - test "$dlopen_support" != yes || - test "$build_libtool_libs" = no; then - # If there is no dlname, no dlopen support or we're linking - # statically, we need to preload. We also need to preload any - # dependent libraries so libltdl's deplib preloader doesn't - # bomb out in the load deplibs phase. - dlprefiles="$dlprefiles $lib $dependency_libs" - else - newdlfiles="$newdlfiles $lib" - fi - continue - fi # $pass = dlopen - - # We need an absolute path. - case $ladir in - [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; - *) - abs_ladir=`cd "$ladir" && pwd` - if test -z "$abs_ladir"; then - $echo "$modename: warning: cannot determine absolute directory name of \`$ladir'" 1>&2 - $echo "$modename: passing it literally to the linker, although it might fail" 1>&2 - abs_ladir="$ladir" - fi - ;; - esac - laname=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` - - # Find the relevant object directory and library name. - if test "X$installed" = Xyes; then - if test ! -f "$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then - $echo "$modename: warning: library \`$lib' was moved." 1>&2 - dir="$ladir" - absdir="$abs_ladir" - libdir="$abs_ladir" - else - dir="$libdir" - absdir="$libdir" - fi - test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes - else - if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then - dir="$ladir" - absdir="$abs_ladir" - # Remove this search path later - notinst_path="$notinst_path $abs_ladir" - else - dir="$ladir/$objdir" - absdir="$abs_ladir/$objdir" - # Remove this search path later - notinst_path="$notinst_path $abs_ladir" - fi - fi # $installed = yes - name=`$echo "X$laname" | $Xsed -e 's/\.la$//' -e 's/^lib//'` - - # This library was specified with -dlpreopen. - if test "$pass" = dlpreopen; then - if test -z "$libdir"; then - $echo "$modename: cannot -dlpreopen a convenience library: \`$lib'" 1>&2 - exit $EXIT_FAILURE - fi - # Prefer using a static library (so that no silly _DYNAMIC symbols - # are required to link). - if test -n "$old_library"; then - newdlprefiles="$newdlprefiles $dir/$old_library" - # Otherwise, use the dlname, so that lt_dlopen finds it. - elif test -n "$dlname"; then - newdlprefiles="$newdlprefiles $dir/$dlname" - else - newdlprefiles="$newdlprefiles $dir/$linklib" - fi - fi # $pass = dlpreopen - - if test -z "$libdir"; then - # Link the convenience library - if test "$linkmode" = lib; then - deplibs="$dir/$old_library $deplibs" - elif test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$dir/$old_library $compile_deplibs" - finalize_deplibs="$dir/$old_library $finalize_deplibs" - else - deplibs="$lib $deplibs" # used for prog,scan pass - fi - continue - fi - - - if test "$linkmode" = prog && test "$pass" != link; then - newlib_search_path="$newlib_search_path $ladir" - deplibs="$lib $deplibs" - - linkalldeplibs=no - if test "$link_all_deplibs" != no || test -z "$library_names" || - test "$build_libtool_libs" = no; then - linkalldeplibs=yes - fi - - tmp_libs= - for deplib in $dependency_libs; do - case $deplib in - -L*) newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'`;; ### testsuite: skip nested quoting test - esac - # Need to link against all dependency_libs? - if test "$linkalldeplibs" = yes; then - deplibs="$deplib $deplibs" - else - # Need to hardcode shared library paths - # or/and link against static libraries - newdependency_libs="$deplib $newdependency_libs" - fi - if test "X$duplicate_deps" = "Xyes" ; then - case "$tmp_libs " in - *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; - esac - fi - tmp_libs="$tmp_libs $deplib" - done # for deplib - continue - fi # $linkmode = prog... - - if test "$linkmode,$pass" = "prog,link"; then - if test -n "$library_names" && - { { test "$prefer_static_libs" = no || - test "$prefer_static_libs,$installed" = "built,yes"; } || - test -z "$old_library"; }; then - # We need to hardcode the library path - if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then - # Make sure the rpath contains only unique directories. - case "$temp_rpath " in - *" $dir "*) ;; - *" $absdir "*) ;; - *) temp_rpath="$temp_rpath $absdir" ;; - esac - fi - - # Hardcode the library path. - # Skip directories that are in the system default run-time - # search path. - case " $sys_lib_dlsearch_path " in - *" $absdir "*) ;; - *) - case "$compile_rpath " in - *" $absdir "*) ;; - *) compile_rpath="$compile_rpath $absdir" - esac - ;; - esac - case " $sys_lib_dlsearch_path " in - *" $libdir "*) ;; - *) - case "$finalize_rpath " in - *" $libdir "*) ;; - *) finalize_rpath="$finalize_rpath $libdir" - esac - ;; - esac - fi # $linkmode,$pass = prog,link... - - if test "$alldeplibs" = yes && - { test "$deplibs_check_method" = pass_all || - { test "$build_libtool_libs" = yes && - test -n "$library_names"; }; }; then - # We only need to search for static libraries - continue - fi - fi - - link_static=no # Whether the deplib will be linked statically - use_static_libs=$prefer_static_libs - if test "$use_static_libs" = built && test "$installed" = yes ; then - use_static_libs=no - fi - if test -n "$library_names" && - { test "$use_static_libs" = no || test -z "$old_library"; }; then - if test "$installed" = no; then - notinst_deplibs="$notinst_deplibs $lib" - need_relink=yes - fi - # This is a shared library - - # Warn about portability, can't link against -module's on - # some systems (darwin) - if test "$shouldnotlink" = yes && test "$pass" = link ; then - $echo - if test "$linkmode" = prog; then - $echo "*** Warning: Linking the executable $output against the loadable module" - else - $echo "*** Warning: Linking the shared library $output against the loadable module" - fi - $echo "*** $linklib is not portable!" - fi - if test "$linkmode" = lib && - test "$hardcode_into_libs" = yes; then - # Hardcode the library path. - # Skip directories that are in the system default run-time - # search path. - case " $sys_lib_dlsearch_path " in - *" $absdir "*) ;; - *) - case "$compile_rpath " in - *" $absdir "*) ;; - *) compile_rpath="$compile_rpath $absdir" - esac - ;; - esac - case " $sys_lib_dlsearch_path " in - *" $libdir "*) ;; - *) - case "$finalize_rpath " in - *" $libdir "*) ;; - *) finalize_rpath="$finalize_rpath $libdir" - esac - ;; - esac - fi - - if test -n "$old_archive_from_expsyms_cmds"; then - # figure out the soname - set dummy $library_names - realname="$2" - shift; shift - libname=`eval \\$echo \"$libname_spec\"` - # use dlname if we got it. it's perfectly good, no? - if test -n "$dlname"; then - soname="$dlname" - elif test -n "$soname_spec"; then - # bleh windows - case $host in - *cygwin* | mingw*) - major=`expr $current - $age` - versuffix="-$major" - ;; - esac - eval soname=\"$soname_spec\" - else - soname="$realname" - fi - - # Make a new name for the extract_expsyms_cmds to use - soroot="$soname" - soname=`$echo $soroot | ${SED} -e 's/^.*\///'` - newlib="libimp-`$echo $soname | ${SED} 's/^lib//;s/\.dll$//'`.a" - - # If the library has no export list, then create one now - if test -f "$output_objdir/$soname-def"; then : - else - $show "extracting exported symbol list from \`$soname'" - save_ifs="$IFS"; IFS='~' - cmds=$extract_expsyms_cmds - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || exit $? - done - IFS="$save_ifs" - fi - - # Create $newlib - if test -f "$output_objdir/$newlib"; then :; else - $show "generating import library for \`$soname'" - save_ifs="$IFS"; IFS='~' - cmds=$old_archive_from_expsyms_cmds - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || exit $? - done - IFS="$save_ifs" - fi - # make sure the library variables are pointing to the new library - dir=$output_objdir - linklib=$newlib - fi # test -n "$old_archive_from_expsyms_cmds" - - if test "$linkmode" = prog || test "$mode" != relink; then - add_shlibpath= - add_dir= - add= - lib_linked=yes - case $hardcode_action in - immediate | unsupported) - if test "$hardcode_direct" = no; then - add="$dir/$linklib" - case $host in - *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;; - *-*-sysv4*uw2*) add_dir="-L$dir" ;; - *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ - *-*-unixware7*) add_dir="-L$dir" ;; - *-*-darwin* ) - # if the lib is a module then we can not link against - # it, someone is ignoring the new warnings I added - if /usr/bin/file -L $add 2> /dev/null | - $EGREP ": [^:]* bundle" >/dev/null ; then - $echo "** Warning, lib $linklib is a module, not a shared library" - if test -z "$old_library" ; then - $echo - $echo "** And there doesn't seem to be a static archive available" - $echo "** The link will probably fail, sorry" - else - add="$dir/$old_library" - fi - fi - esac - elif test "$hardcode_minus_L" = no; then - case $host in - *-*-sunos*) add_shlibpath="$dir" ;; - esac - add_dir="-L$dir" - add="-l$name" - elif test "$hardcode_shlibpath_var" = no; then - add_shlibpath="$dir" - add="-l$name" - else - lib_linked=no - fi - ;; - relink) - if test "$hardcode_direct" = yes; then - add="$dir/$linklib" - elif test "$hardcode_minus_L" = yes; then - add_dir="-L$dir" - # Try looking first in the location we're being installed to. - if test -n "$inst_prefix_dir"; then - case $libdir in - [\\/]*) - add_dir="$add_dir -L$inst_prefix_dir$libdir" - ;; - esac - fi - add="-l$name" - elif test "$hardcode_shlibpath_var" = yes; then - add_shlibpath="$dir" - add="-l$name" - else - lib_linked=no - fi - ;; - *) lib_linked=no ;; - esac - - if test "$lib_linked" != yes; then - $echo "$modename: configuration error: unsupported hardcode properties" - exit $EXIT_FAILURE - fi - - if test -n "$add_shlibpath"; then - case :$compile_shlibpath: in - *":$add_shlibpath:"*) ;; - *) compile_shlibpath="$compile_shlibpath$add_shlibpath:" ;; - esac - fi - if test "$linkmode" = prog; then - test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" - test -n "$add" && compile_deplibs="$add $compile_deplibs" - else - test -n "$add_dir" && deplibs="$add_dir $deplibs" - test -n "$add" && deplibs="$add $deplibs" - if test "$hardcode_direct" != yes && \ - test "$hardcode_minus_L" != yes && \ - test "$hardcode_shlibpath_var" = yes; then - case :$finalize_shlibpath: in - *":$libdir:"*) ;; - *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; - esac - fi - fi - fi - - if test "$linkmode" = prog || test "$mode" = relink; then - add_shlibpath= - add_dir= - add= - # Finalize command for both is simple: just hardcode it. - if test "$hardcode_direct" = yes; then - add="$libdir/$linklib" - elif test "$hardcode_minus_L" = yes; then - add_dir="-L$libdir" - add="-l$name" - elif test "$hardcode_shlibpath_var" = yes; then - case :$finalize_shlibpath: in - *":$libdir:"*) ;; - *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; - esac - add="-l$name" - elif test "$hardcode_automatic" = yes; then - if test -n "$inst_prefix_dir" && - test -f "$inst_prefix_dir$libdir/$linklib" ; then - add="$inst_prefix_dir$libdir/$linklib" - else - add="$libdir/$linklib" - fi - else - # We cannot seem to hardcode it, guess we'll fake it. - add_dir="-L$libdir" - # Try looking first in the location we're being installed to. - if test -n "$inst_prefix_dir"; then - case $libdir in - [\\/]*) - add_dir="$add_dir -L$inst_prefix_dir$libdir" - ;; - esac - fi - add="-l$name" - fi - - if test "$linkmode" = prog; then - test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" - test -n "$add" && finalize_deplibs="$add $finalize_deplibs" - else - test -n "$add_dir" && deplibs="$add_dir $deplibs" - test -n "$add" && deplibs="$add $deplibs" - fi - fi - elif test "$linkmode" = prog; then - # Here we assume that one of hardcode_direct or hardcode_minus_L - # is not unsupported. This is valid on all known static and - # shared platforms. - if test "$hardcode_direct" != unsupported; then - test -n "$old_library" && linklib="$old_library" - compile_deplibs="$dir/$linklib $compile_deplibs" - finalize_deplibs="$dir/$linklib $finalize_deplibs" - else - compile_deplibs="-l$name -L$dir $compile_deplibs" - finalize_deplibs="-l$name -L$dir $finalize_deplibs" - fi - elif test "$build_libtool_libs" = yes; then - # Not a shared library - if test "$deplibs_check_method" != pass_all; then - # We're trying link a shared library against a static one - # but the system doesn't support it. - - # Just print a warning and add the library to dependency_libs so - # that the program can be linked against the static library. - $echo - $echo "*** Warning: This system can not link to static lib archive $lib." - $echo "*** I have the capability to make that library automatically link in when" - $echo "*** you link to this library. But I can only do this if you have a" - $echo "*** shared version of the library, which you do not appear to have." - if test "$module" = yes; then - $echo "*** But as you try to build a module library, libtool will still create " - $echo "*** a static module, that should work as long as the dlopening application" - $echo "*** is linked with the -dlopen flag to resolve symbols at runtime." - if test -z "$global_symbol_pipe"; then - $echo - $echo "*** However, this would only work if libtool was able to extract symbol" - $echo "*** lists from a program, using \`nm' or equivalent, but libtool could" - $echo "*** not find such a program. So, this module is probably useless." - $echo "*** \`nm' from GNU binutils and a full rebuild may help." - fi - if test "$build_old_libs" = no; then - build_libtool_libs=module - build_old_libs=yes - else - build_libtool_libs=no - fi - fi - else - deplibs="$dir/$old_library $deplibs" - link_static=yes - fi - fi # link shared/static library? - - if test "$linkmode" = lib; then - if test -n "$dependency_libs" && - { test "$hardcode_into_libs" != yes || - test "$build_old_libs" = yes || - test "$link_static" = yes; }; then - # Extract -R from dependency_libs - temp_deplibs= - for libdir in $dependency_libs; do - case $libdir in - -R*) temp_xrpath=`$echo "X$libdir" | $Xsed -e 's/^-R//'` - case " $xrpath " in - *" $temp_xrpath "*) ;; - *) xrpath="$xrpath $temp_xrpath";; - esac;; - *) temp_deplibs="$temp_deplibs $libdir";; - esac - done - dependency_libs="$temp_deplibs" - fi - - newlib_search_path="$newlib_search_path $absdir" - # Link against this library - test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" - # ... and its dependency_libs - tmp_libs= - for deplib in $dependency_libs; do - newdependency_libs="$deplib $newdependency_libs" - if test "X$duplicate_deps" = "Xyes" ; then - case "$tmp_libs " in - *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; - esac - fi - tmp_libs="$tmp_libs $deplib" - done - - if test "$link_all_deplibs" != no; then - # Add the search paths of all dependency libraries - for deplib in $dependency_libs; do - case $deplib in - -L*) path="$deplib" ;; - *.la) - dir=`$echo "X$deplib" | $Xsed -e 's%/[^/]*$%%'` - test "X$dir" = "X$deplib" && dir="." - # We need an absolute path. - case $dir in - [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; - *) - absdir=`cd "$dir" && pwd` - if test -z "$absdir"; then - $echo "$modename: warning: cannot determine absolute directory name of \`$dir'" 1>&2 - absdir="$dir" - fi - ;; - esac - if grep "^installed=no" $deplib > /dev/null; then - path="$absdir/$objdir" - else - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` - if test -z "$libdir"; then - $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2 - exit $EXIT_FAILURE - fi - if test "$absdir" != "$libdir"; then - $echo "$modename: warning: \`$deplib' seems to be moved" 1>&2 - fi - path="$absdir" - fi - depdepl= - case $host in - *-*-darwin*) - # we do not want to link against static libs, - # but need to link against shared - eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` - if test -n "$deplibrary_names" ; then - for tmp in $deplibrary_names ; do - depdepl=$tmp - done - if test -f "$path/$depdepl" ; then - depdepl="$path/$depdepl" - fi - # do not add paths which are already there - case " $newlib_search_path " in - *" $path "*) ;; - *) newlib_search_path="$newlib_search_path $path";; - esac - fi - path="" - ;; - *) - path="-L$path" - ;; - esac - ;; - -l*) - case $host in - *-*-darwin*) - # Again, we only want to link against shared libraries - eval tmp_libs=`$echo "X$deplib" | $Xsed -e "s,^\-l,,"` - for tmp in $newlib_search_path ; do - if test -f "$tmp/lib$tmp_libs.dylib" ; then - eval depdepl="$tmp/lib$tmp_libs.dylib" - break - fi - done - path="" - ;; - *) continue ;; - esac - ;; - *) continue ;; - esac - case " $deplibs " in - *" $path "*) ;; - *) deplibs="$path $deplibs" ;; - esac - case " $deplibs " in - *" $depdepl "*) ;; - *) deplibs="$depdepl $deplibs" ;; - esac - done - fi # link_all_deplibs != no - fi # linkmode = lib - done # for deplib in $libs - dependency_libs="$newdependency_libs" - if test "$pass" = dlpreopen; then - # Link the dlpreopened libraries before other libraries - for deplib in $save_deplibs; do - deplibs="$deplib $deplibs" - done - fi - if test "$pass" != dlopen; then - if test "$pass" != conv; then - # Make sure lib_search_path contains only unique directories. - lib_search_path= - for dir in $newlib_search_path; do - case "$lib_search_path " in - *" $dir "*) ;; - *) lib_search_path="$lib_search_path $dir" ;; - esac - done - newlib_search_path= - fi - - if test "$linkmode,$pass" != "prog,link"; then - vars="deplibs" - else - vars="compile_deplibs finalize_deplibs" - fi - for var in $vars dependency_libs; do - # Add libraries to $var in reverse order - eval tmp_libs=\"\$$var\" - new_libs= - for deplib in $tmp_libs; do - # FIXME: Pedantically, this is the right thing to do, so - # that some nasty dependency loop isn't accidentally - # broken: - #new_libs="$deplib $new_libs" - # Pragmatically, this seems to cause very few problems in - # practice: - case $deplib in - -L*) new_libs="$deplib $new_libs" ;; - -R*) ;; - *) - # And here is the reason: when a library appears more - # than once as an explicit dependence of a library, or - # is implicitly linked in more than once by the - # compiler, it is considered special, and multiple - # occurrences thereof are not removed. Compare this - # with having the same library being listed as a - # dependency of multiple other libraries: in this case, - # we know (pedantically, we assume) the library does not - # need to be listed more than once, so we keep only the - # last copy. This is not always right, but it is rare - # enough that we require users that really mean to play - # such unportable linking tricks to link the library - # using -Wl,-lname, so that libtool does not consider it - # for duplicate removal. - case " $specialdeplibs " in - *" $deplib "*) new_libs="$deplib $new_libs" ;; - *) - case " $new_libs " in - *" $deplib "*) ;; - *) new_libs="$deplib $new_libs" ;; - esac - ;; - esac - ;; - esac - done - tmp_libs= - for deplib in $new_libs; do - case $deplib in - -L*) - case " $tmp_libs " in - *" $deplib "*) ;; - *) tmp_libs="$tmp_libs $deplib" ;; - esac - ;; - *) tmp_libs="$tmp_libs $deplib" ;; - esac - done - eval $var=\"$tmp_libs\" - done # for var - fi - # Last step: remove runtime libs from dependency_libs - # (they stay in deplibs) - tmp_libs= - for i in $dependency_libs ; do - case " $predeps $postdeps $compiler_lib_search_path " in - *" $i "*) - i="" - ;; - esac - if test -n "$i" ; then - tmp_libs="$tmp_libs $i" - fi - done - dependency_libs=$tmp_libs - done # for pass - if test "$linkmode" = prog; then - dlfiles="$newdlfiles" - dlprefiles="$newdlprefiles" - fi - - case $linkmode in - oldlib) - if test -n "$deplibs"; then - $echo "$modename: warning: \`-l' and \`-L' are ignored for archives" 1>&2 - fi - - if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then - $echo "$modename: warning: \`-dlopen' is ignored for archives" 1>&2 - fi - - if test -n "$rpath"; then - $echo "$modename: warning: \`-rpath' is ignored for archives" 1>&2 - fi - - if test -n "$xrpath"; then - $echo "$modename: warning: \`-R' is ignored for archives" 1>&2 - fi - - if test -n "$vinfo"; then - $echo "$modename: warning: \`-version-info/-version-number' is ignored for archives" 1>&2 - fi - - if test -n "$release"; then - $echo "$modename: warning: \`-release' is ignored for archives" 1>&2 - fi - - if test -n "$export_symbols" || test -n "$export_symbols_regex"; then - $echo "$modename: warning: \`-export-symbols' is ignored for archives" 1>&2 - fi - - # Now set the variables for building old libraries. - build_libtool_libs=no - oldlibs="$output" - objs="$objs$old_deplibs" - ;; - - lib) - # Make sure we only generate libraries of the form `libNAME.la'. - case $outputname in - lib*) - name=`$echo "X$outputname" | $Xsed -e 's/\.la$//' -e 's/^lib//'` - eval shared_ext=\"$shrext_cmds\" - eval libname=\"$libname_spec\" - ;; - *) - if test "$module" = no; then - $echo "$modename: libtool library \`$output' must begin with \`lib'" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - if test "$need_lib_prefix" != no; then - # Add the "lib" prefix for modules if required - name=`$echo "X$outputname" | $Xsed -e 's/\.la$//'` - eval shared_ext=\"$shrext_cmds\" - eval libname=\"$libname_spec\" - else - libname=`$echo "X$outputname" | $Xsed -e 's/\.la$//'` - fi - ;; - esac - - if test -n "$objs"; then - if test "$deplibs_check_method" != pass_all; then - $echo "$modename: cannot build libtool library \`$output' from non-libtool objects on this host:$objs" 2>&1 - exit $EXIT_FAILURE - else - $echo - $echo "*** Warning: Linking the shared library $output against the non-libtool" - $echo "*** objects $objs is not portable!" - libobjs="$libobjs $objs" - fi - fi - - if test "$dlself" != no; then - $echo "$modename: warning: \`-dlopen self' is ignored for libtool libraries" 1>&2 - fi - - set dummy $rpath - if test "$#" -gt 2; then - $echo "$modename: warning: ignoring multiple \`-rpath's for a libtool library" 1>&2 - fi - install_libdir="$2" - - oldlibs= - if test -z "$rpath"; then - if test "$build_libtool_libs" = yes; then - # Building a libtool convenience library. - # Some compilers have problems with a `.al' extension so - # convenience libraries should have the same extension an - # archive normally would. - oldlibs="$output_objdir/$libname.$libext $oldlibs" - build_libtool_libs=convenience - build_old_libs=yes - fi - - if test -n "$vinfo"; then - $echo "$modename: warning: \`-version-info/-version-number' is ignored for convenience libraries" 1>&2 - fi - - if test -n "$release"; then - $echo "$modename: warning: \`-release' is ignored for convenience libraries" 1>&2 - fi - else - - # Parse the version information argument. - save_ifs="$IFS"; IFS=':' - set dummy $vinfo 0 0 0 - IFS="$save_ifs" - - if test -n "$8"; then - $echo "$modename: too many parameters to \`-version-info'" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - - # convert absolute version numbers to libtool ages - # this retains compatibility with .la files and attempts - # to make the code below a bit more comprehensible - - case $vinfo_number in - yes) - number_major="$2" - number_minor="$3" - number_revision="$4" - # - # There are really only two kinds -- those that - # use the current revision as the major version - # and those that subtract age and use age as - # a minor version. But, then there is irix - # which has an extra 1 added just for fun - # - case $version_type in - darwin|linux|osf|windows|none) - current=`expr $number_major + $number_minor` - age="$number_minor" - revision="$number_revision" - ;; - freebsd-aout|freebsd-elf|sunos) - current="$number_major" - revision="$number_minor" - age="0" - ;; - irix|nonstopux) - current=`expr $number_major + $number_minor - 1` - age="$number_minor" - revision="$number_minor" - ;; - esac - ;; - no) - current="$2" - revision="$3" - age="$4" - ;; - esac - - # Check that each of the things are valid numbers. - case $current in - 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; - *) - $echo "$modename: CURRENT \`$current' must be a nonnegative integer" 1>&2 - $echo "$modename: \`$vinfo' is not valid version information" 1>&2 - exit $EXIT_FAILURE - ;; - esac - - case $revision in - 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; - *) - $echo "$modename: REVISION \`$revision' must be a nonnegative integer" 1>&2 - $echo "$modename: \`$vinfo' is not valid version information" 1>&2 - exit $EXIT_FAILURE - ;; - esac - - case $age in - 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; - *) - $echo "$modename: AGE \`$age' must be a nonnegative integer" 1>&2 - $echo "$modename: \`$vinfo' is not valid version information" 1>&2 - exit $EXIT_FAILURE - ;; - esac - - if test "$age" -gt "$current"; then - $echo "$modename: AGE \`$age' is greater than the current interface number \`$current'" 1>&2 - $echo "$modename: \`$vinfo' is not valid version information" 1>&2 - exit $EXIT_FAILURE - fi - - # Calculate the version variables. - major= - versuffix= - verstring= - case $version_type in - none) ;; - - darwin) - # Like Linux, but with the current version available in - # verstring for coding it into the library header - major=.`expr $current - $age` - versuffix="$major.$age.$revision" - # Darwin ld doesn't like 0 for these options... - minor_current=`expr $current + 1` - verstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" - ;; - - freebsd-aout) - major=".$current" - versuffix=".$current.$revision"; - ;; - - freebsd-elf) - major=".$current" - versuffix=".$current"; - ;; - - irix | nonstopux) - major=`expr $current - $age + 1` - - case $version_type in - nonstopux) verstring_prefix=nonstopux ;; - *) verstring_prefix=sgi ;; - esac - verstring="$verstring_prefix$major.$revision" - - # Add in all the interfaces that we are compatible with. - loop=$revision - while test "$loop" -ne 0; do - iface=`expr $revision - $loop` - loop=`expr $loop - 1` - verstring="$verstring_prefix$major.$iface:$verstring" - done - - # Before this point, $major must not contain `.'. - major=.$major - versuffix="$major.$revision" - ;; - - linux) - major=.`expr $current - $age` - versuffix="$major.$age.$revision" - ;; - - osf) - major=.`expr $current - $age` - versuffix=".$current.$age.$revision" - verstring="$current.$age.$revision" - - # Add in all the interfaces that we are compatible with. - loop=$age - while test "$loop" -ne 0; do - iface=`expr $current - $loop` - loop=`expr $loop - 1` - verstring="$verstring:${iface}.0" - done - - # Make executables depend on our current version. - verstring="$verstring:${current}.0" - ;; - - sunos) - major=".$current" - versuffix=".$current.$revision" - ;; - - windows) - # Use '-' rather than '.', since we only want one - # extension on DOS 8.3 filesystems. - major=`expr $current - $age` - versuffix="-$major" - ;; - - *) - $echo "$modename: unknown library version type \`$version_type'" 1>&2 - $echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 - exit $EXIT_FAILURE - ;; - esac - - # Clear the version info if we defaulted, and they specified a release. - if test -z "$vinfo" && test -n "$release"; then - major= - case $version_type in - darwin) - # we can't check for "0.0" in archive_cmds due to quoting - # problems, so we reset it completely - verstring= - ;; - *) - verstring="0.0" - ;; - esac - if test "$need_version" = no; then - versuffix= - else - versuffix=".0.0" - fi - fi - - # Remove version info from name if versioning should be avoided - if test "$avoid_version" = yes && test "$need_version" = no; then - major= - versuffix= - verstring="" - fi - - # Check to see if the archive will have undefined symbols. - if test "$allow_undefined" = yes; then - if test "$allow_undefined_flag" = unsupported; then - $echo "$modename: warning: undefined symbols not allowed in $host shared libraries" 1>&2 - build_libtool_libs=no - build_old_libs=yes - fi - else - # Don't allow undefined symbols. - allow_undefined_flag="$no_undefined_flag" - fi - fi - - if test "$mode" != relink; then - # Remove our outputs, but don't remove object files since they - # may have been created when compiling PIC objects. - removelist= - tempremovelist=`$echo "$output_objdir/*"` - for p in $tempremovelist; do - case $p in - *.$objext) - ;; - $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) - if test "X$precious_files_regex" != "X"; then - if echo $p | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 - then - continue - fi - fi - removelist="$removelist $p" - ;; - *) ;; - esac - done - if test -n "$removelist"; then - $show "${rm}r $removelist" - $run ${rm}r $removelist - fi - fi - - # Now set the variables for building old libraries. - if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then - oldlibs="$oldlibs $output_objdir/$libname.$libext" - - # Transform .lo files to .o files. - oldobjs="$objs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP` - fi - - # Eliminate all temporary directories. -# for path in $notinst_path; do -# lib_search_path=`$echo "$lib_search_path " | ${SED} -e "s% $path % %g"` -# deplibs=`$echo "$deplibs " | ${SED} -e "s% -L$path % %g"` -# dependency_libs=`$echo "$dependency_libs " | ${SED} -e "s% -L$path % %g"` -# done - - if test -n "$xrpath"; then - # If the user specified any rpath flags, then add them. - temp_xrpath= - for libdir in $xrpath; do - temp_xrpath="$temp_xrpath -R$libdir" - case "$finalize_rpath " in - *" $libdir "*) ;; - *) finalize_rpath="$finalize_rpath $libdir" ;; - esac - done - if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then - dependency_libs="$temp_xrpath $dependency_libs" - fi - fi - - # Make sure dlfiles contains only unique files that won't be dlpreopened - old_dlfiles="$dlfiles" - dlfiles= - for lib in $old_dlfiles; do - case " $dlprefiles $dlfiles " in - *" $lib "*) ;; - *) dlfiles="$dlfiles $lib" ;; - esac - done - - # Make sure dlprefiles contains only unique files - old_dlprefiles="$dlprefiles" - dlprefiles= - for lib in $old_dlprefiles; do - case "$dlprefiles " in - *" $lib "*) ;; - *) dlprefiles="$dlprefiles $lib" ;; - esac - done - - if test "$build_libtool_libs" = yes; then - if test -n "$rpath"; then - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos*) - # these systems don't actually have a c library (as such)! - ;; - *-*-rhapsody* | *-*-darwin1.[012]) - # Rhapsody C library is in the System framework - deplibs="$deplibs -framework System" - ;; - *-*-netbsd*) - # Don't link with libc until the a.out ld.so is fixed. - ;; - *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) - # Do not include libc due to us having libc/libc_r. - ;; - *-*-sco3.2v5* | *-*-sco5v6*) - # Causes problems with __ctype - ;; - *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) - # Compiler inserts libc in the correct place for threads to work - ;; - *) - # Add libc to deplibs on all other systems if necessary. - if test "$build_libtool_need_lc" = "yes"; then - deplibs="$deplibs -lc" - fi - ;; - esac - fi - - # Transform deplibs into only deplibs that can be linked in shared. - name_save=$name - libname_save=$libname - release_save=$release - versuffix_save=$versuffix - major_save=$major - # I'm not sure if I'm treating the release correctly. I think - # release should show up in the -l (ie -lgmp5) so we don't want to - # add it in twice. Is that correct? - release="" - versuffix="" - major="" - newdeplibs= - droppeddeps=no - case $deplibs_check_method in - pass_all) - # Don't check for shared/static. Everything works. - # This might be a little naive. We might want to check - # whether the library exists or not. But this is on - # osf3 & osf4 and I'm not really sure... Just - # implementing what was already the behavior. - newdeplibs=$deplibs - ;; - test_compile) - # This code stresses the "libraries are programs" paradigm to its - # limits. Maybe even breaks it. We compile a program, linking it - # against the deplibs as a proxy for the library. Then we can check - # whether they linked in statically or dynamically with ldd. - $rm conftest.c - cat > conftest.c </dev/null` - for potent_lib in $potential_libs; do - # Follow soft links. - if ls -lLd "$potent_lib" 2>/dev/null \ - | grep " -> " >/dev/null; then - continue - fi - # The statement above tries to avoid entering an - # endless loop below, in case of cyclic links. - # We might still enter an endless loop, since a link - # loop can be closed while we follow links, - # but so what? - potlib="$potent_lib" - while test -h "$potlib" 2>/dev/null; do - potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` - case $potliblink in - [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; - *) potlib=`$echo "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";; - esac - done - if eval $file_magic_cmd \"\$potlib\" 2>/dev/null \ - | ${SED} 10q \ - | $EGREP "$file_magic_regex" > /dev/null; then - newdeplibs="$newdeplibs $a_deplib" - a_deplib="" - break 2 - fi - done - done - fi - if test -n "$a_deplib" ; then - droppeddeps=yes - $echo - $echo "*** Warning: linker path does not have real file for library $a_deplib." - $echo "*** I have the capability to make that library automatically link in when" - $echo "*** you link to this library. But I can only do this if you have a" - $echo "*** shared version of the library, which you do not appear to have" - $echo "*** because I did check the linker path looking for a file starting" - if test -z "$potlib" ; then - $echo "*** with $libname but no candidates were found. (...for file magic test)" - else - $echo "*** with $libname and none of the candidates passed a file format test" - $echo "*** using a file magic. Last file checked: $potlib" - fi - fi - else - # Add a -L argument. - newdeplibs="$newdeplibs $a_deplib" - fi - done # Gone through all deplibs. - ;; - match_pattern*) - set dummy $deplibs_check_method - match_pattern_regex=`expr "$deplibs_check_method" : "$2 \(.*\)"` - for a_deplib in $deplibs; do - name=`expr $a_deplib : '-l\(.*\)'` - # If $name is empty we are operating on a -L argument. - if test -n "$name" && test "$name" != "0"; then - if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then - case " $predeps $postdeps " in - *" $a_deplib "*) - newdeplibs="$newdeplibs $a_deplib" - a_deplib="" - ;; - esac - fi - if test -n "$a_deplib" ; then - libname=`eval \\$echo \"$libname_spec\"` - for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do - potential_libs=`ls $i/$libname[.-]* 2>/dev/null` - for potent_lib in $potential_libs; do - potlib="$potent_lib" # see symlink-check above in file_magic test - if eval $echo \"$potent_lib\" 2>/dev/null \ - | ${SED} 10q \ - | $EGREP "$match_pattern_regex" > /dev/null; then - newdeplibs="$newdeplibs $a_deplib" - a_deplib="" - break 2 - fi - done - done - fi - if test -n "$a_deplib" ; then - droppeddeps=yes - $echo - $echo "*** Warning: linker path does not have real file for library $a_deplib." - $echo "*** I have the capability to make that library automatically link in when" - $echo "*** you link to this library. But I can only do this if you have a" - $echo "*** shared version of the library, which you do not appear to have" - $echo "*** because I did check the linker path looking for a file starting" - if test -z "$potlib" ; then - $echo "*** with $libname but no candidates were found. (...for regex pattern test)" - else - $echo "*** with $libname and none of the candidates passed a file format test" - $echo "*** using a regex pattern. Last file checked: $potlib" - fi - fi - else - # Add a -L argument. - newdeplibs="$newdeplibs $a_deplib" - fi - done # Gone through all deplibs. - ;; - none | unknown | *) - newdeplibs="" - tmp_deplibs=`$echo "X $deplibs" | $Xsed -e 's/ -lc$//' \ - -e 's/ -[LR][^ ]*//g'` - if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then - for i in $predeps $postdeps ; do - # can't use Xsed below, because $i might contain '/' - tmp_deplibs=`$echo "X $tmp_deplibs" | ${SED} -e "1s,^X,," -e "s,$i,,"` - done - fi - if $echo "X $tmp_deplibs" | $Xsed -e 's/[ ]//g' \ - | grep . >/dev/null; then - $echo - if test "X$deplibs_check_method" = "Xnone"; then - $echo "*** Warning: inter-library dependencies are not supported in this platform." - else - $echo "*** Warning: inter-library dependencies are not known to be supported." - fi - $echo "*** All declared inter-library dependencies are being dropped." - droppeddeps=yes - fi - ;; - esac - versuffix=$versuffix_save - major=$major_save - release=$release_save - libname=$libname_save - name=$name_save - - case $host in - *-*-rhapsody* | *-*-darwin1.[012]) - # On Rhapsody replace the C library is the System framework - newdeplibs=`$echo "X $newdeplibs" | $Xsed -e 's/ -lc / -framework System /'` - ;; - esac - - if test "$droppeddeps" = yes; then - if test "$module" = yes; then - $echo - $echo "*** Warning: libtool could not satisfy all declared inter-library" - $echo "*** dependencies of module $libname. Therefore, libtool will create" - $echo "*** a static module, that should work as long as the dlopening" - $echo "*** application is linked with the -dlopen flag." - if test -z "$global_symbol_pipe"; then - $echo - $echo "*** However, this would only work if libtool was able to extract symbol" - $echo "*** lists from a program, using \`nm' or equivalent, but libtool could" - $echo "*** not find such a program. So, this module is probably useless." - $echo "*** \`nm' from GNU binutils and a full rebuild may help." - fi - if test "$build_old_libs" = no; then - oldlibs="$output_objdir/$libname.$libext" - build_libtool_libs=module - build_old_libs=yes - else - build_libtool_libs=no - fi - else - $echo "*** The inter-library dependencies that have been dropped here will be" - $echo "*** automatically added whenever a program is linked with this library" - $echo "*** or is declared to -dlopen it." - - if test "$allow_undefined" = no; then - $echo - $echo "*** Since this library must not contain undefined symbols," - $echo "*** because either the platform does not support them or" - $echo "*** it was explicitly requested with -no-undefined," - $echo "*** libtool will only create a static version of it." - if test "$build_old_libs" = no; then - oldlibs="$output_objdir/$libname.$libext" - build_libtool_libs=module - build_old_libs=yes - else - build_libtool_libs=no - fi - fi - fi - fi - # Done checking deplibs! - deplibs=$newdeplibs - fi - - - # move library search paths that coincide with paths to not yet - # installed libraries to the beginning of the library search list - new_libs= - for path in $notinst_path; do - case " $new_libs " in - *" -L$path/$objdir "*) ;; - *) - case " $deplibs " in - *" -L$path/$objdir "*) - new_libs="$new_libs -L$path/$objdir" ;; - esac - ;; - esac - done - for deplib in $deplibs; do - case $deplib in - -L*) - case " $new_libs " in - *" $deplib "*) ;; - *) new_libs="$new_libs $deplib" ;; - esac - ;; - *) new_libs="$new_libs $deplib" ;; - esac - done - deplibs="$new_libs" - - - # All the library-specific variables (install_libdir is set above). - library_names= - old_library= - dlname= - - # Test again, we may have decided not to build it any more - if test "$build_libtool_libs" = yes; then - if test "$hardcode_into_libs" = yes; then - # Hardcode the library paths - hardcode_libdirs= - dep_rpath= - rpath="$finalize_rpath" - test "$mode" != relink && rpath="$compile_rpath$rpath" - for libdir in $rpath; do - if test -n "$hardcode_libdir_flag_spec"; then - if test -n "$hardcode_libdir_separator"; then - if test -z "$hardcode_libdirs"; then - hardcode_libdirs="$libdir" - else - # Just accumulate the unique libdirs. - case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in - *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) - ;; - *) - hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" - ;; - esac - fi - else - eval flag=\"$hardcode_libdir_flag_spec\" - dep_rpath="$dep_rpath $flag" - fi - elif test -n "$runpath_var"; then - case "$perm_rpath " in - *" $libdir "*) ;; - *) perm_rpath="$perm_rpath $libdir" ;; - esac - fi - done - # Substitute the hardcoded libdirs into the rpath. - if test -n "$hardcode_libdir_separator" && - test -n "$hardcode_libdirs"; then - libdir="$hardcode_libdirs" - if test -n "$hardcode_libdir_flag_spec_ld"; then - eval dep_rpath=\"$hardcode_libdir_flag_spec_ld\" - else - eval dep_rpath=\"$hardcode_libdir_flag_spec\" - fi - fi - if test -n "$runpath_var" && test -n "$perm_rpath"; then - # We should set the runpath_var. - rpath= - for dir in $perm_rpath; do - rpath="$rpath$dir:" - done - eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" - fi - test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" - fi - - shlibpath="$finalize_shlibpath" - test "$mode" != relink && shlibpath="$compile_shlibpath$shlibpath" - if test -n "$shlibpath"; then - eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" - fi - - # Get the real and link names of the library. - eval shared_ext=\"$shrext_cmds\" - eval library_names=\"$library_names_spec\" - set dummy $library_names - realname="$2" - shift; shift - - if test -n "$soname_spec"; then - eval soname=\"$soname_spec\" - else - soname="$realname" - fi - if test -z "$dlname"; then - dlname=$soname - fi - - lib="$output_objdir/$realname" - linknames= - for link - do - linknames="$linknames $link" - done - - # Use standard objects if they are pic - test -z "$pic_flag" && libobjs=`$echo "X$libobjs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` - - # Prepare the list of exported symbols - if test -z "$export_symbols"; then - if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then - $show "generating symbol list for \`$libname.la'" - export_symbols="$output_objdir/$libname.exp" - $run $rm $export_symbols - cmds=$export_symbols_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - if len=`expr "X$cmd" : ".*"` && - test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then - $show "$cmd" - $run eval "$cmd" || exit $? - skipped_export=false - else - # The command line is too long to execute in one step. - $show "using reloadable object file for export list..." - skipped_export=: - # Break out early, otherwise skipped_export may be - # set to false by a later but shorter cmd. - break - fi - done - IFS="$save_ifs" - if test -n "$export_symbols_regex"; then - $show "$EGREP -e \"$export_symbols_regex\" \"$export_symbols\" > \"${export_symbols}T\"" - $run eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' - $show "$mv \"${export_symbols}T\" \"$export_symbols\"" - $run eval '$mv "${export_symbols}T" "$export_symbols"' - fi - fi - fi - - if test -n "$export_symbols" && test -n "$include_expsyms"; then - $run eval '$echo "X$include_expsyms" | $SP2NL >> "$export_symbols"' - fi - - tmp_deplibs= - for test_deplib in $deplibs; do - case " $convenience " in - *" $test_deplib "*) ;; - *) - tmp_deplibs="$tmp_deplibs $test_deplib" - ;; - esac - done - deplibs="$tmp_deplibs" - - if test -n "$convenience"; then - if test -n "$whole_archive_flag_spec"; then - save_libobjs=$libobjs - eval libobjs=\"\$libobjs $whole_archive_flag_spec\" - else - gentop="$output_objdir/${outputname}x" - generated="$generated $gentop" - - func_extract_archives $gentop $convenience - libobjs="$libobjs $func_extract_archives_result" - fi - fi - - if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then - eval flag=\"$thread_safe_flag_spec\" - linker_flags="$linker_flags $flag" - fi - - # Make a backup of the uninstalled library when relinking - if test "$mode" = relink; then - $run eval '(cd $output_objdir && $rm ${realname}U && $mv $realname ${realname}U)' || exit $? - fi - - # Do each of the archive commands. - if test "$module" = yes && test -n "$module_cmds" ; then - if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then - eval test_cmds=\"$module_expsym_cmds\" - cmds=$module_expsym_cmds - else - eval test_cmds=\"$module_cmds\" - cmds=$module_cmds - fi - else - if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then - eval test_cmds=\"$archive_expsym_cmds\" - cmds=$archive_expsym_cmds - else - eval test_cmds=\"$archive_cmds\" - cmds=$archive_cmds - fi - fi - - if test "X$skipped_export" != "X:" && - len=`expr "X$test_cmds" : ".*" 2>/dev/null` && - test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then - : - else - # The command line is too long to link in one step, link piecewise. - $echo "creating reloadable object files..." - - # Save the value of $output and $libobjs because we want to - # use them later. If we have whole_archive_flag_spec, we - # want to use save_libobjs as it was before - # whole_archive_flag_spec was expanded, because we can't - # assume the linker understands whole_archive_flag_spec. - # This may have to be revisited, in case too many - # convenience libraries get linked in and end up exceeding - # the spec. - if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then - save_libobjs=$libobjs - fi - save_output=$output - output_la=`$echo "X$output" | $Xsed -e "$basename"` - - # Clear the reloadable object creation command queue and - # initialize k to one. - test_cmds= - concat_cmds= - objlist= - delfiles= - last_robj= - k=1 - output=$output_objdir/$output_la-${k}.$objext - # Loop over the list of objects to be linked. - for obj in $save_libobjs - do - eval test_cmds=\"$reload_cmds $objlist $last_robj\" - if test "X$objlist" = X || - { len=`expr "X$test_cmds" : ".*" 2>/dev/null` && - test "$len" -le "$max_cmd_len"; }; then - objlist="$objlist $obj" - else - # The command $test_cmds is almost too long, add a - # command to the queue. - if test "$k" -eq 1 ; then - # The first file doesn't have a previous command to add. - eval concat_cmds=\"$reload_cmds $objlist $last_robj\" - else - # All subsequent reloadable object files will link in - # the last one created. - eval concat_cmds=\"\$concat_cmds~$reload_cmds $objlist $last_robj\" - fi - last_robj=$output_objdir/$output_la-${k}.$objext - k=`expr $k + 1` - output=$output_objdir/$output_la-${k}.$objext - objlist=$obj - len=1 - fi - done - # Handle the remaining objects by creating one last - # reloadable object file. All subsequent reloadable object - # files will link in the last one created. - test -z "$concat_cmds" || concat_cmds=$concat_cmds~ - eval concat_cmds=\"\${concat_cmds}$reload_cmds $objlist $last_robj\" - - if ${skipped_export-false}; then - $show "generating symbol list for \`$libname.la'" - export_symbols="$output_objdir/$libname.exp" - $run $rm $export_symbols - libobjs=$output - # Append the command to create the export file. - eval concat_cmds=\"\$concat_cmds~$export_symbols_cmds\" - fi - - # Set up a command to remove the reloadable object files - # after they are used. - i=0 - while test "$i" -lt "$k" - do - i=`expr $i + 1` - delfiles="$delfiles $output_objdir/$output_la-${i}.$objext" - done - - $echo "creating a temporary reloadable object file: $output" - - # Loop through the commands generated above and execute them. - save_ifs="$IFS"; IFS='~' - for cmd in $concat_cmds; do - IFS="$save_ifs" - $show "$cmd" - $run eval "$cmd" || exit $? - done - IFS="$save_ifs" - - libobjs=$output - # Restore the value of output. - output=$save_output - - if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then - eval libobjs=\"\$libobjs $whole_archive_flag_spec\" - fi - # Expand the library linking commands again to reset the - # value of $libobjs for piecewise linking. - - # Do each of the archive commands. - if test "$module" = yes && test -n "$module_cmds" ; then - if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then - cmds=$module_expsym_cmds - else - cmds=$module_cmds - fi - else - if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then - cmds=$archive_expsym_cmds - else - cmds=$archive_cmds - fi - fi - - # Append the command to remove the reloadable object files - # to the just-reset $cmds. - eval cmds=\"\$cmds~\$rm $delfiles\" - fi - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || { - lt_exit=$? - - # Restore the uninstalled library and exit - if test "$mode" = relink; then - $run eval '(cd $output_objdir && $rm ${realname}T && $mv ${realname}U $realname)' - fi - - exit $lt_exit - } - done - IFS="$save_ifs" - - # Restore the uninstalled library and exit - if test "$mode" = relink; then - $run eval '(cd $output_objdir && $rm ${realname}T && $mv $realname ${realname}T && $mv "$realname"U $realname)' || exit $? - - if test -n "$convenience"; then - if test -z "$whole_archive_flag_spec"; then - $show "${rm}r $gentop" - $run ${rm}r "$gentop" - fi - fi - - exit $EXIT_SUCCESS - fi - - # Create links to the real library. - for linkname in $linknames; do - if test "$realname" != "$linkname"; then - $show "(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)" - $run eval '(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)' || exit $? - fi - done - - # If -module or -export-dynamic was specified, set the dlname. - if test "$module" = yes || test "$export_dynamic" = yes; then - # On all known operating systems, these are identical. - dlname="$soname" - fi - fi - ;; - - obj) - if test -n "$deplibs"; then - $echo "$modename: warning: \`-l' and \`-L' are ignored for objects" 1>&2 - fi - - if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then - $echo "$modename: warning: \`-dlopen' is ignored for objects" 1>&2 - fi - - if test -n "$rpath"; then - $echo "$modename: warning: \`-rpath' is ignored for objects" 1>&2 - fi - - if test -n "$xrpath"; then - $echo "$modename: warning: \`-R' is ignored for objects" 1>&2 - fi - - if test -n "$vinfo"; then - $echo "$modename: warning: \`-version-info' is ignored for objects" 1>&2 - fi - - if test -n "$release"; then - $echo "$modename: warning: \`-release' is ignored for objects" 1>&2 - fi - - case $output in - *.lo) - if test -n "$objs$old_deplibs"; then - $echo "$modename: cannot build library object \`$output' from non-libtool objects" 1>&2 - exit $EXIT_FAILURE - fi - libobj="$output" - obj=`$echo "X$output" | $Xsed -e "$lo2o"` - ;; - *) - libobj= - obj="$output" - ;; - esac - - # Delete the old objects. - $run $rm $obj $libobj - - # Objects from convenience libraries. This assumes - # single-version convenience libraries. Whenever we create - # different ones for PIC/non-PIC, this we'll have to duplicate - # the extraction. - reload_conv_objs= - gentop= - # reload_cmds runs $LD directly, so let us get rid of - # -Wl from whole_archive_flag_spec and hope we can get by with - # turning comma into space.. - wl= - - if test -n "$convenience"; then - if test -n "$whole_archive_flag_spec"; then - eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" - reload_conv_objs=$reload_objs\ `$echo "X$tmp_whole_archive_flags" | $Xsed -e 's|,| |g'` - else - gentop="$output_objdir/${obj}x" - generated="$generated $gentop" - - func_extract_archives $gentop $convenience - reload_conv_objs="$reload_objs $func_extract_archives_result" - fi - fi - - # Create the old-style object. - reload_objs="$objs$old_deplibs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test - - output="$obj" - cmds=$reload_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || exit $? - done - IFS="$save_ifs" - - # Exit if we aren't doing a library object file. - if test -z "$libobj"; then - if test -n "$gentop"; then - $show "${rm}r $gentop" - $run ${rm}r $gentop - fi - - exit $EXIT_SUCCESS - fi - - if test "$build_libtool_libs" != yes; then - if test -n "$gentop"; then - $show "${rm}r $gentop" - $run ${rm}r $gentop - fi - - # Create an invalid libtool object if no PIC, so that we don't - # accidentally link it into a program. - # $show "echo timestamp > $libobj" - # $run eval "echo timestamp > $libobj" || exit $? - exit $EXIT_SUCCESS - fi - - if test -n "$pic_flag" || test "$pic_mode" != default; then - # Only do commands if we really have different PIC objects. - reload_objs="$libobjs $reload_conv_objs" - output="$libobj" - cmds=$reload_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || exit $? - done - IFS="$save_ifs" - fi - - if test -n "$gentop"; then - $show "${rm}r $gentop" - $run ${rm}r $gentop - fi - - exit $EXIT_SUCCESS - ;; - - prog) - case $host in - *cygwin*) output=`$echo $output | ${SED} -e 's,.exe$,,;s,$,.exe,'` ;; - esac - if test -n "$vinfo"; then - $echo "$modename: warning: \`-version-info' is ignored for programs" 1>&2 - fi - - if test -n "$release"; then - $echo "$modename: warning: \`-release' is ignored for programs" 1>&2 - fi - - if test "$preload" = yes; then - if test "$dlopen_support" = unknown && test "$dlopen_self" = unknown && - test "$dlopen_self_static" = unknown; then - $echo "$modename: warning: \`AC_LIBTOOL_DLOPEN' not used. Assuming no dlopen support." - fi - fi - - case $host in - *-*-rhapsody* | *-*-darwin1.[012]) - # On Rhapsody replace the C library is the System framework - compile_deplibs=`$echo "X $compile_deplibs" | $Xsed -e 's/ -lc / -framework System /'` - finalize_deplibs=`$echo "X $finalize_deplibs" | $Xsed -e 's/ -lc / -framework System /'` - ;; - esac - - case $host in - *darwin*) - # Don't allow lazy linking, it breaks C++ global constructors - if test "$tagname" = CXX ; then - compile_command="$compile_command ${wl}-bind_at_load" - finalize_command="$finalize_command ${wl}-bind_at_load" - fi - ;; - esac - - - # move library search paths that coincide with paths to not yet - # installed libraries to the beginning of the library search list - new_libs= - for path in $notinst_path; do - case " $new_libs " in - *" -L$path/$objdir "*) ;; - *) - case " $compile_deplibs " in - *" -L$path/$objdir "*) - new_libs="$new_libs -L$path/$objdir" ;; - esac - ;; - esac - done - for deplib in $compile_deplibs; do - case $deplib in - -L*) - case " $new_libs " in - *" $deplib "*) ;; - *) new_libs="$new_libs $deplib" ;; - esac - ;; - *) new_libs="$new_libs $deplib" ;; - esac - done - compile_deplibs="$new_libs" - - - compile_command="$compile_command $compile_deplibs" - finalize_command="$finalize_command $finalize_deplibs" - - if test -n "$rpath$xrpath"; then - # If the user specified any rpath flags, then add them. - for libdir in $rpath $xrpath; do - # This is the magic to use -rpath. - case "$finalize_rpath " in - *" $libdir "*) ;; - *) finalize_rpath="$finalize_rpath $libdir" ;; - esac - done - fi - - # Now hardcode the library paths - rpath= - hardcode_libdirs= - for libdir in $compile_rpath $finalize_rpath; do - if test -n "$hardcode_libdir_flag_spec"; then - if test -n "$hardcode_libdir_separator"; then - if test -z "$hardcode_libdirs"; then - hardcode_libdirs="$libdir" - else - # Just accumulate the unique libdirs. - case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in - *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) - ;; - *) - hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" - ;; - esac - fi - else - eval flag=\"$hardcode_libdir_flag_spec\" - rpath="$rpath $flag" - fi - elif test -n "$runpath_var"; then - case "$perm_rpath " in - *" $libdir "*) ;; - *) perm_rpath="$perm_rpath $libdir" ;; - esac - fi - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) - testbindir=`$echo "X$libdir" | $Xsed -e 's*/lib$*/bin*'` - case :$dllsearchpath: in - *":$libdir:"*) ;; - *) dllsearchpath="$dllsearchpath:$libdir";; - esac - case :$dllsearchpath: in - *":$testbindir:"*) ;; - *) dllsearchpath="$dllsearchpath:$testbindir";; - esac - ;; - esac - done - # Substitute the hardcoded libdirs into the rpath. - if test -n "$hardcode_libdir_separator" && - test -n "$hardcode_libdirs"; then - libdir="$hardcode_libdirs" - eval rpath=\" $hardcode_libdir_flag_spec\" - fi - compile_rpath="$rpath" - - rpath= - hardcode_libdirs= - for libdir in $finalize_rpath; do - if test -n "$hardcode_libdir_flag_spec"; then - if test -n "$hardcode_libdir_separator"; then - if test -z "$hardcode_libdirs"; then - hardcode_libdirs="$libdir" - else - # Just accumulate the unique libdirs. - case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in - *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) - ;; - *) - hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" - ;; - esac - fi - else - eval flag=\"$hardcode_libdir_flag_spec\" - rpath="$rpath $flag" - fi - elif test -n "$runpath_var"; then - case "$finalize_perm_rpath " in - *" $libdir "*) ;; - *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;; - esac - fi - done - # Substitute the hardcoded libdirs into the rpath. - if test -n "$hardcode_libdir_separator" && - test -n "$hardcode_libdirs"; then - libdir="$hardcode_libdirs" - eval rpath=\" $hardcode_libdir_flag_spec\" - fi - finalize_rpath="$rpath" - - if test -n "$libobjs" && test "$build_old_libs" = yes; then - # Transform all the library objects into standard objects. - compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` - finalize_command=`$echo "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` - fi - - dlsyms= - if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then - if test -n "$NM" && test -n "$global_symbol_pipe"; then - dlsyms="${outputname}S.c" - else - $echo "$modename: not configured to extract global symbols from dlpreopened files" 1>&2 - fi - fi - - if test -n "$dlsyms"; then - case $dlsyms in - "") ;; - *.c) - # Discover the nlist of each of the dlfiles. - nlist="$output_objdir/${outputname}.nm" - - $show "$rm $nlist ${nlist}S ${nlist}T" - $run $rm "$nlist" "${nlist}S" "${nlist}T" - - # Parse the name list into a source file. - $show "creating $output_objdir/$dlsyms" - - test -z "$run" && $echo > "$output_objdir/$dlsyms" "\ -/* $dlsyms - symbol resolution table for \`$outputname' dlsym emulation. */ -/* Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP */ - -#ifdef __cplusplus -extern \"C\" { -#endif - -/* Prevent the only kind of declaration conflicts we can make. */ -#define lt_preloaded_symbols some_other_symbol - -/* External symbol declarations for the compiler. */\ -" - - if test "$dlself" = yes; then - $show "generating symbol list for \`$output'" - - test -z "$run" && $echo ': @PROGRAM@ ' > "$nlist" - - # Add our own program objects to the symbol list. - progfiles=`$echo "X$objs$old_deplibs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` - for arg in $progfiles; do - $show "extracting global C symbols from \`$arg'" - $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'" - done - - if test -n "$exclude_expsyms"; then - $run eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' - $run eval '$mv "$nlist"T "$nlist"' - fi - - if test -n "$export_symbols_regex"; then - $run eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' - $run eval '$mv "$nlist"T "$nlist"' - fi - - # Prepare the list of exported symbols - if test -z "$export_symbols"; then - export_symbols="$output_objdir/$outputname.exp" - $run $rm $export_symbols - $run eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' - case $host in - *cygwin* | *mingw* ) - $run eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' - $run eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' - ;; - esac - else - $run eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' - $run eval 'grep -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' - $run eval 'mv "$nlist"T "$nlist"' - case $host in - *cygwin* | *mingw* ) - $run eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' - $run eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' - ;; - esac - fi - fi - - for arg in $dlprefiles; do - $show "extracting global C symbols from \`$arg'" - name=`$echo "$arg" | ${SED} -e 's%^.*/%%'` - $run eval '$echo ": $name " >> "$nlist"' - $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'" - done - - if test -z "$run"; then - # Make sure we have at least an empty file. - test -f "$nlist" || : > "$nlist" - - if test -n "$exclude_expsyms"; then - $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T - $mv "$nlist"T "$nlist" - fi - - # Try sorting and uniquifying the output. - if grep -v "^: " < "$nlist" | - if sort -k 3 /dev/null 2>&1; then - sort -k 3 - else - sort +2 - fi | - uniq > "$nlist"S; then - : - else - grep -v "^: " < "$nlist" > "$nlist"S - fi - - if test -f "$nlist"S; then - eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$dlsyms"' - else - $echo '/* NONE */' >> "$output_objdir/$dlsyms" - fi - - $echo >> "$output_objdir/$dlsyms" "\ - -#undef lt_preloaded_symbols - -#if defined (__STDC__) && __STDC__ -# define lt_ptr void * -#else -# define lt_ptr char * -# define const -#endif - -/* The mapping between symbol names and symbols. */ -" - - case $host in - *cygwin* | *mingw* ) - $echo >> "$output_objdir/$dlsyms" "\ -/* DATA imports from DLLs on WIN32 can't be const, because - runtime relocations are performed -- see ld's documentation - on pseudo-relocs */ -struct { -" - ;; - * ) - $echo >> "$output_objdir/$dlsyms" "\ -const struct { -" - ;; - esac - - - $echo >> "$output_objdir/$dlsyms" "\ - const char *name; - lt_ptr address; -} -lt_preloaded_symbols[] = -{\ -" - - eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$dlsyms" - - $echo >> "$output_objdir/$dlsyms" "\ - {0, (lt_ptr) 0} -}; - -/* This works around a problem in FreeBSD linker */ -#ifdef FREEBSD_WORKAROUND -static const void *lt_preloaded_setup() { - return lt_preloaded_symbols; -} -#endif - -#ifdef __cplusplus -} -#endif\ -" - fi - - pic_flag_for_symtable= - case $host in - # compiling the symbol table file with pic_flag works around - # a FreeBSD bug that causes programs to crash when -lm is - # linked before any other PIC object. But we must not use - # pic_flag when linking with -static. The problem exists in - # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. - *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) - case "$compile_command " in - *" -static "*) ;; - *) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND";; - esac;; - *-*-hpux*) - case "$compile_command " in - *" -static "*) ;; - *) pic_flag_for_symtable=" $pic_flag";; - esac - esac - - # Now compile the dynamic symbol file. - $show "(cd $output_objdir && $LTCC $LTCFLAGS -c$no_builtin_flag$pic_flag_for_symtable \"$dlsyms\")" - $run eval '(cd $output_objdir && $LTCC $LTCFLAGS -c$no_builtin_flag$pic_flag_for_symtable "$dlsyms")' || exit $? - - # Clean up the generated files. - $show "$rm $output_objdir/$dlsyms $nlist ${nlist}S ${nlist}T" - $run $rm "$output_objdir/$dlsyms" "$nlist" "${nlist}S" "${nlist}T" - - # Transform the symbol file into the correct name. - case $host in - *cygwin* | *mingw* ) - if test -f "$output_objdir/${outputname}.def" ; then - compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}.def $output_objdir/${outputname}S.${objext}%" | $NL2SP` - finalize_command=`$echo "X$finalize_command" | $SP2NL | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}.def $output_objdir/${outputname}S.${objext}%" | $NL2SP` - else - compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%" | $NL2SP` - finalize_command=`$echo "X$finalize_command" | $SP2NL | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%" | $NL2SP` - fi - ;; - * ) - compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%" | $NL2SP` - finalize_command=`$echo "X$finalize_command" | $SP2NL | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%" | $NL2SP` - ;; - esac - ;; - *) - $echo "$modename: unknown suffix for \`$dlsyms'" 1>&2 - exit $EXIT_FAILURE - ;; - esac - else - # We keep going just in case the user didn't refer to - # lt_preloaded_symbols. The linker will fail if global_symbol_pipe - # really was required. - - # Nullify the symbol file. - compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e "s% @SYMFILE@%%" | $NL2SP` - finalize_command=`$echo "X$finalize_command" | $SP2NL | $Xsed -e "s% @SYMFILE@%%" | $NL2SP` - fi - - if test "$need_relink" = no || test "$build_libtool_libs" != yes; then - # Replace the output file specification. - compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e 's%@OUTPUT@%'"$output"'%g' | $NL2SP` - link_command="$compile_command$compile_rpath" - - # We have no uninstalled library dependencies, so finalize right now. - $show "$link_command" - $run eval "$link_command" - exit_status=$? - - # Delete the generated files. - if test -n "$dlsyms"; then - $show "$rm $output_objdir/${outputname}S.${objext}" - $run $rm "$output_objdir/${outputname}S.${objext}" - fi - - exit $exit_status - fi - - if test -n "$shlibpath_var"; then - # We should set the shlibpath_var - rpath= - for dir in $temp_rpath; do - case $dir in - [\\/]* | [A-Za-z]:[\\/]*) - # Absolute path. - rpath="$rpath$dir:" - ;; - *) - # Relative path: add a thisdir entry. - rpath="$rpath\$thisdir/$dir:" - ;; - esac - done - temp_rpath="$rpath" - fi - - if test -n "$compile_shlibpath$finalize_shlibpath"; then - compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" - fi - if test -n "$finalize_shlibpath"; then - finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" - fi - - compile_var= - finalize_var= - if test -n "$runpath_var"; then - if test -n "$perm_rpath"; then - # We should set the runpath_var. - rpath= - for dir in $perm_rpath; do - rpath="$rpath$dir:" - done - compile_var="$runpath_var=\"$rpath\$$runpath_var\" " - fi - if test -n "$finalize_perm_rpath"; then - # We should set the runpath_var. - rpath= - for dir in $finalize_perm_rpath; do - rpath="$rpath$dir:" - done - finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " - fi - fi - - if test "$no_install" = yes; then - # We don't need to create a wrapper script. - link_command="$compile_var$compile_command$compile_rpath" - # Replace the output file specification. - link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` - # Delete the old output file. - $run $rm $output - # Link the executable and exit - $show "$link_command" - $run eval "$link_command" || exit $? - exit $EXIT_SUCCESS - fi - - if test "$hardcode_action" = relink; then - # Fast installation is not supported - link_command="$compile_var$compile_command$compile_rpath" - relink_command="$finalize_var$finalize_command$finalize_rpath" - - $echo "$modename: warning: this platform does not like uninstalled shared libraries" 1>&2 - $echo "$modename: \`$output' will be relinked during installation" 1>&2 - else - if test "$fast_install" != no; then - link_command="$finalize_var$compile_command$finalize_rpath" - if test "$fast_install" = yes; then - relink_command=`$echo "X$compile_var$compile_command$compile_rpath" | $SP2NL | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g' | $NL2SP` - else - # fast_install is set to needless - relink_command= - fi - else - link_command="$compile_var$compile_command$compile_rpath" - relink_command="$finalize_var$finalize_command$finalize_rpath" - fi - fi - - # Replace the output file specification. - link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` - - # Delete the old output files. - $run $rm $output $output_objdir/$outputname $output_objdir/lt-$outputname - - $show "$link_command" - $run eval "$link_command" || exit $? - - # Now create the wrapper script. - $show "creating $output" - - # Quote the relink command for shipping. - if test -n "$relink_command"; then - # Preserve any variables that may affect compiler behavior - for var in $variables_saved_for_relink; do - if eval test -z \"\${$var+set}\"; then - relink_command="{ test -z \"\${$var+set}\" || unset $var || { $var=; export $var; }; }; $relink_command" - elif eval var_value=\$$var; test -z "$var_value"; then - relink_command="$var=; export $var; $relink_command" - else - var_value=`$echo "X$var_value" | $Xsed -e "$sed_quote_subst"` - relink_command="$var=\"$var_value\"; export $var; $relink_command" - fi - done - relink_command="(cd `pwd`; $relink_command)" - relink_command=`$echo "X$relink_command" | $SP2NL | $Xsed -e "$sed_quote_subst" | $NL2SP` - fi - - # Quote $echo for shipping. - if test "X$echo" = "X$SHELL $progpath --fallback-echo"; then - case $progpath in - [\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $progpath --fallback-echo";; - *) qecho="$SHELL `pwd`/$progpath --fallback-echo";; - esac - qecho=`$echo "X$qecho" | $Xsed -e "$sed_quote_subst"` - else - qecho=`$echo "X$echo" | $Xsed -e "$sed_quote_subst"` - fi - - # Only actually do things if our run command is non-null. - if test -z "$run"; then - # win32 will think the script is a binary if it has - # a .exe suffix, so we strip it off here. - case $output in - *.exe) output=`$echo $output|${SED} 's,.exe$,,'` ;; - esac - # test for cygwin because mv fails w/o .exe extensions - case $host in - *cygwin*) - exeext=.exe - outputname=`$echo $outputname|${SED} 's,.exe$,,'` ;; - *) exeext= ;; - esac - case $host in - *cygwin* | *mingw* ) - output_name=`basename $output` - output_path=`dirname $output` - cwrappersource="$output_path/$objdir/lt-$output_name.c" - cwrapper="$output_path/$output_name.exe" - $rm $cwrappersource $cwrapper - trap "$rm $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 - - cat > $cwrappersource <> $cwrappersource<<"EOF" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(PATH_MAX) -# define LT_PATHMAX PATH_MAX -#elif defined(MAXPATHLEN) -# define LT_PATHMAX MAXPATHLEN -#else -# define LT_PATHMAX 1024 -#endif - -#ifndef DIR_SEPARATOR -# define DIR_SEPARATOR '/' -# define PATH_SEPARATOR ':' -#endif - -#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ - defined (__OS2__) -# define HAVE_DOS_BASED_FILE_SYSTEM -# ifndef DIR_SEPARATOR_2 -# define DIR_SEPARATOR_2 '\\' -# endif -# ifndef PATH_SEPARATOR_2 -# define PATH_SEPARATOR_2 ';' -# endif -#endif - -#ifndef DIR_SEPARATOR_2 -# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) -#else /* DIR_SEPARATOR_2 */ -# define IS_DIR_SEPARATOR(ch) \ - (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) -#endif /* DIR_SEPARATOR_2 */ - -#ifndef PATH_SEPARATOR_2 -# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) -#else /* PATH_SEPARATOR_2 */ -# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) -#endif /* PATH_SEPARATOR_2 */ - -#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) -#define XFREE(stale) do { \ - if (stale) { free ((void *) stale); stale = 0; } \ -} while (0) - -/* -DDEBUG is fairly common in CFLAGS. */ -#undef DEBUG -#if defined DEBUGWRAPPER -# define DEBUG(format, ...) fprintf(stderr, format, __VA_ARGS__) -#else -# define DEBUG(format, ...) -#endif - -const char *program_name = NULL; - -void * xmalloc (size_t num); -char * xstrdup (const char *string); -const char * base_name (const char *name); -char * find_executable(const char *wrapper); -int check_executable(const char *path); -char * strendzap(char *str, const char *pat); -void lt_fatal (const char *message, ...); - -int -main (int argc, char *argv[]) -{ - char **newargz; - int i; - - program_name = (char *) xstrdup (base_name (argv[0])); - DEBUG("(main) argv[0] : %s\n",argv[0]); - DEBUG("(main) program_name : %s\n",program_name); - newargz = XMALLOC(char *, argc+2); -EOF - - cat >> $cwrappersource <> $cwrappersource <<"EOF" - newargz[1] = find_executable(argv[0]); - if (newargz[1] == NULL) - lt_fatal("Couldn't find %s", argv[0]); - DEBUG("(main) found exe at : %s\n",newargz[1]); - /* we know the script has the same name, without the .exe */ - /* so make sure newargz[1] doesn't end in .exe */ - strendzap(newargz[1],".exe"); - for (i = 1; i < argc; i++) - newargz[i+1] = xstrdup(argv[i]); - newargz[argc+1] = NULL; - - for (i=0; i> $cwrappersource <> $cwrappersource <> $cwrappersource <<"EOF" - return 127; -} - -void * -xmalloc (size_t num) -{ - void * p = (void *) malloc (num); - if (!p) - lt_fatal ("Memory exhausted"); - - return p; -} - -char * -xstrdup (const char *string) -{ - return string ? strcpy ((char *) xmalloc (strlen (string) + 1), string) : NULL -; -} - -const char * -base_name (const char *name) -{ - const char *base; - -#if defined (HAVE_DOS_BASED_FILE_SYSTEM) - /* Skip over the disk name in MSDOS pathnames. */ - if (isalpha ((unsigned char)name[0]) && name[1] == ':') - name += 2; -#endif - - for (base = name; *name; name++) - if (IS_DIR_SEPARATOR (*name)) - base = name + 1; - return base; -} - -int -check_executable(const char * path) -{ - struct stat st; - - DEBUG("(check_executable) : %s\n", path ? (*path ? path : "EMPTY!") : "NULL!"); - if ((!path) || (!*path)) - return 0; - - if ((stat (path, &st) >= 0) && - ( - /* MinGW & native WIN32 do not support S_IXOTH or S_IXGRP */ -#if defined (S_IXOTH) - ((st.st_mode & S_IXOTH) == S_IXOTH) || -#endif -#if defined (S_IXGRP) - ((st.st_mode & S_IXGRP) == S_IXGRP) || -#endif - ((st.st_mode & S_IXUSR) == S_IXUSR)) - ) - return 1; - else - return 0; -} - -/* Searches for the full path of the wrapper. Returns - newly allocated full path name if found, NULL otherwise */ -char * -find_executable (const char* wrapper) -{ - int has_slash = 0; - const char* p; - const char* p_next; - /* static buffer for getcwd */ - char tmp[LT_PATHMAX + 1]; - int tmp_len; - char* concat_name; - - DEBUG("(find_executable) : %s\n", wrapper ? (*wrapper ? wrapper : "EMPTY!") : "NULL!"); - - if ((wrapper == NULL) || (*wrapper == '\0')) - return NULL; - - /* Absolute path? */ -#if defined (HAVE_DOS_BASED_FILE_SYSTEM) - if (isalpha ((unsigned char)wrapper[0]) && wrapper[1] == ':') - { - concat_name = xstrdup (wrapper); - if (check_executable(concat_name)) - return concat_name; - XFREE(concat_name); - } - else - { -#endif - if (IS_DIR_SEPARATOR (wrapper[0])) - { - concat_name = xstrdup (wrapper); - if (check_executable(concat_name)) - return concat_name; - XFREE(concat_name); - } -#if defined (HAVE_DOS_BASED_FILE_SYSTEM) - } -#endif - - for (p = wrapper; *p; p++) - if (*p == '/') - { - has_slash = 1; - break; - } - if (!has_slash) - { - /* no slashes; search PATH */ - const char* path = getenv ("PATH"); - if (path != NULL) - { - for (p = path; *p; p = p_next) - { - const char* q; - size_t p_len; - for (q = p; *q; q++) - if (IS_PATH_SEPARATOR(*q)) - break; - p_len = q - p; - p_next = (*q == '\0' ? q : q + 1); - if (p_len == 0) - { - /* empty path: current directory */ - if (getcwd (tmp, LT_PATHMAX) == NULL) - lt_fatal ("getcwd failed"); - tmp_len = strlen(tmp); - concat_name = XMALLOC(char, tmp_len + 1 + strlen(wrapper) + 1); - memcpy (concat_name, tmp, tmp_len); - concat_name[tmp_len] = '/'; - strcpy (concat_name + tmp_len + 1, wrapper); - } - else - { - concat_name = XMALLOC(char, p_len + 1 + strlen(wrapper) + 1); - memcpy (concat_name, p, p_len); - concat_name[p_len] = '/'; - strcpy (concat_name + p_len + 1, wrapper); - } - if (check_executable(concat_name)) - return concat_name; - XFREE(concat_name); - } - } - /* not found in PATH; assume curdir */ - } - /* Relative path | not found in path: prepend cwd */ - if (getcwd (tmp, LT_PATHMAX) == NULL) - lt_fatal ("getcwd failed"); - tmp_len = strlen(tmp); - concat_name = XMALLOC(char, tmp_len + 1 + strlen(wrapper) + 1); - memcpy (concat_name, tmp, tmp_len); - concat_name[tmp_len] = '/'; - strcpy (concat_name + tmp_len + 1, wrapper); - - if (check_executable(concat_name)) - return concat_name; - XFREE(concat_name); - return NULL; -} - -char * -strendzap(char *str, const char *pat) -{ - size_t len, patlen; - - assert(str != NULL); - assert(pat != NULL); - - len = strlen(str); - patlen = strlen(pat); - - if (patlen <= len) - { - str += len - patlen; - if (strcmp(str, pat) == 0) - *str = '\0'; - } - return str; -} - -static void -lt_error_core (int exit_status, const char * mode, - const char * message, va_list ap) -{ - fprintf (stderr, "%s: %s: ", program_name, mode); - vfprintf (stderr, message, ap); - fprintf (stderr, ".\n"); - - if (exit_status >= 0) - exit (exit_status); -} - -void -lt_fatal (const char *message, ...) -{ - va_list ap; - va_start (ap, message); - lt_error_core (EXIT_FAILURE, "FATAL", message, ap); - va_end (ap); -} -EOF - # we should really use a build-platform specific compiler - # here, but OTOH, the wrappers (shell script and this C one) - # are only useful if you want to execute the "real" binary. - # Since the "real" binary is built for $host, then this - # wrapper might as well be built for $host, too. - $run $LTCC $LTCFLAGS -s -o $cwrapper $cwrappersource - ;; - esac - $rm $output - trap "$rm $output; exit $EXIT_FAILURE" 1 2 15 - - $echo > $output "\ -#! $SHELL - -# $output - temporary wrapper script for $objdir/$outputname -# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP -# -# The $output program cannot be directly executed until all the libtool -# libraries that it depends on are installed. -# -# This wrapper script should never be moved out of the build directory. -# If it is, it will not operate correctly. - -# Sed substitution that helps us do robust quoting. It backslashifies -# metacharacters that are still active within double-quoted strings. -Xsed='${SED} -e 1s/^X//' -sed_quote_subst='$sed_quote_subst' - -# Be Bourne compatible (taken from Autoconf:_AS_BOURNE_COMPATIBLE). -if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then - emulate sh - NULLCMD=: - # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which - # is contrary to our usage. Disable this feature. - alias -g '\${1+\"\$@\"}'='\"\$@\"' - setopt NO_GLOB_SUBST -else - case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac -fi - -# The HP-UX ksh and POSIX shell print the target directory to stdout -# if CDPATH is set. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -relink_command=\"$relink_command\" - -# This environment variable determines our operation mode. -if test \"\$libtool_install_magic\" = \"$magic\"; then - # install mode needs the following variable: - notinst_deplibs='$notinst_deplibs' -else - # When we are sourced in execute mode, \$file and \$echo are already set. - if test \"\$libtool_execute_magic\" != \"$magic\"; then - echo=\"$qecho\" - file=\"\$0\" - # Make sure echo works. - if test \"X\$1\" = X--no-reexec; then - # Discard the --no-reexec flag, and continue. - shift - elif test \"X\`(\$echo '\t') 2>/dev/null\`\" = 'X\t'; then - # Yippee, \$echo works! - : - else - # Restart under the correct shell, and then maybe \$echo will work. - exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"} - fi - fi\ -" - $echo >> $output "\ - - # Find the directory that this script lives in. - thisdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\` - test \"x\$thisdir\" = \"x\$file\" && thisdir=. - - # Follow symbolic links until we get to the real thisdir. - file=\`ls -ld \"\$file\" | ${SED} -n 's/.*-> //p'\` - while test -n \"\$file\"; do - destdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\` - - # If there was a directory component, then change thisdir. - if test \"x\$destdir\" != \"x\$file\"; then - case \"\$destdir\" in - [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; - *) thisdir=\"\$thisdir/\$destdir\" ;; - esac - fi - - file=\`\$echo \"X\$file\" | \$Xsed -e 's%^.*/%%'\` - file=\`ls -ld \"\$thisdir/\$file\" | ${SED} -n 's/.*-> //p'\` - done - - # Try to get the absolute directory name. - absdir=\`cd \"\$thisdir\" && pwd\` - test -n \"\$absdir\" && thisdir=\"\$absdir\" -" - - if test "$fast_install" = yes; then - $echo >> $output "\ - program=lt-'$outputname'$exeext - progdir=\"\$thisdir/$objdir\" - - if test ! -f \"\$progdir/\$program\" || \\ - { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ - test \"X\$file\" != \"X\$progdir/\$program\"; }; then - - file=\"\$\$-\$program\" - - if test ! -d \"\$progdir\"; then - $mkdir \"\$progdir\" - else - $rm \"\$progdir/\$file\" - fi" - - $echo >> $output "\ - - # relink executable if necessary - if test -n \"\$relink_command\"; then - if relink_command_output=\`eval \$relink_command 2>&1\`; then : - else - $echo \"\$relink_command_output\" >&2 - $rm \"\$progdir/\$file\" - exit $EXIT_FAILURE - fi - fi - - $mv \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || - { $rm \"\$progdir/\$program\"; - $mv \"\$progdir/\$file\" \"\$progdir/\$program\"; } - $rm \"\$progdir/\$file\" - fi" - else - $echo >> $output "\ - program='$outputname' - progdir=\"\$thisdir/$objdir\" -" - fi - - $echo >> $output "\ - - if test -f \"\$progdir/\$program\"; then" - - # Export our shlibpath_var if we have one. - if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then - $echo >> $output "\ - # Add our own library path to $shlibpath_var - $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" - - # Some systems cannot cope with colon-terminated $shlibpath_var - # The second colon is a workaround for a bug in BeOS R4 sed - $shlibpath_var=\`\$echo \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\` - - export $shlibpath_var -" - fi - - # fixup the dll searchpath if we need to. - if test -n "$dllsearchpath"; then - $echo >> $output "\ - # Add the dll search path components to the executable PATH - PATH=$dllsearchpath:\$PATH -" - fi - - $echo >> $output "\ - if test \"\$libtool_execute_magic\" != \"$magic\"; then - # Run the actual program with our arguments. -" - case $host in - # Backslashes separate directories on plain windows - *-*-mingw | *-*-os2*) - $echo >> $output "\ - exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} -" - ;; - - *) - $echo >> $output "\ - exec \"\$progdir/\$program\" \${1+\"\$@\"} -" - ;; - esac - $echo >> $output "\ - \$echo \"\$0: cannot exec \$program \$*\" - exit $EXIT_FAILURE - fi - else - # The program doesn't exist. - \$echo \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2 - \$echo \"This script is just a wrapper for \$program.\" 1>&2 - $echo \"See the $PACKAGE documentation for more information.\" 1>&2 - exit $EXIT_FAILURE - fi -fi\ -" - chmod +x $output - fi - exit $EXIT_SUCCESS - ;; - esac - - # See if we need to build an old-fashioned archive. - for oldlib in $oldlibs; do - - if test "$build_libtool_libs" = convenience; then - oldobjs="$libobjs_save" - addlibs="$convenience" - build_libtool_libs=no - else - if test "$build_libtool_libs" = module; then - oldobjs="$libobjs_save" - build_libtool_libs=no - else - oldobjs="$old_deplibs $non_pic_objects" - fi - addlibs="$old_convenience" - fi - - if test -n "$addlibs"; then - gentop="$output_objdir/${outputname}x" - generated="$generated $gentop" - - func_extract_archives $gentop $addlibs - oldobjs="$oldobjs $func_extract_archives_result" - fi - - # Do each command in the archive commands. - if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then - cmds=$old_archive_from_new_cmds - else - # POSIX demands no paths to be encoded in archives. We have - # to avoid creating archives with duplicate basenames if we - # might have to extract them afterwards, e.g., when creating a - # static archive out of a convenience library, or when linking - # the entirety of a libtool archive into another (currently - # not supported by libtool). - if (for obj in $oldobjs - do - $echo "X$obj" | $Xsed -e 's%^.*/%%' - done | sort | sort -uc >/dev/null 2>&1); then - : - else - $echo "copying selected object files to avoid basename conflicts..." - - if test -z "$gentop"; then - gentop="$output_objdir/${outputname}x" - generated="$generated $gentop" - - $show "${rm}r $gentop" - $run ${rm}r "$gentop" - $show "$mkdir $gentop" - $run $mkdir "$gentop" - exit_status=$? - if test "$exit_status" -ne 0 && test ! -d "$gentop"; then - exit $exit_status - fi - fi - - save_oldobjs=$oldobjs - oldobjs= - counter=1 - for obj in $save_oldobjs - do - objbase=`$echo "X$obj" | $Xsed -e 's%^.*/%%'` - case " $oldobjs " in - " ") oldobjs=$obj ;; - *[\ /]"$objbase "*) - while :; do - # Make sure we don't pick an alternate name that also - # overlaps. - newobj=lt$counter-$objbase - counter=`expr $counter + 1` - case " $oldobjs " in - *[\ /]"$newobj "*) ;; - *) if test ! -f "$gentop/$newobj"; then break; fi ;; - esac - done - $show "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" - $run ln "$obj" "$gentop/$newobj" || - $run cp "$obj" "$gentop/$newobj" - oldobjs="$oldobjs $gentop/$newobj" - ;; - *) oldobjs="$oldobjs $obj" ;; - esac - done - fi - - eval cmds=\"$old_archive_cmds\" - - if len=`expr "X$cmds" : ".*"` && - test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then - cmds=$old_archive_cmds - else - # the command line is too long to link in one step, link in parts - $echo "using piecewise archive linking..." - save_RANLIB=$RANLIB - RANLIB=: - objlist= - concat_cmds= - save_oldobjs=$oldobjs - - # Is there a better way of finding the last object in the list? - for obj in $save_oldobjs - do - last_oldobj=$obj - done - for obj in $save_oldobjs - do - oldobjs="$objlist $obj" - objlist="$objlist $obj" - eval test_cmds=\"$old_archive_cmds\" - if len=`expr "X$test_cmds" : ".*" 2>/dev/null` && - test "$len" -le "$max_cmd_len"; then - : - else - # the above command should be used before it gets too long - oldobjs=$objlist - if test "$obj" = "$last_oldobj" ; then - RANLIB=$save_RANLIB - fi - test -z "$concat_cmds" || concat_cmds=$concat_cmds~ - eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" - objlist= - fi - done - RANLIB=$save_RANLIB - oldobjs=$objlist - if test "X$oldobjs" = "X" ; then - eval cmds=\"\$concat_cmds\" - else - eval cmds=\"\$concat_cmds~\$old_archive_cmds\" - fi - fi - fi - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - eval cmd=\"$cmd\" - IFS="$save_ifs" - $show "$cmd" - $run eval "$cmd" || exit $? - done - IFS="$save_ifs" - done - - if test -n "$generated"; then - $show "${rm}r$generated" - $run ${rm}r$generated - fi - - # Now create the libtool archive. - case $output in - *.la) - old_library= - test "$build_old_libs" = yes && old_library="$libname.$libext" - $show "creating $output" - - # Preserve any variables that may affect compiler behavior - for var in $variables_saved_for_relink; do - if eval test -z \"\${$var+set}\"; then - relink_command="{ test -z \"\${$var+set}\" || unset $var || { $var=; export $var; }; }; $relink_command" - elif eval var_value=\$$var; test -z "$var_value"; then - relink_command="$var=; export $var; $relink_command" - else - var_value=`$echo "X$var_value" | $Xsed -e "$sed_quote_subst"` - relink_command="$var=\"$var_value\"; export $var; $relink_command" - fi - done - # Quote the link command for shipping. - relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" - relink_command=`$echo "X$relink_command" | $SP2NL | $Xsed -e "$sed_quote_subst" | $NL2SP` - if test "$hardcode_automatic" = yes ; then - relink_command= - fi - - - # Only create the output if not a dry run. - if test -z "$run"; then - for installed in no yes; do - if test "$installed" = yes; then - if test -z "$install_libdir"; then - break - fi - output="$output_objdir/$outputname"i - # Replace all uninstalled libtool libraries with the installed ones - newdependency_libs= - for deplib in $dependency_libs; do - case $deplib in - *.la) - name=`$echo "X$deplib" | $Xsed -e 's%^.*/%%'` - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` - if test -z "$libdir"; then - $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2 - exit $EXIT_FAILURE - fi - newdependency_libs="$newdependency_libs $libdir/$name" - ;; - *) newdependency_libs="$newdependency_libs $deplib" ;; - esac - done - dependency_libs="$newdependency_libs" - newdlfiles= - for lib in $dlfiles; do - name=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` - if test -z "$libdir"; then - $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 - exit $EXIT_FAILURE - fi - newdlfiles="$newdlfiles $libdir/$name" - done - dlfiles="$newdlfiles" - newdlprefiles= - for lib in $dlprefiles; do - name=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` - if test -z "$libdir"; then - $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 - exit $EXIT_FAILURE - fi - newdlprefiles="$newdlprefiles $libdir/$name" - done - dlprefiles="$newdlprefiles" - else - newdlfiles= - for lib in $dlfiles; do - case $lib in - [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; - *) abs=`pwd`"/$lib" ;; - esac - newdlfiles="$newdlfiles $abs" - done - dlfiles="$newdlfiles" - newdlprefiles= - for lib in $dlprefiles; do - case $lib in - [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; - *) abs=`pwd`"/$lib" ;; - esac - newdlprefiles="$newdlprefiles $abs" - done - dlprefiles="$newdlprefiles" - fi - $rm $output - # place dlname in correct position for cygwin - tdlname=$dlname - case $host,$output,$installed,$module,$dlname in - *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;; - esac - $echo > $output "\ -# $outputname - a libtool library file -# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP -# -# Please DO NOT delete this file! -# It is necessary for linking the library. - -# The name that we can dlopen(3). -dlname='$tdlname' - -# Names of this library. -library_names='$library_names' - -# The name of the static archive. -old_library='$old_library' - -# Libraries that this one depends upon. -dependency_libs='$dependency_libs' - -# Version information for $libname. -current=$current -age=$age -revision=$revision - -# Is this an already installed library? -installed=$installed - -# Should we warn about portability when linking against -modules? -shouldnotlink=$module - -# Files to dlopen/dlpreopen -dlopen='$dlfiles' -dlpreopen='$dlprefiles' - -# Directory that this library needs to be installed in: -libdir='$install_libdir'" - if test "$installed" = no && test "$need_relink" = yes; then - $echo >> $output "\ -relink_command=\"$relink_command\"" - fi - done - fi - - # Do a symbolic link so that the libtool archive can be found in - # LD_LIBRARY_PATH before the program is installed. - $show "(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)" - $run eval '(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)' || exit $? - ;; - esac - exit $EXIT_SUCCESS - ;; - - # libtool install mode - install) - modename="$modename: install" - - # There may be an optional sh(1) argument at the beginning of - # install_prog (especially on Windows NT). - if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || - # Allow the use of GNU shtool's install command. - $echo "X$nonopt" | grep shtool > /dev/null; then - # Aesthetically quote it. - arg=`$echo "X$nonopt" | $Xsed -e "$sed_quote_subst"` - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - install_prog="$arg " - arg="$1" - shift - else - install_prog= - arg=$nonopt - fi - - # The real first argument should be the name of the installation program. - # Aesthetically quote it. - arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - install_prog="$install_prog$arg" - - # We need to accept at least all the BSD install flags. - dest= - files= - opts= - prev= - install_type= - isdir=no - stripme= - for arg - do - if test -n "$dest"; then - files="$files $dest" - dest=$arg - continue - fi - - case $arg in - -d) isdir=yes ;; - -f) - case " $install_prog " in - *[\\\ /]cp\ *) ;; - *) prev=$arg ;; - esac - ;; - -g | -m | -o) prev=$arg ;; - -s) - stripme=" -s" - continue - ;; - -*) - ;; - *) - # If the previous option needed an argument, then skip it. - if test -n "$prev"; then - prev= - else - dest=$arg - continue - fi - ;; - esac - - # Aesthetically quote the argument. - arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - install_prog="$install_prog $arg" - done - - if test -z "$install_prog"; then - $echo "$modename: you must specify an install program" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - - if test -n "$prev"; then - $echo "$modename: the \`$prev' option requires an argument" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - - if test -z "$files"; then - if test -z "$dest"; then - $echo "$modename: no file or destination specified" 1>&2 - else - $echo "$modename: you must specify a destination" 1>&2 - fi - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - - # Strip any trailing slash from the destination. - dest=`$echo "X$dest" | $Xsed -e 's%/$%%'` - - # Check to see that the destination is a directory. - test -d "$dest" && isdir=yes - if test "$isdir" = yes; then - destdir="$dest" - destname= - else - destdir=`$echo "X$dest" | $Xsed -e 's%/[^/]*$%%'` - test "X$destdir" = "X$dest" && destdir=. - destname=`$echo "X$dest" | $Xsed -e 's%^.*/%%'` - - # Not a directory, so check to see that there is only one file specified. - set dummy $files - if test "$#" -gt 2; then - $echo "$modename: \`$dest' is not a directory" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - fi - case $destdir in - [\\/]* | [A-Za-z]:[\\/]*) ;; - *) - for file in $files; do - case $file in - *.lo) ;; - *) - $echo "$modename: \`$destdir' must be an absolute directory name" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - ;; - esac - done - ;; - esac - - # This variable tells wrapper scripts just to set variables rather - # than running their programs. - libtool_install_magic="$magic" - - staticlibs= - future_libdirs= - current_libdirs= - for file in $files; do - - # Do each installation. - case $file in - *.$libext) - # Do the static libraries later. - staticlibs="$staticlibs $file" - ;; - - *.la) - # Check to see that this really is a libtool archive. - if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : - else - $echo "$modename: \`$file' is not a valid libtool archive" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - - library_names= - old_library= - relink_command= - # If there is no directory component, then add one. - case $file in - */* | *\\*) . $file ;; - *) . ./$file ;; - esac - - # Add the libdir to current_libdirs if it is the destination. - if test "X$destdir" = "X$libdir"; then - case "$current_libdirs " in - *" $libdir "*) ;; - *) current_libdirs="$current_libdirs $libdir" ;; - esac - else - # Note the libdir as a future libdir. - case "$future_libdirs " in - *" $libdir "*) ;; - *) future_libdirs="$future_libdirs $libdir" ;; - esac - fi - - dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`/ - test "X$dir" = "X$file/" && dir= - dir="$dir$objdir" - - if test -n "$relink_command"; then - # Determine the prefix the user has applied to our future dir. - inst_prefix_dir=`$echo "$destdir" | $SED "s%$libdir\$%%"` - - # Don't allow the user to place us outside of our expected - # location b/c this prevents finding dependent libraries that - # are installed to the same prefix. - # At present, this check doesn't affect windows .dll's that - # are installed into $libdir/../bin (currently, that works fine) - # but it's something to keep an eye on. - if test "$inst_prefix_dir" = "$destdir"; then - $echo "$modename: error: cannot install \`$file' to a directory not ending in $libdir" 1>&2 - exit $EXIT_FAILURE - fi - - if test -n "$inst_prefix_dir"; then - # Stick the inst_prefix_dir data into the link command. - relink_command=`$echo "$relink_command" | $SP2NL | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%" | $NL2SP` - else - relink_command=`$echo "$relink_command" | $SP2NL | $SED "s%@inst_prefix_dir@%%" | $NL2SP` - fi - - $echo "$modename: warning: relinking \`$file'" 1>&2 - $show "$relink_command" - if $run eval "$relink_command"; then : - else - $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2 - exit $EXIT_FAILURE - fi - fi - - # See the names of the shared library. - set dummy $library_names - if test -n "$2"; then - realname="$2" - shift - shift - - srcname="$realname" - test -n "$relink_command" && srcname="$realname"T - - # Install the shared library and build the symlinks. - $show "$install_prog $dir/$srcname $destdir/$realname" - $run eval "$install_prog $dir/$srcname $destdir/$realname" || exit $? - if test -n "$stripme" && test -n "$striplib"; then - $show "$striplib $destdir/$realname" - $run eval "$striplib $destdir/$realname" || exit $? - fi - - if test "$#" -gt 0; then - # Delete the old symlinks, and create new ones. - # Try `ln -sf' first, because the `ln' binary might depend on - # the symlink we replace! Solaris /bin/ln does not understand -f, - # so we also need to try rm && ln -s. - for linkname - do - if test "$linkname" != "$realname"; then - $show "(cd $destdir && { $LN_S -f $realname $linkname || { $rm $linkname && $LN_S $realname $linkname; }; })" - $run eval "(cd $destdir && { $LN_S -f $realname $linkname || { $rm $linkname && $LN_S $realname $linkname; }; })" - fi - done - fi - - # Do each command in the postinstall commands. - lib="$destdir/$realname" - cmds=$postinstall_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || { - lt_exit=$? - - # Restore the uninstalled library and exit - if test "$mode" = relink; then - $run eval '(cd $output_objdir && $rm ${realname}T && $mv ${realname}U $realname)' - fi - - exit $lt_exit - } - done - IFS="$save_ifs" - fi - - # Install the pseudo-library for information purposes. - name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` - instname="$dir/$name"i - $show "$install_prog $instname $destdir/$name" - $run eval "$install_prog $instname $destdir/$name" || exit $? - - # Maybe install the static library, too. - test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library" - ;; - - *.lo) - # Install (i.e. copy) a libtool object. - - # Figure out destination file name, if it wasn't already specified. - if test -n "$destname"; then - destfile="$destdir/$destname" - else - destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'` - destfile="$destdir/$destfile" - fi - - # Deduce the name of the destination old-style object file. - case $destfile in - *.lo) - staticdest=`$echo "X$destfile" | $Xsed -e "$lo2o"` - ;; - *.$objext) - staticdest="$destfile" - destfile= - ;; - *) - $echo "$modename: cannot copy a libtool object to \`$destfile'" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - ;; - esac - - # Install the libtool object if requested. - if test -n "$destfile"; then - $show "$install_prog $file $destfile" - $run eval "$install_prog $file $destfile" || exit $? - fi - - # Install the old object if enabled. - if test "$build_old_libs" = yes; then - # Deduce the name of the old-style object file. - staticobj=`$echo "X$file" | $Xsed -e "$lo2o"` - - $show "$install_prog $staticobj $staticdest" - $run eval "$install_prog \$staticobj \$staticdest" || exit $? - fi - exit $EXIT_SUCCESS - ;; - - *) - # Figure out destination file name, if it wasn't already specified. - if test -n "$destname"; then - destfile="$destdir/$destname" - else - destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'` - destfile="$destdir/$destfile" - fi - - # If the file is missing, and there is a .exe on the end, strip it - # because it is most likely a libtool script we actually want to - # install - stripped_ext="" - case $file in - *.exe) - if test ! -f "$file"; then - file=`$echo $file|${SED} 's,.exe$,,'` - stripped_ext=".exe" - fi - ;; - esac - - # Do a test to see if this is really a libtool program. - case $host in - *cygwin*|*mingw*) - wrapper=`$echo $file | ${SED} -e 's,.exe$,,'` - ;; - *) - wrapper=$file - ;; - esac - if (${SED} -e '4q' $wrapper | grep "^# Generated by .*$PACKAGE")>/dev/null 2>&1; then - notinst_deplibs= - relink_command= - - # Note that it is not necessary on cygwin/mingw to append a dot to - # foo even if both foo and FILE.exe exist: automatic-append-.exe - # behavior happens only for exec(3), not for open(2)! Also, sourcing - # `FILE.' does not work on cygwin managed mounts. - # - # If there is no directory component, then add one. - case $wrapper in - */* | *\\*) . ${wrapper} ;; - *) . ./${wrapper} ;; - esac - - # Check the variables that should have been set. - if test -z "$notinst_deplibs"; then - $echo "$modename: invalid libtool wrapper script \`$wrapper'" 1>&2 - exit $EXIT_FAILURE - fi - - finalize=yes - for lib in $notinst_deplibs; do - # Check to see that each library is installed. - libdir= - if test -f "$lib"; then - # If there is no directory component, then add one. - case $lib in - */* | *\\*) . $lib ;; - *) . ./$lib ;; - esac - fi - libfile="$libdir/"`$echo "X$lib" | $Xsed -e 's%^.*/%%g'` ### testsuite: skip nested quoting test - if test -n "$libdir" && test ! -f "$libfile"; then - $echo "$modename: warning: \`$lib' has not been installed in \`$libdir'" 1>&2 - finalize=no - fi - done - - relink_command= - # Note that it is not necessary on cygwin/mingw to append a dot to - # foo even if both foo and FILE.exe exist: automatic-append-.exe - # behavior happens only for exec(3), not for open(2)! Also, sourcing - # `FILE.' does not work on cygwin managed mounts. - # - # If there is no directory component, then add one. - case $wrapper in - */* | *\\*) . ${wrapper} ;; - *) . ./${wrapper} ;; - esac - - outputname= - if test "$fast_install" = no && test -n "$relink_command"; then - if test "$finalize" = yes && test -z "$run"; then - tmpdir=`func_mktempdir` - file=`$echo "X$file$stripped_ext" | $Xsed -e 's%^.*/%%'` - outputname="$tmpdir/$file" - # Replace the output file specification. - relink_command=`$echo "X$relink_command" | $SP2NL | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g' | $NL2SP` - - $show "$relink_command" - if $run eval "$relink_command"; then : - else - $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2 - ${rm}r "$tmpdir" - continue - fi - file="$outputname" - else - $echo "$modename: warning: cannot relink \`$file'" 1>&2 - fi - else - # Install the binary that we compiled earlier. - file=`$echo "X$file$stripped_ext" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"` - fi - fi - - # remove .exe since cygwin /usr/bin/install will append another - # one anyway - case $install_prog,$host in - */usr/bin/install*,*cygwin*) - case $file:$destfile in - *.exe:*.exe) - # this is ok - ;; - *.exe:*) - destfile=$destfile.exe - ;; - *:*.exe) - destfile=`$echo $destfile | ${SED} -e 's,.exe$,,'` - ;; - esac - ;; - esac - $show "$install_prog$stripme $file $destfile" - $run eval "$install_prog\$stripme \$file \$destfile" || exit $? - test -n "$outputname" && ${rm}r "$tmpdir" - ;; - esac - done - - for file in $staticlibs; do - name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` - - # Set up the ranlib parameters. - oldlib="$destdir/$name" - - $show "$install_prog $file $oldlib" - $run eval "$install_prog \$file \$oldlib" || exit $? - - if test -n "$stripme" && test -n "$old_striplib"; then - $show "$old_striplib $oldlib" - $run eval "$old_striplib $oldlib" || exit $? - fi - - # Do each command in the postinstall commands. - cmds=$old_postinstall_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || exit $? - done - IFS="$save_ifs" - done - - if test -n "$future_libdirs"; then - $echo "$modename: warning: remember to run \`$progname --finish$future_libdirs'" 1>&2 - fi - - if test -n "$current_libdirs"; then - # Maybe just do a dry run. - test -n "$run" && current_libdirs=" -n$current_libdirs" - exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' - else - exit $EXIT_SUCCESS - fi - ;; - - # libtool finish mode - finish) - modename="$modename: finish" - libdirs="$nonopt" - admincmds= - - if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then - for dir - do - libdirs="$libdirs $dir" - done - - for libdir in $libdirs; do - if test -n "$finish_cmds"; then - # Do each command in the finish commands. - cmds=$finish_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || admincmds="$admincmds - $cmd" - done - IFS="$save_ifs" - fi - if test -n "$finish_eval"; then - # Do the single finish_eval. - eval cmds=\"$finish_eval\" - $run eval "$cmds" || admincmds="$admincmds - $cmds" - fi - done - fi - - # Exit here if they wanted silent mode. - test "$show" = : && exit $EXIT_SUCCESS - - $echo "X----------------------------------------------------------------------" | $Xsed - $echo "Libraries have been installed in:" - for libdir in $libdirs; do - $echo " $libdir" - done - $echo - $echo "If you ever happen to want to link against installed libraries" - $echo "in a given directory, LIBDIR, you must either use libtool, and" - $echo "specify the full pathname of the library, or use the \`-LLIBDIR'" - $echo "flag during linking and do at least one of the following:" - if test -n "$shlibpath_var"; then - $echo " - add LIBDIR to the \`$shlibpath_var' environment variable" - $echo " during execution" - fi - if test -n "$runpath_var"; then - $echo " - add LIBDIR to the \`$runpath_var' environment variable" - $echo " during linking" - fi - if test -n "$hardcode_libdir_flag_spec"; then - libdir=LIBDIR - eval flag=\"$hardcode_libdir_flag_spec\" - - $echo " - use the \`$flag' linker flag" - fi - if test -n "$admincmds"; then - $echo " - have your system administrator run these commands:$admincmds" - fi - if test -f /etc/ld.so.conf; then - $echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" - fi - $echo - $echo "See any operating system documentation about shared libraries for" - $echo "more information, such as the ld(1) and ld.so(8) manual pages." - $echo "X----------------------------------------------------------------------" | $Xsed - exit $EXIT_SUCCESS - ;; - - # libtool execute mode - execute) - modename="$modename: execute" - - # The first argument is the command name. - cmd="$nonopt" - if test -z "$cmd"; then - $echo "$modename: you must specify a COMMAND" 1>&2 - $echo "$help" - exit $EXIT_FAILURE - fi - - # Handle -dlopen flags immediately. - for file in $execute_dlfiles; do - if test ! -f "$file"; then - $echo "$modename: \`$file' is not a file" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - - dir= - case $file in - *.la) - # Check to see that this really is a libtool archive. - if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : - else - $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - - # Read the libtool library. - dlname= - library_names= - - # If there is no directory component, then add one. - case $file in - */* | *\\*) . $file ;; - *) . ./$file ;; - esac - - # Skip this library if it cannot be dlopened. - if test -z "$dlname"; then - # Warn if it was a shared library. - test -n "$library_names" && $echo "$modename: warning: \`$file' was not linked with \`-export-dynamic'" - continue - fi - - dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` - test "X$dir" = "X$file" && dir=. - - if test -f "$dir/$objdir/$dlname"; then - dir="$dir/$objdir" - else - $echo "$modename: cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" 1>&2 - exit $EXIT_FAILURE - fi - ;; - - *.lo) - # Just add the directory containing the .lo file. - dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` - test "X$dir" = "X$file" && dir=. - ;; - - *) - $echo "$modename: warning \`-dlopen' is ignored for non-libtool libraries and objects" 1>&2 - continue - ;; - esac - - # Get the absolute pathname. - absdir=`cd "$dir" && pwd` - test -n "$absdir" && dir="$absdir" - - # Now add the directory to shlibpath_var. - if eval "test -z \"\$$shlibpath_var\""; then - eval "$shlibpath_var=\"\$dir\"" - else - eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" - fi - done - - # This variable tells wrapper scripts just to set shlibpath_var - # rather than running their programs. - libtool_execute_magic="$magic" - - # Check if any of the arguments is a wrapper script. - args= - for file - do - case $file in - -*) ;; - *) - # Do a test to see if this is really a libtool program. - if (${SED} -e '4q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then - # If there is no directory component, then add one. - case $file in - */* | *\\*) . $file ;; - *) . ./$file ;; - esac - - # Transform arg to wrapped name. - file="$progdir/$program" - fi - ;; - esac - # Quote arguments (to preserve shell metacharacters). - file=`$echo "X$file" | $Xsed -e "$sed_quote_subst"` - args="$args \"$file\"" - done - - if test -z "$run"; then - if test -n "$shlibpath_var"; then - # Export the shlibpath_var. - eval "export $shlibpath_var" - fi - - # Restore saved environment variables - for lt_var in LANG LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES - do - eval "if test \"\${save_$lt_var+set}\" = set; then - $lt_var=\$save_$lt_var; export $lt_var - else - $lt_unset $lt_var - fi" - done - - - # Now prepare to actually exec the command. - exec_cmd="\$cmd$args" - else - # Display what would be done. - if test -n "$shlibpath_var"; then - eval "\$echo \"\$shlibpath_var=\$$shlibpath_var\"" - $echo "export $shlibpath_var" - fi - $echo "$cmd$args" - exit $EXIT_SUCCESS - fi - ;; - - # libtool clean and uninstall mode - clean | uninstall) - modename="$modename: $mode" - rm="$nonopt" - files= - rmforce= - exit_status=0 - - # This variable tells wrapper scripts just to set variables rather - # than running their programs. - libtool_install_magic="$magic" - - for arg - do - case $arg in - -f) rm="$rm $arg"; rmforce=yes ;; - -*) rm="$rm $arg" ;; - *) files="$files $arg" ;; - esac - done - - if test -z "$rm"; then - $echo "$modename: you must specify an RM program" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - - rmdirs= - - origobjdir="$objdir" - for file in $files; do - dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` - if test "X$dir" = "X$file"; then - dir=. - objdir="$origobjdir" - else - objdir="$dir/$origobjdir" - fi - name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` - test "$mode" = uninstall && objdir="$dir" - - # Remember objdir for removal later, being careful to avoid duplicates - if test "$mode" = clean; then - case " $rmdirs " in - *" $objdir "*) ;; - *) rmdirs="$rmdirs $objdir" ;; - esac - fi - - # Don't error if the file doesn't exist and rm -f was used. - if (test -L "$file") >/dev/null 2>&1 \ - || (test -h "$file") >/dev/null 2>&1 \ - || test -f "$file"; then - : - elif test -d "$file"; then - exit_status=1 - continue - elif test "$rmforce" = yes; then - continue - fi - - rmfiles="$file" - - case $name in - *.la) - # Possibly a libtool archive, so verify it. - if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then - . $dir/$name - - # Delete the libtool libraries and symlinks. - for n in $library_names; do - rmfiles="$rmfiles $objdir/$n" - done - test -n "$old_library" && rmfiles="$rmfiles $objdir/$old_library" - - case "$mode" in - clean) - case " $library_names " in - # " " in the beginning catches empty $dlname - *" $dlname "*) ;; - *) rmfiles="$rmfiles $objdir/$dlname" ;; - esac - test -n "$libdir" && rmfiles="$rmfiles $objdir/$name $objdir/${name}i" - ;; - uninstall) - if test -n "$library_names"; then - # Do each command in the postuninstall commands. - cmds=$postuninstall_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" - if test "$?" -ne 0 && test "$rmforce" != yes; then - exit_status=1 - fi - done - IFS="$save_ifs" - fi - - if test -n "$old_library"; then - # Do each command in the old_postuninstall commands. - cmds=$old_postuninstall_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" - if test "$?" -ne 0 && test "$rmforce" != yes; then - exit_status=1 - fi - done - IFS="$save_ifs" - fi - # FIXME: should reinstall the best remaining shared library. - ;; - esac - fi - ;; - - *.lo) - # Possibly a libtool object, so verify it. - if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then - - # Read the .lo file - . $dir/$name - - # Add PIC object to the list of files to remove. - if test -n "$pic_object" \ - && test "$pic_object" != none; then - rmfiles="$rmfiles $dir/$pic_object" - fi - - # Add non-PIC object to the list of files to remove. - if test -n "$non_pic_object" \ - && test "$non_pic_object" != none; then - rmfiles="$rmfiles $dir/$non_pic_object" - fi - fi - ;; - - *) - if test "$mode" = clean ; then - noexename=$name - case $file in - *.exe) - file=`$echo $file|${SED} 's,.exe$,,'` - noexename=`$echo $name|${SED} 's,.exe$,,'` - # $file with .exe has already been added to rmfiles, - # add $file without .exe - rmfiles="$rmfiles $file" - ;; - esac - # Do a test to see if this is a libtool program. - if (${SED} -e '4q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then - relink_command= - . $dir/$noexename - - # note $name still contains .exe if it was in $file originally - # as does the version of $file that was added into $rmfiles - rmfiles="$rmfiles $objdir/$name $objdir/${name}S.${objext}" - if test "$fast_install" = yes && test -n "$relink_command"; then - rmfiles="$rmfiles $objdir/lt-$name" - fi - if test "X$noexename" != "X$name" ; then - rmfiles="$rmfiles $objdir/lt-${noexename}.c" - fi - fi - fi - ;; - esac - $show "$rm $rmfiles" - $run $rm $rmfiles || exit_status=1 - done - objdir="$origobjdir" - - # Try to remove the ${objdir}s in the directories where we deleted files - for dir in $rmdirs; do - if test -d "$dir"; then - $show "rmdir $dir" - $run rmdir $dir >/dev/null 2>&1 - fi - done - - exit $exit_status - ;; - - "") - $echo "$modename: you must specify a MODE" 1>&2 - $echo "$generic_help" 1>&2 - exit $EXIT_FAILURE - ;; - esac - - if test -z "$exec_cmd"; then - $echo "$modename: invalid operation mode \`$mode'" 1>&2 - $echo "$generic_help" 1>&2 - exit $EXIT_FAILURE - fi -fi # test -z "$show_help" - -if test -n "$exec_cmd"; then - eval exec $exec_cmd - exit $EXIT_FAILURE -fi - -# We need to display help for each of the modes. -case $mode in -"") $echo \ -"Usage: $modename [OPTION]... [MODE-ARG]... - -Provide generalized library-building support services. - - --config show all configuration variables - --debug enable verbose shell tracing --n, --dry-run display commands without modifying any files - --features display basic configuration information and exit - --finish same as \`--mode=finish' - --help display this help message and exit - --mode=MODE use operation mode MODE [default=inferred from MODE-ARGS] - --quiet same as \`--silent' - --silent don't print informational messages - --tag=TAG use configuration variables from tag TAG - --version print version information - -MODE must be one of the following: - - clean remove files from the build directory - compile compile a source file into a libtool object - execute automatically set library path, then run a program - finish complete the installation of libtool libraries - install install libraries or executables - link create a library or an executable - uninstall remove libraries from an installed directory - -MODE-ARGS vary depending on the MODE. Try \`$modename --help --mode=MODE' for -a more detailed description of MODE. - -Report bugs to ." - exit $EXIT_SUCCESS - ;; - -clean) - $echo \ -"Usage: $modename [OPTION]... --mode=clean RM [RM-OPTION]... FILE... - -Remove files from the build directory. - -RM is the name of the program to use to delete files associated with each FILE -(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed -to RM. - -If FILE is a libtool library, object or program, all the files associated -with it are deleted. Otherwise, only FILE itself is deleted using RM." - ;; - -compile) - $echo \ -"Usage: $modename [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE - -Compile a source file into a libtool library object. - -This mode accepts the following additional options: - - -o OUTPUT-FILE set the output file name to OUTPUT-FILE - -prefer-pic try to building PIC objects only - -prefer-non-pic try to building non-PIC objects only - -static always build a \`.o' file suitable for static linking - -COMPILE-COMMAND is a command to be used in creating a \`standard' object file -from the given SOURCEFILE. - -The output file name is determined by removing the directory component from -SOURCEFILE, then substituting the C source code suffix \`.c' with the -library object suffix, \`.lo'." - ;; - -execute) - $echo \ -"Usage: $modename [OPTION]... --mode=execute COMMAND [ARGS]... - -Automatically set library path, then run a program. - -This mode accepts the following additional options: - - -dlopen FILE add the directory containing FILE to the library path - -This mode sets the library path environment variable according to \`-dlopen' -flags. - -If any of the ARGS are libtool executable wrappers, then they are translated -into their corresponding uninstalled binary, and any of their required library -directories are added to the library path. - -Then, COMMAND is executed, with ARGS as arguments." - ;; - -finish) - $echo \ -"Usage: $modename [OPTION]... --mode=finish [LIBDIR]... - -Complete the installation of libtool libraries. - -Each LIBDIR is a directory that contains libtool libraries. - -The commands that this mode executes may require superuser privileges. Use -the \`--dry-run' option if you just want to see what would be executed." - ;; - -install) - $echo \ -"Usage: $modename [OPTION]... --mode=install INSTALL-COMMAND... - -Install executables or libraries. - -INSTALL-COMMAND is the installation command. The first component should be -either the \`install' or \`cp' program. - -The rest of the components are interpreted as arguments to that command (only -BSD-compatible install options are recognized)." - ;; - -link) - $echo \ -"Usage: $modename [OPTION]... --mode=link LINK-COMMAND... - -Link object files or libraries together to form another library, or to -create an executable program. - -LINK-COMMAND is a command using the C compiler that you would use to create -a program from several object files. - -The following components of LINK-COMMAND are treated specially: - - -all-static do not do any dynamic linking at all - -avoid-version do not add a version suffix if possible - -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime - -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols - -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) - -export-symbols SYMFILE - try to export only the symbols listed in SYMFILE - -export-symbols-regex REGEX - try to export only the symbols matching REGEX - -LLIBDIR search LIBDIR for required installed libraries - -lNAME OUTPUT-FILE requires the installed library libNAME - -module build a library that can dlopened - -no-fast-install disable the fast-install mode - -no-install link a not-installable executable - -no-undefined declare that a library does not refer to external symbols - -o OUTPUT-FILE create OUTPUT-FILE from the specified objects - -objectlist FILE Use a list of object files found in FILE to specify objects - -precious-files-regex REGEX - don't remove output files matching REGEX - -release RELEASE specify package release information - -rpath LIBDIR the created library will eventually be installed in LIBDIR - -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries - -static do not do any dynamic linking of uninstalled libtool libraries - -static-libtool-libs - do not do any dynamic linking of libtool libraries - -version-info CURRENT[:REVISION[:AGE]] - specify library version info [each variable defaults to 0] - -All other options (arguments beginning with \`-') are ignored. - -Every other argument is treated as a filename. Files ending in \`.la' are -treated as uninstalled libtool libraries, other files are standard or library -object files. - -If the OUTPUT-FILE ends in \`.la', then a libtool library is created, -only library objects (\`.lo' files) may be specified, and \`-rpath' is -required, except when creating a convenience library. - -If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created -using \`ar' and \`ranlib', or on Windows using \`lib'. - -If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file -is created, otherwise an executable program is created." - ;; - -uninstall) - $echo \ -"Usage: $modename [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... - -Remove libraries from an installation directory. - -RM is the name of the program to use to delete files associated with each FILE -(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed -to RM. - -If FILE is a libtool library, all the files associated with it are deleted. -Otherwise, only FILE itself is deleted using RM." - ;; - -*) - $echo "$modename: invalid operation mode \`$mode'" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - ;; -esac - -$echo -$echo "Try \`$modename --help' for more information about other modes." - -exit $? - -# The TAGs below are defined such that we never get into a situation -# in which we disable both kinds of libraries. Given conflicting -# choices, we go for a static library, that is the most portable, -# since we can't tell whether shared libraries were disabled because -# the user asked for that or because the platform doesn't support -# them. This is particularly important on AIX, because we don't -# support having both static and shared libraries enabled at the same -# time on that platform, so we default to a shared-only configuration. -# If a disable-shared tag is given, we'll fallback to a static-only -# configuration. But we'll never go from static-only to shared-only. - -# ### BEGIN LIBTOOL TAG CONFIG: disable-shared -disable_libs=shared -# ### END LIBTOOL TAG CONFIG: disable-shared - -# ### BEGIN LIBTOOL TAG CONFIG: disable-static -disable_libs=static -# ### END LIBTOOL TAG CONFIG: disable-static - -# Local Variables: -# mode:shell-script -# sh-indentation:2 -# End: diff --git a/api/libsangoma/.svn/text-base/sources.svn-base b/api/libsangoma/.svn/text-base/sources.svn-base index 6aa4111..d89d9b4 100644 --- a/api/libsangoma/.svn/text-base/sources.svn-base +++ b/api/libsangoma/.svn/text-base/sources.svn-base @@ -18,13 +18,15 @@ INCLUDES=$(SDK_INC_PATH);\ $(SANG_WP_DEVEL)\wanpipe_common\include;\ $(SANG_WP_DEVEL)\wanpipe_common\include\aft_core;\ $(SANG_WP_DEVEL)\wanpipe_windows\include;\ -$(SANG_WP_DEVEL)\libsangoma +$(SANG_WP_DEVEL)\libsangoma;\ +$(SANG_WP_DEVEL)\wanpipe_common\wantools\wanec_apilib TARGETLIBS=$(SDK_LIB_PATH)\kernel32.lib \ $(SDK_LIB_PATH)\user32.lib \ $(SDK_LIB_PATH)\SetupApi.lib \ -$(SDK_LIB_PATH)\Advapi32.lib +$(SDK_LIB_PATH)\Advapi32.lib \ +$(SANG_WP_DEVEL)\wanpipe_common\wantools\wanec_apilib\$(O)\waneclib.lib -SOURCES=libsangoma.c libsangoma.rc +SOURCES=libsangoma.c libsangoma.rc libsangoma_hwec.c diff --git a/api/libsangoma/.svn/tmp/tempfile.17.tmp b/api/libsangoma/.svn/tmp/tempfile.17.tmp new file mode 100644 index 0000000..6bb1ae7 --- /dev/null +++ b/api/libsangoma/.svn/tmp/tempfile.17.tmp @@ -0,0 +1,3060 @@ +/*******************************************************************************//** + * \file libsangoma.c + * \brief Wanpipe API Code Library for Sangoma AFT T1/E1/Analog/BRI/Serial hardware + * + * Author(s): Nenad Corbic + * David Rokhvarg + * Michael Jerris + * Anthony Minessale II + * + * Copyright: (c) 2005-2008 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 Sangoma Technologies 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 Sangoma Technologies ``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 Sangoma Technologies 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 + ******************************************************************************* + */ + +#include "libsangoma-pvt.h" + +#if defined(__WINDOWS__) +# include /* SetupDiXXX() functions */ +# include /* GUID instantination */ +# include /* DEFINE_GUID() */ +# include "public.h" /* GUID_DEVCLASS_SANGOMA_ADAPTER */ + +# define MAX_COMP_INSTID 2096 +# define MAX_COMP_DESC 2096 +# define MAX_FRIENDLY 2096 +# define TMP_BUFFER_LEN 256 + +/*!+! jpboily used to tell get_out_flags no objects were signaled */ +# define INVALID_INDEX (uint32_t) -1 +# define WP_ALL_BITS_SET ((uint32_t)-1) +#endif + +static void libsng_dbg(const char * fmt, ...) +{ + va_list args; + char buf[1024]; + va_start(args, fmt); + _vsnprintf(buf, sizeof(buf), fmt, args); +#if defined(__WINDOWS__) + OutputDebugString(buf); +#else + printf(buf); +#endif + va_end(args); +} + +/*********************************************************************//** + * WINDOWS Only Section + *************************************************************************/ + +#define DBG_POLL if(0)libsng_dbg +#define DBG_EVNT if(0)libsng_dbg +#define DBG_ERR if(0)libsng_dbg("Error: %s() line: %d : ", __FUNCTION__, __LINE__);if(0)libsng_dbg +#define DBG_INIT if(0)libsng_dbg + +#if defined(__WINDOWS__) +#define DBG_REGISTRY if(0)libsng_dbg + +/* + \fn static void DecodeLastError(LPSTR lpszFunction) + \brief Decodes the Error in radable format. + \param lpszFunction error string + + Private Windows Only Function + */ +static void LibSangomaDecodeLastError(LPSTR lpszFunction) +{ + LPVOID lpMsgBuf; + DWORD dwLastErr = GetLastError(); + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dwLastErr, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ + (LPTSTR) &lpMsgBuf, + 0, + NULL + ); + /* Display the string. */ + DBG_POLL("Last Error in %s(): %s (%d)\n", lpszFunction, lpMsgBuf, dwLastErr); + /* Free the buffer. */ + LocalFree( lpMsgBuf ); +} + +/* + \fn static int handle_device_ioctl_result(int bResult) + \brief Checks result code of ioctl + \param bResult result of ioctl call + + Private Windows Only Function + */ +static u16 handle_device_ioctl_result(int bResult, char *caller_name) +{ + if(bResult == 0){ + /*error*/ + LibSangomaDecodeLastError(caller_name); + return SANG_STATUS_IO_ERROR; + }else{ + return SANG_STATUS_SUCCESS; + } +} + +/* + \fn static int UdpManagementCommand(sng_fd_t fd, wan_udp_hdr_t* wan_udp) + \brief Executes Driver Management Command + \param fd device file descriptor + \param wan_udp managemet cmd structure + + Private Windows Function + */ +static int UdpManagementCommand(sng_fd_t fd, wan_udp_hdr_t* wan_udp) +{ + DWORD ln, bIoResult; + unsigned char id = 0; + + wan_udp->wan_udphdr_request_reply = 0x01; + wan_udp->wan_udphdr_id = id; + wan_udp->wan_udphdr_return_code = WAN_UDP_TIMEOUT_CMD; + + bIoResult = DeviceIoControl( + fd, + IoctlManagementCommand, + (LPVOID)wan_udp, + sizeof(wan_udp_hdr_t), + (LPVOID)wan_udp, + sizeof(wan_udp_hdr_t), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL + ); + + return handle_device_ioctl_result(bIoResult, __FUNCTION__); +} + +/* + \fn static int tdmv_api_ioctl(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) + \brief Executes Driver TDM API Command Wrapper Function + \param fd device file descriptor + \param api_cmd tdm_api managemet cmd structure + + Private Windows Function + */ +static int tdmv_api_ioctl(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) +{ + DWORD ln, bIoResult; + + bIoResult = DeviceIoControl( + fd, + IoctlTdmApiCommand, + (LPVOID)api_cmd, + sizeof(wanpipe_tdm_api_cmd_t), + (LPVOID)api_cmd, + sizeof(wanpipe_tdm_api_cmd_t), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL + ); + + return handle_device_ioctl_result(bIoResult, __FUNCTION__); +} + +/* + \fn static USHORT DoReadCommand(sng_fd_t fd, RX_DATA_STRUCT * pRx) + \brief API READ Function + \param drv device file descriptor + \param pRx receive data structure + + Private Windows Function + This function will NOT block because using IoctlReadCommandNonBlocking. + */ +static USHORT DoReadCommand(sng_fd_t fd, RX_DATA_STRUCT * pRx) +{ + DWORD ln, bIoResult; + + bIoResult = DeviceIoControl( + fd, + IoctlReadCommandNonBlocking, + (LPVOID)NULL,/*NO input buffer!*/ + 0, + (LPVOID)pRx, + sizeof(RX_DATA_STRUCT), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL); + + return handle_device_ioctl_result(bIoResult, __FUNCTION__); +} + +/* + \fn static UCHAR DoWriteCommand(sng_fd_t fd, TX_DATA_STRUCT * pTx) + \brief API Write Function + \param drv device file descriptor + \param pRx receive data structure + + Private Windows Function + In Legacy API mode this fuction will Block if data is busy. + In API mode no function is allowed to Block + */ +static UCHAR DoWriteCommand(sng_fd_t fd, + void *input_data_buffer, u32 size_of_input_data_buffer, + void *output_data_buffer, u32 size_of_output_data_buffer + ) +{ + DWORD BytesReturned, bIoResult; + + bIoResult = DeviceIoControl( + fd, + IoctlWriteCommand, + (LPVOID)input_data_buffer, + size_of_input_data_buffer, + (LPVOID)output_data_buffer, + size_of_output_data_buffer, + (LPDWORD)(&BytesReturned), + (LPOVERLAPPED)NULL); + + return (UCHAR)handle_device_ioctl_result(bIoResult, __FUNCTION__); +} + +/* + \fn static USHORT sangoma_poll_fd(sng_fd_t fd, API_POLL_STRUCT *api_poll_ptr) + \brief Non Blocking API Poll function used to find out if Rx Data, Events or + Free Tx buffer available + \param drv device file descriptor + \param api_poll_ptr poll device that stores polling information read/write/event + \param overlapped pointer to system overlapped io structure. + + Private Windows Function + */ +static USHORT sangoma_poll_fd(sng_fd_t fd, API_POLL_STRUCT *api_poll_ptr) +{ + DWORD ln, bIoResult; + + bIoResult = DeviceIoControl( + fd, + IoctlApiPoll, + (LPVOID)NULL, + 0L, + (LPVOID)api_poll_ptr, + sizeof(API_POLL_STRUCT), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL); + + return handle_device_ioctl_result(bIoResult, __FUNCTION__); +} + +/* + \fn static int logger_api_ioctl(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Executes Logger API IOCTL + \param fd device file descriptor + \param logger_cmd Logger API command structure + + Private Windows Function + */ +static sangoma_status_t logger_api_ioctl(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + DWORD ln, bIoResult; + + bIoResult = DeviceIoControl( + fd, + IoctlLoggerApiCommand, + (LPVOID)logger_cmd, + sizeof(wp_logger_cmd_t), + (LPVOID)logger_cmd, + sizeof(wp_logger_cmd_t), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL ); + + return handle_device_ioctl_result(bIoResult, __FUNCTION__); +} + +/* This function is exported only for debugging purposes and NOT a part of API. */ +sangoma_status_t _SAPI_CALL sangoma_cdev_ctrl_cmd(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) +{ + DWORD ln, bIoResult; + + bIoResult = DeviceIoControl( + fd, + IoctlCdevControlCommand, + (LPVOID)api_cmd, + sizeof(wanpipe_tdm_api_cmd_t), + (LPVOID)api_cmd, + sizeof(wanpipe_tdm_api_cmd_t), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL + ); + + return handle_device_ioctl_result(bIoResult, __FUNCTION__); +} + +static sangoma_status_t init_sangoma_event_object(sangoma_wait_obj_t *sng_wait_obj, int flags_in, sng_fd_t fd) +{ + int event_index = -1; + wanpipe_tdm_api_cmd_t tdm_api_cmd; + sangoma_status_t sng_status; + char event_name[200], tmp_event_name[200]; + + memset(&tdm_api_cmd, 0x00, sizeof(tdm_api_cmd)); + + tdm_api_cmd.cmd = WP_CDEV_CMD_GET_INTERFACE_NAME; + sng_status = sangoma_cdev_ctrl_cmd(fd, &tdm_api_cmd); + if(SANG_ERROR(sng_status)){ + DBG_ERR("sangoma_cdev_ctrl_cmd() failed!\n"); + return sng_status; + } + + DBG_EVNT("%s(): interface name: %s\n", __FUNCTION__, tdm_api_cmd.data); + + if(flags_in & POLLIN){ + event_index = LIBSNG_EVENT_INDEX_POLLIN; + /* Form the Event Name from Interface Name and Event Type (i.e. wanpipe1_if1_pollin). */ + _snprintf(tmp_event_name, sizeof(tmp_event_name), "%s_pollin", tdm_api_cmd.data); + } + + if(flags_in & POLLOUT){ + event_index = LIBSNG_EVENT_INDEX_POLLOUT; + /* Form the Event Name from Interface Name and Event Type (i.e. wanpipe1_if1_pollout). */ + _snprintf(tmp_event_name, sizeof(tmp_event_name), "%s_pollout", tdm_api_cmd.data); + } + + if(flags_in & POLLPRI){ + event_index = LIBSNG_EVENT_INDEX_POLLPRI; + /* Form the Event Name from Interface Name and Event Type (i.e. wanpipe1_if1_pollpri). */ + _snprintf(tmp_event_name, sizeof(tmp_event_name), "%s_pollpri", tdm_api_cmd.data); + } + + if(event_index == -1){ + /* invalid 'flags_in', this should be an assert */ + return SANG_STATUS_GENERAL_ERROR; + } + + /* 1. The Sangoma Device Driver creates Notification Events in "\\BaseNamedObjects\\Global\\" + * when first CreateFile() was called by a process. That means the Events will inherit + * the security attributes of the calling process, so the calling process will have + * the permissions to open the Events by calling OpenEvent(). Since Events are created + * "Global" subdirectory, the calling process does NOT need Administrator priveleges. + * 2. The Events are deleted when the last HANDLE for a device is closed by CloseHandle() + * or automatically by the system when calling process exits. */ + _snprintf(event_name, sizeof(event_name), "Global\\%s", tmp_event_name); + + sng_wait_obj->sng_event_objects[event_index] = OpenEvent(EVENT_ALL_ACCESS, TRUE, event_name); + if(NULL == sng_wait_obj->sng_event_objects[event_index]){ + /* error */ + LibSangomaDecodeLastError(__FUNCTION__); + return SANG_STATUS_GENERAL_ERROR; + } + + return SANG_STATUS_SUCCESS; +} + +static sangoma_status_t _SAPI_CALL sangoma_wait_obj_poll(sangoma_wait_obj_t *sangoma_wait_object, int flags_in, int *flags_out) +{ + int err; + sangoma_wait_obj_t *sng_wait_obj = sangoma_wait_object; + /*! api structure used by windows IoctlApiPoll call */ + API_POLL_STRUCT api_poll; + + *flags_out = 0; + + memset(&api_poll, 0x00, sizeof(API_POLL_STRUCT)); + api_poll.user_flags_bitmap = flags_in; + + /* This call is non-blocking - it will return immediatly. */ + if(sangoma_poll_fd(sng_wait_obj->fd, &api_poll)){ + /* error - ioctl failed */ + return SANG_STATUS_IO_ERROR; + } + + if(api_poll.operation_status == SANG_STATUS_SUCCESS){ + *flags_out = api_poll.poll_events_bitmap; + err = 0; + }else{ + /* error - command failed */ + err = api_poll.operation_status; + } + + if(*flags_out == 0){ + /*DBG_POLL("======%s(): *flags_out: 0x%X, flags_in: 0x%X\n", __FUNCTION__, *flags_out, flags_in);*/ + } + return err; +} + +static int check_number_of_wait_objects(uint32_t number_of_objects, const char *caller_function, int lineno) +{ + if(number_of_objects >= MAXIMUM_WAIT_OBJECTS){ + DBG_ERR("Caller: %s(): Line: %d: 'number_of_objects': %d is greater than the Maximum of: %d\n", + caller_function, lineno, number_of_objects, MAXIMUM_WAIT_OBJECTS); + return 1; + } + return 0; +} + +static sangoma_status_t get_out_flags(IN sangoma_wait_obj_t *sng_wait_objects[], + IN uint32_t first_signaled_obj_index, + IN uint32_t in_flags[], OUT uint32_t out_flags[], + IN uint32_t number_of_sangoma_wait_objects, + OUT BOOL *at_least_one_poll_set_flags_out) +{ + uint32_t i, j; + + if(at_least_one_poll_set_flags_out){ + *at_least_one_poll_set_flags_out = FALSE; + } + + for(i = 0; i < number_of_sangoma_wait_objects; i++) { + + sangoma_wait_obj_t *sangoma_wait_object = sng_wait_objects[i]; + + if (!SANGOMA_OBJ_HAS_DEVICE(sangoma_wait_object)) { + + /* This object does not has a device, but, may have been signaled via sangoma_wait_obj_signal() + * test if the object is signaled, if it is, set SANG_WAIT_OBJ_IS_SIGNALED */ + + if((i == first_signaled_obj_index) || (WaitForSingleObject(sangoma_wait_object->generic_event_object, 0) == WAIT_OBJECT_0)) { + /* !+! jpboily : Since WaitForMultipleObjects cleared the status + * of the first signaled object, we make sure that the out_flag + * for this object is set */ + out_flags[i] |= SANG_WAIT_OBJ_IS_SIGNALED; + } + continue; + } + + for(j = 0; j < LIBSNG_NUMBER_OF_EVENT_OBJECTS; j++){ + + if(!sangoma_wait_object->sng_event_objects[j]) { + continue; + } + + if(sangoma_wait_obj_poll(sangoma_wait_object, in_flags[i], &out_flags[i])){ + return SANG_STATUS_GENERAL_ERROR; + } + + if( out_flags[i] & in_flags[i] ){ + if(at_least_one_poll_set_flags_out){ + *at_least_one_poll_set_flags_out = TRUE; + } + } +#if 0 + /* Check if a device-related object was signalled, if it is, set SANG_WAIT_OBJ_IS_SIGNALED. */ + if (WaitForSingleObject(sangoma_wait_object->sng_event_objects[j], 0) == WAIT_OBJECT_0) { + out_flags[i] |= SANG_WAIT_OBJ_IS_SIGNALED; + } +#endif + }/* for(j = 0; j < LIBSNG_NUMBER_OF_EVENT_OBJECTS; j++) */ + }/* for(i = 0; i < number_of_sangoma_wait_objects; i++) */ + + return SANG_STATUS_SUCCESS; +} + +/*! + \brief Brief description + */ +static LONG registry_get_string_value(HKEY hkey, LPTSTR szKeyname, OUT LPSTR szValue, OUT DWORD *pdwSize) +{ + LONG iReturnCode; + + /* reading twice to set pdwSize to needed value */ + RegQueryValueEx(hkey, szKeyname, NULL, NULL, (unsigned char *)szValue, pdwSize); + + iReturnCode = RegQueryValueEx(hkey, szKeyname, NULL, NULL, (unsigned char *)szValue, pdwSize); + if(ERROR_SUCCESS == iReturnCode){ + iReturnCode = 0; + } + DBG_REGISTRY("%s(): %s: %s: iReturnCode: %d\n", __FUNCTION__, szKeyname, szValue, iReturnCode); + return iReturnCode; +} + +/*! + \brief Brief description + */ +static LONG registry_set_string_value(HKEY hkey, LPTSTR szKeyname, IN LPSTR szValue) +{ + DWORD dwSize; + LONG iReturnCode; + + dwSize = strlen(szValue) + 1; + + iReturnCode = RegSetValueEx(hkey, szKeyname, 0, REG_SZ, (unsigned char *)szValue, dwSize); + DBG_REGISTRY("%s(): %s: %s\n", __FUNCTION__, szKeyname, szValue); + + if(ERROR_SUCCESS == iReturnCode){ + iReturnCode = 0; + } + return iReturnCode; +} + +/*! + \brief Convert an integer (iValue) to string and write it to registry + */ +static LONG registry_set_integer_value(HKEY hkey, LPTSTR szKeyname, IN int iValue) +{ + DWORD dwSize; + char szTemp[TMP_BUFFER_LEN]; + LONG iReturnCode; + + sprintf(szTemp, "%u", iValue); + + dwSize = strlen(szTemp) + 1; + iReturnCode = RegSetValueEx(hkey, szKeyname, 0, REG_SZ, (unsigned char *)szTemp, dwSize); + + if(ERROR_SUCCESS == iReturnCode){ + iReturnCode = 0; + } + + DBG_REGISTRY("%s(): %s: %d: iReturnCode: %d\n", __FUNCTION__, szKeyname, iValue, iReturnCode); + return iReturnCode; +} + +/*! + * \brief Go through the list of ALL "Sangoma Hardware Abstraction Driver" ports installed on the system. + * Read Bus/Slot/Port information for a port and copare with what is searched for. + */ +static HKEY registry_open_port_key(hardware_info_t *hardware_info) +{ + int i, iRegistryReturnCode; + SP_DEVINFO_DATA deid={sizeof(SP_DEVINFO_DATA)}; + HDEVINFO hdi = SetupDiGetClassDevs((struct _GUID *)&GUID_DEVCLASS_SANGOMA_ADAPTER, NULL,NULL, DIGCF_PRESENT); + char DeviceName[TMP_BUFFER_LEN], PCI_Bus[TMP_BUFFER_LEN], PCI_Slot[TMP_BUFFER_LEN], Port_Number[TMP_BUFFER_LEN]; + DWORD dwTemp; + HKEY hKeyTmp = (struct HKEY__ *)INVALID_HANDLE_VALUE; + HKEY hPortRegistryKey = (struct HKEY__ *)INVALID_HANDLE_VALUE; + + TCHAR name[MAX_FRIENDLY]; + char szCompInstanceId[MAX_COMP_INSTID]; + TCHAR szCompDescription[MAX_COMP_DESC]; + DWORD dwRegType; + + /* Possible Port Names (refer to sdladrv.inf): + Sangoma Hardware Abstraction Driver (Port 1) + Sangoma Hardware Abstraction Driver (Port 2) + Sangoma Hardware Abstraction Driver (Port 3) + Sangoma Hardware Abstraction Driver (Port 4) + Sangoma Hardware Abstraction Driver (Port 5) + Sangoma Hardware Abstraction Driver (Port 6) + Sangoma Hardware Abstraction Driver (Port 7) + Sangoma Hardware Abstraction Driver (Port 8) + Sangoma Hardware Abstraction Driver (Analog) + Sangoma Hardware Abstraction Driver (ISDN BRI) */ + + /* search for all (AFT) ports: */ + sprintf(name," Sangoma Hardware Abstraction Driver"); + + for (i = 0; SetupDiEnumDeviceInfo(hdi, i, &deid); i++){ + + BOOL fSuccess = SetupDiGetDeviceInstanceId(hdi, &deid, (PSTR)szCompInstanceId, MAX_COMP_INSTID, NULL); + if (!fSuccess){ + continue; + } + + fSuccess = SetupDiGetDeviceRegistryProperty(hdi, &deid,SPDRP_DEVICEDESC, &dwRegType, (BYTE*)szCompDescription, MAX_COMP_DESC, NULL); + if (!fSuccess){ + continue; + } + + if (strncmp(szCompDescription, name, strlen(name)) != 0) { /* Windows can add #2 etc - do NOT consider */ + /* This is a "Sangoma Card" device, we are interested only in "Sangoma Port" devices. */ + continue; + } + + DBG_REGISTRY("* %s\n", szCompDescription); + + hKeyTmp = SetupDiOpenDevRegKey(hdi, &deid, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ | KEY_SET_VALUE ); + if(hKeyTmp == INVALID_HANDLE_VALUE){ + DBG_REGISTRY("Error: Failed to open Registry key!!\n"); + continue; + } + + PCI_Bus[0] = '\0'; + iRegistryReturnCode = registry_get_string_value(hKeyTmp, "PCI_Bus", PCI_Bus, &dwTemp); + if(iRegistryReturnCode){ + continue; + } + + PCI_Slot[0] = '\0'; + iRegistryReturnCode = registry_get_string_value(hKeyTmp, "PCI_Slot", PCI_Slot, &dwTemp); + if(iRegistryReturnCode){ + continue; + } + + Port_Number[0] = '\0'; + iRegistryReturnCode = registry_get_string_value(hKeyTmp, "Port_Number", Port_Number, &dwTemp); + if(iRegistryReturnCode){ + continue; + } + + if( atoi(PCI_Bus) == hardware_info->pci_bus_number && + atoi(PCI_Slot) == hardware_info->pci_slot_number && + atoi(Port_Number) == hardware_info->port_number ){ + + hPortRegistryKey = hKeyTmp; + + /* read device name for debugging only */ + DeviceName[0] = '\0'; + registry_get_string_value(hPortRegistryKey, "DeviceName", DeviceName, &dwTemp); + + DBG_REGISTRY("Found Port's Registry key: DeviceName: %s, PCI_Bus: %s, PCI_Slot: %s, Port_Number: %s\n", + DeviceName, PCI_Bus, PCI_Slot, Port_Number); + break; + }/* if() */ + }/* for() */ + + SetupDiDestroyDeviceInfoList(hdi); + + DBG_REGISTRY("hPortRegistryKey: 0x%X\n", hPortRegistryKey); + + return hPortRegistryKey; +} + +static char* timeslot_bitmap_to_string(int bitmap) +{ + int i = 0, range_counter = 0; + int start_channel = -1, stop_channel = -1; + static char tmp_string[256]; + + if( WP_ALL_BITS_SET == bitmap ){ + return "ALL"; /* If all bits are set, use the "ALL" keyword. */ + } + + tmp_string[0] = '\0'; + + /* all ranges between two zeros is what we will look for */ + for(i = 0; i < sizeof(bitmap) * 8; i++){ + + if(start_channel < 0){ + /* found a starting one of a range */ + if(bitmap & (1 << i)){ + start_channel = i + 1; + } + } + + if(start_channel >= 0){ + if((bitmap & (1 << i)) == 0){ + + /* we hit a zero, that means the previous channel is one */ + stop_channel = i; + + }else if(i == (sizeof(bitmap) * 8 - 1)){ + /* The most significant bit is set - there will be no delimiting zero. + * It will also take care of "all channels" which is a special + * case - there is a start but no stop channel because all bits + * are set. result will be 1-32 */ + stop_channel = (sizeof(bitmap) * 8); + } + } + + if(start_channel >= 0 && stop_channel >= 0){ + + if(range_counter){ + /* put '.' separator from the previous range */ + _snprintf(&tmp_string[strlen(tmp_string)], sizeof(tmp_string) - strlen(tmp_string), "."); + } + + if(start_channel == stop_channel){ + /* the range contains a SINGLE channel */ + _snprintf(&tmp_string[strlen(tmp_string)], sizeof(tmp_string) - strlen(tmp_string), + "%d", start_channel); + }else{ + /* the range contains MULTIPLE channels */ + _snprintf(&tmp_string[strlen(tmp_string)], sizeof(tmp_string) - strlen(tmp_string), + "%d-%d", start_channel, stop_channel); + } + + start_channel = stop_channel = -1; + range_counter++; + } + }/* for() */ + + return tmp_string; +} + +static int registry_write_front_end_cfg(HKEY hPortRegistryKey, port_cfg_t *port_cfg) +{ + wandev_conf_t *wandev_conf = &port_cfg->wandev_conf; + sdla_fe_cfg_t *sdla_fe_cfg = &wandev_conf->fe_cfg; + sdla_te_cfg_t *te_cfg = &sdla_fe_cfg->cfg.te_cfg; + sdla_remora_cfg_t *analog_cfg = &sdla_fe_cfg->cfg.remora; + sdla_bri_cfg_t *bri_cfg = &sdla_fe_cfg->cfg.bri; + int iReturnCode = 0; + + /* set Media specific values. */ + switch(sdla_fe_cfg->media) + { + case WAN_MEDIA_T1: + case WAN_MEDIA_J1: /* the same as T1 */ + case WAN_MEDIA_E1: + /* only T1/E1 Port can change the Media, all other ports ignore this parameter. */ + iReturnCode = registry_set_integer_value(hPortRegistryKey, "Media", sdla_fe_cfg->media /*WAN_MEDIA_T1*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "LDecoding", sdla_fe_cfg->lcode /*WAN_LCODE_B8ZS*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "Framing", sdla_fe_cfg->frame /*WAN_FR_ESF*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "ClkRefPort", te_cfg->te_ref_clock); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "TE_IGNORE_YEL", te_cfg->ignore_yel_alarm); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "ACTIVE_CH", ENABLE_ALL_CHANNELS /*must be hardcoded*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "TE_RBS_CH", te_cfg->te_rbs_ch); /*not used by DS chip code, only by PMC */ + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "LBO", te_cfg->lbo /*WAN_T1_LBO_0_DB*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "ClkMode", te_cfg->te_clock /*WAN_NORMAL_CLK*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "HighImpedanceMode",te_cfg->high_impedance_mode /*WANOPT_NO*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "TE_RX_SLEVEL", te_cfg->rx_slevel /*WAN_TE1_RX_SLEVEL_12_DB*/); + if(iReturnCode){ + break; + } + + /* write E1 signalling for both T1 and E1 */ + iReturnCode = registry_set_integer_value(hPortRegistryKey, "E1Signalling", te_cfg->sig_mode /*WAN_TE1_SIG_CCS*/); + if(iReturnCode){ + break; + } + break; + + case WAN_MEDIA_56K: + /* do nothing */ + iReturnCode = 0; + break; + + case WAN_MEDIA_FXOFXS: + /* Analog global (per-card) settings */ + iReturnCode = registry_set_string_value(hPortRegistryKey, "remora_fxo_operation_mode_name", analog_cfg->opermode_name); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_BATTTHRESH", analog_cfg->battthresh); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_BATTDEBOUNCE", analog_cfg->battdebounce); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_FXO_TAPPING", analog_cfg->rm_mode); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_FXO_TAPPING_OFF_HOOK_THRESHOLD",analog_cfg->ohthresh); + if(iReturnCode){ + break; + } + break; + + case WAN_MEDIA_BRI: + iReturnCode = registry_set_integer_value(hPortRegistryKey, "aft_bri_clock_mode", bri_cfg->clock_mode); + if(iReturnCode){ + break; + } + break; + + case WAN_MEDIA_SERIAL: + iReturnCode = registry_set_integer_value(hPortRegistryKey, "clock_source", wandev_conf->clocking); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "BaudRate", wandev_conf->bps); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "serial_connection_type", wandev_conf->connection); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "serial_line_coding", wandev_conf->line_coding); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "serial_line_idle", wandev_conf->line_idle); + if(iReturnCode){ + break; + } + break; + + default: + DBG_ERR("Invalid Media Type %d!\n", sdla_fe_cfg->media); + iReturnCode = 1; + } + + return iReturnCode; +} + +static int registry_write_wan_tdmv_conf(HKEY hPortRegistryKey, port_cfg_t *port_cfg) +{ + wandev_conf_t *wandev_conf = &port_cfg->wandev_conf; + sdla_fe_cfg_t *sdla_fe_cfg = &wandev_conf->fe_cfg; + wan_tdmv_conf_t *tdmv_conf = &wandev_conf->tdmv_conf; + int iReturnCode = 1; + + /* set Media specific values. */ + switch(sdla_fe_cfg->media) + { + case WAN_MEDIA_T1: + case WAN_MEDIA_J1: /* the same as T1 */ + case WAN_MEDIA_E1: + /* write dchannel bitmap as a string (the same way as active channels) */ + iReturnCode = registry_set_string_value(hPortRegistryKey, "TDMV_DCHAN", timeslot_bitmap_to_string(tdmv_conf->dchan)); + break; + + case WAN_MEDIA_56K: + case WAN_MEDIA_FXOFXS: + case WAN_MEDIA_BRI: + case WAN_MEDIA_SERIAL: + /* do nothing */ + iReturnCode = 0; + break; + + default: + DBG_ERR("Invalid Media Type %d!\n", sdla_fe_cfg->media); + iReturnCode = 2; + break; + } + + return iReturnCode; +} + +static int registry_write_channel_group_cfg(HKEY hPortRegistryKey, port_cfg_t *port_cfg, int interface_index, wanif_conf_t wanif_conf) +{ + char szTemp[TMP_BUFFER_LEN]; + int iReturnCode = 0; + + do{ + // Line mode + _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_line_mode", interface_index); + iReturnCode = registry_set_string_value(hPortRegistryKey, szTemp, + (wanif_conf.hdlc_streaming == WANOPT_YES ? MODE_OPTION_HDLC : MODE_OPTION_BITSTRM)); + if(iReturnCode){ + break; + } + + // MTU + _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_mtu", interface_index); + iReturnCode = registry_set_integer_value(hPortRegistryKey, szTemp, wanif_conf.mtu); + if(iReturnCode){ + break; + } + + // Operational mode + _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_operational_mode", interface_index); + iReturnCode = registry_set_string_value(hPortRegistryKey, szTemp, wanif_conf.usedby); + if(iReturnCode){ + break; + } + + // Active Timeslots for the Group + _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_active_ch", interface_index); + iReturnCode = registry_set_string_value(hPortRegistryKey, szTemp, timeslot_bitmap_to_string(wanif_conf.active_ch)); + if(iReturnCode){ + break; + } + + // Idle char. + _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_idle_char", interface_index); + iReturnCode = registry_set_integer_value(hPortRegistryKey, szTemp, wanif_conf.u.aft.idle_flag); + if(iReturnCode){ + break; + } + + }while(0); + + return iReturnCode; +} + +#endif /* __WINDOWS__ */ + + +/*********************************************************************//** + * Common Linux & Windows Code + *************************************************************************/ + + + +/************************************************************//** + * Device POLL Functions + ***************************************************************/ + +/*! + \fn sangoma_status_t sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma_wait_object, sng_fd_t fd, sangoma_wait_obj_type_t object_type) + \brief Create a wait object that will be used with sangoma_waitfor_many() API + \param sangoma_wait_object pointer a single device object + \param fd device file descriptor + \param object_type type of the wait object. see sangoma_wait_obj_type_t for types + \see sangoma_wait_obj_type_t + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _SAPI_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma_wait_object, sng_fd_t fd, sangoma_wait_obj_type_t object_type) +{ + int err = 0; + sangoma_wait_obj_t *sng_wait_obj = NULL; + + if (!sangoma_wait_object) { + return SANG_STATUS_INVALID_DEVICE; + } + *sangoma_wait_object = NULL; + sng_wait_obj = malloc(sizeof(**sangoma_wait_object)); + if (!sng_wait_obj) { + return SANG_STATUS_FAILED_ALLOCATE_MEMORY; + } + + memset(sng_wait_obj, 0x00, sizeof(*sng_wait_obj)); + /* it is a first initialization of the object */ + sng_wait_obj->init_flag = LIBSNG_MAGIC_NO; + + sng_wait_obj->fd = fd; + sng_wait_obj->object_type = object_type; + +#if defined(__WINDOWS__) + DBG_INIT("%s(): sng_wait_obj ptr: 0x%p\n", __FUNCTION__, sng_wait_obj); + DBG_INIT("%s(): fd: 0x%X, object_type: %s\n", __FUNCTION__, fd, DECODE_SANGOMA_WAIT_OBJECT_TYPE(object_type)); + DBG_INIT("%s(): sizeof(**sangoma_wait_object): %d\n", __FUNCTION__, sizeof(**sangoma_wait_object)); + + if (SANGOMA_OBJ_IS_SIGNALABLE(sng_wait_obj)) { + sng_wait_obj->generic_event_object = CreateEvent( NULL, FALSE, FALSE, NULL); + if(!sng_wait_obj->generic_event_object){ + err = SANG_STATUS_GENERAL_ERROR; + goto failed; + } + } + + if(SANGOMA_GENERIC_WAIT_OBJ == object_type){ + /* everything is done for the generic wait object */ + *sangoma_wait_object = sng_wait_obj; + return SANG_STATUS_SUCCESS; + } + + err = init_sangoma_event_object(sng_wait_obj, POLLIN, fd); + if(SANG_STATUS_SUCCESS != err){ + goto failed; + } + + err = init_sangoma_event_object(sng_wait_obj, POLLOUT, fd); + if(SANG_STATUS_SUCCESS != err){ + goto failed; + } + + err = init_sangoma_event_object(sng_wait_obj, POLLPRI, fd); + if(SANG_STATUS_SUCCESS != err) { + goto failed; + } + + DBG_INIT("%s(): returning: %d", __FUNCTION__, err); +#else + int filedes[2]; + if (SANGOMA_OBJ_IS_SIGNALABLE(sng_wait_obj)) { + sng_wait_obj->signal_read_fd = INVALID_HANDLE_VALUE; + sng_wait_obj->signal_write_fd = INVALID_HANDLE_VALUE; + /* if we want cross-process event notification we can use a named pipe with mkfifo() */ + if (pipe(filedes)) { + err = SANG_STATUS_GENERAL_ERROR; + goto failed; + } + sng_wait_obj->signal_read_fd = filedes[0]; + sng_wait_obj->signal_write_fd = filedes[1]; + } +#endif + *sangoma_wait_object = sng_wait_obj; + return err; + +failed: + if (sng_wait_obj) { + sangoma_wait_obj_delete(&sng_wait_obj); + } + return err; +} + +/*! + \fn sangoma_status_t sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object) + \brief De-allocate all resources in a wait object + \param sangoma_wait_object pointer to a pointer to a single device object + \return SANG_STATUS_SUCCESS on success or some sangoma status error +*/ +sangoma_status_t _SAPI_CALL sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object) +{ + sangoma_wait_obj_t *sng_wait_obj = *sangoma_wait_object; +#if defined(__WINDOWS__) + int index = 0; +#endif + + if(sng_wait_obj->init_flag != LIBSNG_MAGIC_NO){ + /* error. object was not initialized by sangoma_wait_obj_init() */ + return SANG_STATUS_INVALID_DEVICE; + } + +#if defined(__WINDOWS__) + if (SANGOMA_OBJ_IS_SIGNALABLE(sng_wait_obj)) { + sangoma_close(&sng_wait_obj->generic_event_object); + } + if (SANGOMA_OBJ_HAS_DEVICE(sng_wait_obj)) { + for(index = 0; index < LIBSNG_NUMBER_OF_EVENT_OBJECTS; index++){ + sangoma_close(&sng_wait_obj->sng_event_objects[index]); + } + } +#else + if (SANGOMA_OBJ_IS_SIGNALABLE(sng_wait_obj)) { + sangoma_close(&sng_wait_obj->signal_read_fd); + sangoma_close(&sng_wait_obj->signal_write_fd); + } +#endif + sng_wait_obj->init_flag = 0; + sng_wait_obj->object_type = UNKNOWN_WAIT_OBJ; + free(sng_wait_obj); + *sangoma_wait_object = NULL; + return SANG_STATUS_SUCCESS; +} + +/*! + \fn void sangoma_wait_obj_signal(void *sangoma_wait_object) + \brief Set wait object to a signaled state + \param sangoma_wait_object pointer a single device object + \return 0 on success, non-zero on error +*/ +int _SAPI_CALL sangoma_wait_obj_signal(sangoma_wait_obj_t *sng_wait_obj) +{ + if (!SANGOMA_OBJ_IS_SIGNALABLE(sng_wait_obj)) { + /* even when Windows objects are always signalable for the sake of providing + * a consistent interface to the user we downgrade the capabilities of Windows + * objects unless the sangoma wait object is explicitly initialized as signalable + * */ + return SANG_STATUS_INVALID_DEVICE; + } +#if defined(__WINDOWS__) + if(sng_wait_obj->generic_event_object){ + if (!SetEvent(sng_wait_obj->generic_event_object)) { + return SANG_STATUS_GENERAL_ERROR; + } + } +#else + /* at this point we know is a signalable object and has a signal_write_fd */ + if (write(sng_wait_obj->signal_write_fd, "s", 1) < 1) { + return SANG_STATUS_GENERAL_ERROR; + } +#endif + return SANG_STATUS_SUCCESS; +} + +/*! + \fn sng_fd_t sangoma_wait_obj_get_fd(void *sangoma_wait_object) + \brief Retrieve fd device file descriptor which was the 'fd' parameter for sangoma_wait_obj_init() + \param sangoma_wait_object pointer a single device object + \return fd - device file descriptor +*/ +sng_fd_t _SAPI_CALL sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sng_wait_obj) +{ + return sng_wait_obj->fd; +} + +/*! + \fn void sangoma_wait_obj_set_context(sangoma_wait_obj_t *sangoma_wait_object) + \brief Store the given context into provided sangoma wait object. + \brief This function is useful to associate a context with a sangoma wait object. + \param sangoma_wait_object pointer a single device object + \param context void pointer to user context + \return void +*/ +void _SAPI_CALL sangoma_wait_obj_set_context(sangoma_wait_obj_t *sng_wait_obj, void *context) +{ + sng_wait_obj->context = context; +} + +/*! + \fn void *sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object) + \brief Retrieve the user context (if any) that was set via sangoma_wait_obj_set_context. + \param sangoma_wait_object pointer a single device object + \return void* +*/ +void* _SAPI_CALL sangoma_wait_obj_get_context(sangoma_wait_obj_t *sng_wait_obj) +{ + return sng_wait_obj->context; +} + +/*! + \fn sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], int32_t in_flags[], int32_t out_flags[] uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout); + \brief Wait for multiple sangoma waitable objects + \param sangoma_wait_objects pointer to array of wait objects previously created with sangoma_wait_obj_create + \param in_flags array of flags corresponding to the flags the user is interested on for each waitable object + \param out_flags array of flags corresponding to the flags that are ready in the waitable objects + \param number_of_sangoma_wait_objects size of the array of waitable objects and flags + \param system_wait_timeout timeout in miliseconds in case of no event + \return negative: SANG_STATUS_APIPOLL_TIMEOUT: timeout, SANG_STATUS_SUCCESS: event occured check in sangoma_wait_objects, any other return code is some error +*/ +sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sng_wait_objects[], uint32_t in_flags[], uint32_t out_flags[], + uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout) +{ +#if defined(__WINDOWS__) + HANDLE hEvents[MAXIMUM_WAIT_OBJECTS]; + uint32_t uiMapEventIndexToWaitObjectIndex[MAXIMUM_WAIT_OBJECTS]; + DWORD dwResult; + int at_least_one_poll_set_flags_out, number_of_internal_signaling_objects, err; +#endif + uint32_t i = 0, j = 0; + + memset(out_flags, 0x00, number_of_sangoma_wait_objects * sizeof(out_flags[0])); +#if defined(__WINDOWS__) + /* This loop will calculate 'number_of_internal_signaling_objects' and will initialize 'hEvents' + * based on 'number_of_sangoma_wait_objects' and 'in_flags'. */ + number_of_internal_signaling_objects = 0; + for(i = 0; i < number_of_sangoma_wait_objects; i++){ + sangoma_wait_obj_t *sangoma_wait_object = sng_wait_objects[i]; + + /* if SANGOMA_OBJ_IS_SIGNALABLE add the generic_event_object.hEvent to the hEvents */ + if(sangoma_wait_object->generic_event_object){ + if(check_number_of_wait_objects(number_of_internal_signaling_objects, __FUNCTION__, __LINE__)){ + return SANG_STATUS_NO_FREE_BUFFERS; + } + hEvents[number_of_internal_signaling_objects] = sangoma_wait_object->generic_event_object; + uiMapEventIndexToWaitObjectIndex[number_of_internal_signaling_objects] = i; + number_of_internal_signaling_objects++; + } + + for(j = 0; j < LIBSNG_NUMBER_OF_EVENT_OBJECTS; j++){ + if(sangoma_wait_object->sng_event_objects[j]){ + if( ((j == LIBSNG_EVENT_INDEX_POLLIN) && (in_flags[i] & POLLIN)) || + ((j == LIBSNG_EVENT_INDEX_POLLOUT) && (in_flags[i] & POLLOUT)) || + ((j == LIBSNG_EVENT_INDEX_POLLPRI) && (in_flags[i] & POLLPRI)) ){ + + if(check_number_of_wait_objects(number_of_internal_signaling_objects, __FUNCTION__, __LINE__)){ + return SANG_STATUS_NO_FREE_BUFFERS; + } + hEvents[number_of_internal_signaling_objects] = sangoma_wait_object->sng_event_objects[j]; + uiMapEventIndexToWaitObjectIndex[number_of_internal_signaling_objects] = i; + number_of_internal_signaling_objects++; + } + }/* if () */ + }/* for() */ + }/* for() */ + + if(number_of_internal_signaling_objects < 1){ + DBG_ERR("'number_of_internal_signaling_objects': %d is less than the Minimum of: 1!\n", + number_of_internal_signaling_objects); + /* error - most likely the user did not initialize sng_wait_objects[] */ + return SANG_STATUS_INVALID_PARAMETER; + } + + at_least_one_poll_set_flags_out = FALSE; + + /* It is important to get 'out flags' BEFORE the WaitForMultipleObjects() + * because it allows to keep API driver's transmit queue full. */ + err = get_out_flags(sng_wait_objects, INVALID_INDEX, in_flags, out_flags, number_of_sangoma_wait_objects, &at_least_one_poll_set_flags_out); + if(SANG_ERROR(err)){ + return err; + } + + if(TRUE == at_least_one_poll_set_flags_out){ + return SANG_STATUS_SUCCESS; + } + + /* wait untill at least one of the events is signaled OR a 'system_wait_timeout' occured */ + dwResult = WaitForMultipleObjects(number_of_internal_signaling_objects, &hEvents[0], FALSE, system_wait_timeout); + if (WAIT_TIMEOUT == dwResult){ + return SANG_STATUS_APIPOLL_TIMEOUT; + } + + if( dwResult >= (DWORD)number_of_internal_signaling_objects ) { + return SANG_STATUS_GENERAL_ERROR; + } + + /* WaitForMultipleObjects() was waken by a Sangoma or by a non-Sangoma wait object. */ + err = get_out_flags(sng_wait_objects, + uiMapEventIndexToWaitObjectIndex[dwResult],/* This is the index of first signaled object in + * the original sng_wait_objects[] array, not in hEvents[] array. */ + in_flags, out_flags, number_of_sangoma_wait_objects, NULL); + if(SANG_ERROR(err)){ + return err; + } + + return SANG_STATUS_SUCCESS; +#else + struct pollfd pfds[number_of_sangoma_wait_objects*2]; /* we need twice as many polls because of the sangoma signalable objects */ + char dummy_buf[1]; + int res; + j = 0; + + memset(pfds, 0, sizeof(pfds)); + + for(i = 0; i < number_of_sangoma_wait_objects; i++){ + + if (SANGOMA_OBJ_HAS_DEVICE(sng_wait_objects[i])) { + pfds[i].fd = sng_wait_objects[i]->fd; + pfds[i].events = in_flags[i]; + } + + if (SANGOMA_OBJ_IS_SIGNALABLE(sng_wait_objects[i])) { + pfds[number_of_sangoma_wait_objects+j].fd = sng_wait_objects[i]->signal_read_fd; + pfds[number_of_sangoma_wait_objects+j].events = POLLIN; + j++; + } + } + + poll_try_again: + + res = poll(pfds, (number_of_sangoma_wait_objects + j), system_wait_timeout); + if (res > 0) { + for(i = 0; i < number_of_sangoma_wait_objects; i++){ + out_flags[i] = pfds[i].revents; + /* POLLIN, POLLOUT, POLLPRI have same values as SANG_WAIT_OBJ_ HAS_INPUT, CAN_OUTPUT and HAS_EVENTS, no need to set them */ + } + for(i = 0; i < j; i++){ + if (pfds[number_of_sangoma_wait_objects+i].revents & POLLIN) { + /* read and discard the signal byte */ + read(pfds[number_of_sangoma_wait_objects+i].fd, &dummy_buf, 1); + /* set the proper flag so users may know this object was signaled */ + out_flags[i] |= SANG_WAIT_OBJ_IS_SIGNALED; + } + } + } else if (res < 0 && errno == EINTR) { + /* TODO: decrement system_wait_timeout */ + goto poll_try_again; + } + if (res < 0) { + return SANG_STATUS_GENERAL_ERROR; + } + if (res == 0) { + return SANG_STATUS_APIPOLL_TIMEOUT; + } + return SANG_STATUS_SUCCESS; +#endif +} + + +/*! + \fn sangoma_status_t _SAPI_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, int32_t inflags, int32_t *outflags, int32_t timeout) + \brief Wait for a single waitable object + \param sangoma_wait_obj pointer to a wait object previously created with sangoma_wait_obj_create + \param timeout timeout in miliseconds in case of no event + \return SANG_STATUS_APIPOLL_TIMEOUT: timeout, SANG_STATUS_SUCCESS: event occured use sangoma_wait_obj_get_out_flags to check or error status +*/ +sangoma_status_t _SAPI_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, uint32_t inflags, uint32_t *outflags, int32_t timeout) +{ + return sangoma_waitfor_many(&sangoma_wait_obj, &inflags, outflags, 1, timeout); +} + + +/************************************************************//** + * Device OPEN / CLOSE Functions + ***************************************************************/ + + +int _SAPI_CALL sangoma_span_chan_toif(int span, int chan, char *interface_name) +{ +#if defined(__WINDOWS__) +/* FIXME: Not implemented */ + return -1; +#else + sprintf(interface_name,"s%ic%i",span,chan); +#endif + return 0; +} + +int _SAPI_CALL sangoma_interface_toi(char *interface_name, int *span, int *chan) +{ + char *p=NULL, *sp = NULL, *ch = NULL; + int ret = 0; + char data[FNAME_LEN]; + + strncpy(data, interface_name, FNAME_LEN); + if ((data[0])) { + for (p = data; *p; p++) { + if (sp && *p == 'g') { + *p = '\0'; + ch = (p + 1); + break; + } else if (*p == 'w') { + sp = (p + 1); + } + } + + if(ch && sp) { + *span = atoi(sp); + *chan = atoi(ch); + ret = 1; + } else { + *span = -1; + *chan = -1; + } + } + + return ret; +} + +int _SAPI_CALL sangoma_interface_wait_up(int span, int chan, int sectimeout) +{ +#if defined(__WINDOWS__) + /* Windows does not need to wait for interfaces to come up */ + return 0; +#else + char interface_name[FNAME_LEN]; + struct stat statbuf; + struct timeval endtime = {0,0}; + struct timeval curtime = {0,0}; + int counter; + int rc; + if (sectimeout >= 0 && gettimeofday(&endtime, NULL)) { + return -1; + } + snprintf(interface_name, sizeof(interface_name), "/dev/" WP_INTERFACE_NAME_FORM, span, chan); + endtime.tv_sec += sectimeout; + do { + counter = 0; + while ((rc = stat(interface_name, &statbuf)) && errno == ENOENT && counter != 10) { + poll(0, 0, 100); // test in 100ms increments + counter++; + } + if (!rc || errno != ENOENT) break; + if (gettimeofday(&curtime, NULL)) { + return -1; + } + } while (sectimeout < 0 || timercmp(&endtime, &curtime,>)); + return rc; +#endif +} + +int _SAPI_CALL sangoma_span_chan_fromif(char *interface_name, int *span, int *chan) +{ + char *p = NULL, *sp = NULL, *ch = NULL; + int ret = 0; + char data[FNAME_LEN]; + + strncpy(data, interface_name, FNAME_LEN); + if ((data[0])) { + for (p = data; *p; p++) { + if (sp && *p == 'c') { + *p = '\0'; + ch = (p + 1); + break; + } else if (*p == 's') { + sp = (p + 1); + } + } + + if(ch && sp) { + *span = atoi(sp); + *chan = atoi(ch); + ret = 1; + } else { + *span = -1; + *chan = -1; + } + } + + return ret; +} + +sng_fd_t _SAPI_CALL sangoma_open_api_span_chan(int span, int chan) +{ + sng_fd_t fd = INVALID_HANDLE_VALUE; + wanpipe_api_t tdm_api; + int err; + + fd = __sangoma_open_api_span_chan(span, chan); + +#if defined(__WINDOWS__) + if(fd == INVALID_HANDLE_VALUE){ + return fd; + } +#else + if (fd < 0) { + return fd; + } +#endif + + memset(&tdm_api,0,sizeof(tdm_api)); + tdm_api.wp_cmd.cmd = WP_API_CMD_OPEN_CNT; + err=sangoma_cmd_exec(fd,&tdm_api); + if (err){ + sangoma_close(&fd); + return fd; + } + + if (tdm_api.wp_cmd.open_cnt > 1) { + /* this is NOT the first open request for this span/chan */ + sangoma_close(&fd); + fd = INVALID_HANDLE_VALUE;/* fd is NOT valid anymore */ + } + + return fd; +} + +static sng_fd_t _SAPI_CALL sangoma_open_dev_by_name(const char *dev_name) +{ + char fname[FNAME_LEN]; + +#if defined(__WINDOWS__) + _snprintf(fname , FNAME_LEN, "\\\\.\\%s", dev_name); + + return CreateFile( fname, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + (LPSECURITY_ATTRIBUTES)NULL, + OPEN_EXISTING, + FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, + (HANDLE)NULL + ); +#else + sprintf(fname,"/dev/%s", dev_name); + + return open(fname, O_RDWR); +#endif +} + +/* no checks done for multiple open */ +sng_fd_t _SAPI_CALL __sangoma_open_api_span_chan(int span, int chan) +{ + char tmp_fname[FNAME_LEN]; + + /* Form the Interface Name from span and chan number (i.e. wanpipe1_if1). */ + _snprintf(tmp_fname, DEV_NAME_LEN, WP_INTERFACE_NAME_FORM, span, chan); + + return sangoma_open_dev_by_name(tmp_fname); +} + +sng_fd_t _SAPI_CALL sangoma_open_api_ctrl(void) +{ + return sangoma_open_dev_by_name(WP_CTRL_DEV_NAME); +} + +#ifdef WP_API_FEATURE_LOGGER +sng_fd_t _SAPI_CALL sangoma_logger_open(void) +{ + return sangoma_open_dev_by_name(WP_LOGGER_DEV_NAME); +} +#endif + +int _SAPI_CALL sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + tdm_api->wp_cmd.cmd = WP_API_CMD_OPEN_CNT; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return -1; + } + + return tdm_api->wp_cmd.open_cnt; +} + +sng_fd_t _SAPI_CALL sangoma_create_socket_by_name(char *device, char *card) +{ + int span,chan; + sangoma_interface_toi(device,&span,&chan); + + return sangoma_open_api_span_chan(span,chan); +} + + +sng_fd_t _SAPI_CALL sangoma_open_api_span(int span) +{ + int i=0; + sng_fd_t fd = INVALID_HANDLE_VALUE; + + for(i = 1; i < 32; i++){ + + fd = sangoma_open_api_span_chan(span, i); + +#if defined(__WINDOWS__) + if(fd != INVALID_HANDLE_VALUE){ +#else + if (fd >= 0) { +#endif + + /* found free chan */ + break; + } + + }/*for()*/ + + return fd; +} + + +/*! + \fn void sangoma_close(sng_fd_t *fd) + \brief Close device file descriptor + \param fd device file descriptor + \return void +*/ +void _SAPI_CALL sangoma_close(sng_fd_t *fd) +{ + if (!fd) { + return; + } +#if defined(__WINDOWS__) + if (*fd != INVALID_HANDLE_VALUE){ + CloseHandle(*fd); + *fd = INVALID_HANDLE_VALUE; + } +#else + if (*fd >= 0) { + close(*fd); + *fd = -1; + } +#endif +} + + + +/************************************************************//** + * Device READ / WRITE Functions + ***************************************************************/ + +int _SAPI_CALL sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, int datalen, int flag) +{ + int rx_len=0; + +#if defined(__WINDOWS__) + wp_api_hdr_t *rx_hdr = (wp_api_hdr_t*)hdrbuf; + wp_api_element_t wp_api_element; + + if(hdrlen != sizeof(wp_api_hdr_t)){ + /*error*/ + DBG_ERR("hdrlen (%i) != sizeof(wp_api_hdr_t) (%i)\n", hdrlen, sizeof(wp_api_hdr_t)); + return -1; + } + + if(DoReadCommand(fd, &wp_api_element)){ + /*error*/ + DBG_ERR("DoReadCommand() failed! Check messages log.\n"); + return -4; + } + + memcpy(rx_hdr, &wp_api_element.hdr, sizeof(wp_api_hdr_t)); + + switch(rx_hdr->operation_status) + { + case SANG_STATUS_RX_DATA_AVAILABLE: + /* ok */ + if(rx_hdr->data_length <= datalen){ + memcpy(databuf, wp_api_element.data, rx_hdr->data_length); + }else{ + rx_hdr->operation_status = SANG_STATUS_BUFFER_TOO_SMALL; + } + break; + default: + /* note that SANG_STATUS_NO_DATA_AVAILABLE is NOT an error! */ + if(0)DBG_ERR("Operation Status: %s(%d)\n", + SDLA_DECODE_SANG_STATUS(rx_hdr->operation_status), rx_hdr->operation_status); + return -5; + } + + rx_len = rx_hdr->data_length; +#else + wan_msghdr_t msg; + wan_iovec_t iov[2]; + + memset(&msg,0,sizeof(msg)); + memset(&iov[0],0,sizeof(iov[0])*2); + + 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,sizeof(msg)); + + if (rx_len <= sizeof(wp_api_hdr_t)){ + return -EINVAL; + } + + rx_len-=sizeof(wp_api_hdr_t); +#endif + return rx_len; +} + +int _SAPI_CALL sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, unsigned short datalen, int flag) +{ + int bsent=-1; + wp_api_hdr_t *wp_api_hdr = hdrbuf; + + if (hdrlen != sizeof(wp_api_hdr_t)) { + /* error. Possible cause is a mismatch between versions of API header files. */ + DBG_ERR("hdrlen (%i) != sizeof(wp_api_hdr_t) (%i)\n", hdrlen, sizeof(wp_api_hdr_t)); + return -1; + } + +#if defined(__WINDOWS__) + + wp_api_hdr->data_length = datalen; + + /*queue data for transmission*/ + if(DoWriteCommand(fd, databuf, datalen, hdrbuf, hdrlen)){ + /*error*/ + DBG_ERR("DoWriteCommand() failed!! Check messages log.\n"); + return -1; + } + + bsent=0; + /*check that frame was transmitted*/ + switch(wp_api_hdr->operation_status) + { + case SANG_STATUS_SUCCESS: + bsent = datalen; + break; + default: + DBG_ERR("Operation Status: %s(%d)\n", + SDLA_DECODE_SANG_STATUS(wp_api_hdr->operation_status), wp_api_hdr->operation_status); + break; + }/*switch()*/ +#else + wan_msghdr_t msg; + wan_iovec_t iov[2]; + + memset(&msg,0,sizeof(msg)); + memset(&iov[0],0,sizeof(iov[0])*2); + + 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,sizeof(msg)); + + if (bsent == (datalen+hdrlen)){ + wp_api_hdr->wp_api_hdr_operation_status=SANG_STATUS_SUCCESS; + bsent-=sizeof(wp_api_hdr_t); + } else if (errno == EBUSY){ + wp_api_hdr->wp_api_hdr_operation_status=SANG_STATUS_DEVICE_BUSY; + } else { + wp_api_hdr->wp_api_hdr_operation_status=SANG_STATUS_IO_ERROR; + } + wp_api_hdr->wp_api_hdr_data_length=bsent; + +#endif + return bsent; +} + + +#ifdef WANPIPE_TDM_API + + +/************************************************************//** + * Device API COMMAND Functions + ***************************************************************/ + + + +/*======================================================== + * Execute TDM command + * + */ +int _SAPI_CALL sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + +#if defined(__WINDOWS__) + err = tdmv_api_ioctl(fd, &tdm_api->wp_cmd); +#else + err = ioctl(fd,WANPIPE_IOCTL_API_CMD,&tdm_api->wp_cmd); + if (err < 0){ + char tmp[50]; + sprintf(tmp,"TDM API: CMD: %i\n",tdm_api->wp_cmd.cmd); + perror(tmp); + return -1; + } +#endif + return err; +} + +/*======================================================== + * Get Full TDM API configuration per channel + * + */ +int _SAPI_CALL sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_FULL_CFG; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + +#if 0 + printf("TDM API CFG:\n"); + printf("\thw_tdm_coding:\t%d\n",tdm_api->wp_cmd.hw_tdm_coding); + printf("\thw_mtu_mru:\t%d\n",tdm_api->wp_cmd.hw_mtu_mru); + printf("\tusr_period:\t%d\n",tdm_api->wp_cmd.usr_period); + printf("\ttdm_codec:\t%d\n",tdm_api->wp_cmd.tdm_codec); + printf("\tpower_level:\t%d\n",tdm_api->wp_cmd.power_level); + printf("\trx_disable:\t%d\n",tdm_api->wp_cmd.rx_disable); + printf("\ttx_disable:\t%d\n",tdm_api->wp_cmd.tx_disable); + printf("\tusr_mtu_mru:\t%d\n",tdm_api->wp_cmd.usr_mtu_mru); + printf("\tidle flag:\t0x%02X\n",tdm_api->wp_cmd.idle_flag); + +#ifdef WP_API_FEATURE_FE_ALARM + printf("\tfe alarms:\t0x%02X\n",tdm_api->wp_cmd.fe_alarms); +#endif + + printf("\trx pkt\t%d\ttx pkt\t%d\n",tdm_api->wp_cmd.stats.rx_packets, + tdm_api->wp_cmd.stats.tx_packets); + printf("\trx err\t%d\ttx err\t%d\n", + tdm_api->wp_cmd.stats.rx_errors, + tdm_api->wp_cmd.stats.tx_errors); +#ifndef __WINDOWS__ + printf("\trx ovr\t%d\ttx idl\t%d\n", + tdm_api->wp_cmd.stats.rx_fifo_errors, + tdm_api->wp_cmd.stats.tx_carrier_errors); +#endif +#endif + + return 0; +} + +/*======================================================== + * SET Codec on a particular Channel. + * + * Available codecs are defined in + * /usr/src/linux/include/linux/wanpipe_cfg.h + * + * enum wan_codec_format { + * WP_NONE, + * WP_SLINEAR + * } + * + */ +int _SAPI_CALL sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int codec) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_CODEC; + tdm_api->wp_cmd.tdm_codec = codec; + + err=sangoma_cmd_exec(fd,tdm_api); + + return err; +} + +/*======================================================== + * GET Codec from a particular Channel. + * + * Available codecs are defined in + * /usr/src/linux/include/linux/wanpipe_cfg.h + * + * enum wan_codec_format { + * WP_NONE, + * WP_SLINEAR + * } + * + */ +int _SAPI_CALL sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_CODEC; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return tdm_api->wp_cmd.tdm_codec; +} + +/*======================================================== + * SET Rx/Tx Hardware Period in milliseconds. + * + * Available options are: + * 10,20,30,40,50 ms + * + */ +int _SAPI_CALL sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, int period) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_USR_PERIOD; + tdm_api->wp_cmd.usr_period = period; + + err=sangoma_cmd_exec(fd,tdm_api); + + return err; +} + +/*======================================================== + * GET Rx/Tx Hardware Period in milliseconds. + * + * Available options are: + * 10,20,30,40,50 ms + * + */ +int _SAPI_CALL sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_USR_PERIOD; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return tdm_api->wp_cmd.usr_period; +} + +/*======================================================== + * GET Current User Hardware Coding Format + * + * Coding Format will be ULAW/ALAW based on T1/E1 + */ + +int _SAPI_CALL sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_HW_CODING; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + return tdm_api->wp_cmd.hw_tdm_coding; +} + +#ifdef WP_API_FEATURE_FAX_EVENTS +/*======================================================== + * GET Current User Hardware FAX Detect Enabled/Disabled + * + * Will return true if HW FAX Detect is enabled on Octasic + */ + +int _SAPI_CALL sangoma_tdm_get_hw_fax(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_HW_FAX_DETECT; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + return tdm_api->wp_cmd.hw_dtmf; +} + +int _SAPI_CALL sangoma_tdm_enable_fax_detect(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_FAX_DETECT; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + +int _SAPI_CALL sangoma_tdm_disable_fax_detect(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_FAX_DETECT; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + +#endif + +#ifdef WP_API_FEATURE_DTMF_EVENTS +/*======================================================== + * GET Current User Hardware DTMF Enabled/Disabled + * + * Will return true if HW DTMF is enabled on Octasic + */ + +int _SAPI_CALL sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_HW_DTMF; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + return tdm_api->wp_cmd.hw_dtmf; +} + +/*======================================================== + * GET Current User Hardware EC Enabled/Disabled + * + * Will return true if HW EC is enabled + */ + +int _SAPI_CALL sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_HW_EC; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + return tdm_api->wp_cmd.hw_ec; +} +#endif + +/*======================================================== + * GET Current User MTU/MRU values in bytes. + * + * The USER MTU/MRU values will change each time a PERIOD + * or CODEC is adjusted. + */ +int _SAPI_CALL sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_USR_MTU_MRU; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return tdm_api->wp_cmd.usr_mtu_mru; +} + +/*======================================================== + * SET TDM Power Level + * + * This option is not implemented yet + * + */ +int _SAPI_CALL sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, int power) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_POWER_LEVEL; + tdm_api->wp_cmd.power_level = power; + + err=sangoma_cmd_exec(fd,tdm_api); + + return err; +} + +/*======================================================== + * GET TDM Power Level + * + * This option is not implemented yet + * + */ +int _SAPI_CALL sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_POWER_LEVEL; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return tdm_api->wp_cmd.power_level; +} + +int _SAPI_CALL sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + tdm_api->wp_cmd.cmd = WP_API_CMD_FLUSH_BUFFERS; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + +int _SAPI_CALL sangoma_flush_rx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + tdm_api->wp_cmd.cmd = WP_API_CMD_FLUSH_RX_BUFFERS; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + +int _SAPI_CALL sangoma_flush_tx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + tdm_api->wp_cmd.cmd = WP_API_CMD_FLUSH_TX_BUFFERS; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + +int _SAPI_CALL sangoma_flush_event_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + tdm_api->wp_cmd.cmd = WP_API_CMD_FLUSH_EVENT_BUFFERS; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + +int _SAPI_CALL sangoma_tdm_enable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api, int poll_in_sec) { + + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_ENABLE_RBS_EVENTS; + tdm_api->wp_cmd.rbs_poll=poll_in_sec; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + + +int _SAPI_CALL sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { + + int err; + tdm_api->wp_cmd.cmd = WP_API_CMD_DISABLE_RBS_EVENTS; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + +int _SAPI_CALL sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char rbs) +{ + + int err; + tdm_api->wp_cmd.cmd = WP_API_CMD_WRITE_RBS_BITS; + tdm_api->wp_cmd.chan = (unsigned char)channel; + tdm_api->wp_cmd.rbs_tx_bits=rbs; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + + +int _SAPI_CALL sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char *rbs) +{ + + int err; + tdm_api->wp_cmd.cmd = WP_API_CMD_READ_RBS_BITS; + tdm_api->wp_cmd.chan = (unsigned char)channel; + tdm_api->wp_cmd.rbs_tx_bits=0; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + *rbs=(unsigned char)tdm_api->wp_cmd.rbs_rx_bits; + + return 0; +} + +int _SAPI_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + +#ifdef WP_API_FEATURE_EVENTS + wp_api_event_t *rx_event; + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_READ_EVENT; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + rx_event = &tdm_api->wp_cmd.event; + + + /* + The use of callbacks here is purely optional and is left + here for backward compatibility purposes. By default user + should handle events outside this funciton. This function + should only be used to read the event + */ + + switch (rx_event->wp_api_event_type){ + + case WP_API_EVENT_RBS: + if (tdm_api->wp_callback.wp_rbs_event) { + tdm_api->wp_callback.wp_rbs_event(fd,rx_event->wp_api_event_rbs_bits); + } + break; + +#ifdef WP_API_FEATURE_DTMF_EVENTS + case WP_API_EVENT_DTMF: + if (tdm_api->wp_callback.wp_dtmf_event) { + tdm_api->wp_callback.wp_dtmf_event(fd, + rx_event->wp_api_event_dtmf_digit, + rx_event->wp_api_event_dtmf_type, + rx_event->wp_api_event_dtmf_port); + } + break; +#endif + + case WP_API_EVENT_RXHOOK: + if (tdm_api->wp_callback.wp_rxhook_event) { + tdm_api->wp_callback.wp_rxhook_event(fd, + rx_event->wp_api_event_hook_state); + } + break; + + case WP_API_EVENT_RING_DETECT: + if (tdm_api->wp_callback.wp_ring_detect_event) { + tdm_api->wp_callback.wp_ring_detect_event(fd, + rx_event->wp_api_event_ring_state); + } + break; + + case WP_API_EVENT_RING_TRIP_DETECT: + if (tdm_api->wp_callback.wp_ring_trip_detect_event) { + tdm_api->wp_callback.wp_ring_trip_detect_event(fd, + rx_event->wp_api_event_ring_state); + } + break; + +#ifdef WP_API_FEATURE_FE_ALARM + case WP_API_EVENT_ALARM: + if (tdm_api->wp_callback.wp_fe_alarm_event) { + tdm_api->wp_callback.wp_fe_alarm_event(fd, + rx_event->wp_api_event_alarm); + } + break; +#endif + +#ifdef WP_API_FEATURE_LINK_STATUS + /* Link Status */ + case WP_API_EVENT_LINK_STATUS: + if(tdm_api->wp_callback.wp_link_status_event){ + tdm_api->wp_callback.wp_link_status_event(fd, + rx_event->wp_api_event_link_status); + } + + break; +#endif + +#ifdef WP_API_FEATURE_POL_REV + case WP_API_EVENT_POLARITY_REVERSE: + break; +#endif + default: +#ifdef __WINDOWS__ + if(0)printf("Warning: libsangoma: %s fd=0x%p: Unknown TDM event!", __FUNCTION__,fd); +#else + if(0)printf("Warning: libsangoma: %s fd=%d: Unknown TDM event!", __FUNCTION__, fd); +#endif + break; + } + + return 0; +#else + printf("Error: Read Event not supported!\n"); + return -1; +#endif +} + + +#ifdef WP_API_FEATURE_LOGGER +/*======================================================== + * Execute Wanpipe Logger command + */ +sangoma_status_t _SAPI_CALL sangoma_logger_cmd_exec(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ +#if defined(__WINDOWS__) + return logger_api_ioctl(fd, logger_cmd); +#else + /* FIXME: not implemented! */ + return -1; +#endif +} + +sangoma_status_t _SAPI_CALL sangoma_logger_read_event(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_READ_EVENT; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _SAPI_CALL sangoma_logger_flush_buffers(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_FLUSH_BUFFERS; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _SAPI_CALL sangoma_logger_get_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_GET_STATS; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _SAPI_CALL sangoma_logger_reset_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_RESET_STATS; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _SAPI_CALL sangoma_logger_get_open_handle_counter(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_OPEN_CNT; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _SAPI_CALL sangoma_logger_get_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_GET_LOGGER_LEVEL; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _SAPI_CALL sangoma_logger_set_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_SET_LOGGER_LEVEL; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +#endif /* WP_API_FEATURE_LOGGER */ + +#ifdef WP_API_FEATURE_DTMF_EVENTS +int _SAPI_CALL sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_DTMF; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + +int _SAPI_CALL sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_DTMF; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + + +int _SAPI_CALL sangoma_tdm_enable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RM_DTMF; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + +int _SAPI_CALL sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RM_DTMF; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + +int _SAPI_CALL sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RXHOOK; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + +int _SAPI_CALL sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RXHOOK; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + +int _SAPI_CALL sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { + + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + + +int _SAPI_CALL sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { + + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + +int _SAPI_CALL sangoma_tdm_enable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { + + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING_DETECT; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return err; +} + + +int _SAPI_CALL sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { + + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING_DETECT; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + +int _SAPI_CALL sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { + + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING_TRIP_DETECT; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return tdm_api->wp_cmd.rbs_poll; +} + + +int _SAPI_CALL sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { + + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING_DETECT; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + +int _SAPI_CALL sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api) { + + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TXSIG_KEWL; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + +int _SAPI_CALL sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api) { + + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TXSIG_START; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + +int _SAPI_CALL sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api) { + + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TXSIG_ONHOOK; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + +int _SAPI_CALL sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api) { + + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TXSIG_OFFHOOK; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + + +int _SAPI_CALL sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api, uint16_t tone_id) { + + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TONE; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + tdm_api->wp_cmd.event.wp_api_event_tone_type = tone_id; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return tdm_api->wp_cmd.rbs_poll; +} + +int _SAPI_CALL sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { + + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TONE; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; + tdm_api->wp_cmd.event.wp_api_event_tone_type = 0x00; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return tdm_api->wp_cmd.rbs_poll; +} + +#endif + +int _SAPI_CALL sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_ENABLE_HWEC; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + +int _SAPI_CALL sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_DISABLE_HWEC; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return 0; +} + + +/*======================================================== + * GET Front End Alarms + * + */ +#ifdef WP_API_FEATURE_FE_ALARM +int _SAPI_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_FE_ALARMS; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + *alarms=tdm_api->wp_cmd.fe_alarms; + + return 0; +} + +/* get current Line Connection state - Connected/Disconnected */ +int _SAPI_CALL sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_FE_STATUS; + err = sangoma_cmd_exec(fd, tdm_api); + *current_status = tdm_api->wp_cmd.fe_status; + + return err; +} +#endif + +/* get current Line Connection state - Connected/Disconnected */ +#ifdef WP_API_FEATURE_LINK_STATUS +int _SAPI_CALL sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_FE_STATUS; + err = sangoma_cmd_exec(fd, tdm_api); + *current_status = tdm_api->wp_cmd.fe_status; + + return err; +} + +/* set current Line Connection state - Connected/Disconnected. valid only for ISDN BRI */ +int _SAPI_CALL sangoma_set_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char new_status) +{ + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_FE_STATUS; + tdm_api->wp_cmd.fe_status = new_status; + + return sangoma_cmd_exec(fd, tdm_api); +} +#endif + +int _SAPI_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) +{ + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.channel = (unsigned char)channel; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_BRI_CHAN_LOOPBACK; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; + return sangoma_cmd_exec(fd, tdm_api); +} + +int _SAPI_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) +{ + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.channel = (unsigned char)channel; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_BRI_CHAN_LOOPBACK; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + return sangoma_cmd_exec(fd, tdm_api); +} + + +int _SAPI_CALL sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_TX_Q_SIZE; + tdm_api->wp_cmd.tx_queue_sz = 0; + + err=sangoma_cmd_exec(fd, tdm_api); + if (err < 0) { + return err; + } + + return tdm_api->wp_cmd.tx_queue_sz; +} + +int _SAPI_CALL sangoma_set_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) +{ + if (size < 0) { + return -1; + } + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_TX_Q_SIZE; + tdm_api->wp_cmd.tx_queue_sz = size; + + return sangoma_cmd_exec(fd, tdm_api); +} + +int _SAPI_CALL sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_RX_Q_SIZE; + tdm_api->wp_cmd.rx_queue_sz = 0; + + err=sangoma_cmd_exec(fd, tdm_api); + if (err < 0) { + return err; + } + + return tdm_api->wp_cmd.rx_queue_sz; + +} + +int _SAPI_CALL sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) +{ + if (size < 0) { + return -1; + } + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_RX_Q_SIZE; + tdm_api->wp_cmd.rx_queue_sz = size; + + return sangoma_cmd_exec(fd, tdm_api); + +} + +int _SAPI_CALL sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, wan_driver_version_t *drv_ver) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_DRIVER_VERSION; + + err = sangoma_cmd_exec(fd, tdm_api); + if (err == 0) { + if (tdm_api->wp_cmd.data_len == sizeof(wan_driver_version_t)) { + if (drv_ver) { + memcpy(drv_ver,&tdm_api->wp_cmd.version,sizeof(wan_driver_version_t)); + } + } else { + return -1; + } + } + + return err; +} + +int _SAPI_CALL sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_FIRMWARE_VERSION; + + err = sangoma_cmd_exec(fd, tdm_api); + if (err == 0) { + if (tdm_api->wp_cmd.data_len == sizeof(unsigned char)) { + *ver = tdm_api->wp_cmd.data[0]; + } else { + return -1; + } + } + + return err; +} + + +int _SAPI_CALL sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_CPLD_VERSION; + + err = sangoma_cmd_exec(fd, tdm_api); + if (err == 0) { + if (tdm_api->wp_cmd.data_len == sizeof(unsigned char)) { + *ver = tdm_api->wp_cmd.data[0]; + } else { + return -1; + } + } + + return err; +} + +int _SAPI_CALL sangoma_get_stats(sng_fd_t fd, wanpipe_api_t *tdm_api, wanpipe_chan_stats_t *stats) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_STATS; + + err = sangoma_cmd_exec(fd, tdm_api); + if (err == 0) { + if (stats) { + memcpy(stats,&tdm_api->wp_cmd.stats,sizeof(wanpipe_chan_stats_t)); + } + } + + return err; +} + +int _SAPI_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + tdm_api->wp_cmd.cmd = WP_API_CMD_RESET_STATS; + return sangoma_cmd_exec(fd, tdm_api); +} + +int _SAPI_CALL sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, int rxflashtime) +{ + int err; + + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_RM_RXFLASHTIME; + tdm_api->wp_cmd.rxflashtime=rxflashtime; + err = sangoma_cmd_exec(fd, tdm_api); + return err; +} + + +#ifndef LIBSANGOMA_LIGHT + + +/************************************************************//** + * Device PORT Control Functions + ***************************************************************/ + +static int sangoma_port_mgmnt_ioctl(sng_fd_t fd, port_management_struct_t *port_management) +{ + int err = 0; +#if defined(__WINDOWS__) + DWORD ln; + if(DeviceIoControl( + fd, + IoctlPortManagementCommand, + (LPVOID)port_management, + sizeof(port_management_struct_t), + (LPVOID)port_management, + sizeof(port_management_struct_t), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL + ) == FALSE){ + /* Call OS specific code to find cause of the error and check messages log. */ + DBG_ERR("%s():Error: IoctlPortManagementCommand failed!!\n", __FUNCTION__); + err = -1; + } +#else + err=ioctl(fd,WANPIPE_IOCTL_PORT_MGMT,port_management); + if (err) { + err = -1; + } +#endif + if(err){ + port_management->operation_status = SANG_STATUS_INVALID_DEVICE; + } + + return err; +} + +static int sangoma_port_cfg_ioctl(sng_fd_t fd, port_cfg_t *port_cfg) +{ + int err = 0; +#if defined(__WINDOWS__) + DWORD ln; + if(DeviceIoControl( + fd, + IoctlPortConfigurationCommand, + (LPVOID)port_cfg, + sizeof(port_cfg_t), + (LPVOID)port_cfg, + sizeof(port_cfg_t), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL + ) == FALSE){ + /* Call OS specific code to find cause of the error and check messages log. */ + DBG_ERR("%s():Error: IoctlPortConfigurationCommand failed!!\n", __FUNCTION__); + err = -1; + } +#else + err=ioctl(fd,WANPIPE_IOCTL_PORT_CONFIG,port_cfg); + if (err) { + err = -1; + } +#endif + if(err){ + port_cfg->operation_status = SANG_STATUS_INVALID_DEVICE; + } + + return err; +} + +/* open wanpipe configuration device */ +sng_fd_t _SAPI_CALL sangoma_open_driver_ctrl(int port_no) +{ + char tmp_fname[FNAME_LEN]; + +#if defined(__WINDOWS__) + /* Form the Config Device Name (i.e. wanpipe1, wanpipe2,...). */ + _snprintf(tmp_fname, DEV_NAME_LEN, WP_PORT_NAME_FORM, port_no); +#else + /* Form the Config Device Name. ("/dev/wanpipe") */ + _snprintf(tmp_fname, DEV_NAME_LEN, WP_CONFIG_DEV_NAME); +#endif + return sangoma_open_dev_by_name(tmp_fname); +} + + +int _SAPI_CALL sangoma_mgmt_cmd(sng_fd_t fd, wan_udp_hdr_t* wan_udp) +{ +#if defined(__WINDOWS__) + if(UdpManagementCommand(fd, wan_udp)){ + return 1; + } +#else + unsigned char id = 0; + int err=0; + wan_udp->wan_udphdr_request_reply = 0x01; + wan_udp->wan_udphdr_id = id; + wan_udp->wan_udphdr_return_code = WAN_UDP_TIMEOUT_CMD; + + err=ioctl(fd,WANPIPE_IOCTL_PIPEMON,wan_udp); + if (err < 0) { + return 1; + } +#endif + + if(wan_udp->wan_udphdr_return_code != WAN_CMD_OK){ + return 2; + } + return 0; +} + +int _SAPI_CALL sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) +{ + int err; + port_mgmnt->command_code = START_PORT_VOLATILE_CONFIG; + port_mgmnt->port_no = port_no; + + err = sangoma_port_mgmnt_ioctl(fd, port_mgmnt); + if (err) { + /* ioctl failed */ + return err; + } + + return port_mgmnt->operation_status; +} + +int _SAPI_CALL sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) +{ + int err; + port_mgmnt->command_code = STOP_PORT; + port_mgmnt->port_no = port_no; + + err = sangoma_port_mgmnt_ioctl(fd, port_mgmnt); + if (err) { + /* ioctl failed */ + return err; + } + + switch(port_mgmnt->operation_status) + { + case SANG_STATUS_CAN_NOT_STOP_DEVICE_WHEN_ALREADY_STOPPED: + /* This is not an error, rather a state indication. + * Return SANG_STATUS_SUCCESS, but real return code will be available + * for the caller at port_mgmnt->operation_status. */ + err = SANG_STATUS_SUCCESS; + break; + default: + err = port_mgmnt->operation_status; + break; + } + + return err; +} + +int _SAPI_CALL sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) +{ + int err; + port_mgmnt->command_code = GET_HARDWARE_INFO; + port_mgmnt->port_no = port_no; + + err = sangoma_port_mgmnt_ioctl(fd, port_mgmnt); + if (err) { + return err; + } + + return port_mgmnt->operation_status; +} + +int _SAPI_CALL sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no) +{ + port_cfg->command_code = SET_PORT_VOLATILE_CONFIG; + port_cfg->port_no = port_no; + + return sangoma_port_cfg_ioctl(fd, port_cfg); +} + +int _SAPI_CALL sangoma_driver_port_get_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no) +{ + + port_cfg->command_code = GET_PORT_VOLATILE_CONFIG; + port_cfg->port_no = port_no; + + return sangoma_port_cfg_ioctl(fd, port_cfg); +} + +int _SAPI_CALL sangoma_write_port_config_on_persistent_storage(hardware_info_t *hardware_info, port_cfg_t *port_cfg, unsigned short port_no) +{ + int err = 0; +#if defined(__WINDOWS__) + HKEY hPortRegistryKey = registry_open_port_key(hardware_info); + wandev_conf_t *wandev_conf = &port_cfg->wandev_conf; + sdla_fe_cfg_t *sdla_fe_cfg = &wandev_conf->fe_cfg; + unsigned int ind; + + if(hPortRegistryKey == INVALID_HANDLE_VALUE){ + return 1; + } + + /* write T1/E1/BRI/Analog configuration */ + if(registry_write_front_end_cfg(hPortRegistryKey, port_cfg)){ + return 2; + } + + /* write TDM Voice configuration */ + if(registry_write_wan_tdmv_conf(hPortRegistryKey, port_cfg)){ + return 3; + } + + /* write number of groups */ + err = registry_set_integer_value(hPortRegistryKey, "aft_number_of_logic_channels", port_cfg->num_of_ifs); + if(err){ + return err; + } + + /* write configuration of each group */ + for(ind = 0; ind < port_cfg->num_of_ifs; ind++){ + registry_write_channel_group_cfg(hPortRegistryKey, port_cfg, ind, port_cfg->if_cfg[ind]); + } + +#else + printf("%s(): Warning: function not implemented\n", __FUNCTION__); + err = 1; +#endif + return err; +} + +#endif /* #ifndef LIBSANGOMA_LIGHT */ + +#endif /* WANPIPE_TDM_API */ diff --git a/api/libsangoma/.svn/tmp/tempfile.18.tmp b/api/libsangoma/.svn/tmp/tempfile.18.tmp new file mode 100644 index 0000000..fd5d59e --- /dev/null +++ b/api/libsangoma/.svn/tmp/tempfile.18.tmp @@ -0,0 +1,1574 @@ +/*******************************************************************************//** + * \file libsangoma.h + * \brief Wanpipe API Library header for Sangoma AFT T1/E1/Analog/BRI/Serial Hardware - + * \brief Provides User a Unified/OS Agnostic API to Wanpipe/Sangoma Drivers and Hardware + * + * Author(s): Nenad Corbic + * David Rokhvarg + * Michael Jerris + * Anthony Minessale II + * + * Copyright: (c) 2005-2008 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 Sangoma Technologies 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 Sangoma Technologies ``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 Sangoma Technologies 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 + * =============================================================================== + * v.2.0.0 Nenad Corbic + * Jan 30 2009 + * Added sangoma_get_driver_version, sangoma_get_firmware_version, + * sangoma_get_cpld_version functions,sangoma_get_stats,sangoma_flush_stats + */ + +#ifndef _LIBSNAGOMA_H +#define _LIBSNAGOMA_H + +#ifdef __linux__ +#ifndef __LINUX__ +/* most wanpipe driver headers require __LINUX__ to be defined */ +#define __LINUX__ +#endif +#endif + +#ifdef __cplusplus +extern "C" { /* for C++ users */ +#endif + +#include + + +/*! + \def WANPIPE_TDM_API + \brief Used by compiler and driver to enable TDM API +*/ +#define WANPIPE_TDM_API 1 + +/*TODO: LIBSANGOMA_VERSION_CODE should be generated out of LIBSANGOMA_LT_CURRENT and friends in configure.in */ + +/*! + \def LIBSANGOMA_VERSION + \brief LibSangoma Macro to check the Version Number +*/ +#define LIBSANGOMA_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +/*! + \def LIBSANGOMA_VERSION_CODE + \brief LibSangoma Current Version Number to be checked against the LIBSANGOMA_VERSION Macro +*/ +#define LIBSANGOMA_VERSION_CODE LIBSANGOMA_VERSION(3,3,0) + +/*! + \def LIBSANGOMA_VERSION_STR + \brief LibSangoma Version in string format +*/ +#define LIBSANGOMA_VERSION_STR "3.3.0" + +struct sangoma_wait_obj; +#define sangoma_wait_obj_t struct sangoma_wait_obj + +#if defined(WIN32) || defined(WIN64) +#ifndef __WINDOWS__ +#define __WINDOWS__ +#endif +#include +#include +#include +#include +#include + +/*! + \def _SAPI_CALL + \brief libsangoma.dll functions exported as __cdecl calling convention +*/ +#define _SAPI_CALL __cdecl + +/*! + \def SANGOMA_INFINITE_API_POLL_WAIT (deprecated, use SANGOMA_WAIT_INFINITE instead) + \brief Infinite poll timeout value +*/ +#define SANGOMA_INFINITE_API_POLL_WAIT INFINITE +#define SANGOMA_WAIT_INFINITE INFINITE + +/*! + \def sangoma_msleep(x) + \brief milisecond sleep function +*/ +#define sangoma_msleep(x) Sleep(x) + +/*! + \def sangoma_ctime(time_val) + \brief Convert a time value to a string + \param time_val pointer to 64-bit time value +*/ +#define sangoma_ctime(time) _ctime64(time) + +#else +/* L I N U X */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*! + \def _SAPI_CALL + \brief Not used in Linux +*/ +#define _SAPI_CALL + +/*! + \def INVALID_HANDLE_VALUE + \brief Invalid file handle value -1, Ported from Windows +*/ +#define INVALID_HANDLE_VALUE -1 + +/*! + \def SANGOMA_INFINITE_API_POLL_WAIT (deprecated, use SANGOMA_WAIT_INFINITE instead) + \brief Infinite poll timeout value -1, Ported from Windows +*/ +#define SANGOMA_INFINITE_API_POLL_WAIT -1 +#define SANGOMA_WAIT_INFINITE -1 + +/*! + \def __cdecl + \brief Ported from Windows + \typedef BOOL + \brief Boolean type int, Ported from Windows + \typedef DWORD + \brief DWORD type is int, Ported from Windows + \def FALSE + \brief FALSE value is 0, Ported from Windows + \def TRUE + \brief TRUE value is 1, Ported from Windows + \def sangoma_msleep(x) + \brief milisecond sleep function + \def _getch + \brief get character, Ported from Windows + \def Sleep + \brief milisecond sleep function + \typedef HANDLE + \brief file handle type int, Ported from Windows + \typedef TCHAR + \brief TCHAN type mapped to char, Ported from Windows + \typedef ULONG + \brief ULONG type mapped to unsigned long, Ported from Windows + \typedef UCHAR + \brief ULONG type mapped to unsigned char, Ported from Windows + \typedef USHORT + \brief USHORT type mapped to unsigned short, Ported from Windows + \typedef LPSTR + \brief LPSTR type mapped to unsigned char *, Ported from Windows + \typedef PUCHAR + \brief PUCHAR type mapped to unsigned char *, Ported from Windows + \typedef LPTHREAD_START_ROUTINE + \brief LPTHREAD_START_ROUTINE type mapped to unsigned char *, Ported from Windows + \def _stricmp + \brief _stricmp type mapped to _stricmp, Ported from Windows + \def _snprintf + \brief _snprintf type mapped to snprintf, Ported from Windows +*/ + +#define __cdecl + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#define sangoma_msleep(x) usleep(x*1000) +#define _getch getchar +#define Sleep sangoma_msleep +#define _stricmp strcmp +#define _snprintf snprintf +#define _vsnprintf vsnprintf + +typedef int HANDLE; +typedef int BOOL; +typedef int DWORD; +typedef char TCHAR; +typedef unsigned char UCHAR; +typedef unsigned long ULONG; +typedef unsigned short USHORT; +typedef unsigned char * LPSTR; +typedef unsigned char * PUCHAR; +typedef void * LPTHREAD_START_ROUTINE; +typedef pthread_mutex_t CRITICAL_SECTION; + +#define EnterCriticalSection(arg) pthread_mutex_lock(arg) +#define LeaveCriticalSection(arg) pthread_mutex_unlock(arg) +#define InitializeCriticalSection(arg) pthread_mutex_init(arg, NULL); + +typedef struct tm SYSTEMTIME; +typedef char * LPCTSTR; + +/*! + \def sangoma_ctime(time_val) + \brief Convert a time value to a string + \param time_val pointer to time value +*/ +#define sangoma_ctime(time) ctime(time) + +#endif/* WIN32 */ + + +/*! + LIBSANGOMA_LIGHT can be used to enable only IO and EVENT + libsangoma functions. The DRIVER configuration/start/stop + functions are not compiled. + + LIBSANGOMA_LIGHT depends only on 3 header files. Instead + of all wanpipe header files needed for DRIVER management + + LIBSANGMOA_LIGHT is NOT enabled by default. +*/ + +#ifdef LIBSANGOMA_LIGHT +#include "wanpipe_api_iface.h" +#include "wanpipe_api_hdr.h" +#include "sdla_te1.h" +#include "wanpipe_events.h" +#include "wanpipe_api_deprecated.h" +#else +#include "wanpipe_api.h" +#endif + +#ifdef __LINUX__ +#include "wanpipe_kernel.h" +#endif + +/*! + * As of now this typedef maps exactly to SANG_STATUS_T, however that + * is a kernel type, ugly, ugly, uglyyyyy, we should have strictly + * minimum set of shared data structures between kernel and user + * many return codes specified in SANG_STATUS_T are kernel specific + * like FAILED_TO_LOCK_USER_MEMORY or INVALID_IRQL, the libsangoma + * user does not need that much information, and even if ever needs + * it we should provide simpler defaults + * \brief return status from sangoma APIs + */ +typedef int32_t sangoma_status_t; + +/*! + \def FNAME_LEN + \brief string length of a file name + \def FUNC_DBG(x) + \brief function debug print function + \def DBG_PRINT + \brief debug print function +*/ +#define FNAME_LEN 100 +#define FUNC_DBG(x) if(0)printf("%s():%d\n", x, __LINE__) +#define DBG_PRINT if(1)printf + +/*! + \typedef sangoma_api_hdr_t + \brief Backward comaptible define of wp_api_hdr_t +*/ +typedef wp_api_hdr_t sangoma_api_hdr_t; + +/*! + \enum _sangoma_wait_obj_type_t + \brief Wait object type definition + \typedef sangoma_wait_obj_type_t + \brief Wait object type definition +*/ +typedef enum _sangoma_wait_obj_type +{ + /*! \brief deprecated, use SANGOMA_GENERIC_WAIT_OBJ */ + UNKNOWN_WAIT_OBJ = 0, + /*! \brief Generic object that can be signaled but is not associated to any sangoma device */ + SANGOMA_GENERIC_WAIT_OBJ = 0, + /*! \brief Sangoma object associated to some device which cannot be signaled (cannot call sangoma_wait_obj_signal on it) */ + SANGOMA_DEVICE_WAIT_OBJ, + /*! \brief Sangoma object that is associated to a device AND can be signaled */ + SANGOMA_DEVICE_WAIT_OBJ_SIG, +} sangoma_wait_obj_type_t; + +#define DECODE_SANGOMA_WAIT_OBJECT_TYPE(type)\ + type == SANGOMA_GENERIC_WAIT_OBJ ? "SANGOMA_GENERIC_WAIT_OBJ" :\ + type == SANGOMA_DEVICE_WAIT_OBJ ? "SANGOMA_DEVICE_WAIT_OBJ" :\ + type == SANGOMA_DEVICE_WAIT_OBJ_SIG ? "SANGOMA_DEVICE_WAIT_OBJ_SIG" :\ + "Invalid Wait Object type!" + +/* + * Possible flags for sangoma waitable objects + * this definitions MUST match POLLIN, POLLOUT and POLLPRI + * SANG_WAIT_OBJ_IS_SIGNALED has no posix equivalent though + * Users are encouraged to use this flags instead of the system ones + */ +typedef enum _sangoma_wait_obj_flags { + SANG_WAIT_OBJ_HAS_INPUT = POLLIN, + SANG_WAIT_OBJ_HAS_OUTPUT = POLLOUT, + SANG_WAIT_OBJ_HAS_EVENTS = POLLPRI, + SANG_WAIT_OBJ_IS_SIGNALED = 0x400 /* matches GNU extension POLLMSG */ +} sangoma_wait_obj_flags_t; + +/************************************************************//** + * Device OPEN / CLOSE Functions + ***************************************************************/ + +/*! + \fn sng_fd_t sangoma_open_api_span_chan(int span, int chan) + \brief Open a Device based on Span/Chan values + \param span span number starting from 1 to 255 + \param chan chan number starting from 1 to 32 + \return File Descriptor: -1 error, 0 or positive integer: valid file descriptor + + Restriced open, device will allowed to be open only once. +*/ +sng_fd_t _SAPI_CALL sangoma_open_api_span_chan(int span, int chan); + + +/*! + \fn sng_fd_t __sangoma_open_api_span_chan(int span, int chan) + \brief Open a Device based on Span/Chan values + \param span span number starting from 1 to 255 + \param chan chan number starting from 1 to 32 + \return File Descriptor: -1 error, 0 or positive integer: valid file descriptor + + Unrestriced open, allows mutiple open calls on a single device +*/ +sng_fd_t _SAPI_CALL __sangoma_open_api_span_chan(int span, int chan); +#define __sangoma_open_tdmapi_span_chan __sangoma_open_api_span_chan + +/*! + \fn sng_fd_t sangoma_open_tdmapi_span(int span) + \brief Open a first available device on a Span + \param span span number starting from 1 to 255 + \return File Descriptor: -1 error, 0 or positive integer: valid file descriptor + + Unrestriced open, allows mutiple open calls on a single device +*/ +sng_fd_t _SAPI_CALL sangoma_open_api_span(int span); + + +/*! + \def sangoma_create_socket_intr + \brief Backward compatible open span chan call +*/ + + + +/*! + \def LIBSANGOMA_TDMAPI_CTRL + \brief Global control device feature +*/ +#ifndef LIBSANGOMA_TDMAPI_CTRL +#define LIBSANGOMA_TDMAPI_CTRL 1 +#endif + +/*! + \fn sng_fd_t sangoma_open_api_ctrl(void) + \brief Open a Global Control Device + \return File Descriptor - negative=error 0 or greater = fd + + The global control device receives events for all devices + configured. +*/ +sng_fd_t _SAPI_CALL sangoma_open_api_ctrl(void); + +/*! + \fn sng_fd_t sangoma_logger_open(void) + \brief Open a Global Logger Device + \return File Descriptor - negative=error 0 or greater = fd + + The global Logger device receives Logger Events for all devices + configured. +*/ +sng_fd_t _SAPI_CALL sangoma_logger_open(void); + +/*! + \fn sng_fd_t sangoma_open_driver_ctrl(int port_no) + \brief Open a Global Driver Control Device + \return File Descriptor - negative=error 0 or greater = fd + + The global control device receives events for all devices + configured. +*/ +sng_fd_t _SAPI_CALL sangoma_open_driver_ctrl(int port_no); + + + +/*! + \fn void sangoma_close(sng_fd_t *fd) + \brief Close device file descriptor + \param fd device file descriptor + \return void + +*/ +void _SAPI_CALL sangoma_close(sng_fd_t *fd); + + + +/*! + \fn int sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get device open count + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative or 0: error, greater than 1 : open count +*/ + +int _SAPI_CALL sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/************************************************************//** + * Device READ / WRITE Functions + ***************************************************************/ + +/*! + \fn int sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, unsigned short datalen, int flag) + \brief Write Data to device + \param fd device file descriptor + \param hdrbuf pointer to header structure wp_api_hdr_t + \param hdrlen size of wp_api_hdr_t + \param databuf pointer to data buffer to be transmitted + \param datalen length of data buffer + \param flag currently not used, set to 0 + \return transmit size, must be equal to datalen, anything else is error + + In case of error return code, one must check the header operation_status + variable to identify the reason of an error. Please refer to the error codes. +*/ + +int _SAPI_CALL sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, + void *databuf, unsigned short datalen, int flag); + + +/*! + \fn int sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, int datalen, int flag) + \brief Read Data from device + \param fd device file descriptor + \param hdrbuf pointer to header structure wp_api_hdr_t + \param hdrlen size of wp_api_hdr_t + \param databuf pointer to data buffer to be received + \param datalen length of data buffer + \param flag currently not used, set to 0 + \return received size, must be equal to datalen, anything else is error or busy + + In case of error return code, one must check the header operation_status + variable to identify the reason of error. Please refer to the error codes. +*/ +int _SAPI_CALL sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, + void *databuf, int datalen, int flag); + + + + +/************************************************************//** + * Device POLL Functions + ***************************************************************/ + +/*! + \fn sangoma_status_t _SAPI_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, int32_t inflags, int32_t *outflags, int32_t timeout) + \brief Wait for a single waitable object + \param sangoma_wait_obj pointer to a wait object previously created with sangoma_wait_obj_create + \param timeout timeout in miliseconds in case of no event + \return SANG_STATUS_APIPOLL_TIMEOUT: timeout, SANG_STATUS_SUCCESS: event occured use sangoma_wait_obj_get_out_flags to check or error status +*/ +sangoma_status_t _SAPI_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, uint32_t inflags, uint32_t *outflags, int32_t timeout); + +/*! + \fn sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], int32_t in_flags[], int32_t out_flags[] uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout); + \brief Wait for multiple sangoma waitable objects + \param sangoma_wait_objects pointer to array of wait objects previously created with sangoma_wait_obj_create + \param in_flags array of flags corresponding to the flags the user is interested on for each waitable object + \param out_flags array of flags corresponding to the flags that are ready in the waitable objects + \param number_of_sangoma_wait_objects size of the array of waitable objects and flags + \param system_wait_timeout timeout in miliseconds in case of no event + \return negative: SANG_STATUS_APIPOLL_TIMEOUT: timeout, SANG_STATUS_SUCCESS: event occured check in sangoma_wait_objects, any other return code is some error +*/ +sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], uint32_t in_flags[], uint32_t out_flags[], + uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout); + +/*! + \fn sangoma_status_t sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma_wait_object, sng_fd_t fd, sangoma_wait_obj_type_t object_type) + \brief Create a wait object that will be used with sangoma_waitfor_many() API + \param sangoma_wait_object pointer a single device object + \param fd device file descriptor + \param object_type type of the wait object. see sangoma_wait_obj_type_t for types + \see sangoma_wait_obj_type_t + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _SAPI_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma_wait_object, sng_fd_t fd, sangoma_wait_obj_type_t object_type); + +/*! + \fn sangoma_status_t sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object) + \brief De-allocate all resources inside a wait object which were allocated by sangoma_wait_obj_init(). + \param sangoma_wait_object pointer to a pointer to a single device object + \return SANG_STATUS_SUCCESS on success or some sangoma status error +*/ +sangoma_status_t _SAPI_CALL sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object); + +/*! + \fn void sangoma_wait_obj_signal(sangoma_wait_obj_t *sangoma_wait_object) + \brief Set wait object to a signaled state + \param sangoma_wait_object pointer a single device object that can be signaled + \return sangoma_status_t +*/ +sangoma_status_t _SAPI_CALL sangoma_wait_obj_signal(sangoma_wait_obj_t *sangoma_wait_object); + +/*! + \fn sng_fd_t sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sangoma_wait_object) + \brief Get fd device file descriptor which was the 'fd' parameter for sangoma_wait_obj_create(), not useful for generic objects + \param sangoma_wait_object pointer a single device object + \return sng_fd_t - device file descriptor +*/ +sng_fd_t _SAPI_CALL sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sangoma_wait_object); + +/*! + \fn void sangoma_wait_obj_set_context(sangoma_wait_obj_t *sangoma_wait_object) + \brief Store the given context into provided sangoma wait object. + \brief This function is useful to associate a context with a sangoma wait object. + \param sangoma_wait_object pointer a single device object + \param context void pointer to user context + \return void +*/ +void _SAPI_CALL sangoma_wait_obj_set_context(sangoma_wait_obj_t *sangoma_wait_object, void *context); + +/*! + \fn void *sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object) + \brief Retrieve the user context (if any) that was set via sangoma_wait_obj_set_context. + \param sangoma_wait_object pointer a single device object + \return void* +*/ +void* _SAPI_CALL sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object); + + +/************************************************************//** + * Device API COMMAND Functions + ***************************************************************/ + +/*! + \fn int sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Execute Sangoma API Command + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Read tdm api device configuration + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, int period) + \brief Set Tx/Rx Period in Milliseconds + \param fd device file descriptor + \param tdm_api tdm api command structure + \param period value in miliseconds (1,2,5,10) + \return non-zero: error, 0: ok + + Only valid in CHAN Operation Mode +*/ +int _SAPI_CALL sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, int period); + +/*! + \fn int sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Tx/Rx Period in Milliseconds + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative: error or configured period value +*/ +int _SAPI_CALL sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Tx/Rx MTU/MRU in bytes + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative: error or configured mtu/mru in bytes +*/ +int _SAPI_CALL sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush all (tx/rx/event) buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _SAPI_CALL sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_flush_rx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush only rx buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _SAPI_CALL sangoma_flush_rx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); +/*! + \fn int sangoma_flush_tx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush only tx buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _SAPI_CALL sangoma_flush_tx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_flush_event_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush only event buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _SAPI_CALL sangoma_flush_event_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_tdm_enable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api, int poll_in_sec) + \brief Enable RBS Events on a device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param poll_in_sec driver poll period for rbs events + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_tdm_enable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api, int poll_in_sec); + +/*! + \fn int sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RBS Events for a device + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char rbs) + \brief Write RBS Bits on a device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param channel t1/e1 timeslot + \param rbs rbs bits (ABCD) + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char rbs); + +/*! + \fn int sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char *rbs) + \brief Read RBS Bits on a device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param channel t1/e1 timeslot + \param rbs pointer to rbs bits (ABCD) + \return non-zero: error, 0: ok +*/ + +int _SAPI_CALL sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char *rbs); + +/*! + \fn int sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable DTMF Detection on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _SAPI_CALL sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable DTMF Detection on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _SAPI_CALL sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_tdm_enable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable DTMF Detection on Analog/Remora SLIC Chip + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_enable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable DTMF Detection on Analog/Remora SLIC Chip + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +#ifdef WP_API_FEATURE_FAX_EVENTS +/*! + \fn int sangoma_tdm_get_hw_fax(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Check if HW FAX detect is enabled or disabled (Octasic only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Cards with Octasic HWEC +*/ +int _SAPI_CALL sangoma_tdm_get_hw_fax(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_fax_detect(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable HW FAX detect (Octasic only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Cards with Octasic HWEC +*/ +int _SAPI_CALL sangoma_tdm_enable_fax_detect(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_fax_detect(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable HW FAX detect (Octasic only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Cards with Octasic HWEC +*/ +int _SAPI_CALL sangoma_tdm_disable_fax_detect(sng_fd_t fd, wanpipe_api_t *tdm_api) + + +#endif + +/*! + \fn int sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable RX HOOK Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RX HOOK Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable RING Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RING Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_tdm_enable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable RING DETECT Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_enable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RING DETECT Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable RING TRIP Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RING TRIP Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api, uint16_t tone_id) + \brief Transmit a TONE on this device (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param tone_id tone type to transmit + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api, uint16_t tone_id); + +/*! + \fn int sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable TONE Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG ON HOOK (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG OFF HOOK (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG START (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG KEWL START (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable HWEC on this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _SAPI_CALL sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable HWEC on this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _SAPI_CALL sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int _SAPI_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms); + \brief Get Front End Alarms (T1/E1 Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param alarms bit map status of T1/E1 alarms + \return non-zero: error, 0: ok + + Supported only on T1/E1 Cards +*/ +int _SAPI_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms); + + + +#ifdef WP_API_FEATURE_LINK_STATUS +# ifndef LIBSANGOMA_GET_LINKSTATUS +/*! + \def LIBSANGOMA_GET_LINKSTATUS + \brief Get Link Status feature +*/ +# define LIBSANGOMA_GET_LINKSTATUS 1 +# endif + +/*! + \fn int sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status) + \brief Get Device Link Status (Connected/Disconnected) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param current_status pointer where result will be filled: 0=Link UP 1=Link Down + \return non-zero: error, 0: ok -> check current_status + +*/ +int _SAPI_CALL sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status); + +#endif + +/* set current Line Connection state - Connected/Disconnected */ +#ifndef LIBSANGOMA_GET_FESTATUS +/*! + \def LIBSANGOMA_GET_FESTATUS + \brief Get Front End Status feature +*/ +#define LIBSANGOMA_GET_FESTATUS 1 +#endif + +/*! + \fn int sangoma_set_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char new_status) + \brief Set Device Link Status (Connected/Disconnected) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param new_status new status 0=Link UP 1=Link Down + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_set_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char new_status); + + +/*! + \fn int _SAPI_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) + \brief Enable BRI Bchannel loopback - used when debugging bri device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param channel bri bchannel 1 or 2 + \return non-zero: error, 0: ok + +*/ +int _SAPI_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel); + +/*! + \fn int _SAPI_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) + \brief Disable BRI Bchannel loopback - used when debugging bri device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param channel bri bchannel 1 or 2 + \return non-zero: error, 0: ok + +*/ +int _SAPI_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel); + + +/*! + \fn int sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Tx Queue Size for this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_set_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) + \brief Get Tx Queue Size for this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \param size tx queue size (minimum value of 1) + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_set_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size); + +/*! + \fn int sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Rx Queue Size for this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) + \brief Get Tx Queue Size for this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \param size rx queue size (minimum value of 1) + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size); + + +#ifndef LIBSANGOMA_GET_HWCODING +/*! + \def LIBSANGOMA_GET_HWCODING + \brief Get HW Coding Feature +*/ +#define LIBSANGOMA_GET_HWCODING 1 +#endif + +/*! + \fn int sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get HW Voice Coding (ulaw/alaw) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + This function will return the low level voice coding + depending on configuration. (ulaw or alaw) +*/ +int _SAPI_CALL sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api); + + + +#ifndef LIBSANGOMA_GET_HWDTMF +/*! + \def LIBSANGOMA_GET_HWDTMF + \brief HW DTMF Feature +*/ +#define LIBSANGOMA_GET_HWDTMF 1 +#endif +/*! + \fn int sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Check if hwdtmf support is available + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + This function will check if hw supports HW DTMF. +*/ +int _SAPI_CALL sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Check if hw echo cancelation support is available + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + This function will check if hw supports HW EC. +*/ +int _SAPI_CALL sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_span_chan_toif(int span, int chan, char *interface_name) + \brief Convert Span & Chan to interface name + \param span span number starting from 1 to 255 + \param chan chan number starting from 1 to 32 + \param interface_name pointer to string where interface name will be written + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_span_chan_toif(int span, int chan, char *interface_name); + +/*! + \fn int sangoma_span_chan_fromif(char *interface_name, int *span, int *chan) + \brief Convert Interace Name to Span & Chan + \param interface_name pointer to string containing interface name + \param span integer pointer where to write span value + \param chan integer pointer where to write chan value + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_span_chan_fromif(char *interface_name, int *span, int *chan); + + +/*! + \fn int sangoma_interface_wait_up(int span, int chan, int sectimeout) + \brief Wait for a sangoma device to come up (ie: Linux wait for /dev/wanpipex_1 to come up) + \param span span number of the device to wait + \param chan chan number of the device to wait + \param sectimeout how many seconds to wait for the device to come up, -1 to wait forever + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_interface_wait_up(int span, int chan, int sectimeout); + +/*! + \fn int sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, wan_driver_version_t *drv_ver) + \brief Get Device Driver Version Number + \param fd device file descriptor + \param tdm_api tdm api command structure + \param drv_ver driver version structure that will contain the driver version + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, wan_driver_version_t *drv_ver); + +/*! + \fn int sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) + \brief Get Hardware/Firmware Version + \param fd device file descriptor + \param tdm_api tdm api command structure + \param ver hardware/firmware version number + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver); + +/*! + \fn int sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) + \brief Get Hardare/CPLD Version + \param fd device file descriptor + \param tdm_api tdm api command structure + \param ver hardware/cpld version number + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver); + + +/*! + \fn int sangoma_get_stats(sng_fd_t fd, wanpipe_api_t *tdm_api, wanpipe_chan_stats_t *stats) + \brief Get Device Statistics. Statistics will be available in tdm_api->wp_cmd.stats structure. + \param fd device file descriptor + \param tdm_api tdm api command structure + \param stats stats structure will be filled with device stats. (Optional, can be left NULL) + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_get_stats(sng_fd_t fd, wanpipe_api_t *tdm_api, wanpipe_chan_stats_t *stats); + +/*! + \fn int _SAPI_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush/Reset device statistics + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, int rxflashtime) + \brief Set rxflashtime for FXS module Wink-Flash Event + \param fd device file descriptor + \param tdm_api tdm api command structure + \param rxflashtime time value + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, int rxflashtime); + + + +/************************************************************//** + * Device EVENT Function + ***************************************************************/ + + +/*! + \fn int sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Read API Events + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + The TDM API structure will be populated with a TDM API or WAN Event. + This function usually used after wait() function indicated that event + has occured. +*/ +int _SAPI_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api); + +#ifdef WP_API_FEATURE_LOGGER +/*! + \fn sangoma_status_t sangoma_logger_read_event(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Read Wanpipe Logger Events + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error + + The Logger API structure will be populated with a Logger Event. + This function usually used after wait() function indicated that + an event has occured. +*/ +sangoma_status_t _SAPI_CALL sangoma_logger_read_event(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_flush_buffers(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Flush Wanpipe Logger internal buffers + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _SAPI_CALL sangoma_logger_flush_buffers(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_get_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Get Wanpipe Logger statistics + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _SAPI_CALL sangoma_logger_get_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_reset_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Reset Wanpipe Logger statistics + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _SAPI_CALL sangoma_logger_reset_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_get_open_handle_counter(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Get Counter of open Handles/File Descriptors of Wanpipe Logger + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _SAPI_CALL sangoma_logger_get_open_handle_counter(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_get_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Get current level (types of events) of Wanpipe Logger + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _SAPI_CALL sangoma_logger_get_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_set_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Set current level (types of events) of Wanpipe Logger + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _SAPI_CALL sangoma_logger_set_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +#endif /* WP LOGGER FEATURE */ + +#ifndef LIBSANGOMA_LIGHT + +/************************************************************//** + * Device PORT Control Functions + ***************************************************************/ + +/*! + \fn int sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) + \brief Start a Port, create Sangoma Communication interfaces. + \param[in] fd Port Device file descriptor + \param[out] port_mgmnt pointer to a port_management_struct_t structure. + On return, sangoma_driver_port_start() updates operation_status field + of this structure. + \param[in] port_no 1-based Port Number. Port numbers correspond to Port Names. + For example, a 2-Port card will have ports named WANPIPE1 and WANPIPE2. + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_mgmt->operation_status. +*/ +int _SAPI_CALL sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); + + +/*! + \fn int sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) + \brief Start a Port, create Sangoma Communication interfaces. + \param[in] fd Port Device file descriptor + \param[out] port_mgmnt pointer to a port_management_struct_t structure. + On return, sangoma_driver_port_stop() updates operation_status field + of this structure. + \param[in] port_no 1-based Port Number. Port numbers correspond to Port Names. + For example, a 2-Port card will have ports named WANPIPE1 and WANPIPE2. + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_mgmt->operation_status. +*/ +int _SAPI_CALL sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); + + +/*! + \fn int sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no) + \brief Set Port's "Volatile" configuration. The configuration will not persist between system restarts. + Before calling this function please stop the port by calling sangoma_driver_port_stop(). + After calling this function please start the port by calling sangoma_driver_port_start(). + \param[in] fd Port Device file descriptor + \param[in, out] port_cfg pointer to port_cfg_t structure that specifies complete Port configuration. + On return, sangoma_driver_port_set_config() updates operation_status field + of this structure. + \param[in] port_no 1-based Port Number. Port numbers correspond to Port Names. + For example, a 2-Port card will have ports named WANPIPE1 and WANPIPE2. + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_cfg->operation_status. +*/ +int _SAPI_CALL sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no); + + +/*! + \fn int sangoma_driver_port_get_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no) + \brief Retrieve Port's "Volatile" configuration. + \param[in] fd Port Device file descriptor + \param[out] port_cfg pointer to port_cfg_t structure. + On return, sangoma_driver_port_get_config() will copy current Port configuration + into this structure. + \param[in] port_no please see comment of sangoma_driver_port_set_config() + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_cfg->operation_status. +*/ +int _SAPI_CALL sangoma_driver_port_get_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no); + + +/*! + \fn int sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) + \brief Retrieve information about a single instance of Sangoma hardware. + \param[in] fd Port Device file descriptor + \param[out] port_mgmnt pointer to port_management_struct_t structure which will contain hardware_info_t at + it's "data" field, when this function returns. + \param[in] port_no please see comment of sangoma_driver_port_set_config() + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_mgmt->operation_status. +*/ +int _SAPI_CALL sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); + + +/*! + \fn int sangoma_write_port_config_on_persistent_storage(hardware_info_t *hardware_info, port_cfg_t *port_cfg) + \brief Write Port's configuration on the hard disk. + Linux Specific: the "Persistent" configuration of a Port N (e.g. WANPIPE1) is stored in + /etc/wanpipe/wanpipeN.conf (e.g. wanpipe1.conf). + Configuration can be manualy viewed/changed by editing the ".conf" file. + Currently this functionality is not implemented. + Windows Specific: the "Persistent" configuration of a Port (e.g. WANPIPE1) is stored in + Windows Registry. + Configuration can be manualy viewed/changed in the Device Manager. + \param[in] hardware_info pointer to hardware_info_t structure containing information about a + single instance of Sangoma hardware. + \param[in] port_cfg pointer to structure containing complete Port configuration. + \param[in] port_no please see comment of sangoma_driver_port_set_config() + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_write_port_config_on_persistent_storage(hardware_info_t *hardware_info, port_cfg_t *port_cfg, unsigned short port_no); + + +/************************************************************//** + * Device MANAGEMENT Functions + ***************************************************************/ + +/*! + \fn int sangoma_mgmt_cmd(sng_fd_t fd, wan_udp_hdr_t* wan_udp) + \brief Execute Sangoma Management Command + \param fd device file descriptor + \param wan_udp management command structure + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_mgmt_cmd(sng_fd_t fd, wan_udp_hdr_t* wan_udp); + + +#endif /* LIBSANGOMA_LIGHT */ + + +/*================================================================ + * DEPRECATED Function Calls - Not to be used any more + * Here for backward compatibility + *================================================================*/ + + +#ifndef LIBSANGOMA_SET_FESTATUS +/*! + \def LIBSANGOMA_SET_FESTATUS + \brief Set Front End Status Feature +*/ +#define LIBSANGOMA_SET_FESTATUS 1 +#endif + +/*! + \fn int sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status) + \brief Get Device Link Status (Connected/Disconnected) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param current_status pointer where result will be filled: 0=Link UP 1=Link Down + \return non-zero: error, 0: ok -> check current_status + + Deprecated - replaced by sangoma_tdm_get_link_status function +*/ +int _SAPI_CALL sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status); + + + +/*! + \fn int sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int codec) + \brief Set TDM Codec per chan + \param fd device file descriptor + \param tdm_api tdm api command structure + \param codec codec to set (ulaw/alaw/slinear) + \return non-zero: error, 0: ok + + Deprecated Function - Here for backward compatibility + Only valid in CHAN Operation Mode +*/ +int _SAPI_CALL sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int codec); + +/*! + \fn int sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Configured TDM Codec per chan + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative: error or configured codec value + + Deprecated Function - Here for backward compatibility + Only valid in CHAN Operation Mode +*/ +int _SAPI_CALL sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn sng_fd_t sangoma_create_socket_by_name(char *device, char *card) + \brief Open a device based on a interface and card name + \param device interface name + \param card card name + \return File Descriptor: -1 error, 0 or positive integer: valid file descriptor + + Deprecated - here for backward compatibility +*/ +sng_fd_t _SAPI_CALL sangoma_create_socket_by_name(char *device, char *card); + +/*! + \fn int sangoma_interface_toi(char *interface_name, int *span, int *chan) + \brief Convert Span & Chan to interface name + \param interface_name pointer to string where interface name will be written + \param span span number starting from 1 to 255 + \param chan chan number starting from 1 to 32 + \return non-zero = error, 0 = ok + Deprecated - here for backward compatibility +*/ +int _SAPI_CALL sangoma_interface_toi(char *interface_name, int *span, int *chan); + + +/*! + \fn int sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, int power) + \brief Set Power Level - so only data matching the power level would be passed up. + \param fd device file descriptor + \param tdm_api tdm api command structure + \param power value of power + \return non-zero: error, 0: ok + + Deprecated - not used/implemented +*/ +int _SAPI_CALL sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, int power); + +/*! + \fn int sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Configured Power Level + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative: error or configured power level + + Deprecated - not used/implemented +*/ +int _SAPI_CALL sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +#ifdef __cplusplus +} +#endif + +/*! Backward compabile defines */ +#if !defined(__WINDOWS__) +#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 + +#endif /* _LIBSNAGOMA_H */ + diff --git a/api/libsangoma/.svn/tmp/tempfile.19.tmp b/api/libsangoma/.svn/tmp/tempfile.19.tmp new file mode 100644 index 0000000..72c0c9c --- /dev/null +++ b/api/libsangoma/.svn/tmp/tempfile.19.tmp @@ -0,0 +1,2982 @@ +/*******************************************************************************//** + * \file libsangoma.c + * \brief Wanpipe API Code Library for Sangoma AFT T1/E1/Analog/BRI/Serial hardware + * + * Author(s): Nenad Corbic + * David Rokhvarg + * Michael Jerris + * Anthony Minessale II + * + * Copyright: (c) 2005-2008 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 Sangoma Technologies 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 Sangoma Technologies ``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 Sangoma Technologies 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 + ******************************************************************************* + */ + +#include "libsangoma-pvt.h" + +#if defined(__WINDOWS__) +# include /* SetupDiXXX() functions */ +# include /* GUID instantination */ +# include /* DEFINE_GUID() */ +# include "public.h" /* GUID_DEVCLASS_SANGOMA_ADAPTER */ + +# define MAX_COMP_INSTID 2096 +# define MAX_COMP_DESC 2096 +# define MAX_FRIENDLY 2096 +# define TMP_BUFFER_LEN 256 + +/*!+! jpboily used to tell get_out_flags no objects were signaled */ +# define INVALID_INDEX (uint32_t) -1 +# define WP_ALL_BITS_SET ((uint32_t)-1) +#endif + +static void libsng_dbg(const char * fmt, ...) +{ + va_list args; + char buf[1024]; + va_start(args, fmt); + _vsnprintf(buf, sizeof(buf), fmt, args); +#if defined(__WINDOWS__) + OutputDebugString(buf); +#else + printf(buf); +#endif + va_end(args); +} + +/*********************************************************************//** + * WINDOWS Only Section + *************************************************************************/ + +#define DBG_POLL if(0)libsng_dbg +#define DBG_EVNT if(0)libsng_dbg +#define DBG_ERR if(0)libsng_dbg("Error: %s() line: %d : ", __FUNCTION__, __LINE__);if(0)libsng_dbg +#define DBG_INIT if(0)libsng_dbg + +#if defined(__WINDOWS__) +#define DBG_REGISTRY if(0)libsng_dbg + +/* + \fn static void DecodeLastError(LPSTR lpszFunction) + \brief Decodes the Error in radable format. + \param lpszFunction error string + + Private Windows Only Function + */ +static void LibSangomaDecodeLastError(LPSTR lpszFunction) +{ + LPVOID lpMsgBuf; + DWORD dwLastErr = GetLastError(); + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dwLastErr, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ + (LPTSTR) &lpMsgBuf, + 0, + NULL + ); + /* Display the string. */ + DBG_POLL("Last Error in %s(): %s (%d)\n", lpszFunction, lpMsgBuf, dwLastErr); + /* Free the buffer. */ + LocalFree( lpMsgBuf ); +} + +/* + \fn static int handle_device_ioctl_result(int bResult) + \brief Checks result code of ioctl + \param bResult result of ioctl call + + Private Windows Only Function + */ +static u16 handle_device_ioctl_result(int bResult, char *caller_name) +{ + if(bResult == 0){ + /*error*/ + LibSangomaDecodeLastError(caller_name); + return SANG_STATUS_IO_ERROR; + }else{ + return SANG_STATUS_SUCCESS; + } +} + +/* + \fn static int UdpManagementCommand(sng_fd_t fd, wan_udp_hdr_t* wan_udp) + \brief Executes Driver Management Command + \param fd device file descriptor + \param wan_udp managemet cmd structure + + Private Windows Function + */ +static int UdpManagementCommand(sng_fd_t fd, wan_udp_hdr_t* wan_udp) +{ + DWORD ln, bIoResult; + unsigned char id = 0; + + wan_udp->wan_udphdr_request_reply = 0x01; + wan_udp->wan_udphdr_id = id; + wan_udp->wan_udphdr_return_code = WAN_UDP_TIMEOUT_CMD; + + bIoResult = DeviceIoControl( + fd, + IoctlManagementCommand, + (LPVOID)wan_udp, + sizeof(wan_udp_hdr_t), + (LPVOID)wan_udp, + sizeof(wan_udp_hdr_t), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL + ); + + return handle_device_ioctl_result(bIoResult, __FUNCTION__); +} + +/* + \fn static int tdmv_api_ioctl(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) + \brief Executes Driver TDM API Command Wrapper Function + \param fd device file descriptor + \param api_cmd tdm_api managemet cmd structure + + Private Windows Function + */ +static int tdmv_api_ioctl(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) +{ + DWORD ln, bIoResult; + + bIoResult = DeviceIoControl( + fd, + IoctlTdmApiCommand, + (LPVOID)api_cmd, + sizeof(wanpipe_tdm_api_cmd_t), + (LPVOID)api_cmd, + sizeof(wanpipe_tdm_api_cmd_t), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL + ); + + return handle_device_ioctl_result(bIoResult, __FUNCTION__); +} + +/* + \fn static USHORT DoReadCommand(sng_fd_t fd, RX_DATA_STRUCT * pRx) + \brief API READ Function + \param drv device file descriptor + \param pRx receive data structure + + Private Windows Function + This function will NOT block because using IoctlReadCommandNonBlocking. + */ +static USHORT DoReadCommand(sng_fd_t fd, RX_DATA_STRUCT * pRx) +{ + DWORD ln, bIoResult; + + bIoResult = DeviceIoControl( + fd, + IoctlReadCommandNonBlocking, + (LPVOID)NULL,/*NO input buffer!*/ + 0, + (LPVOID)pRx, + sizeof(RX_DATA_STRUCT), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL); + + return handle_device_ioctl_result(bIoResult, __FUNCTION__); +} + +/* + \fn static UCHAR DoWriteCommand(sng_fd_t fd, TX_DATA_STRUCT * pTx) + \brief API Write Function + \param drv device file descriptor + \param pRx receive data structure + + Private Windows Function + In Legacy API mode this fuction will Block if data is busy. + In API mode no function is allowed to Block + */ +static UCHAR DoWriteCommand(sng_fd_t fd, + void *input_data_buffer, u32 size_of_input_data_buffer, + void *output_data_buffer, u32 size_of_output_data_buffer + ) +{ + DWORD BytesReturned, bIoResult; + + bIoResult = DeviceIoControl( + fd, + IoctlWriteCommand, + (LPVOID)input_data_buffer, + size_of_input_data_buffer, + (LPVOID)output_data_buffer, + size_of_output_data_buffer, + (LPDWORD)(&BytesReturned), + (LPOVERLAPPED)NULL); + + return (UCHAR)handle_device_ioctl_result(bIoResult, __FUNCTION__); +} + +/* + \fn static USHORT sangoma_poll_fd(sng_fd_t fd, API_POLL_STRUCT *api_poll_ptr) + \brief Non Blocking API Poll function used to find out if Rx Data, Events or + Free Tx buffer available + \param drv device file descriptor + \param api_poll_ptr poll device that stores polling information read/write/event + \param overlapped pointer to system overlapped io structure. + + Private Windows Function + */ +static USHORT sangoma_poll_fd(sng_fd_t fd, API_POLL_STRUCT *api_poll_ptr) +{ + DWORD ln, bIoResult; + + bIoResult = DeviceIoControl( + fd, + IoctlApiPoll, + (LPVOID)NULL, + 0L, + (LPVOID)api_poll_ptr, + sizeof(API_POLL_STRUCT), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL); + + return handle_device_ioctl_result(bIoResult, __FUNCTION__); +} + +/* + \fn static int logger_api_ioctl(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Executes Logger API IOCTL + \param fd device file descriptor + \param logger_cmd Logger API command structure + + Private Windows Function + */ +static sangoma_status_t logger_api_ioctl(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + DWORD ln, bIoResult; + + bIoResult = DeviceIoControl( + fd, + IoctlLoggerApiCommand, + (LPVOID)logger_cmd, + sizeof(wp_logger_cmd_t), + (LPVOID)logger_cmd, + sizeof(wp_logger_cmd_t), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL ); + + return handle_device_ioctl_result(bIoResult, __FUNCTION__); +} + +/* This function is exported only for debugging purposes and NOT a part of API. */ +sangoma_status_t _SAPI_CALL sangoma_cdev_ctrl_cmd(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) +{ + DWORD ln, bIoResult; + + bIoResult = DeviceIoControl( + fd, + IoctlCdevControlCommand, + (LPVOID)api_cmd, + sizeof(wanpipe_tdm_api_cmd_t), + (LPVOID)api_cmd, + sizeof(wanpipe_tdm_api_cmd_t), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL + ); + + return handle_device_ioctl_result(bIoResult, __FUNCTION__); +} + +static sangoma_status_t init_sangoma_event_object(sangoma_wait_obj_t *sng_wait_obj, int flags_in, sng_fd_t fd) +{ + int event_index = -1; + wanpipe_tdm_api_cmd_t tdm_api_cmd; + sangoma_status_t sng_status; + char event_name[200], tmp_event_name[200]; + + memset(&tdm_api_cmd, 0x00, sizeof(tdm_api_cmd)); + + tdm_api_cmd.cmd = WP_CDEV_CMD_GET_INTERFACE_NAME; + sng_status = sangoma_cdev_ctrl_cmd(fd, &tdm_api_cmd); + if(SANG_ERROR(sng_status)){ + DBG_ERR("sangoma_cdev_ctrl_cmd() failed!\n"); + return sng_status; + } + + DBG_EVNT("%s(): interface name: %s\n", __FUNCTION__, tdm_api_cmd.data); + + if(flags_in & POLLIN){ + event_index = LIBSNG_EVENT_INDEX_POLLIN; + /* Form the Event Name from Interface Name and Event Type (i.e. wanpipe1_if1_pollin). */ + _snprintf(tmp_event_name, sizeof(tmp_event_name), "%s_pollin", tdm_api_cmd.data); + } + + if(flags_in & POLLOUT){ + event_index = LIBSNG_EVENT_INDEX_POLLOUT; + /* Form the Event Name from Interface Name and Event Type (i.e. wanpipe1_if1_pollout). */ + _snprintf(tmp_event_name, sizeof(tmp_event_name), "%s_pollout", tdm_api_cmd.data); + } + + if(flags_in & POLLPRI){ + event_index = LIBSNG_EVENT_INDEX_POLLPRI; + /* Form the Event Name from Interface Name and Event Type (i.e. wanpipe1_if1_pollpri). */ + _snprintf(tmp_event_name, sizeof(tmp_event_name), "%s_pollpri", tdm_api_cmd.data); + } + + if(event_index == -1){ + /* invalid 'flags_in', this should be an assert */ + return SANG_STATUS_GENERAL_ERROR; + } + + /* 1. The Sangoma Device Driver creates Notification Events in "\\BaseNamedObjects\\Global\\" + * when first CreateFile() was called by a process. That means the Events will inherit + * the security attributes of the calling process, so the calling process will have + * the permissions to open the Events by calling OpenEvent(). Since Events are created + * "Global" subdirectory, the calling process does NOT need Administrator priveleges. + * 2. The Events are deleted when the last HANDLE for a device is closed by CloseHandle() + * or automatically by the system when calling process exits. */ + _snprintf(event_name, sizeof(event_name), "Global\\%s", tmp_event_name); + + sng_wait_obj->sng_event_objects[event_index] = OpenEvent(EVENT_ALL_ACCESS, TRUE, event_name); + if(NULL == sng_wait_obj->sng_event_objects[event_index]){ + /* error */ + LibSangomaDecodeLastError(__FUNCTION__); + return SANG_STATUS_GENERAL_ERROR; + } + + return SANG_STATUS_SUCCESS; +} + +static sangoma_status_t _SAPI_CALL sangoma_wait_obj_poll(sangoma_wait_obj_t *sangoma_wait_object, int flags_in, int *flags_out) +{ + int err; + sangoma_wait_obj_t *sng_wait_obj = sangoma_wait_object; + /*! api structure used by windows IoctlApiPoll call */ + API_POLL_STRUCT api_poll; + + *flags_out = 0; + + memset(&api_poll, 0x00, sizeof(API_POLL_STRUCT)); + api_poll.user_flags_bitmap = flags_in; + + /* This call is non-blocking - it will return immediatly. */ + if(sangoma_poll_fd(sng_wait_obj->fd, &api_poll)){ + /* error - ioctl failed */ + return SANG_STATUS_IO_ERROR; + } + + if(api_poll.operation_status == SANG_STATUS_SUCCESS){ + *flags_out = api_poll.poll_events_bitmap; + err = 0; + }else{ + /* error - command failed */ + err = api_poll.operation_status; + } + + if(*flags_out == 0){ + /*DBG_POLL("======%s(): *flags_out: 0x%X, flags_in: 0x%X\n", __FUNCTION__, *flags_out, flags_in);*/ + } + return err; +} + +static int check_number_of_wait_objects(uint32_t number_of_objects, const char *caller_function, int lineno) +{ + if(number_of_objects >= MAXIMUM_WAIT_OBJECTS){ + DBG_ERR("Caller: %s(): Line: %d: 'number_of_objects': %d is greater than the Maximum of: %d\n", + caller_function, lineno, number_of_objects, MAXIMUM_WAIT_OBJECTS); + return 1; + } + return 0; +} + +static sangoma_status_t get_out_flags(IN sangoma_wait_obj_t *sng_wait_objects[], + IN uint32_t first_signaled_obj_index, + IN uint32_t in_flags[], OUT uint32_t out_flags[], + IN uint32_t number_of_sangoma_wait_objects, + OUT BOOL *at_least_one_poll_set_flags_out) +{ + uint32_t i, j; + + if(at_least_one_poll_set_flags_out){ + *at_least_one_poll_set_flags_out = FALSE; + } + + for(i = 0; i < number_of_sangoma_wait_objects; i++) { + + sangoma_wait_obj_t *sangoma_wait_object = sng_wait_objects[i]; + + if (!SANGOMA_OBJ_HAS_DEVICE(sangoma_wait_object)) { + + /* This object does not has a device, but, may have been signaled via sangoma_wait_obj_signal() + * test if the object is signaled, if it is, set SANG_WAIT_OBJ_IS_SIGNALED */ + + if((i == first_signaled_obj_index) || (WaitForSingleObject(sangoma_wait_object->generic_event_object, 0) == WAIT_OBJECT_0)) { + /* !+! jpboily : Since WaitForMultipleObjects cleared the status + * of the first signaled object, we make sure that the out_flag + * for this object is set */ + out_flags[i] |= SANG_WAIT_OBJ_IS_SIGNALED; + } + continue; + } + + for(j = 0; j < LIBSNG_NUMBER_OF_EVENT_OBJECTS; j++){ + + if(!sangoma_wait_object->sng_event_objects[j]) { + continue; + } + + if(sangoma_wait_obj_poll(sangoma_wait_object, in_flags[i], &out_flags[i])){ + return SANG_STATUS_GENERAL_ERROR; + } + + if( out_flags[i] & in_flags[i] ){ + if(at_least_one_poll_set_flags_out){ + *at_least_one_poll_set_flags_out = TRUE; + } + } +#if 0 + /* Check if a device-related object was signalled, if it is, set SANG_WAIT_OBJ_IS_SIGNALED. */ + if (WaitForSingleObject(sangoma_wait_object->sng_event_objects[j], 0) == WAIT_OBJECT_0) { + out_flags[i] |= SANG_WAIT_OBJ_IS_SIGNALED; + } +#endif + }/* for(j = 0; j < LIBSNG_NUMBER_OF_EVENT_OBJECTS; j++) */ + }/* for(i = 0; i < number_of_sangoma_wait_objects; i++) */ + + return SANG_STATUS_SUCCESS; +} + +/*! + \brief Brief description + */ +static LONG registry_get_string_value(HKEY hkey, LPTSTR szKeyname, OUT LPSTR szValue, OUT DWORD *pdwSize) +{ + LONG iReturnCode; + + /* reading twice to set pdwSize to needed value */ + RegQueryValueEx(hkey, szKeyname, NULL, NULL, (unsigned char *)szValue, pdwSize); + + iReturnCode = RegQueryValueEx(hkey, szKeyname, NULL, NULL, (unsigned char *)szValue, pdwSize); + if(ERROR_SUCCESS == iReturnCode){ + iReturnCode = 0; + } + DBG_REGISTRY("%s(): %s: %s: iReturnCode: %d\n", __FUNCTION__, szKeyname, szValue, iReturnCode); + return iReturnCode; +} + +/*! + \brief Brief description + */ +static LONG registry_set_string_value(HKEY hkey, LPTSTR szKeyname, IN LPSTR szValue) +{ + DWORD dwSize; + LONG iReturnCode; + + dwSize = strlen(szValue) + 1; + + iReturnCode = RegSetValueEx(hkey, szKeyname, 0, REG_SZ, (unsigned char *)szValue, dwSize); + DBG_REGISTRY("%s(): %s: %s\n", __FUNCTION__, szKeyname, szValue); + + if(ERROR_SUCCESS == iReturnCode){ + iReturnCode = 0; + } + return iReturnCode; +} + +/*! + \brief Convert an integer (iValue) to string and write it to registry + */ +static LONG registry_set_integer_value(HKEY hkey, LPTSTR szKeyname, IN int iValue) +{ + DWORD dwSize; + char szTemp[TMP_BUFFER_LEN]; + LONG iReturnCode; + + sprintf(szTemp, "%u", iValue); + + dwSize = strlen(szTemp) + 1; + iReturnCode = RegSetValueEx(hkey, szKeyname, 0, REG_SZ, (unsigned char *)szTemp, dwSize); + + if(ERROR_SUCCESS == iReturnCode){ + iReturnCode = 0; + } + + DBG_REGISTRY("%s(): %s: %d: iReturnCode: %d\n", __FUNCTION__, szKeyname, iValue, iReturnCode); + return iReturnCode; +} + +/*! + * \brief Go through the list of ALL "Sangoma Hardware Abstraction Driver" ports installed on the system. + * Read Bus/Slot/Port information for a port and compare with what is searched for. + */ +static HKEY registry_open_port_key(hardware_info_t *hardware_info) +{ + int i, iRegistryReturnCode; + SP_DEVINFO_DATA deid={sizeof(SP_DEVINFO_DATA)}; + HDEVINFO hdi = SetupDiGetClassDevs((struct _GUID *)&GUID_DEVCLASS_SANGOMA_ADAPTER, NULL,NULL, DIGCF_PRESENT); + char DeviceName[TMP_BUFFER_LEN], PCI_Bus[TMP_BUFFER_LEN], PCI_Slot[TMP_BUFFER_LEN], Port_Number[TMP_BUFFER_LEN]; + DWORD dwTemp; + HKEY hKeyTmp = (struct HKEY__ *)INVALID_HANDLE_VALUE; + HKEY hPortRegistryKey = (struct HKEY__ *)INVALID_HANDLE_VALUE; + + char szCompInstanceId[MAX_COMP_INSTID]; + TCHAR szCompDescription[MAX_COMP_DESC]; + DWORD dwRegType; + /* Possible Sangoma Port Names (refer to sdladrv.inf): + Sangoma Hardware Abstraction Driver (Port 1) + Sangoma Hardware Abstraction Driver (Port 2) + Sangoma Hardware Abstraction Driver (Port 3) + Sangoma Hardware Abstraction Driver (Port 4) + Sangoma Hardware Abstraction Driver (Port 5) + Sangoma Hardware Abstraction Driver (Port 6) + Sangoma Hardware Abstraction Driver (Port 7) + Sangoma Hardware Abstraction Driver (Port 8) + Sangoma Hardware Abstraction Driver (Analog) + Sangoma Hardware Abstraction Driver (ISDN BRI) */ + const TCHAR *search_SubStr = "Sangoma Hardware Abstraction Driver"; + + /* search for all (AFT) ports: */ + for (i = 0; SetupDiEnumDeviceInfo(hdi, i, &deid); i++){ + + BOOL fSuccess = SetupDiGetDeviceInstanceId(hdi, &deid, (PSTR)szCompInstanceId, MAX_COMP_INSTID, NULL); + if (!fSuccess){ + continue; + } + + fSuccess = SetupDiGetDeviceRegistryProperty(hdi, &deid, SPDRP_DEVICEDESC, &dwRegType, (BYTE*)szCompDescription, MAX_COMP_DESC, NULL); + if (!fSuccess){ + continue; + } + + if (strstr(szCompDescription, search_SubStr)) { /* Windows can add #2 etc - do NOT consider */ + /* This is a "Sangoma Card" device, we are interested only in "Sangoma Port" devices. */ + continue; + } + + DBG_REGISTRY("* %s\n", szCompDescription); + + hKeyTmp = SetupDiOpenDevRegKey(hdi, &deid, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ | KEY_SET_VALUE ); + if(hKeyTmp == INVALID_HANDLE_VALUE){ + DBG_REGISTRY("Error: Failed to open Registry key!!\n"); + continue; + } + + PCI_Bus[0] = '\0'; + iRegistryReturnCode = registry_get_string_value(hKeyTmp, "PCI_Bus", PCI_Bus, &dwTemp); + if(iRegistryReturnCode){ + continue; + } + + PCI_Slot[0] = '\0'; + iRegistryReturnCode = registry_get_string_value(hKeyTmp, "PCI_Slot", PCI_Slot, &dwTemp); + if(iRegistryReturnCode){ + continue; + } + + Port_Number[0] = '\0'; + iRegistryReturnCode = registry_get_string_value(hKeyTmp, "Port_Number", Port_Number, &dwTemp); + if(iRegistryReturnCode){ + continue; + } + + if( atoi(PCI_Bus) == hardware_info->pci_bus_number && + atoi(PCI_Slot) == hardware_info->pci_slot_number && + atoi(Port_Number) == hardware_info->port_number ){ + + hPortRegistryKey = hKeyTmp; + + /* read device name for debugging only */ + DeviceName[0] = '\0'; + registry_get_string_value(hPortRegistryKey, "DeviceName", DeviceName, &dwTemp); + + DBG_REGISTRY("Found Port's Registry key: DeviceName: %s, PCI_Bus: %s, PCI_Slot: %s, Port_Number: %s\n", + DeviceName, PCI_Bus, PCI_Slot, Port_Number); + break; + }/* if() */ + }/* for() */ + + SetupDiDestroyDeviceInfoList(hdi); + + DBG_REGISTRY("hPortRegistryKey: 0x%X\n", hPortRegistryKey); + + return hPortRegistryKey; +} + +static char* timeslot_bitmap_to_string(int bitmap) +{ + int i = 0, range_counter = 0; + int start_channel = -1, stop_channel = -1; + static char tmp_string[256]; + + if( WP_ALL_BITS_SET == bitmap ){ + return "ALL"; /* If all bits are set, use the "ALL" keyword. */ + } + + tmp_string[0] = '\0'; + + /* all ranges between two zeros is what we will look for */ + for(i = 0; i < sizeof(bitmap) * 8; i++){ + + if(start_channel < 0){ + /* found a starting one of a range */ + if(bitmap & (1 << i)){ + start_channel = i + 1; + } + } + + if(start_channel >= 0){ + if((bitmap & (1 << i)) == 0){ + + /* we hit a zero, that means the previous channel is one */ + stop_channel = i; + + }else if(i == (sizeof(bitmap) * 8 - 1)){ + /* The most significant bit is set - there will be no delimiting zero. + * It will also take care of "all channels" which is a special + * case - there is a start but no stop channel because all bits + * are set. result will be 1-32 */ + stop_channel = (sizeof(bitmap) * 8); + } + } + + if(start_channel >= 0 && stop_channel >= 0){ + + if(range_counter){ + /* put '.' separator from the previous range */ + _snprintf(&tmp_string[strlen(tmp_string)], sizeof(tmp_string) - strlen(tmp_string), "."); + } + + if(start_channel == stop_channel){ + /* the range contains a SINGLE channel */ + _snprintf(&tmp_string[strlen(tmp_string)], sizeof(tmp_string) - strlen(tmp_string), + "%d", start_channel); + }else{ + /* the range contains MULTIPLE channels */ + _snprintf(&tmp_string[strlen(tmp_string)], sizeof(tmp_string) - strlen(tmp_string), + "%d-%d", start_channel, stop_channel); + } + + start_channel = stop_channel = -1; + range_counter++; + } + }/* for() */ + + return tmp_string; +} + +static int registry_write_front_end_cfg(HKEY hPortRegistryKey, port_cfg_t *port_cfg) +{ + wandev_conf_t *wandev_conf = &port_cfg->wandev_conf; + sdla_fe_cfg_t *sdla_fe_cfg = &wandev_conf->fe_cfg; + sdla_te_cfg_t *te_cfg = &sdla_fe_cfg->cfg.te_cfg; + sdla_remora_cfg_t *analog_cfg = &sdla_fe_cfg->cfg.remora; + sdla_bri_cfg_t *bri_cfg = &sdla_fe_cfg->cfg.bri; + int iReturnCode = 0; + + /* set Media specific values. */ + switch(sdla_fe_cfg->media) + { + case WAN_MEDIA_T1: + case WAN_MEDIA_J1: /* the same as T1 */ + case WAN_MEDIA_E1: + /* only T1/E1 Port can change the Media, all other ports ignore this parameter. */ + iReturnCode = registry_set_integer_value(hPortRegistryKey, "Media", sdla_fe_cfg->media /*WAN_MEDIA_T1*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "LDecoding", sdla_fe_cfg->lcode /*WAN_LCODE_B8ZS*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "Framing", sdla_fe_cfg->frame /*WAN_FR_ESF*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "ClkRefPort", te_cfg->te_ref_clock); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "TE_IGNORE_YEL", te_cfg->ignore_yel_alarm); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "ACTIVE_CH", ENABLE_ALL_CHANNELS /*must be hardcoded*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "TE_RBS_CH", te_cfg->te_rbs_ch); /*not used by DS chip code, only by PMC */ + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "LBO", te_cfg->lbo /*WAN_T1_LBO_0_DB*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "ClkMode", te_cfg->te_clock /*WAN_NORMAL_CLK*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "HighImpedanceMode",te_cfg->high_impedance_mode /*WANOPT_NO*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "TE_RX_SLEVEL", te_cfg->rx_slevel /*WAN_TE1_RX_SLEVEL_12_DB*/); + if(iReturnCode){ + break; + } + + /* write E1 signalling for both T1 and E1 */ + iReturnCode = registry_set_integer_value(hPortRegistryKey, "E1Signalling", te_cfg->sig_mode /*WAN_TE1_SIG_CCS*/); + if(iReturnCode){ + break; + } + break; + + case WAN_MEDIA_56K: + /* do nothing */ + iReturnCode = 0; + break; + + case WAN_MEDIA_FXOFXS: + /* Analog global (per-card) settings */ + iReturnCode = registry_set_string_value(hPortRegistryKey, "remora_fxo_operation_mode_name", analog_cfg->opermode_name); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_BATTTHRESH", analog_cfg->battthresh); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_BATTDEBOUNCE", analog_cfg->battdebounce); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_FXO_TAPPING", analog_cfg->rm_mode); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_FXO_TAPPING_OFF_HOOK_THRESHOLD",analog_cfg->ohthresh); + if(iReturnCode){ + break; + } + break; + + case WAN_MEDIA_BRI: + iReturnCode = registry_set_integer_value(hPortRegistryKey, "aft_bri_clock_mode", bri_cfg->clock_mode); + if(iReturnCode){ + break; + } + break; + + case WAN_MEDIA_SERIAL: + iReturnCode = registry_set_integer_value(hPortRegistryKey, "clock_source", wandev_conf->clocking); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "BaudRate", wandev_conf->bps); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "serial_connection_type", wandev_conf->connection); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "serial_line_coding", wandev_conf->line_coding); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "serial_line_idle", wandev_conf->line_idle); + if(iReturnCode){ + break; + } + break; + + default: + DBG_ERR("Invalid Media Type %d!\n", sdla_fe_cfg->media); + iReturnCode = 1; + } + + return iReturnCode; +} + +static int registry_write_wan_tdmv_conf(HKEY hPortRegistryKey, port_cfg_t *port_cfg) +{ + wandev_conf_t *wandev_conf = &port_cfg->wandev_conf; + sdla_fe_cfg_t *sdla_fe_cfg = &wandev_conf->fe_cfg; + wan_tdmv_conf_t *tdmv_conf = &wandev_conf->tdmv_conf; + int iReturnCode = 1; + + /* set Media specific values. */ + switch(sdla_fe_cfg->media) + { + case WAN_MEDIA_T1: + case WAN_MEDIA_J1: /* the same as T1 */ + case WAN_MEDIA_E1: + /* write dchannel bitmap as a string (the same way as active channels) */ + iReturnCode = registry_set_string_value(hPortRegistryKey, "TDMV_DCHAN", timeslot_bitmap_to_string(tdmv_conf->dchan)); + break; + + case WAN_MEDIA_56K: + case WAN_MEDIA_FXOFXS: + case WAN_MEDIA_BRI: + case WAN_MEDIA_SERIAL: + /* do nothing */ + iReturnCode = 0; + break; + + default: + DBG_ERR("Invalid Media Type %d!\n", sdla_fe_cfg->media); + iReturnCode = 2; + break; + } + + return iReturnCode; +} + +static int registry_write_channel_group_cfg(HKEY hPortRegistryKey, port_cfg_t *port_cfg, int interface_index, wanif_conf_t wanif_conf) +{ + char szTemp[TMP_BUFFER_LEN]; + int iReturnCode = 0; + + do{ + // Line mode + _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_line_mode", interface_index); + iReturnCode = registry_set_string_value(hPortRegistryKey, szTemp, + (wanif_conf.hdlc_streaming == WANOPT_YES ? MODE_OPTION_HDLC : MODE_OPTION_BITSTRM)); + if(iReturnCode){ + break; + } + + // MTU + _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_mtu", interface_index); + iReturnCode = registry_set_integer_value(hPortRegistryKey, szTemp, wanif_conf.mtu); + if(iReturnCode){ + break; + } + + // Operational mode + _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_operational_mode", interface_index); + iReturnCode = registry_set_string_value(hPortRegistryKey, szTemp, wanif_conf.usedby); + if(iReturnCode){ + break; + } + + // Active Timeslots for the Group + _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_active_ch", interface_index); + iReturnCode = registry_set_string_value(hPortRegistryKey, szTemp, timeslot_bitmap_to_string(wanif_conf.active_ch)); + if(iReturnCode){ + break; + } + + // Idle char. + _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_idle_char", interface_index); + iReturnCode = registry_set_integer_value(hPortRegistryKey, szTemp, wanif_conf.u.aft.idle_flag); + if(iReturnCode){ + break; + } + + }while(0); + + return iReturnCode; +} + +/*! + * \brief Go through the list of ALL Sangoma PCI Adapters (Cards) on the system. + * Enable or Disable each one of them (state change will be visible in the Device Manager). + */ +static sangoma_status_t _SAPI_CALL sangoma_control_cards(DWORD StateChange) +{ + sangoma_status_t rc; + int i; + SP_DEVINFO_DATA deid = {sizeof(SP_DEVINFO_DATA)}; + HDEVINFO hdi; + DWORD dwTemp, dwRegType; + TCHAR szInstanceId[MAX_COMP_INSTID]; + + const TCHAR *szAftSearchSubStr = "PCI\\VEN_1923"; /* Sangoma PCI Vendor ID is 1923 */ + /*const TCHAR *szS518AdslSearchSubStr = "PCI\\VEN_14BC&DEV_D002";*/ /* Note: S518 is end-of-life */ + + SP_PROPCHANGE_PARAMS pcp; + + hdi = SetupDiGetClassDevs((struct _GUID *)&GUID_DEVCLASS_SANGOMA_ADAPTER, NULL, NULL, DIGCF_PRESENT); + if(INVALID_HANDLE_VALUE == hdi){ + /* no sangoma cards installed on the system */ + return SANG_STATUS_INVALID_DEVICE; + } + + /* search for all AFT Cards: */ + for (i = 0; SetupDiEnumDeviceInfo(hdi, i, &deid); i++){ + + BOOL fSuccess = SetupDiGetDeviceInstanceId(hdi, &deid, (PSTR)szInstanceId, sizeof(szInstanceId), NULL); + if (!fSuccess){ + rc = SANG_STATUS_REGISTRY_ERROR; + break; + } + if(!strstr(szInstanceId, szAftSearchSubStr)){ + /* Found Sangoma Port, but we are interested only Cards. */ + continue; + } + + memset(&pcp, 0x00, sizeof(pcp)); + //Set the PropChangeParams structure. + pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER); + pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE; + pcp.Scope = DICS_FLAG_GLOBAL; /* DICS_FLAG_CONFIGSPECIFIC will enable ALL cards, including + * those explicetly DISABLED by the user in the Device Manager, + * and we don't want to force anything on the user, hence we + * use DICS_FLAG_GLOBAL. */ + pcp.StateChange = StateChange; + + if(!SetupDiSetClassInstallParams(hdi, &deid, (SP_CLASSINSTALL_HEADER *)&pcp, sizeof(pcp))){ + rc = SANG_STATUS_REGISTRY_ERROR; + break; + } + + if(!SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, hdi, &deid)){ + rc = SANG_STATUS_REGISTRY_ERROR; + break; + } + + }/* for() */ + + SetupDiDestroyDeviceInfoList(hdi); + + return SANG_STATUS_SUCCESS; +} + +sangoma_status_t _SAPI_CALL sangoma_unload_driver() +{ + return sangoma_control_cards(DICS_DISABLE); +} + +sangoma_status_t _SAPI_CALL sangoma_load_driver() +{ + return sangoma_control_cards(DICS_ENABLE); +} + +#endif /* __WINDOWS__ */ + + +/*********************************************************************//** + * Common Linux & Windows Code + *************************************************************************/ + + + +/************************************************************//** + * Device POLL Functions + ***************************************************************/ + +/*! + \fn sangoma_status_t sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma_wait_object, sng_fd_t fd, sangoma_wait_obj_type_t object_type) + \brief Create a wait object that will be used with sangoma_waitfor_many() API + \param sangoma_wait_object pointer a single device object + \param fd device file descriptor + \param object_type type of the wait object. see sangoma_wait_obj_type_t for types + \see sangoma_wait_obj_type_t + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _SAPI_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma_wait_object, sng_fd_t fd, sangoma_wait_obj_type_t object_type) +{ + int err = 0; + sangoma_wait_obj_t *sng_wait_obj = NULL; + + if (!sangoma_wait_object) { + return SANG_STATUS_INVALID_DEVICE; + } + *sangoma_wait_object = NULL; + sng_wait_obj = malloc(sizeof(**sangoma_wait_object)); + if (!sng_wait_obj) { + return SANG_STATUS_FAILED_ALLOCATE_MEMORY; + } + + memset(sng_wait_obj, 0x00, sizeof(*sng_wait_obj)); + /* it is a first initialization of the object */ + sng_wait_obj->init_flag = LIBSNG_MAGIC_NO; + + sng_wait_obj->fd = fd; + sng_wait_obj->object_type = object_type; + +#if defined(__WINDOWS__) + DBG_INIT("%s(): sng_wait_obj ptr: 0x%p\n", __FUNCTION__, sng_wait_obj); + DBG_INIT("%s(): fd: 0x%X, object_type: %s\n", __FUNCTION__, fd, DECODE_SANGOMA_WAIT_OBJECT_TYPE(object_type)); + DBG_INIT("%s(): sizeof(**sangoma_wait_object): %d\n", __FUNCTION__, sizeof(**sangoma_wait_object)); + + if (SANGOMA_OBJ_IS_SIGNALABLE(sng_wait_obj)) { + sng_wait_obj->generic_event_object = CreateEvent( NULL, FALSE, FALSE, NULL); + if(!sng_wait_obj->generic_event_object){ + err = SANG_STATUS_GENERAL_ERROR; + goto failed; + } + } + + if(SANGOMA_GENERIC_WAIT_OBJ == object_type){ + /* everything is done for the generic wait object */ + *sangoma_wait_object = sng_wait_obj; + return SANG_STATUS_SUCCESS; + } + + err = init_sangoma_event_object(sng_wait_obj, POLLIN, fd); + if(SANG_STATUS_SUCCESS != err){ + goto failed; + } + + err = init_sangoma_event_object(sng_wait_obj, POLLOUT, fd); + if(SANG_STATUS_SUCCESS != err){ + goto failed; + } + + err = init_sangoma_event_object(sng_wait_obj, POLLPRI, fd); + if(SANG_STATUS_SUCCESS != err) { + goto failed; + } + + DBG_INIT("%s(): returning: %d", __FUNCTION__, err); +#else + int filedes[2]; + if (SANGOMA_OBJ_IS_SIGNALABLE(sng_wait_obj)) { + sng_wait_obj->signal_read_fd = INVALID_HANDLE_VALUE; + sng_wait_obj->signal_write_fd = INVALID_HANDLE_VALUE; + /* if we want cross-process event notification we can use a named pipe with mkfifo() */ + if (pipe(filedes)) { + err = SANG_STATUS_GENERAL_ERROR; + goto failed; + } + sng_wait_obj->signal_read_fd = filedes[0]; + sng_wait_obj->signal_write_fd = filedes[1]; + } +#endif + *sangoma_wait_object = sng_wait_obj; + return err; + +failed: + if (sng_wait_obj) { + sangoma_wait_obj_delete(&sng_wait_obj); + } + return err; +} + +/*! + \fn sangoma_status_t sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object) + \brief De-allocate all resources in a wait object + \param sangoma_wait_object pointer to a pointer to a single device object + \return SANG_STATUS_SUCCESS on success or some sangoma status error +*/ +sangoma_status_t _SAPI_CALL sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object) +{ + sangoma_wait_obj_t *sng_wait_obj = *sangoma_wait_object; +#if defined(__WINDOWS__) + int index = 0; +#endif + + if(sng_wait_obj->init_flag != LIBSNG_MAGIC_NO){ + /* error. object was not initialized by sangoma_wait_obj_init() */ + return SANG_STATUS_INVALID_DEVICE; + } + +#if defined(__WINDOWS__) + if (SANGOMA_OBJ_IS_SIGNALABLE(sng_wait_obj)) { + sangoma_close(&sng_wait_obj->generic_event_object); + } + if (SANGOMA_OBJ_HAS_DEVICE(sng_wait_obj)) { + for(index = 0; index < LIBSNG_NUMBER_OF_EVENT_OBJECTS; index++){ + sangoma_close(&sng_wait_obj->sng_event_objects[index]); + } + } +#else + if (SANGOMA_OBJ_IS_SIGNALABLE(sng_wait_obj)) { + sangoma_close(&sng_wait_obj->signal_read_fd); + sangoma_close(&sng_wait_obj->signal_write_fd); + } +#endif + sng_wait_obj->init_flag = 0; + sng_wait_obj->object_type = UNKNOWN_WAIT_OBJ; + free(sng_wait_obj); + *sangoma_wait_object = NULL; + return SANG_STATUS_SUCCESS; +} + +/*! + \fn void sangoma_wait_obj_signal(void *sangoma_wait_object) + \brief Set wait object to a signaled state + \param sangoma_wait_object pointer a single device object + \return 0 on success, non-zero on error +*/ +int _SAPI_CALL sangoma_wait_obj_signal(sangoma_wait_obj_t *sng_wait_obj) +{ + if (!SANGOMA_OBJ_IS_SIGNALABLE(sng_wait_obj)) { + /* even when Windows objects are always signalable for the sake of providing + * a consistent interface to the user we downgrade the capabilities of Windows + * objects unless the sangoma wait object is explicitly initialized as signalable + * */ + return SANG_STATUS_INVALID_DEVICE; + } +#if defined(__WINDOWS__) + if(sng_wait_obj->generic_event_object){ + if (!SetEvent(sng_wait_obj->generic_event_object)) { + return SANG_STATUS_GENERAL_ERROR; + } + } +#else + /* at this point we know is a signalable object and has a signal_write_fd */ + if (write(sng_wait_obj->signal_write_fd, "s", 1) < 1) { + return SANG_STATUS_GENERAL_ERROR; + } +#endif + return SANG_STATUS_SUCCESS; +} + +/*! + \fn sng_fd_t sangoma_wait_obj_get_fd(void *sangoma_wait_object) + \brief Retrieve fd device file descriptor which was the 'fd' parameter for sangoma_wait_obj_init() + \param sangoma_wait_object pointer a single device object + \return fd - device file descriptor +*/ +sng_fd_t _SAPI_CALL sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sng_wait_obj) +{ + return sng_wait_obj->fd; +} + +/*! + \fn void sangoma_wait_obj_set_context(sangoma_wait_obj_t *sangoma_wait_object) + \brief Store the given context into provided sangoma wait object. + \brief This function is useful to associate a context with a sangoma wait object. + \param sangoma_wait_object pointer a single device object + \param context void pointer to user context + \return void +*/ +void _SAPI_CALL sangoma_wait_obj_set_context(sangoma_wait_obj_t *sng_wait_obj, void *context) +{ + sng_wait_obj->context = context; +} + +/*! + \fn void *sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object) + \brief Retrieve the user context (if any) that was set via sangoma_wait_obj_set_context. + \param sangoma_wait_object pointer a single device object + \return void* +*/ +void* _SAPI_CALL sangoma_wait_obj_get_context(sangoma_wait_obj_t *sng_wait_obj) +{ + return sng_wait_obj->context; +} + +/*! + \fn sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], int32_t in_flags[], int32_t out_flags[] uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout); + \brief Wait for multiple sangoma waitable objects + \param sangoma_wait_objects pointer to array of wait objects previously created with sangoma_wait_obj_create + \param in_flags array of flags corresponding to the flags the user is interested on for each waitable object + \param out_flags array of flags corresponding to the flags that are ready in the waitable objects + \param number_of_sangoma_wait_objects size of the array of waitable objects and flags + \param system_wait_timeout timeout in miliseconds in case of no event + \return negative: SANG_STATUS_APIPOLL_TIMEOUT: timeout, SANG_STATUS_SUCCESS: event occured check in sangoma_wait_objects, any other return code is some error +*/ +sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sng_wait_objects[], uint32_t in_flags[], uint32_t out_flags[], + uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout) +{ +#if defined(__WINDOWS__) + HANDLE hEvents[MAXIMUM_WAIT_OBJECTS]; + uint32_t uiMapEventIndexToWaitObjectIndex[MAXIMUM_WAIT_OBJECTS]; + DWORD dwResult; + int at_least_one_poll_set_flags_out, number_of_internal_signaling_objects, err; +#endif + uint32_t i = 0, j = 0; + + memset(out_flags, 0x00, number_of_sangoma_wait_objects * sizeof(out_flags[0])); +#if defined(__WINDOWS__) + /* This loop will calculate 'number_of_internal_signaling_objects' and will initialize 'hEvents' + * based on 'number_of_sangoma_wait_objects' and 'in_flags'. */ + number_of_internal_signaling_objects = 0; + for(i = 0; i < number_of_sangoma_wait_objects; i++){ + sangoma_wait_obj_t *sangoma_wait_object = sng_wait_objects[i]; + + /* if SANGOMA_OBJ_IS_SIGNALABLE add the generic_event_object.hEvent to the hEvents */ + if(sangoma_wait_object->generic_event_object){ + if(check_number_of_wait_objects(number_of_internal_signaling_objects, __FUNCTION__, __LINE__)){ + return SANG_STATUS_NO_FREE_BUFFERS; + } + hEvents[number_of_internal_signaling_objects] = sangoma_wait_object->generic_event_object; + uiMapEventIndexToWaitObjectIndex[number_of_internal_signaling_objects] = i; + number_of_internal_signaling_objects++; + } + + for(j = 0; j < LIBSNG_NUMBER_OF_EVENT_OBJECTS; j++){ + if(sangoma_wait_object->sng_event_objects[j]){ + if( ((j == LIBSNG_EVENT_INDEX_POLLIN) && (in_flags[i] & POLLIN)) || + ((j == LIBSNG_EVENT_INDEX_POLLOUT) && (in_flags[i] & POLLOUT)) || + ((j == LIBSNG_EVENT_INDEX_POLLPRI) && (in_flags[i] & POLLPRI)) ){ + + if(check_number_of_wait_objects(number_of_internal_signaling_objects, __FUNCTION__, __LINE__)){ + return SANG_STATUS_NO_FREE_BUFFERS; + } + hEvents[number_of_internal_signaling_objects] = sangoma_wait_object->sng_event_objects[j]; + uiMapEventIndexToWaitObjectIndex[number_of_internal_signaling_objects] = i; + number_of_internal_signaling_objects++; + } + }/* if () */ + }/* for() */ + }/* for() */ + + if(number_of_internal_signaling_objects < 1){ + DBG_ERR("'number_of_internal_signaling_objects': %d is less than the Minimum of: 1!\n", + number_of_internal_signaling_objects); + /* error - most likely the user did not initialize sng_wait_objects[] */ + return SANG_STATUS_INVALID_PARAMETER; + } + + at_least_one_poll_set_flags_out = FALSE; + + /* It is important to get 'out flags' BEFORE the WaitForMultipleObjects() + * because it allows to keep API driver's transmit queue full. */ + err = get_out_flags(sng_wait_objects, INVALID_INDEX, in_flags, out_flags, number_of_sangoma_wait_objects, &at_least_one_poll_set_flags_out); + if(SANG_ERROR(err)){ + return err; + } + + if(TRUE == at_least_one_poll_set_flags_out){ + return SANG_STATUS_SUCCESS; + } + + /* wait untill at least one of the events is signaled OR a 'system_wait_timeout' occured */ + dwResult = WaitForMultipleObjects(number_of_internal_signaling_objects, &hEvents[0], FALSE, system_wait_timeout); + if (WAIT_TIMEOUT == dwResult){ + return SANG_STATUS_APIPOLL_TIMEOUT; + } + + if( dwResult >= (DWORD)number_of_internal_signaling_objects ) { + return SANG_STATUS_GENERAL_ERROR; + } + + /* WaitForMultipleObjects() was waken by a Sangoma or by a non-Sangoma wait object. */ + err = get_out_flags(sng_wait_objects, + uiMapEventIndexToWaitObjectIndex[dwResult],/* This is the index of first signaled object in + * the original sng_wait_objects[] array, not in hEvents[] array. */ + in_flags, out_flags, number_of_sangoma_wait_objects, NULL); + if(SANG_ERROR(err)){ + return err; + } + + return SANG_STATUS_SUCCESS; +#else + struct pollfd pfds[number_of_sangoma_wait_objects*2]; /* we need twice as many polls because of the sangoma signalable objects */ + char dummy_buf[1]; + int res; + j = 0; + + memset(pfds, 0, sizeof(pfds)); + + for(i = 0; i < number_of_sangoma_wait_objects; i++){ + + if (SANGOMA_OBJ_HAS_DEVICE(sng_wait_objects[i])) { + pfds[i].fd = sng_wait_objects[i]->fd; + pfds[i].events = in_flags[i]; + } + + if (SANGOMA_OBJ_IS_SIGNALABLE(sng_wait_objects[i])) { + pfds[number_of_sangoma_wait_objects+j].fd = sng_wait_objects[i]->signal_read_fd; + pfds[number_of_sangoma_wait_objects+j].events = POLLIN; + j++; + } + } + + poll_try_again: + + res = poll(pfds, (number_of_sangoma_wait_objects + j), system_wait_timeout); + if (res > 0) { + for(i = 0; i < number_of_sangoma_wait_objects; i++){ + out_flags[i] = pfds[i].revents; + /* POLLIN, POLLOUT, POLLPRI have same values as SANG_WAIT_OBJ_ HAS_INPUT, CAN_OUTPUT and HAS_EVENTS, no need to set them */ + } + for(i = 0; i < j; i++){ + if (pfds[number_of_sangoma_wait_objects+i].revents & POLLIN) { + /* read and discard the signal byte */ + read(pfds[number_of_sangoma_wait_objects+i].fd, &dummy_buf, 1); + /* set the proper flag so users may know this object was signaled */ + out_flags[i] |= SANG_WAIT_OBJ_IS_SIGNALED; + } + } + } else if (res < 0 && errno == EINTR) { + /* TODO: decrement system_wait_timeout */ + goto poll_try_again; + } + if (res < 0) { + return SANG_STATUS_GENERAL_ERROR; + } + if (res == 0) { + return SANG_STATUS_APIPOLL_TIMEOUT; + } + return SANG_STATUS_SUCCESS; +#endif +} + + +/*! + \fn sangoma_status_t _SAPI_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, int32_t inflags, int32_t *outflags, int32_t timeout) + \brief Wait for a single waitable object + \param sangoma_wait_obj pointer to a wait object previously created with sangoma_wait_obj_create + \param timeout timeout in miliseconds in case of no event + \return SANG_STATUS_APIPOLL_TIMEOUT: timeout, SANG_STATUS_SUCCESS: event occured use sangoma_wait_obj_get_out_flags to check or error status +*/ +sangoma_status_t _SAPI_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, uint32_t inflags, uint32_t *outflags, int32_t timeout) +{ + return sangoma_waitfor_many(&sangoma_wait_obj, &inflags, outflags, 1, timeout); +} + + +/************************************************************//** + * Device OPEN / CLOSE Functions + ***************************************************************/ + + +int _SAPI_CALL sangoma_span_chan_toif(int span, int chan, char *interface_name) +{ +#if defined(__WINDOWS__) +/* FIXME: Not implemented */ + return -1; +#else + sprintf(interface_name,"s%ic%i",span,chan); +#endif + return 0; +} + +int _SAPI_CALL sangoma_interface_toi(char *interface_name, int *span, int *chan) +{ + char *p=NULL, *sp = NULL, *ch = NULL; + int ret = 0; + char data[FNAME_LEN]; + + strncpy(data, interface_name, FNAME_LEN); + if ((data[0])) { + for (p = data; *p; p++) { + if (sp && *p == 'g') { + *p = '\0'; + ch = (p + 1); + break; + } else if (*p == 'w') { + sp = (p + 1); + } + } + + if(ch && sp) { + *span = atoi(sp); + *chan = atoi(ch); + ret = 1; + } else { + *span = -1; + *chan = -1; + } + } + + return ret; +} + +int _SAPI_CALL sangoma_interface_wait_up(int span, int chan, int sectimeout) +{ +#if defined(__WINDOWS__) + /* Windows does not need to wait for interfaces to come up */ + return 0; +#else + char interface_name[FNAME_LEN]; + struct stat statbuf; + struct timeval endtime = {0,0}; + struct timeval curtime = {0,0}; + int counter; + int rc; + if (sectimeout >= 0 && gettimeofday(&endtime, NULL)) { + return -1; + } + snprintf(interface_name, sizeof(interface_name), "/dev/" WP_INTERFACE_NAME_FORM, span, chan); + endtime.tv_sec += sectimeout; + do { + counter = 0; + while ((rc = stat(interface_name, &statbuf)) && errno == ENOENT && counter != 10) { + poll(0, 0, 100); // test in 100ms increments + counter++; + } + if (!rc || errno != ENOENT) break; + if (gettimeofday(&curtime, NULL)) { + return -1; + } + } while (sectimeout < 0 || timercmp(&endtime, &curtime,>)); + return rc; +#endif +} + +int _SAPI_CALL sangoma_span_chan_fromif(char *interface_name, int *span, int *chan) +{ + char *p = NULL, *sp = NULL, *ch = NULL; + int ret = 0; + char data[FNAME_LEN]; + + strncpy(data, interface_name, FNAME_LEN); + if ((data[0])) { + for (p = data; *p; p++) { + if (sp && *p == 'c') { + *p = '\0'; + ch = (p + 1); + break; + } else if (*p == 's') { + sp = (p + 1); + } + } + + if(ch && sp) { + *span = atoi(sp); + *chan = atoi(ch); + ret = 1; + } else { + *span = -1; + *chan = -1; + } + } + + return ret; +} + +sng_fd_t _SAPI_CALL sangoma_open_api_span_chan(int span, int chan) +{ + sng_fd_t fd = INVALID_HANDLE_VALUE; + wanpipe_api_t tdm_api; + int err; + + fd = __sangoma_open_api_span_chan(span, chan); + +#if defined(__WINDOWS__) + if(fd == INVALID_HANDLE_VALUE){ + return fd; + } +#else + if (fd < 0) { + return fd; + } +#endif + + memset(&tdm_api,0,sizeof(tdm_api)); + tdm_api.wp_cmd.cmd = WP_API_CMD_OPEN_CNT; + err=sangoma_cmd_exec(fd,&tdm_api); + if (err){ + sangoma_close(&fd); + return fd; + } + + if (tdm_api.wp_cmd.open_cnt > 1) { + /* this is NOT the first open request for this span/chan */ + sangoma_close(&fd); + fd = INVALID_HANDLE_VALUE;/* fd is NOT valid anymore */ + } + + return fd; +} + +static sng_fd_t _SAPI_CALL sangoma_open_dev_by_name(const char *dev_name) +{ + char fname[FNAME_LEN]; + +#if defined(__WINDOWS__) + _snprintf(fname , FNAME_LEN, "\\\\.\\%s", dev_name); + + return CreateFile( fname, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + (LPSECURITY_ATTRIBUTES)NULL, + OPEN_EXISTING, + FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, + (HANDLE)NULL + ); +#else + sprintf(fname,"/dev/%s", dev_name); + + return open(fname, O_RDWR); +#endif +} + +/* no checks done for multiple open */ +sng_fd_t _SAPI_CALL __sangoma_open_api_span_chan(int span, int chan) +{ + char tmp_fname[FNAME_LEN]; + + /* Form the Interface Name from span and chan number (i.e. wanpipe1_if1). */ + _snprintf(tmp_fname, DEV_NAME_LEN, WP_INTERFACE_NAME_FORM, span, chan); + + return sangoma_open_dev_by_name(tmp_fname); +} + +sng_fd_t _SAPI_CALL sangoma_open_api_ctrl(void) +{ + return sangoma_open_dev_by_name(WP_CTRL_DEV_NAME); +} + +#ifdef WP_API_FEATURE_LOGGER +sng_fd_t _SAPI_CALL sangoma_logger_open(void) +{ + return sangoma_open_dev_by_name(WP_LOGGER_DEV_NAME); +} +#endif + +int _SAPI_CALL sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_OPEN_CNT; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return -1; + } + + return tdm_api->wp_cmd.open_cnt; +} + +sng_fd_t _SAPI_CALL sangoma_create_socket_by_name(char *device, char *card) +{ + int span,chan; + sangoma_interface_toi(device,&span,&chan); + + return sangoma_open_api_span_chan(span,chan); +} + + +sng_fd_t _SAPI_CALL sangoma_open_api_span(int span) +{ + int i=0; + sng_fd_t fd = INVALID_HANDLE_VALUE; + + for(i = 1; i < 32; i++){ + + fd = sangoma_open_api_span_chan(span, i); + +#if defined(__WINDOWS__) + if(fd != INVALID_HANDLE_VALUE){ +#else + if (fd >= 0) { +#endif + + /* found free chan */ + break; + } + + }/*for()*/ + + return fd; +} + + +/*! + \fn void sangoma_close(sng_fd_t *fd) + \brief Close device file descriptor + \param fd device file descriptor + \return void +*/ +void _SAPI_CALL sangoma_close(sng_fd_t *fd) +{ + if (!fd) { + return; + } +#if defined(__WINDOWS__) + if (*fd != INVALID_HANDLE_VALUE){ + CloseHandle(*fd); + *fd = INVALID_HANDLE_VALUE; + } +#else + if (*fd >= 0) { + close(*fd); + *fd = -1; + } +#endif +} + + + +/************************************************************//** + * Device READ / WRITE Functions + ***************************************************************/ + +int _SAPI_CALL sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, int datalen, int flag) +{ + int rx_len=0; + +#if defined(__WINDOWS__) + wp_api_hdr_t *rx_hdr = (wp_api_hdr_t*)hdrbuf; + wp_api_element_t wp_api_element; + + if(hdrlen != sizeof(wp_api_hdr_t)){ + /*error*/ + DBG_ERR("hdrlen (%i) != sizeof(wp_api_hdr_t) (%i)\n", hdrlen, sizeof(wp_api_hdr_t)); + return -1; + } + + if(DoReadCommand(fd, &wp_api_element)){ + /*error*/ + DBG_ERR("DoReadCommand() failed! Check messages log.\n"); + return -4; + } + + memcpy(rx_hdr, &wp_api_element.hdr, sizeof(wp_api_hdr_t)); + + switch(rx_hdr->operation_status) + { + case SANG_STATUS_RX_DATA_AVAILABLE: + /* ok */ + if(rx_hdr->data_length <= datalen){ + memcpy(databuf, wp_api_element.data, rx_hdr->data_length); + }else{ + rx_hdr->operation_status = SANG_STATUS_BUFFER_TOO_SMALL; + } + break; + default: + /* note that SANG_STATUS_NO_DATA_AVAILABLE is NOT an error! */ + if(0)DBG_ERR("Operation Status: %s(%d)\n", + SDLA_DECODE_SANG_STATUS(rx_hdr->operation_status), rx_hdr->operation_status); + return -5; + } + + rx_len = rx_hdr->data_length; +#else + wan_msghdr_t msg; + wan_iovec_t iov[2]; + + memset(&msg,0,sizeof(msg)); + memset(&iov[0],0,sizeof(iov[0])*2); + + 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,sizeof(msg)); + + if (rx_len <= sizeof(wp_api_hdr_t)){ + return -EINVAL; + } + + rx_len-=sizeof(wp_api_hdr_t); +#endif + return rx_len; +} + +int _SAPI_CALL sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, unsigned short datalen, int flag) +{ + int bsent=-1; + wp_api_hdr_t *wp_api_hdr = hdrbuf; + + if (hdrlen != sizeof(wp_api_hdr_t)) { + /* error. Possible cause is a mismatch between versions of API header files. */ + DBG_ERR("hdrlen (%i) != sizeof(wp_api_hdr_t) (%i)\n", hdrlen, sizeof(wp_api_hdr_t)); + return -1; + } + +#if defined(__WINDOWS__) + + wp_api_hdr->data_length = datalen; + + /*queue data for transmission*/ + if(DoWriteCommand(fd, databuf, datalen, hdrbuf, hdrlen)){ + /*error*/ + DBG_ERR("DoWriteCommand() failed!! Check messages log.\n"); + return -1; + } + + bsent=0; + /*check that frame was transmitted*/ + switch(wp_api_hdr->operation_status) + { + case SANG_STATUS_SUCCESS: + bsent = datalen; + break; + default: + DBG_ERR("Operation Status: %s(%d)\n", + SDLA_DECODE_SANG_STATUS(wp_api_hdr->operation_status), wp_api_hdr->operation_status); + break; + }/*switch()*/ +#else + wan_msghdr_t msg; + wan_iovec_t iov[2]; + + memset(&msg,0,sizeof(msg)); + memset(&iov[0],0,sizeof(iov[0])*2); + + 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,sizeof(msg)); + + if (bsent == (datalen+hdrlen)){ + wp_api_hdr->wp_api_hdr_operation_status=SANG_STATUS_SUCCESS; + bsent-=sizeof(wp_api_hdr_t); + } else if (errno == EBUSY){ + wp_api_hdr->wp_api_hdr_operation_status=SANG_STATUS_DEVICE_BUSY; + } else { + wp_api_hdr->wp_api_hdr_operation_status=SANG_STATUS_IO_ERROR; + } + wp_api_hdr->wp_api_hdr_data_length=bsent; + +#endif + return bsent; +} + + +#ifdef WANPIPE_TDM_API + + +/************************************************************//** + * Device API COMMAND Functions + ***************************************************************/ + + + +/*======================================================== + * Execute TDM command + * + */ +int _SAPI_CALL sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + +#if defined(__WINDOWS__) + err = tdmv_api_ioctl(fd, &tdm_api->wp_cmd); +#else + err = ioctl(fd,WANPIPE_IOCTL_API_CMD,&tdm_api->wp_cmd); + if (err < 0){ + char tmp[50]; + sprintf(tmp,"TDM API: CMD: %i\n",tdm_api->wp_cmd.cmd); + perror(tmp); + return -1; + } +#endif + return err; +} + +/*======================================================== + * Get Full TDM API configuration per channel + * + */ +int _SAPI_CALL sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_FULL_CFG; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + +#if 0 + printf("TDM API CFG:\n"); + printf("\thw_tdm_coding:\t%d\n",tdm_api->wp_cmd.hw_tdm_coding); + printf("\thw_mtu_mru:\t%d\n",tdm_api->wp_cmd.hw_mtu_mru); + printf("\tusr_period:\t%d\n",tdm_api->wp_cmd.usr_period); + printf("\ttdm_codec:\t%d\n",tdm_api->wp_cmd.tdm_codec); + printf("\tpower_level:\t%d\n",tdm_api->wp_cmd.power_level); + printf("\trx_disable:\t%d\n",tdm_api->wp_cmd.rx_disable); + printf("\ttx_disable:\t%d\n",tdm_api->wp_cmd.tx_disable); + printf("\tusr_mtu_mru:\t%d\n",tdm_api->wp_cmd.usr_mtu_mru); + printf("\tidle flag:\t0x%02X\n",tdm_api->wp_cmd.idle_flag); + +#ifdef WP_API_FEATURE_FE_ALARM + printf("\tfe alarms:\t0x%02X\n",tdm_api->wp_cmd.fe_alarms); +#endif + + printf("\trx pkt\t%d\ttx pkt\t%d\n",tdm_api->wp_cmd.stats.rx_packets, + tdm_api->wp_cmd.stats.tx_packets); + printf("\trx err\t%d\ttx err\t%d\n", + tdm_api->wp_cmd.stats.rx_errors, + tdm_api->wp_cmd.stats.tx_errors); +#ifndef __WINDOWS__ + printf("\trx ovr\t%d\ttx idl\t%d\n", + tdm_api->wp_cmd.stats.rx_fifo_errors, + tdm_api->wp_cmd.stats.tx_carrier_errors); +#endif +#endif + + return 0; +} + +/*======================================================== + * SET Codec on a particular Channel. + * + * Available codecs are defined in + * /usr/src/linux/include/linux/wanpipe_cfg.h + * + * enum wan_codec_format { + * WP_NONE, + * WP_SLINEAR + * } + * + */ +int _SAPI_CALL sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int codec) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_CODEC; + tdm_api->wp_cmd.tdm_codec = codec; + return sangoma_cmd_exec(fd,tdm_api); +} + +/*======================================================== + * GET Codec from a particular Channel. + * + * Available codecs are defined in + * /usr/src/linux/include/linux/wanpipe_cfg.h + * + * enum wan_codec_format { + * WP_NONE, + * WP_SLINEAR + * } + * + */ +int _SAPI_CALL sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_CODEC; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return tdm_api->wp_cmd.tdm_codec; +} + +/*======================================================== + * SET Rx/Tx Hardware Period in milliseconds. + * + * Available options are: + * 10,20,30,40,50 ms + * + */ +int _SAPI_CALL sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, int period) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_USR_PERIOD; + tdm_api->wp_cmd.usr_period = period; + return sangoma_cmd_exec(fd,tdm_api); +} + +/*======================================================== + * GET Rx/Tx Hardware Period in milliseconds. + * + * Available options are: + * 10,20,30,40,50 ms + * + */ +int _SAPI_CALL sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_USR_PERIOD; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return tdm_api->wp_cmd.usr_period; +} + +/*======================================================== + * GET Current User Hardware Coding Format + * + * Coding Format will be ULAW/ALAW based on T1/E1 + */ + +int _SAPI_CALL sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_HW_CODING; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + return tdm_api->wp_cmd.hw_tdm_coding; +} + +#ifdef WP_API_FEATURE_DTMF_EVENTS +/*======================================================== + * GET Current User Hardware DTMF Enabled/Disabled + * + * Will return true if HW DTMF is enabled on Octasic + */ + +int _SAPI_CALL sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_HW_DTMF; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + return tdm_api->wp_cmd.hw_dtmf; +} + +/*======================================================== + * GET Current User Hardware EC Enabled/Disabled + * + * Will return true if HW EC is enabled + */ + +int _SAPI_CALL sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_HW_EC; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + return tdm_api->wp_cmd.hw_ec; +} +#endif + +/*======================================================== + * GET Current User MTU/MRU values in bytes. + * + * The USER MTU/MRU values will change each time a PERIOD + * or CODEC is adjusted. + */ +int _SAPI_CALL sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_USR_MTU_MRU; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return tdm_api->wp_cmd.usr_mtu_mru; +} + +/*======================================================== + * SET TDM Power Level + * + * This option is not implemented yet + * + */ +int _SAPI_CALL sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, int power) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_POWER_LEVEL; + tdm_api->wp_cmd.power_level = power; + return sangoma_cmd_exec(fd,tdm_api); +} + +/*======================================================== + * GET TDM Power Level + * + * This option is not implemented yet + * + */ +int _SAPI_CALL sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_POWER_LEVEL; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + return tdm_api->wp_cmd.power_level; +} + +int _SAPI_CALL sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_FLUSH_BUFFERS; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_flush_rx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_FLUSH_RX_BUFFERS; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_flush_tx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_FLUSH_TX_BUFFERS; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_flush_event_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_FLUSH_EVENT_BUFFERS; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_enable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api, int poll_in_sec) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_ENABLE_RBS_EVENTS; + tdm_api->wp_cmd.rbs_poll = poll_in_sec; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_DISABLE_RBS_EVENTS; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char rbs) +{ + WANPIPE_API_INIT_CHAN(tdm_api, channel); + tdm_api->wp_cmd.cmd = WP_API_CMD_WRITE_RBS_BITS; + tdm_api->wp_cmd.rbs_tx_bits=rbs; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char *rbs) +{ + int err; + WANPIPE_API_INIT_CHAN(tdm_api, channel); + tdm_api->wp_cmd.cmd = WP_API_CMD_READ_RBS_BITS; + tdm_api->wp_cmd.rbs_tx_bits=0; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + *rbs=(unsigned char)tdm_api->wp_cmd.rbs_rx_bits; + return 0; +} + +int _SAPI_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + +#ifdef WP_API_FEATURE_EVENTS + wp_api_event_t *rx_event; + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_READ_EVENT; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + rx_event = &tdm_api->wp_cmd.event; + + /* + The use of callbacks here is purely optional and is left + here for backward compatibility purposes. By default user + should handle events outside this funciton. This function + should only be used to read the event + */ + + switch (rx_event->wp_api_event_type){ + + case WP_API_EVENT_RBS: + if (tdm_api->wp_callback.wp_rbs_event) { + tdm_api->wp_callback.wp_rbs_event(fd,rx_event->wp_api_event_rbs_bits); + } + break; + +#ifdef WP_API_FEATURE_DTMF_EVENTS + case WP_API_EVENT_DTMF: + if (tdm_api->wp_callback.wp_dtmf_event) { + tdm_api->wp_callback.wp_dtmf_event(fd, + rx_event->wp_api_event_dtmf_digit, + rx_event->wp_api_event_dtmf_type, + rx_event->wp_api_event_dtmf_port); + } + break; +#endif + + case WP_API_EVENT_RXHOOK: + if (tdm_api->wp_callback.wp_rxhook_event) { + tdm_api->wp_callback.wp_rxhook_event(fd, + rx_event->wp_api_event_hook_state); + } + break; + + case WP_API_EVENT_RING_DETECT: + if (tdm_api->wp_callback.wp_ring_detect_event) { + tdm_api->wp_callback.wp_ring_detect_event(fd, + rx_event->wp_api_event_ring_state); + } + break; + + case WP_API_EVENT_RING_TRIP_DETECT: + if (tdm_api->wp_callback.wp_ring_trip_detect_event) { + tdm_api->wp_callback.wp_ring_trip_detect_event(fd, + rx_event->wp_api_event_ring_state); + } + break; + +#ifdef WP_API_FEATURE_FE_ALARM + case WP_API_EVENT_ALARM: + if (tdm_api->wp_callback.wp_fe_alarm_event) { + tdm_api->wp_callback.wp_fe_alarm_event(fd, + rx_event->wp_api_event_alarm); + } + break; +#endif + +#ifdef WP_API_FEATURE_LINK_STATUS + /* Link Status */ + case WP_API_EVENT_LINK_STATUS: + if(tdm_api->wp_callback.wp_link_status_event){ + tdm_api->wp_callback.wp_link_status_event(fd, + rx_event->wp_api_event_link_status); + } + + break; +#endif + +#ifdef WP_API_FEATURE_POL_REV + case WP_API_EVENT_POLARITY_REVERSE: + break; +#endif + default: +#ifdef __WINDOWS__ + if(0)printf("Warning: libsangoma: %s fd=0x%p: Unknown TDM event!", __FUNCTION__,fd); +#else + if(0)printf("Warning: libsangoma: %s fd=%d: Unknown TDM event!", __FUNCTION__, fd); +#endif + break; + } + + return 0; +#else + printf("Error: Read Event not supported!\n"); + return -1; +#endif +} + + +#ifdef WP_API_FEATURE_LOGGER +/*======================================================== + * Execute Wanpipe Logger command + */ +sangoma_status_t _SAPI_CALL sangoma_logger_cmd_exec(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ +#if defined(__WINDOWS__) + return logger_api_ioctl(fd, logger_cmd); +#else + /* FIXME: not implemented! */ + return -1; +#endif +} + +sangoma_status_t _SAPI_CALL sangoma_logger_read_event(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_READ_EVENT; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _SAPI_CALL sangoma_logger_flush_buffers(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_FLUSH_BUFFERS; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _SAPI_CALL sangoma_logger_get_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_GET_STATS; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _SAPI_CALL sangoma_logger_reset_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_RESET_STATS; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _SAPI_CALL sangoma_logger_get_open_handle_counter(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_OPEN_CNT; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _SAPI_CALL sangoma_logger_get_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_GET_LOGGER_LEVEL; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _SAPI_CALL sangoma_logger_set_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_SET_LOGGER_LEVEL; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +#endif /* WP_API_FEATURE_LOGGER */ + +#ifdef WP_API_FEATURE_FAX_EVENTS +int _SAPI_CALL sangoma_tdm_enable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_FAX_DETECT; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_disable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_FAX_DETECT; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_get_hw_fax(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_HW_FAX_DETECT; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + return tdm_api->wp_cmd.hw_fax; +} +#endif + +#ifdef WP_API_FEATURE_DTMF_EVENTS +int _SAPI_CALL sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_DTMF; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_DTMF; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_enable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RM_DTMF; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RM_DTMF; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RXHOOK; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RXHOOK; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_enable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING_DETECT; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING_DETECT; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING_TRIP_DETECT; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING_TRIP_DETECT; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TXSIG_KEWL; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TXSIG_START; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TXSIG_ONHOOK; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TXSIG_OFFHOOK; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api, uint16_t tone_id) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TONE; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + tdm_api->wp_cmd.event.wp_api_event_tone_type = tone_id; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TONE; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; + tdm_api->wp_cmd.event.wp_api_event_tone_type = 0x00; + return sangoma_cmd_exec(fd,tdm_api); +} +#endif + +int _SAPI_CALL sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_ENABLE_HWEC; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _SAPI_CALL sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_DISABLE_HWEC; + return sangoma_cmd_exec(fd,tdm_api); +} + + +/*======================================================== + * GET Front End Alarms + * + */ +#ifdef WP_API_FEATURE_FE_ALARM +int _SAPI_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_FE_ALARMS; + + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + + *alarms=tdm_api->wp_cmd.fe_alarms; + + return 0; +} + +/* get current Line Connection state - Connected/Disconnected */ +int _SAPI_CALL sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_FE_STATUS; + err = sangoma_cmd_exec(fd, tdm_api); + *current_status = tdm_api->wp_cmd.fe_status; + + return err; +} +#endif + +/* get current Line Connection state - Connected/Disconnected */ +#ifdef WP_API_FEATURE_LINK_STATUS +int _SAPI_CALL sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_FE_STATUS; + err = sangoma_cmd_exec(fd, tdm_api); + *current_status = tdm_api->wp_cmd.fe_status; + + return err; +} + +/* set current Line Connection state - Connected/Disconnected. valid only for ISDN BRI */ +int _SAPI_CALL sangoma_set_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char new_status) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_FE_STATUS; + tdm_api->wp_cmd.fe_status = new_status; + return sangoma_cmd_exec(fd, tdm_api); +} +#endif + +int _SAPI_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.channel = (unsigned char)channel; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_BRI_CHAN_LOOPBACK; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; + return sangoma_cmd_exec(fd, tdm_api); +} + +int _SAPI_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.channel = (unsigned char)channel; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_BRI_CHAN_LOOPBACK; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + return sangoma_cmd_exec(fd, tdm_api); +} + +int _SAPI_CALL sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_TX_Q_SIZE; + tdm_api->wp_cmd.tx_queue_sz = 0; + + err=sangoma_cmd_exec(fd, tdm_api); + if (err < 0) { + return err; + } + + return tdm_api->wp_cmd.tx_queue_sz; +} + +int _SAPI_CALL sangoma_set_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) +{ + if (size < 0) { + return -1; + } + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_TX_Q_SIZE; + tdm_api->wp_cmd.tx_queue_sz = size; + return sangoma_cmd_exec(fd, tdm_api); +} + +int _SAPI_CALL sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_RX_Q_SIZE; + tdm_api->wp_cmd.rx_queue_sz = 0; + + err=sangoma_cmd_exec(fd, tdm_api); + if (err < 0) { + return err; + } + + return tdm_api->wp_cmd.rx_queue_sz; + +} + +int _SAPI_CALL sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) +{ + if (size < 0) { + return -1; + } + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_RX_Q_SIZE; + tdm_api->wp_cmd.rx_queue_sz = size; + return sangoma_cmd_exec(fd, tdm_api); +} + +int _SAPI_CALL sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, wan_driver_version_t *drv_ver) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_DRIVER_VERSION; + + err = sangoma_cmd_exec(fd, tdm_api); + if (err == 0) { + if (tdm_api->wp_cmd.data_len == sizeof(wan_driver_version_t)) { + if (drv_ver) { + memcpy(drv_ver,&tdm_api->wp_cmd.version,sizeof(wan_driver_version_t)); + } + } else { + return -1; + } + } + + return err; +} + +int _SAPI_CALL sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_FIRMWARE_VERSION; + + err = sangoma_cmd_exec(fd, tdm_api); + if (err == 0) { + if (tdm_api->wp_cmd.data_len == sizeof(unsigned char)) { + *ver = tdm_api->wp_cmd.data[0]; + } else { + return -1; + } + } + + return err; +} + +int _SAPI_CALL sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_CPLD_VERSION; + + err = sangoma_cmd_exec(fd, tdm_api); + if (err == 0) { + if (tdm_api->wp_cmd.data_len == sizeof(unsigned char)) { + *ver = tdm_api->wp_cmd.data[0]; + } else { + return -1; + } + } + + return err; +} + +int _SAPI_CALL sangoma_get_stats(sng_fd_t fd, wanpipe_api_t *tdm_api, wanpipe_chan_stats_t *stats) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_STATS; + + err = sangoma_cmd_exec(fd, tdm_api); + if (err == 0) { + if (stats) { + memcpy(stats, &tdm_api->wp_cmd.stats, sizeof(wanpipe_chan_stats_t)); + } + } + + return err; +} + +int _SAPI_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_RESET_STATS; + return sangoma_cmd_exec(fd, tdm_api); +} + +int _SAPI_CALL sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, int rxflashtime) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_RM_RXFLASHTIME; + tdm_api->wp_cmd.rxflashtime=rxflashtime; + return sangoma_cmd_exec(fd, tdm_api); +} + +<<<<<<< .mine +#ifdef WP_API_FEATURE_RM_TX_GAIN +======= +#ifdef WP_API_FEATURE_RM_GAIN +>>>>>>> .r251 +int _SAPI_CALL sangoma_set_rm_tx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_SET_RM_TX_GAIN; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + tdm_api->wp_cmd.event.wp_api_event_gain_value = value; + return sangoma_cmd_exec(fd, tdm_api); +} + + +int _SAPI_CALL sangoma_set_rm_rx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_SET_RM_RX_GAIN; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + tdm_api->wp_cmd.event.wp_api_event_gain_value = value; + return sangoma_cmd_exec(fd, tdm_api); +} +#endif /* WP_API_FEATURE_RM_GAIN */ + +int _SAPI_CALL sangoma_tdm_set_polarity(sng_fd_t fd, wanpipe_api_t *tdm_api, int polarity) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_SETPOLARITY; + tdm_api->wp_cmd.event.wp_api_event_polarity = polarity; + err = sangoma_cmd_exec(fd, tdm_api); + return err; + +} +<<<<<<< .mine +#endif + +======= + +>>>>>>> .r251 +#ifndef LIBSANGOMA_LIGHT + +/************************************************************//** + * Device PORT Control Functions + ***************************************************************/ + +static int sangoma_port_mgmnt_ioctl(sng_fd_t fd, port_management_struct_t *port_management) +{ + int err = 0; +#if defined(__WINDOWS__) + DWORD ln; + if(DeviceIoControl( + fd, + IoctlPortManagementCommand, + (LPVOID)port_management, + sizeof(port_management_struct_t), + (LPVOID)port_management, + sizeof(port_management_struct_t), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL + ) == FALSE){ + /* Call OS specific code to find cause of the error and check messages log. */ + DBG_ERR("%s():Error: IoctlPortManagementCommand failed!!\n", __FUNCTION__); + err = -1; + } +#else + err=ioctl(fd,WANPIPE_IOCTL_PORT_MGMT,port_management); + if (err) { + err = -1; + } +#endif + if(err){ + port_management->operation_status = SANG_STATUS_INVALID_DEVICE; + } + + return err; +} + +static int sangoma_port_cfg_ioctl(sng_fd_t fd, port_cfg_t *port_cfg) +{ + int err = 0; +#if defined(__WINDOWS__) + DWORD ln; + if(DeviceIoControl( + fd, + IoctlPortConfigurationCommand, + (LPVOID)port_cfg, + sizeof(port_cfg_t), + (LPVOID)port_cfg, + sizeof(port_cfg_t), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL + ) == FALSE){ + /* Call OS specific code to find cause of the error and check messages log. */ + DBG_ERR("%s():Error: IoctlPortConfigurationCommand failed!!\n", __FUNCTION__); + err = -1; + } +#else + err=ioctl(fd,WANPIPE_IOCTL_PORT_CONFIG,port_cfg); + if (err) { + err = -1; + } +#endif + if(err){ + port_cfg->operation_status = SANG_STATUS_INVALID_DEVICE; + } + + return err; +} + +/* open wanpipe configuration device */ +sng_fd_t _SAPI_CALL sangoma_open_driver_ctrl(int port_no) +{ + char tmp_fname[FNAME_LEN]; + +#if defined(__WINDOWS__) + /* Form the Config Device Name (i.e. wanpipe1, wanpipe2,...). */ + _snprintf(tmp_fname, DEV_NAME_LEN, WP_PORT_NAME_FORM, port_no); +#else + /* Form the Config Device Name. ("/dev/wanpipe") */ + _snprintf(tmp_fname, DEV_NAME_LEN, WP_CONFIG_DEV_NAME); +#endif + return sangoma_open_dev_by_name(tmp_fname); +} + +int _SAPI_CALL sangoma_mgmt_cmd(sng_fd_t fd, wan_udp_hdr_t* wan_udp) +{ +#if defined(__WINDOWS__) + if(UdpManagementCommand(fd, wan_udp)){ + return 1; + } +#else + unsigned char id = 0; + int err=0; + wan_udp->wan_udphdr_request_reply = 0x01; + wan_udp->wan_udphdr_id = id; + wan_udp->wan_udphdr_return_code = WAN_UDP_TIMEOUT_CMD; + + err=ioctl(fd,WANPIPE_IOCTL_PIPEMON,wan_udp); + if (err < 0) { + return 1; + } +#endif + + if(wan_udp->wan_udphdr_return_code != WAN_CMD_OK){ + return 2; + } + return 0; +} + +int _SAPI_CALL sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) +{ + int err; + port_mgmnt->command_code = START_PORT_VOLATILE_CONFIG; + port_mgmnt->port_no = port_no; + + err = sangoma_port_mgmnt_ioctl(fd, port_mgmnt); + if (err) { + /* ioctl failed */ + return err; + } + + return port_mgmnt->operation_status; +} + +int _SAPI_CALL sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) +{ + int err; + port_mgmnt->command_code = STOP_PORT; + port_mgmnt->port_no = port_no; + + err = sangoma_port_mgmnt_ioctl(fd, port_mgmnt); + if (err) { + /* ioctl failed */ + return err; + } + + switch(port_mgmnt->operation_status) + { + case SANG_STATUS_CAN_NOT_STOP_DEVICE_WHEN_ALREADY_STOPPED: + /* This is not an error, rather a state indication. + * Return SANG_STATUS_SUCCESS, but real return code will be available + * for the caller at port_mgmnt->operation_status. */ + err = SANG_STATUS_SUCCESS; + break; + default: + err = port_mgmnt->operation_status; + break; + } + + return err; +} + +int _SAPI_CALL sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) +{ + int err; + port_mgmnt->command_code = GET_HARDWARE_INFO; + port_mgmnt->port_no = port_no; + + err = sangoma_port_mgmnt_ioctl(fd, port_mgmnt); + if (err) { + return err; + } + + return port_mgmnt->operation_status; +} + +int _SAPI_CALL sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no) +{ + port_cfg->command_code = SET_PORT_VOLATILE_CONFIG; + port_cfg->port_no = port_no; + + return sangoma_port_cfg_ioctl(fd, port_cfg); +} + +int _SAPI_CALL sangoma_driver_port_get_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no) +{ + port_cfg->command_code = GET_PORT_VOLATILE_CONFIG; + port_cfg->port_no = port_no; + return sangoma_port_cfg_ioctl(fd, port_cfg); +} + +int _SAPI_CALL sangoma_write_port_config_on_persistent_storage(hardware_info_t *hardware_info, port_cfg_t *port_cfg, unsigned short port_no) +{ + int err = 0; +#if defined(__WINDOWS__) + HKEY hPortRegistryKey = registry_open_port_key(hardware_info); + wandev_conf_t *wandev_conf = &port_cfg->wandev_conf; + sdla_fe_cfg_t *sdla_fe_cfg = &wandev_conf->fe_cfg; + unsigned int ind; + + if(hPortRegistryKey == INVALID_HANDLE_VALUE){ + return 1; + } + + /* write T1/E1/BRI/Analog configuration */ + if(registry_write_front_end_cfg(hPortRegistryKey, port_cfg)){ + return 2; + } + + /* write TDM Voice configuration */ + if(registry_write_wan_tdmv_conf(hPortRegistryKey, port_cfg)){ + return 3; + } + + /* write number of groups */ + err = registry_set_integer_value(hPortRegistryKey, "aft_number_of_logic_channels", port_cfg->num_of_ifs); + if(err){ + return err; + } + + /* write configuration of each group */ + for(ind = 0; ind < port_cfg->num_of_ifs; ind++){ + registry_write_channel_group_cfg(hPortRegistryKey, port_cfg, ind, port_cfg->if_cfg[ind]); + } + +#else + printf("%s(): Warning: function not implemented\n", __FUNCTION__); + err = 1; +#endif + return err; +} +#endif /* #ifndef LIBSANGOMA_LIGHT */ + +#endif /* WANPIPE_TDM_API */ diff --git a/api/libsangoma/.svn/tmp/tempfile.20.tmp b/api/libsangoma/.svn/tmp/tempfile.20.tmp new file mode 100644 index 0000000..8ea2bb1 --- /dev/null +++ b/api/libsangoma/.svn/tmp/tempfile.20.tmp @@ -0,0 +1,1623 @@ +/*******************************************************************************//** + * \file libsangoma.h + * \brief Wanpipe API Library header for Sangoma AFT T1/E1/Analog/BRI/Serial Hardware - + * \brief Provides User a Unified/OS Agnostic API to Wanpipe/Sangoma Drivers and Hardware + * + * Author(s): Nenad Corbic + * David Rokhvarg + * Michael Jerris + * Anthony Minessale II + * + * Copyright: (c) 2005-2008 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 Sangoma Technologies 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 Sangoma Technologies ``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 Sangoma Technologies 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 + * =============================================================================== + * v.2.0.0 Nenad Corbic + * Jan 30 2009 + * Added sangoma_get_driver_version, sangoma_get_firmware_version, + * sangoma_get_cpld_version functions,sangoma_get_stats,sangoma_flush_stats + */ + +#ifndef _LIBSNAGOMA_H +#define _LIBSNAGOMA_H + +#ifdef __linux__ +#ifndef __LINUX__ +/* most wanpipe driver headers require __LINUX__ to be defined */ +#define __LINUX__ +#endif +#endif + +#ifdef __cplusplus +extern "C" { /* for C++ users */ +#endif + +#include + + +/*! + \def WANPIPE_TDM_API + \brief Used by compiler and driver to enable TDM API +*/ +#define WANPIPE_TDM_API 1 + +/*TODO: LIBSANGOMA_VERSION_CODE should be generated out of LIBSANGOMA_LT_CURRENT and friends in configure.in */ + +/*! + \def LIBSANGOMA_VERSION + \brief LibSangoma Macro to check the Version Number +*/ +#define LIBSANGOMA_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +/*! + \def LIBSANGOMA_VERSION_CODE + \brief LibSangoma Current Version Number to be checked against the LIBSANGOMA_VERSION Macro +*/ +#define LIBSANGOMA_VERSION_CODE LIBSANGOMA_VERSION(3,3,0) + +/*! + \def LIBSANGOMA_VERSION_STR + \brief LibSangoma Version in string format +*/ +#define LIBSANGOMA_VERSION_STR "3.3.0" + +struct sangoma_wait_obj; +#define sangoma_wait_obj_t struct sangoma_wait_obj + +#if defined(WIN32) || defined(WIN64) +#ifndef __WINDOWS__ +#define __WINDOWS__ +#endif +#include +#include +#include +#include +#include + +/*! + \def _SAPI_CALL + \brief libsangoma.dll functions exported as __cdecl calling convention +*/ +#define _SAPI_CALL __cdecl + +/*! + \def SANGOMA_INFINITE_API_POLL_WAIT (deprecated, use SANGOMA_WAIT_INFINITE instead) + \brief Infinite poll timeout value +*/ +#define SANGOMA_INFINITE_API_POLL_WAIT INFINITE +#define SANGOMA_WAIT_INFINITE INFINITE + +/*! + \def sangoma_msleep(x) + \brief milisecond sleep function +*/ +#define sangoma_msleep(x) Sleep(x) + +/*! + \def sangoma_ctime(time_val) + \brief Convert a time value to a string + \param time_val pointer to 64-bit time value +*/ +#define sangoma_ctime(time) _ctime64(time) + +#else +/* L I N U X */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*! + \def _SAPI_CALL + \brief Not used in Linux +*/ +#define _SAPI_CALL + +/*! + \def INVALID_HANDLE_VALUE + \brief Invalid file handle value -1, Ported from Windows +*/ +#define INVALID_HANDLE_VALUE -1 + +/*! + \def SANGOMA_INFINITE_API_POLL_WAIT (deprecated, use SANGOMA_WAIT_INFINITE instead) + \brief Infinite poll timeout value -1, Ported from Windows +*/ +#define SANGOMA_INFINITE_API_POLL_WAIT -1 +#define SANGOMA_WAIT_INFINITE -1 + +/*! + \def __cdecl + \brief Ported from Windows + \typedef BOOL + \brief Boolean type int, Ported from Windows + \typedef DWORD + \brief DWORD type is int, Ported from Windows + \def FALSE + \brief FALSE value is 0, Ported from Windows + \def TRUE + \brief TRUE value is 1, Ported from Windows + \def sangoma_msleep(x) + \brief milisecond sleep function + \def _getch + \brief get character, Ported from Windows + \def Sleep + \brief milisecond sleep function + \typedef HANDLE + \brief file handle type int, Ported from Windows + \typedef TCHAR + \brief TCHAN type mapped to char, Ported from Windows + \typedef ULONG + \brief ULONG type mapped to unsigned long, Ported from Windows + \typedef UCHAR + \brief ULONG type mapped to unsigned char, Ported from Windows + \typedef USHORT + \brief USHORT type mapped to unsigned short, Ported from Windows + \typedef LPSTR + \brief LPSTR type mapped to unsigned char *, Ported from Windows + \typedef PUCHAR + \brief PUCHAR type mapped to unsigned char *, Ported from Windows + \typedef LPTHREAD_START_ROUTINE + \brief LPTHREAD_START_ROUTINE type mapped to unsigned char *, Ported from Windows + \def _stricmp + \brief _stricmp type mapped to _stricmp, Ported from Windows + \def _snprintf + \brief _snprintf type mapped to snprintf, Ported from Windows +*/ + +#define __cdecl + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#define sangoma_msleep(x) usleep(x*1000) +#define _getch getchar +#define Sleep sangoma_msleep +#define _stricmp strcmp +#define _snprintf snprintf +#define _vsnprintf vsnprintf + +typedef int HANDLE; +typedef int BOOL; +typedef int DWORD; +typedef char TCHAR; +typedef unsigned char UCHAR; +typedef unsigned long ULONG; +typedef unsigned short USHORT; +typedef unsigned char * LPSTR; +typedef unsigned char * PUCHAR; +typedef void * LPTHREAD_START_ROUTINE; +typedef pthread_mutex_t CRITICAL_SECTION; + +#define EnterCriticalSection(arg) pthread_mutex_lock(arg) +#define LeaveCriticalSection(arg) pthread_mutex_unlock(arg) +#define InitializeCriticalSection(arg) pthread_mutex_init(arg, NULL); + +typedef struct tm SYSTEMTIME; +typedef char * LPCTSTR; + +/*! + \def sangoma_ctime(time_val) + \brief Convert a time value to a string + \param time_val pointer to time value +*/ +#define sangoma_ctime(time) ctime((time_t*)time) + +#endif/* WIN32 */ + + +/*! + LIBSANGOMA_LIGHT can be used to enable only IO and EVENT + libsangoma functions. The DRIVER configuration/start/stop + functions are not compiled. + + LIBSANGOMA_LIGHT depends only on 3 header files. Instead + of all wanpipe header files needed for DRIVER management + + LIBSANGMOA_LIGHT is NOT enabled by default. +*/ + +#ifdef LIBSANGOMA_LIGHT +#include "wanpipe_api_iface.h" +#include "wanpipe_api_hdr.h" +#include "sdla_te1.h" +#include "wanpipe_events.h" +#include "wanpipe_api_deprecated.h" +#else +#include "wanpipe_api.h" +#endif + +#ifdef __LINUX__ +#include "wanpipe_kernel.h" +#endif + +/*! + * As of now this typedef maps exactly to SANG_STATUS_T, however that + * is a kernel type, ugly, ugly, uglyyyyy, we should have strictly + * minimum set of shared data structures between kernel and user + * many return codes specified in SANG_STATUS_T are kernel specific + * like FAILED_TO_LOCK_USER_MEMORY or INVALID_IRQL, the libsangoma + * user does not need that much information, and even if ever needs + * it we should provide simpler defaults + * \brief return status from sangoma APIs + */ +typedef int32_t sangoma_status_t; + +/*! + \def FNAME_LEN + \brief string length of a file name + \def FUNC_DBG(x) + \brief function debug print function + \def DBG_PRINT + \brief debug print function +*/ +#define FNAME_LEN 100 +#define LIBSNG_FUNC_DBG() if(0)printf("%s(): line:%d\n", __FUNCTION__, __LINE__) +#define LIBSNG_DBG_PRINT if(0)printf + +/*! + \typedef sangoma_api_hdr_t + \brief Backward comaptible define of wp_api_hdr_t +*/ +typedef wp_api_hdr_t sangoma_api_hdr_t; + +/*! + \enum _sangoma_wait_obj_type_t + \brief Wait object type definition + \typedef sangoma_wait_obj_type_t + \brief Wait object type definition +*/ +typedef enum _sangoma_wait_obj_type +{ + /*! \brief deprecated, use SANGOMA_GENERIC_WAIT_OBJ */ + UNKNOWN_WAIT_OBJ = 0, + /*! \brief Generic object that can be signaled but is not associated to any sangoma device */ + SANGOMA_GENERIC_WAIT_OBJ = 0, + /*! \brief Sangoma object associated to some device which cannot be signaled (cannot call sangoma_wait_obj_signal on it) */ + SANGOMA_DEVICE_WAIT_OBJ, + /*! \brief Sangoma object that is associated to a device AND can be signaled */ + SANGOMA_DEVICE_WAIT_OBJ_SIG, +} sangoma_wait_obj_type_t; + +#define DECODE_SANGOMA_WAIT_OBJECT_TYPE(type)\ + type == SANGOMA_GENERIC_WAIT_OBJ ? "SANGOMA_GENERIC_WAIT_OBJ" :\ + type == SANGOMA_DEVICE_WAIT_OBJ ? "SANGOMA_DEVICE_WAIT_OBJ" :\ + type == SANGOMA_DEVICE_WAIT_OBJ_SIG ? "SANGOMA_DEVICE_WAIT_OBJ_SIG" :\ + "Invalid Wait Object type!" + +/* + * Possible flags for sangoma waitable objects + * this definitions MUST match POLLIN, POLLOUT and POLLPRI + * SANG_WAIT_OBJ_IS_SIGNALED has no posix equivalent though + * Users are encouraged to use this flags instead of the system ones + */ +typedef enum _sangoma_wait_obj_flags { + SANG_WAIT_OBJ_HAS_INPUT = POLLIN, + SANG_WAIT_OBJ_HAS_OUTPUT = POLLOUT, + SANG_WAIT_OBJ_HAS_EVENTS = POLLPRI, + SANG_WAIT_OBJ_IS_SIGNALED = 0x400 /* matches GNU extension POLLMSG */ +} sangoma_wait_obj_flags_t; + +/************************************************************//** + * Device OPEN / CLOSE Functions + ***************************************************************/ + +/*! + \fn sng_fd_t sangoma_open_api_span_chan(int span, int chan) + \brief Open a Device based on Span/Chan values + \param span span number starting from 1 to 255 + \param chan chan number starting from 1 to 32 + \return File Descriptor: -1 error, 0 or positive integer: valid file descriptor + + Restriced open, device will allowed to be open only once. +*/ +sng_fd_t _SAPI_CALL sangoma_open_api_span_chan(int span, int chan); + + +/*! + \fn sng_fd_t __sangoma_open_api_span_chan(int span, int chan) + \brief Open a Device based on Span/Chan values + \param span span number starting from 1 to 255 + \param chan chan number starting from 1 to 32 + \return File Descriptor: -1 error, 0 or positive integer: valid file descriptor + + Unrestriced open, allows mutiple open calls on a single device +*/ +sng_fd_t _SAPI_CALL __sangoma_open_api_span_chan(int span, int chan); +#define __sangoma_open_tdmapi_span_chan __sangoma_open_api_span_chan + +/*! + \fn sng_fd_t sangoma_open_tdmapi_span(int span) + \brief Open a first available device on a Span + \param span span number starting from 1 to 255 + \return File Descriptor: -1 error, 0 or positive integer: valid file descriptor + + Unrestriced open, allows mutiple open calls on a single device +*/ +sng_fd_t _SAPI_CALL sangoma_open_api_span(int span); + + +/*! + \def sangoma_create_socket_intr + \brief Backward compatible open span chan call +*/ + + + +/*! + \def LIBSANGOMA_TDMAPI_CTRL + \brief Global control device feature +*/ +#ifndef LIBSANGOMA_TDMAPI_CTRL +#define LIBSANGOMA_TDMAPI_CTRL 1 +#endif + +/*! + \fn sng_fd_t sangoma_open_api_ctrl(void) + \brief Open a Global Control Device + \return File Descriptor - negative=error 0 or greater = fd + + The global control device receives events for all devices + configured. +*/ +sng_fd_t _SAPI_CALL sangoma_open_api_ctrl(void); + +/*! + \fn sng_fd_t sangoma_logger_open(void) + \brief Open a Global Logger Device + \return File Descriptor - negative=error 0 or greater = fd + + The global Logger device receives Logger Events for all devices + configured. +*/ +sng_fd_t _SAPI_CALL sangoma_logger_open(void); + +/*! + \fn sng_fd_t sangoma_open_driver_ctrl(int port_no) + \brief Open a Global Driver Control Device + \return File Descriptor - negative=error 0 or greater = fd + + The global control device receives events for all devices + configured. +*/ +sng_fd_t _SAPI_CALL sangoma_open_driver_ctrl(int port_no); + + + +/*! + \fn void sangoma_close(sng_fd_t *fd) + \brief Close device file descriptor + \param fd device file descriptor + \return void + +*/ +void _SAPI_CALL sangoma_close(sng_fd_t *fd); + + + +/*! + \fn int sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get device open count + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative or 0: error, greater than 1 : open count +*/ + +int _SAPI_CALL sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/************************************************************//** + * Device READ / WRITE Functions + ***************************************************************/ + +/*! + \fn int sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, unsigned short datalen, int flag) + \brief Write Data to device + \param fd device file descriptor + \param hdrbuf pointer to header structure wp_api_hdr_t + \param hdrlen size of wp_api_hdr_t + \param databuf pointer to data buffer to be transmitted + \param datalen length of data buffer + \param flag currently not used, set to 0 + \return transmit size, must be equal to datalen, anything else is error + + In case of error return code, one must check the header operation_status + variable to identify the reason of an error. Please refer to the error codes. +*/ + +int _SAPI_CALL sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, + void *databuf, unsigned short datalen, int flag); + + +/*! + \fn int sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, int datalen, int flag) + \brief Read Data from device + \param fd device file descriptor + \param hdrbuf pointer to header structure wp_api_hdr_t + \param hdrlen size of wp_api_hdr_t + \param databuf pointer to data buffer to be received + \param datalen length of data buffer + \param flag currently not used, set to 0 + \return received size, must be equal to datalen, anything else is error or busy + + In case of error return code, one must check the header operation_status + variable to identify the reason of error. Please refer to the error codes. +*/ +int _SAPI_CALL sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, + void *databuf, int datalen, int flag); + + + + +/************************************************************//** + * Device POLL Functions + ***************************************************************/ + +/*! + \fn sangoma_status_t _SAPI_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, int32_t inflags, int32_t *outflags, int32_t timeout) + \brief Wait for a single waitable object + \param sangoma_wait_obj pointer to a wait object previously created with sangoma_wait_obj_create + \param timeout timeout in miliseconds in case of no event + \return SANG_STATUS_APIPOLL_TIMEOUT: timeout, SANG_STATUS_SUCCESS: event occured use sangoma_wait_obj_get_out_flags to check or error status +*/ +sangoma_status_t _SAPI_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, uint32_t inflags, uint32_t *outflags, int32_t timeout); + +/*! + \fn sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], int32_t in_flags[], int32_t out_flags[] uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout); + \brief Wait for multiple sangoma waitable objects + \param sangoma_wait_objects pointer to array of wait objects previously created with sangoma_wait_obj_create + \param in_flags array of flags corresponding to the flags the user is interested on for each waitable object + \param out_flags array of flags corresponding to the flags that are ready in the waitable objects + \param number_of_sangoma_wait_objects size of the array of waitable objects and flags + \param system_wait_timeout timeout in miliseconds in case of no event + \return negative: SANG_STATUS_APIPOLL_TIMEOUT: timeout, SANG_STATUS_SUCCESS: event occured check in sangoma_wait_objects, any other return code is some error +*/ +sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], uint32_t in_flags[], uint32_t out_flags[], + uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout); + +/*! + \fn sangoma_status_t sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma_wait_object, sng_fd_t fd, sangoma_wait_obj_type_t object_type) + \brief Create a wait object that will be used with sangoma_waitfor_many() API + \param sangoma_wait_object pointer a single device object + \param fd device file descriptor + \param object_type type of the wait object. see sangoma_wait_obj_type_t for types + \see sangoma_wait_obj_type_t + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _SAPI_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma_wait_object, sng_fd_t fd, sangoma_wait_obj_type_t object_type); + +/*! + \fn sangoma_status_t sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object) + \brief De-allocate all resources inside a wait object which were allocated by sangoma_wait_obj_init(). + \param sangoma_wait_object pointer to a pointer to a single device object + \return SANG_STATUS_SUCCESS on success or some sangoma status error +*/ +sangoma_status_t _SAPI_CALL sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object); + +/*! + \fn void sangoma_wait_obj_signal(sangoma_wait_obj_t *sangoma_wait_object) + \brief Set wait object to a signaled state + \param sangoma_wait_object pointer a single device object that can be signaled + \return sangoma_status_t +*/ +sangoma_status_t _SAPI_CALL sangoma_wait_obj_signal(sangoma_wait_obj_t *sangoma_wait_object); + +/*! + \fn sng_fd_t sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sangoma_wait_object) + \brief Get fd device file descriptor which was the 'fd' parameter for sangoma_wait_obj_create(), not useful for generic objects + \param sangoma_wait_object pointer a single device object + \return sng_fd_t - device file descriptor +*/ +sng_fd_t _SAPI_CALL sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sangoma_wait_object); + +/*! + \fn void sangoma_wait_obj_set_context(sangoma_wait_obj_t *sangoma_wait_object) + \brief Store the given context into provided sangoma wait object. + \brief This function is useful to associate a context with a sangoma wait object. + \param sangoma_wait_object pointer a single device object + \param context void pointer to user context + \return void +*/ +void _SAPI_CALL sangoma_wait_obj_set_context(sangoma_wait_obj_t *sangoma_wait_object, void *context); + +/*! + \fn void *sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object) + \brief Retrieve the user context (if any) that was set via sangoma_wait_obj_set_context. + \param sangoma_wait_object pointer a single device object + \return void* +*/ +void* _SAPI_CALL sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object); + + +/************************************************************//** + * Device API COMMAND Functions + ***************************************************************/ + +/*! + \fn int sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Execute Sangoma API Command + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Read tdm api device configuration + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, int period) + \brief Set Tx/Rx Period in Milliseconds + \param fd device file descriptor + \param tdm_api tdm api command structure + \param period value in miliseconds (1,2,5,10) + \return non-zero: error, 0: ok + + Only valid in CHAN Operation Mode +*/ +int _SAPI_CALL sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, int period); + +/*! + \fn int sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Tx/Rx Period in Milliseconds + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative: error or configured period value +*/ +int _SAPI_CALL sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Tx/Rx MTU/MRU in bytes + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative: error or configured mtu/mru in bytes +*/ +int _SAPI_CALL sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush all (tx/rx/event) buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _SAPI_CALL sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_flush_rx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush only rx buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _SAPI_CALL sangoma_flush_rx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); +/*! + \fn int sangoma_flush_tx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush only tx buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _SAPI_CALL sangoma_flush_tx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_flush_event_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush only event buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _SAPI_CALL sangoma_flush_event_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_tdm_enable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api, int poll_in_sec) + \brief Enable RBS Events on a device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param poll_in_sec driver poll period for rbs events + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_tdm_enable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api, int poll_in_sec); + +/*! + \fn int sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RBS Events for a device + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char rbs) + \brief Write RBS Bits on a device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param channel t1/e1 timeslot + \param rbs rbs bits (ABCD) + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char rbs); + +/*! + \fn int sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char *rbs) + \brief Read RBS Bits on a device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param channel t1/e1 timeslot + \param rbs pointer to rbs bits (ABCD) + \return non-zero: error, 0: ok +*/ + +int _SAPI_CALL sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char *rbs); + +/*! + \fn int sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable DTMF Detection on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _SAPI_CALL sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable DTMF Detection on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _SAPI_CALL sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +#ifdef WP_API_FEATURE_FAX_EVENTS +/*! + \fn int sangoma_tdm_enable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable FAX Detection on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _SAPI_CALL sangoma_tdm_enable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable FAX Detection on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _SAPI_CALL sangoma_tdm_disable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_get_hw_fax(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get HW FAX Detection State (Enable or Disabled) on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: Disabled, 1: Enabled + + Supported only on cards that have HWEC +*/ +int _SAPI_CALL sangoma_tdm_get_hw_fax(sng_fd_t fd, wanpipe_api_t *tdm_api); + +#endif + + +/*! + \fn int sangoma_tdm_enable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable DTMF Detection on Analog/Remora SLIC Chip + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_enable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable DTMF Detection on Analog/Remora SLIC Chip + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable RX HOOK Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RX HOOK Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable RING Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RING Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_tdm_enable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable RING DETECT Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_enable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RING DETECT Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable RING TRIP Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RING TRIP Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api, uint16_t tone_id) + \brief Transmit a TONE on this device (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param tone_id tone type to transmit + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api, uint16_t tone_id); + +/*! + \fn int sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable TONE Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG ON HOOK (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG OFF HOOK (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG START (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG KEWL START (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable HWEC on this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _SAPI_CALL sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable HWEC on this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _SAPI_CALL sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int _SAPI_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms); + \brief Get Front End Alarms (T1/E1 Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param alarms bit map status of T1/E1 alarms + \return non-zero: error, 0: ok + + Supported only on T1/E1 Cards +*/ +int _SAPI_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms); + + + +#ifdef WP_API_FEATURE_LINK_STATUS +# ifndef LIBSANGOMA_GET_LINKSTATUS +/*! + \def LIBSANGOMA_GET_LINKSTATUS + \brief Get Link Status feature +*/ +# define LIBSANGOMA_GET_LINKSTATUS 1 +# endif + +/*! + \fn int sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status) + \brief Get Device Link Status (Connected/Disconnected) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param current_status pointer where result will be filled: 0=Link UP 1=Link Down + \return non-zero: error, 0: ok -> check current_status + +*/ +int _SAPI_CALL sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status); + +#endif + +/* set current Line Connection state - Connected/Disconnected */ +#ifndef LIBSANGOMA_GET_FESTATUS +/*! + \def LIBSANGOMA_GET_FESTATUS + \brief Get Front End Status feature +*/ +#define LIBSANGOMA_GET_FESTATUS 1 +#endif + +/*! + \fn int sangoma_set_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char new_status) + \brief Set Device Link Status (Connected/Disconnected) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param new_status new status 0=Link UP 1=Link Down + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_set_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char new_status); + + +/*! + \fn int _SAPI_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) + \brief Enable BRI Bchannel loopback - used when debugging bri device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param channel bri bchannel 1 or 2 + \return non-zero: error, 0: ok + +*/ +int _SAPI_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel); + +/*! + \fn int _SAPI_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) + \brief Disable BRI Bchannel loopback - used when debugging bri device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param channel bri bchannel 1 or 2 + \return non-zero: error, 0: ok + +*/ +int _SAPI_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel); + + +/*! + \fn int sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Tx Queue Size for this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_set_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) + \brief Get Tx Queue Size for this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \param size tx queue size (minimum value of 1) + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_set_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size); + +/*! + \fn int sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Rx Queue Size for this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) + \brief Get Tx Queue Size for this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \param size rx queue size (minimum value of 1) + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size); + + +#ifndef LIBSANGOMA_GET_HWCODING +/*! + \def LIBSANGOMA_GET_HWCODING + \brief Get HW Coding Feature +*/ +#define LIBSANGOMA_GET_HWCODING 1 +#endif + +/*! + \fn int sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get HW Voice Coding (ulaw/alaw) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + This function will return the low level voice coding + depending on configuration. (ulaw or alaw) +*/ +int _SAPI_CALL sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api); + + + +#ifndef LIBSANGOMA_GET_HWDTMF +/*! + \def LIBSANGOMA_GET_HWDTMF + \brief HW DTMF Feature +*/ +#define LIBSANGOMA_GET_HWDTMF 1 +#endif +/*! + \fn int sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Check if hwdtmf support is available + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + This function will check if hw supports HW DTMF. +*/ +int _SAPI_CALL sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Check if hw echo cancelation support is available + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + This function will check if hw supports HW EC. +*/ +int _SAPI_CALL sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_span_chan_toif(int span, int chan, char *interface_name) + \brief Convert Span & Chan to interface name + \param span span number starting from 1 to 255 + \param chan chan number starting from 1 to 32 + \param interface_name pointer to string where interface name will be written + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_span_chan_toif(int span, int chan, char *interface_name); + +/*! + \fn int sangoma_span_chan_fromif(char *interface_name, int *span, int *chan) + \brief Convert Interace Name to Span & Chan + \param interface_name pointer to string containing interface name + \param span integer pointer where to write span value + \param chan integer pointer where to write chan value + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_span_chan_fromif(char *interface_name, int *span, int *chan); + + +/*! + \fn int sangoma_interface_wait_up(int span, int chan, int sectimeout) + \brief Wait for a sangoma device to come up (ie: Linux wait for /dev/wanpipex_1 to come up) + \param span span number of the device to wait + \param chan chan number of the device to wait + \param sectimeout how many seconds to wait for the device to come up, -1 to wait forever + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_interface_wait_up(int span, int chan, int sectimeout); + +/*! + \fn int sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, wan_driver_version_t *drv_ver) + \brief Get Device Driver Version Number + \param fd device file descriptor + \param tdm_api tdm api command structure + \param drv_ver driver version structure that will contain the driver version + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, wan_driver_version_t *drv_ver); + +/*! + \fn int sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) + \brief Get Hardware/Firmware Version + \param fd device file descriptor + \param tdm_api tdm api command structure + \param ver hardware/firmware version number + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver); + +/*! + \fn int sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) + \brief Get Hardare/CPLD Version + \param fd device file descriptor + \param tdm_api tdm api command structure + \param ver hardware/cpld version number + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver); + + +/*! + \fn int sangoma_get_stats(sng_fd_t fd, wanpipe_api_t *tdm_api, wanpipe_chan_stats_t *stats) + \brief Get Device Statistics. Statistics will be available in tdm_api->wp_cmd.stats structure. + \param fd device file descriptor + \param tdm_api tdm api command structure + \param stats stats structure will be filled with device stats. (Optional, can be left NULL) + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_get_stats(sng_fd_t fd, wanpipe_api_t *tdm_api, wanpipe_chan_stats_t *stats); + +/*! + \fn int _SAPI_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush/Reset device statistics + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, int rxflashtime) + \brief Set rxflashtime for FXS module Wink-Flash Event + \param fd device file descriptor + \param tdm_api tdm api command structure + \param rxflashtime time value + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, int rxflashtime); + +<<<<<<< .mine +#ifdef WP_API_FEATURE_RM_TX_GAIN +======= +#ifdef WP_API_FEATURE_RM_GAIN +>>>>>>> .r251 +/*! + \fn int sangoma_set_rm_tx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) + \brief set tx gaom for FXO/FXS module + \param fd device file descriptor + \param tdm_api tdm api command structure + \param value txgain (FXO - txgain value ranges from -150 to 120 , FXS - txgain value 35,-35) + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_set_rm_tx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value); + +/*! + \fn int sangoma_set_rm_rx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) + \brief set rx gaom for FXO/FXS module + \param fd device file descriptor + \param tdm_api tdm api command structure + \param value rxgain (FXO - rxgain value ranges from -150 to 120 , FXS -rxgain value 35,-35) + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_set_rm_rx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value); +#endif + +#endif /* WP RM GAIN feature */ + +/*! + \fn int sangoma_set_polarity(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) + \brief set rx gaom for FXO/FXS module + \param fd device file descriptor + \param tdm_api tdm api command structure + \param value 0 fwd, 1 rev + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_tdm_set_polarity(sng_fd_t fd, wanpipe_api_t *tdm_api, int polarity); + + +/************************************************************//** + * Device EVENT Function + ***************************************************************/ + + +/*! + \fn int sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Read API Events + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + The TDM API structure will be populated with a TDM API or WAN Event. + This function usually used after wait() function indicated that event + has occured. +*/ +int _SAPI_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api); + +#ifdef WP_API_FEATURE_LOGGER +/*! + \fn sangoma_status_t sangoma_logger_read_event(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Read Wanpipe Logger Events + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error + + The Logger API structure will be populated with a Logger Event. + This function usually used after wait() function indicated that + an event has occured. +*/ +sangoma_status_t _SAPI_CALL sangoma_logger_read_event(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_flush_buffers(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Flush Wanpipe Logger internal buffers + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _SAPI_CALL sangoma_logger_flush_buffers(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_get_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Get Wanpipe Logger statistics + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _SAPI_CALL sangoma_logger_get_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_reset_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Reset Wanpipe Logger statistics + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _SAPI_CALL sangoma_logger_reset_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_get_open_handle_counter(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Get Counter of open Handles/File Descriptors of Wanpipe Logger + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _SAPI_CALL sangoma_logger_get_open_handle_counter(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_get_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Get current level (types of events) of Wanpipe Logger + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _SAPI_CALL sangoma_logger_get_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_set_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Set current level (types of events) of Wanpipe Logger + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _SAPI_CALL sangoma_logger_set_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +#endif /* WP LOGGER FEATURE */ + +#ifndef LIBSANGOMA_LIGHT + +/************************************************************//** + * Device PORT Control Functions + ***************************************************************/ + +/*! + \fn int sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) + \brief Start a Port, create Sangoma Communication interfaces. + \param[in] fd Port Device file descriptor + \param[out] port_mgmnt pointer to a port_management_struct_t structure. + On return, sangoma_driver_port_start() updates operation_status field + of this structure. + \param[in] port_no 1-based Port Number. Port numbers correspond to Port Names. + For example, a 2-Port card will have ports named WANPIPE1 and WANPIPE2. + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_mgmt->operation_status. +*/ +int _SAPI_CALL sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); + + +/*! + \fn int sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) + \brief Start a Port, create Sangoma Communication interfaces. + \param[in] fd Port Device file descriptor + \param[out] port_mgmnt pointer to a port_management_struct_t structure. + On return, sangoma_driver_port_stop() updates operation_status field + of this structure. + \param[in] port_no 1-based Port Number. Port numbers correspond to Port Names. + For example, a 2-Port card will have ports named WANPIPE1 and WANPIPE2. + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_mgmt->operation_status. +*/ +int _SAPI_CALL sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); + + +/*! + \fn int sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no) + \brief Set Port's "Volatile" configuration. The configuration will not persist between system restarts. + Before calling this function please stop the port by calling sangoma_driver_port_stop(). + After calling this function please start the port by calling sangoma_driver_port_start(). + \param[in] fd Port Device file descriptor + \param[in, out] port_cfg pointer to port_cfg_t structure that specifies complete Port configuration. + On return, sangoma_driver_port_set_config() updates operation_status field + of this structure. + \param[in] port_no 1-based Port Number. Port numbers correspond to Port Names. + For example, a 2-Port card will have ports named WANPIPE1 and WANPIPE2. + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_cfg->operation_status. +*/ +int _SAPI_CALL sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no); + + +/*! + \fn int sangoma_driver_port_get_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no) + \brief Retrieve Port's "Volatile" configuration. + \param[in] fd Port Device file descriptor + \param[out] port_cfg pointer to port_cfg_t structure. + On return, sangoma_driver_port_get_config() will copy current Port configuration + into this structure. + \param[in] port_no please see comment of sangoma_driver_port_set_config() + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_cfg->operation_status. +*/ +int _SAPI_CALL sangoma_driver_port_get_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no); + + +/*! + \fn int sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) + \brief Retrieve information about a single instance of Sangoma hardware. + \param[in] fd Port Device file descriptor + \param[out] port_mgmnt pointer to port_management_struct_t structure which will contain hardware_info_t at + it's "data" field, when this function returns. + \param[in] port_no please see comment of sangoma_driver_port_set_config() + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_mgmt->operation_status. +*/ +int _SAPI_CALL sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); + + +/*! + \fn int sangoma_write_port_config_on_persistent_storage(hardware_info_t *hardware_info, port_cfg_t *port_cfg) + \brief Write Port's configuration on the hard disk. + Linux Specific: the "Persistent" configuration of a Port N (e.g. WANPIPE1) is stored in + /etc/wanpipe/wanpipeN.conf (e.g. wanpipe1.conf). + Configuration can be manualy viewed/changed by editing the ".conf" file. + Currently this functionality is not implemented. + Windows Specific: the "Persistent" configuration of a Port (e.g. WANPIPE1) is stored in + Windows Registry. + Configuration can be manualy viewed/changed in the Device Manager. + \param[in] hardware_info pointer to hardware_info_t structure containing information about a + single instance of Sangoma hardware. + \param[in] port_cfg pointer to structure containing complete Port configuration. + \param[in] port_no please see comment of sangoma_driver_port_set_config() + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_write_port_config_on_persistent_storage(hardware_info_t *hardware_info, port_cfg_t *port_cfg, unsigned short port_no); + + +/************************************************************//** + * Device MANAGEMENT Functions + ***************************************************************/ + +/*! + \fn int sangoma_mgmt_cmd(sng_fd_t fd, wan_udp_hdr_t* wan_udp) + \brief Execute Sangoma Management Command + \param fd device file descriptor + \param wan_udp management command structure + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_mgmt_cmd(sng_fd_t fd, wan_udp_hdr_t* wan_udp); + + +#endif /* LIBSANGOMA_LIGHT */ + + +/*================================================================ + * DEPRECATED Function Calls - Not to be used any more + * Here for backward compatibility + *================================================================*/ + + +#ifndef LIBSANGOMA_SET_FESTATUS +/*! + \def LIBSANGOMA_SET_FESTATUS + \brief Set Front End Status Feature +*/ +#define LIBSANGOMA_SET_FESTATUS 1 +#endif + +/*! + \fn int sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status) + \brief Get Device Link Status (Connected/Disconnected) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param current_status pointer where result will be filled: 0=Link UP 1=Link Down + \return non-zero: error, 0: ok -> check current_status + + Deprecated - replaced by sangoma_tdm_get_link_status function +*/ +int _SAPI_CALL sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status); + + + +/*! + \fn int sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int codec) + \brief Set TDM Codec per chan + \param fd device file descriptor + \param tdm_api tdm api command structure + \param codec codec to set (ulaw/alaw/slinear) + \return non-zero: error, 0: ok + + Deprecated Function - Here for backward compatibility + Only valid in CHAN Operation Mode +*/ +int _SAPI_CALL sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int codec); + +/*! + \fn int sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Configured TDM Codec per chan + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative: error or configured codec value + + Deprecated Function - Here for backward compatibility + Only valid in CHAN Operation Mode +*/ +int _SAPI_CALL sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn sng_fd_t sangoma_create_socket_by_name(char *device, char *card) + \brief Open a device based on a interface and card name + \param device interface name + \param card card name + \return File Descriptor: -1 error, 0 or positive integer: valid file descriptor + + Deprecated - here for backward compatibility +*/ +sng_fd_t _SAPI_CALL sangoma_create_socket_by_name(char *device, char *card); + +/*! + \fn int sangoma_interface_toi(char *interface_name, int *span, int *chan) + \brief Convert Span & Chan to interface name + \param interface_name pointer to string where interface name will be written + \param span span number starting from 1 to 255 + \param chan chan number starting from 1 to 32 + \return non-zero = error, 0 = ok + Deprecated - here for backward compatibility +*/ +int _SAPI_CALL sangoma_interface_toi(char *interface_name, int *span, int *chan); + + +/*! + \fn int sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, int power) + \brief Set Power Level - so only data matching the power level would be passed up. + \param fd device file descriptor + \param tdm_api tdm api command structure + \param power value of power + \return non-zero: error, 0: ok + + Deprecated - not used/implemented +*/ +int _SAPI_CALL sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, int power); + +/*! + \fn int sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Configured Power Level + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative: error or configured power level + + Deprecated - not used/implemented +*/ +int _SAPI_CALL sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +#ifdef __cplusplus +} +#endif/* __WINDOWS__ */ + + +#if defined(__WINDOWS__) +/*! Windows specific API */ +#ifdef __cplusplus +extern "C" { /* for C++ users */ +#endif + +sangoma_status_t _SAPI_CALL sangoma_unload_driver(); +sangoma_status_t _SAPI_CALL sangoma_load_driver(); + +#ifdef __cplusplus +} +#endif/* __WINDOWS__ */ +#else +/*! Backward compabile defines */ +#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 + +#endif /* _LIBSNAGOMA_H */ + diff --git a/api/libsangoma/.svn/tmp/tempfile.21.tmp b/api/libsangoma/.svn/tmp/tempfile.21.tmp new file mode 100644 index 0000000..e7909c2 --- /dev/null +++ b/api/libsangoma/.svn/tmp/tempfile.21.tmp @@ -0,0 +1,1667 @@ +/*******************************************************************************//** + * \file libsangoma.h + * \brief Wanpipe API Library header for Sangoma AFT T1/E1/Analog/BRI/Serial Hardware - + * \brief Provides User a Unified/OS Agnostic API to Wanpipe/Sangoma Drivers and Hardware + * + * Author(s): Nenad Corbic + * David Rokhvarg + * Michael Jerris + * Anthony Minessale II + * + * Copyright: (c) 2005-2008 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 Sangoma Technologies 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 Sangoma Technologies ``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 Sangoma Technologies 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 + * =============================================================================== + * v.2.0.0 Nenad Corbic + * Jan 30 2009 + * Added sangoma_get_driver_version, sangoma_get_firmware_version, + * sangoma_get_cpld_version functions,sangoma_get_stats,sangoma_flush_stats + */ + +#ifndef _LIBSNAGOMA_H +#define _LIBSNAGOMA_H + +#ifdef __linux__ +#ifndef __LINUX__ +/* most wanpipe driver headers require __LINUX__ to be defined */ +#define __LINUX__ +#endif +#endif + +#ifdef __cplusplus +extern "C" { /* for C++ users */ +#endif + +#include + + +/*! + \def WANPIPE_TDM_API + \brief Used by compiler and driver to enable TDM API +*/ +#define WANPIPE_TDM_API 1 + +/*TODO: LIBSANGOMA_VERSION_CODE should be generated out of LIBSANGOMA_LT_CURRENT and friends in configure.in */ + +/*! + \def LIBSANGOMA_VERSION + \brief LibSangoma Macro to check the Version Number +*/ +#define LIBSANGOMA_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +/*! + \def LIBSANGOMA_VERSION_CODE + \brief LibSangoma Current Version Number to be checked against the LIBSANGOMA_VERSION Macro +*/ +#define LIBSANGOMA_VERSION_CODE LIBSANGOMA_VERSION(3,3,0) + +/*! + \def LIBSANGOMA_VERSION_STR + \brief LibSangoma Version in string format +*/ +#define LIBSANGOMA_VERSION_STR "3.3.0" + +struct sangoma_wait_obj; +#define sangoma_wait_obj_t struct sangoma_wait_obj + + +/*! + \def SANGOMA_DECLARE_TDM_API_CMD + \brief Instantiate/Declare a tdm api cmd strucure + \def SANGOMA_INIT_TDM_API_CMD + \brief Initialize the tdm api cmd structure. Set to 0. + \def SANGOMA_DECLARE_INIT_TDM_API_CMD + \brief Declare and initialize the tdm api cmd structure. +*/ +#define SANGOMA_DECLARE_TDM_API_CMD(_name_) wanpipe_api_t _name_ +#define SANGOMA_INIT_TDM_API_CMD(_name_) memset(&_name_,0,sizeof(_name_)) +#define SANGOMA_DECLARE_INIT_TDM_API_CMD(_name_) wanpipe_tdm_api_t _name_; SANGOMA_INIT_TDM_API_CMD(_name_) + + +#if defined(WIN32) || defined(WIN64) +#ifndef __WINDOWS__ +#define __WINDOWS__ +#endif +#include +#include +#include +#include +#include + +/*! + \def _SAPI_CALL + \brief libsangoma.dll functions exported as __cdecl calling convention +*/ +#define _SAPI_CALL __cdecl + +/*! + \def SANGOMA_INFINITE_API_POLL_WAIT (deprecated, use SANGOMA_WAIT_INFINITE instead) + \brief Infinite poll timeout value +*/ +#define SANGOMA_INFINITE_API_POLL_WAIT INFINITE +#define SANGOMA_WAIT_INFINITE INFINITE + +/*! + \def sangoma_msleep(x) + \brief milisecond sleep function +*/ +#define sangoma_msleep(x) Sleep(x) + +/*! + \def sangoma_ctime(time_val) + \brief Convert a time value to a string + \param time_val pointer to 64-bit time value +*/ +#define sangoma_ctime(time) _ctime64(time) + +#else +/* L I N U X */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*! + \def _SAPI_CALL + \brief Not used in Linux +*/ +#define _SAPI_CALL + +/*! + \def INVALID_HANDLE_VALUE + \brief Invalid file handle value -1, Ported from Windows +*/ +#define INVALID_HANDLE_VALUE -1 + +/*! + \def SANGOMA_INFINITE_API_POLL_WAIT (deprecated, use SANGOMA_WAIT_INFINITE instead) + \brief Infinite poll timeout value -1, Ported from Windows +*/ +#define SANGOMA_INFINITE_API_POLL_WAIT -1 +#define SANGOMA_WAIT_INFINITE -1 + +/*! + \def __cdecl + \brief Ported from Windows + \typedef BOOL + \brief Boolean type int, Ported from Windows + \typedef DWORD + \brief DWORD type is int, Ported from Windows + \def FALSE + \brief FALSE value is 0, Ported from Windows + \def TRUE + \brief TRUE value is 1, Ported from Windows + \def sangoma_msleep(x) + \brief milisecond sleep function + \def _getch + \brief get character, Ported from Windows + \def Sleep + \brief milisecond sleep function + \typedef HANDLE + \brief file handle type int, Ported from Windows + \typedef TCHAR + \brief TCHAN type mapped to char, Ported from Windows + \typedef ULONG + \brief ULONG type mapped to unsigned long, Ported from Windows + \typedef UCHAR + \brief ULONG type mapped to unsigned char, Ported from Windows + \typedef USHORT + \brief USHORT type mapped to unsigned short, Ported from Windows + \typedef LPSTR + \brief LPSTR type mapped to unsigned char *, Ported from Windows + \typedef PUCHAR + \brief PUCHAR type mapped to unsigned char *, Ported from Windows + \typedef LPTHREAD_START_ROUTINE + \brief LPTHREAD_START_ROUTINE type mapped to unsigned char *, Ported from Windows + \def _stricmp + \brief _stricmp type mapped to _stricmp, Ported from Windows + \def _snprintf + \brief _snprintf type mapped to snprintf, Ported from Windows +*/ + +#define __cdecl + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#define sangoma_msleep(x) usleep(x*1000) +#define _getch getchar +#define Sleep sangoma_msleep +#define _stricmp strcmp +#define _snprintf snprintf +#define _vsnprintf vsnprintf + +typedef int HANDLE; +typedef int BOOL; +typedef int DWORD; +typedef char TCHAR; +typedef unsigned char UCHAR; +typedef unsigned long ULONG; +typedef unsigned short USHORT; +typedef unsigned char * LPSTR; +typedef unsigned char * PUCHAR; +typedef void * LPTHREAD_START_ROUTINE; +typedef pthread_mutex_t CRITICAL_SECTION; + +#define EnterCriticalSection(arg) pthread_mutex_lock(arg) +#define LeaveCriticalSection(arg) pthread_mutex_unlock(arg) +#define InitializeCriticalSection(arg) pthread_mutex_init(arg, NULL); + +typedef struct tm SYSTEMTIME; +typedef char * LPCTSTR; + +/*! + \def sangoma_ctime(time_val) + \brief Convert a time value to a string + \param time_val pointer to time value +*/ +#define sangoma_ctime(time) ctime((time_t*)time) + +#endif/* WIN32 */ + + +/*! + LIBSANGOMA_LIGHT can be used to enable only IO and EVENT + libsangoma functions. The DRIVER configuration/start/stop + functions are not compiled. + + LIBSANGOMA_LIGHT depends only on 3 header files. Instead + of all wanpipe header files needed for DRIVER management + + LIBSANGMOA_LIGHT is NOT enabled by default. +*/ + +#ifdef LIBSANGOMA_LIGHT +#include "wanpipe_api_iface.h" +#include "wanpipe_api_hdr.h" +#include "sdla_te1.h" +#include "wanpipe_events.h" +#include "wanpipe_api_deprecated.h" +#else +#include "wanpipe_api.h" +#endif + +#ifdef __LINUX__ +#include "wanpipe_kernel.h" +#endif + +/*! + * As of now this typedef maps exactly to SANG_STATUS_T, however that + * is a kernel type, ugly, ugly, uglyyyyy, we should have strictly + * minimum set of shared data structures between kernel and user + * many return codes specified in SANG_STATUS_T are kernel specific + * like FAILED_TO_LOCK_USER_MEMORY or INVALID_IRQL, the libsangoma + * user does not need that much information, and even if ever needs + * it we should provide simpler defaults + * \brief return status from sangoma APIs + */ +typedef int32_t sangoma_status_t; + +/*! + \def FNAME_LEN + \brief string length of a file name + \def FUNC_DBG(x) + \brief function debug print function + \def DBG_PRINT + \brief debug print function +*/ +#define FNAME_LEN 100 +#define LIBSNG_FUNC_DBG() if(0)printf("%s(): line:%d\n", __FUNCTION__, __LINE__) +#define LIBSNG_DBG_PRINT if(0)printf + +/*! + \typedef sangoma_api_hdr_t + \brief Backward comaptible define of wp_api_hdr_t +*/ +typedef wp_api_hdr_t sangoma_api_hdr_t; + +/*! + \enum _sangoma_wait_obj_type_t + \brief Wait object type definition + \typedef sangoma_wait_obj_type_t + \brief Wait object type definition +*/ +typedef enum _sangoma_wait_obj_type +{ + /*! \brief deprecated, use SANGOMA_GENERIC_WAIT_OBJ */ + UNKNOWN_WAIT_OBJ = 0, + /*! \brief Generic object that can be signaled but is not associated to any sangoma device */ + SANGOMA_GENERIC_WAIT_OBJ = 0, + /*! \brief Sangoma object associated to some device which cannot be signaled (cannot call sangoma_wait_obj_signal on it) */ + SANGOMA_DEVICE_WAIT_OBJ, + /*! \brief Sangoma object that is associated to a device AND can be signaled */ + SANGOMA_DEVICE_WAIT_OBJ_SIG, +} sangoma_wait_obj_type_t; + +#define DECODE_SANGOMA_WAIT_OBJECT_TYPE(type)\ + type == SANGOMA_GENERIC_WAIT_OBJ ? "SANGOMA_GENERIC_WAIT_OBJ" :\ + type == SANGOMA_DEVICE_WAIT_OBJ ? "SANGOMA_DEVICE_WAIT_OBJ" :\ + type == SANGOMA_DEVICE_WAIT_OBJ_SIG ? "SANGOMA_DEVICE_WAIT_OBJ_SIG" :\ + "Invalid Wait Object type!" + +/* + * Possible flags for sangoma waitable objects + * this definitions MUST match POLLIN, POLLOUT and POLLPRI + * SANG_WAIT_OBJ_IS_SIGNALED has no posix equivalent though + * Users are encouraged to use this flags instead of the system ones + */ +typedef enum _sangoma_wait_obj_flags { + SANG_WAIT_OBJ_HAS_INPUT = POLLIN, + SANG_WAIT_OBJ_HAS_OUTPUT = POLLOUT, + SANG_WAIT_OBJ_HAS_EVENTS = POLLPRI, + SANG_WAIT_OBJ_IS_SIGNALED = 0x400 /* matches GNU extension POLLMSG */ +} sangoma_wait_obj_flags_t; + +/************************************************************//** + * Device OPEN / CLOSE Functions + ***************************************************************/ + +/*! + \fn sng_fd_t sangoma_open_api_span_chan(int span, int chan) + \brief Open a Device based on Span/Chan values + \param span span number starting from 1 to 255 + \param chan chan number starting from 1 to 32 + \return File Descriptor: -1 error, 0 or positive integer: valid file descriptor + + Restriced open, device will allowed to be open only once. +*/ +sng_fd_t _SAPI_CALL sangoma_open_api_span_chan(int span, int chan); + + +/*! + \fn sng_fd_t __sangoma_open_api_span_chan(int span, int chan) + \brief Open a Device based on Span/Chan values + \param span span number starting from 1 to 255 + \param chan chan number starting from 1 to 32 + \return File Descriptor: -1 error, 0 or positive integer: valid file descriptor + + Unrestriced open, allows mutiple open calls on a single device +*/ +sng_fd_t _SAPI_CALL __sangoma_open_api_span_chan(int span, int chan); +#define __sangoma_open_tdmapi_span_chan __sangoma_open_api_span_chan + +/*! + \fn sng_fd_t sangoma_open_tdmapi_span(int span) + \brief Open a first available device on a Span + \param span span number starting from 1 to 255 + \return File Descriptor: -1 error, 0 or positive integer: valid file descriptor + + Unrestriced open, allows mutiple open calls on a single device +*/ +sng_fd_t _SAPI_CALL sangoma_open_api_span(int span); + + +/*! + \def sangoma_create_socket_intr + \brief Backward compatible open span chan call +*/ + + + +/*! + \def LIBSANGOMA_TDMAPI_CTRL + \brief Global control device feature +*/ +#ifndef LIBSANGOMA_TDMAPI_CTRL +#define LIBSANGOMA_TDMAPI_CTRL 1 +#endif + +/*! + \fn sng_fd_t sangoma_open_api_ctrl(void) + \brief Open a Global Control Device + \return File Descriptor - negative=error 0 or greater = fd + + The global control device receives events for all devices + configured. +*/ +sng_fd_t _SAPI_CALL sangoma_open_api_ctrl(void); + +/*! + \fn sng_fd_t sangoma_logger_open(void) + \brief Open a Global Logger Device + \return File Descriptor - negative=error 0 or greater = fd + + The global Logger device receives Logger Events for all devices + configured. +*/ +sng_fd_t _SAPI_CALL sangoma_logger_open(void); + +/*! + \fn sng_fd_t sangoma_open_driver_ctrl(int port_no) + \brief Open a Global Driver Control Device + \return File Descriptor - negative=error 0 or greater = fd + + The global control device receives events for all devices + configured. +*/ +sng_fd_t _SAPI_CALL sangoma_open_driver_ctrl(int port_no); + + + +/*! + \fn void sangoma_close(sng_fd_t *fd) + \brief Close device file descriptor + \param fd device file descriptor + \return void + +*/ +void _SAPI_CALL sangoma_close(sng_fd_t *fd); + + + +/*! + \fn int sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get device open count + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative or 0: error, greater than 1 : open count +*/ + +int _SAPI_CALL sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/************************************************************//** + * Device READ / WRITE Functions + ***************************************************************/ + +/*! + \fn int sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, unsigned short datalen, int flag) + \brief Write Data to device + \param fd device file descriptor + \param hdrbuf pointer to header structure wp_api_hdr_t + \param hdrlen size of wp_api_hdr_t + \param databuf pointer to data buffer to be transmitted + \param datalen length of data buffer + \param flag currently not used, set to 0 + \return transmit size, must be equal to datalen, anything else is error + + In case of error return code, one must check the header operation_status + variable to identify the reason of an error. Please refer to the error codes. +*/ + +int _SAPI_CALL sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, + void *databuf, unsigned short datalen, int flag); + + +/*! + \fn int sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, int datalen, int flag) + \brief Read Data from device + \param fd device file descriptor + \param hdrbuf pointer to header structure wp_api_hdr_t + \param hdrlen size of wp_api_hdr_t + \param databuf pointer to data buffer to be received + \param datalen length of data buffer + \param flag currently not used, set to 0 + \return received size, must be equal to datalen, anything else is error or busy + + In case of error return code, one must check the header operation_status + variable to identify the reason of error. Please refer to the error codes. +*/ +int _SAPI_CALL sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, + void *databuf, int datalen, int flag); + + + + +/************************************************************//** + * Device POLL Functions + ***************************************************************/ + +/*! + \fn sangoma_status_t _SAPI_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, int32_t inflags, int32_t *outflags, int32_t timeout) + \brief Wait for a single waitable object + \param sangoma_wait_obj pointer to a wait object previously created with sangoma_wait_obj_create + \param timeout timeout in miliseconds in case of no event + \return SANG_STATUS_APIPOLL_TIMEOUT: timeout, SANG_STATUS_SUCCESS: event occured use sangoma_wait_obj_get_out_flags to check or error status +*/ +sangoma_status_t _SAPI_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, uint32_t inflags, uint32_t *outflags, int32_t timeout); + +/*! + \fn sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], int32_t in_flags[], int32_t out_flags[] uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout); + \brief Wait for multiple sangoma waitable objects + \param sangoma_wait_objects pointer to array of wait objects previously created with sangoma_wait_obj_create + \param in_flags array of flags corresponding to the flags the user is interested on for each waitable object + \param out_flags array of flags corresponding to the flags that are ready in the waitable objects + \param number_of_sangoma_wait_objects size of the array of waitable objects and flags + \param system_wait_timeout timeout in miliseconds in case of no event + \return negative: SANG_STATUS_APIPOLL_TIMEOUT: timeout, SANG_STATUS_SUCCESS: event occured check in sangoma_wait_objects, any other return code is some error +*/ +sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], uint32_t in_flags[], uint32_t out_flags[], + uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout); + +/*! + \fn sangoma_status_t sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma_wait_object, sng_fd_t fd, sangoma_wait_obj_type_t object_type) + \brief Create a wait object that will be used with sangoma_waitfor_many() API + \param sangoma_wait_object pointer a single device object + \param fd device file descriptor + \param object_type type of the wait object. see sangoma_wait_obj_type_t for types + \see sangoma_wait_obj_type_t + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _SAPI_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma_wait_object, sng_fd_t fd, sangoma_wait_obj_type_t object_type); + +/*! + \fn sangoma_status_t sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object) + \brief De-allocate all resources inside a wait object which were allocated by sangoma_wait_obj_init(). + \param sangoma_wait_object pointer to a pointer to a single device object + \return SANG_STATUS_SUCCESS on success or some sangoma status error +*/ +sangoma_status_t _SAPI_CALL sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object); + +/*! + \fn void sangoma_wait_obj_signal(sangoma_wait_obj_t *sangoma_wait_object) + \brief Set wait object to a signaled state + \param sangoma_wait_object pointer a single device object that can be signaled + \return sangoma_status_t +*/ +sangoma_status_t _SAPI_CALL sangoma_wait_obj_signal(sangoma_wait_obj_t *sangoma_wait_object); + +/*! + \fn sng_fd_t sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sangoma_wait_object) + \brief Get fd device file descriptor which was the 'fd' parameter for sangoma_wait_obj_create(), not useful for generic objects + \param sangoma_wait_object pointer a single device object + \return sng_fd_t - device file descriptor +*/ +sng_fd_t _SAPI_CALL sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sangoma_wait_object); + +/*! + \fn void sangoma_wait_obj_set_context(sangoma_wait_obj_t *sangoma_wait_object) + \brief Store the given context into provided sangoma wait object. + \brief This function is useful to associate a context with a sangoma wait object. + \param sangoma_wait_object pointer a single device object + \param context void pointer to user context + \return void +*/ +void _SAPI_CALL sangoma_wait_obj_set_context(sangoma_wait_obj_t *sangoma_wait_object, void *context); + +/*! + \fn void *sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object) + \brief Retrieve the user context (if any) that was set via sangoma_wait_obj_set_context. + \param sangoma_wait_object pointer a single device object + \return void* +*/ +void* _SAPI_CALL sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object); + + +/************************************************************//** + * Device API COMMAND Functions + ***************************************************************/ + +/*! + \fn int sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Execute Sangoma API Command + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Read tdm api device configuration + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, int period) + \brief Set Tx/Rx Period in Milliseconds + \param fd device file descriptor + \param tdm_api tdm api command structure + \param period value in miliseconds (1,2,5,10) + \return non-zero: error, 0: ok + + Only valid in CHAN Operation Mode +*/ +int _SAPI_CALL sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, int period); + +/*! + \fn int sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Tx/Rx Period in Milliseconds + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative: error or configured period value +*/ +int _SAPI_CALL sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Tx/Rx MTU/MRU in bytes + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative: error or configured mtu/mru in bytes +*/ +int _SAPI_CALL sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush all (tx/rx/event) buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _SAPI_CALL sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_flush_rx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush only rx buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _SAPI_CALL sangoma_flush_rx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); +/*! + \fn int sangoma_flush_tx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush only tx buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _SAPI_CALL sangoma_flush_tx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_flush_event_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush only event buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _SAPI_CALL sangoma_flush_event_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_tdm_enable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api, int poll_in_sec) + \brief Enable RBS Events on a device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param poll_in_sec driver poll period for rbs events + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_tdm_enable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api, int poll_in_sec); + +/*! + \fn int sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RBS Events for a device + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char rbs) + \brief Write RBS Bits on a device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param channel t1/e1 timeslot + \param rbs rbs bits (ABCD) + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char rbs); + +/*! + \fn int sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char *rbs) + \brief Read RBS Bits on a device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param channel t1/e1 timeslot + \param rbs pointer to rbs bits (ABCD) + \return non-zero: error, 0: ok +*/ + +int _SAPI_CALL sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char *rbs); + +/*! + \fn int sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable DTMF Detection on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _SAPI_CALL sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable DTMF Detection on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _SAPI_CALL sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +#ifdef WP_API_FEATURE_FAX_EVENTS +/*! + \fn int sangoma_tdm_enable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable FAX Detection on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _SAPI_CALL sangoma_tdm_enable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable FAX Detection on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _SAPI_CALL sangoma_tdm_disable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_get_hw_fax(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get HW FAX Detection State (Enable or Disabled) on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: Disabled, 1: Enabled + + Supported only on cards that have HWEC +*/ +int _SAPI_CALL sangoma_tdm_get_hw_fax(sng_fd_t fd, wanpipe_api_t *tdm_api); + +#endif + + +/*! + \fn int sangoma_tdm_enable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable DTMF Detection on Analog/Remora SLIC Chip + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_enable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable DTMF Detection on Analog/Remora SLIC Chip + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable RX HOOK Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RX HOOK Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable RING Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RING Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_tdm_enable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable RING DETECT Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_enable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RING DETECT Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable RING TRIP Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RING TRIP Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api, uint16_t tone_id) + \brief Transmit a TONE on this device (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param tone_id tone type to transmit + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api, uint16_t tone_id); + +/*! + \fn int sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable TONE Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG ON HOOK (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG OFF HOOK (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG START (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG KEWL START (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable HWEC on this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _SAPI_CALL sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable HWEC on this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _SAPI_CALL sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int _SAPI_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms); + \brief Get Front End Alarms (T1/E1 Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param alarms bit map status of T1/E1 alarms + \return non-zero: error, 0: ok + + Supported only on T1/E1 Cards +*/ +int _SAPI_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms); + + + +#ifdef WP_API_FEATURE_LINK_STATUS +# ifndef LIBSANGOMA_GET_LINKSTATUS +/*! + \def LIBSANGOMA_GET_LINKSTATUS + \brief Get Link Status feature +*/ +# define LIBSANGOMA_GET_LINKSTATUS 1 +# endif + +/*! + \fn int sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status) + \brief Get Device Link Status (Connected/Disconnected) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param current_status pointer where result will be filled: 0=Link UP 1=Link Down + \return non-zero: error, 0: ok -> check current_status + +*/ +int _SAPI_CALL sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status); + +#endif + +/* set current Line Connection state - Connected/Disconnected */ +#ifndef LIBSANGOMA_GET_FESTATUS +/*! + \def LIBSANGOMA_GET_FESTATUS + \brief Get Front End Status feature +*/ +#define LIBSANGOMA_GET_FESTATUS 1 +#endif + +/*! + \fn int sangoma_set_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char new_status) + \brief Set Device Link Status (Connected/Disconnected) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param new_status new status 0=Link UP 1=Link Down + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_set_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char new_status); + + +/*! + \fn int _SAPI_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) + \brief Enable BRI Bchannel loopback - used when debugging bri device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param channel bri bchannel 1 or 2 + \return non-zero: error, 0: ok + +*/ +int _SAPI_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel); + +/*! + \fn int _SAPI_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) + \brief Disable BRI Bchannel loopback - used when debugging bri device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param channel bri bchannel 1 or 2 + \return non-zero: error, 0: ok + +*/ +int _SAPI_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel); + + +/*! + \fn int sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Tx Queue Size for this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_set_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) + \brief Get Tx Queue Size for this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \param size tx queue size (minimum value of 1) + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_set_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size); + +/*! + \fn int sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Rx Queue Size for this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) + \brief Get Tx Queue Size for this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \param size rx queue size (minimum value of 1) + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size); + + +#ifndef LIBSANGOMA_GET_HWCODING +/*! + \def LIBSANGOMA_GET_HWCODING + \brief Get HW Coding Feature +*/ +#define LIBSANGOMA_GET_HWCODING 1 +#endif + +/*! + \fn int sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get HW Voice Coding (ulaw/alaw) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + This function will return the low level voice coding + depending on configuration. (ulaw or alaw) +*/ +int _SAPI_CALL sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api); + + + +#ifndef LIBSANGOMA_GET_HWDTMF +/*! + \def LIBSANGOMA_GET_HWDTMF + \brief HW DTMF Feature +*/ +#define LIBSANGOMA_GET_HWDTMF 1 +#endif +/*! + \fn int sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Check if hwdtmf support is available + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + This function will check if hw supports HW DTMF. +*/ +int _SAPI_CALL sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Check if hw echo cancelation support is available + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + This function will check if hw supports HW EC. +*/ +int _SAPI_CALL sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_span_chan_toif(int span, int chan, char *interface_name) + \brief Convert Span & Chan to interface name + \param span span number starting from 1 to 255 + \param chan chan number starting from 1 to 32 + \param interface_name pointer to string where interface name will be written + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_span_chan_toif(int span, int chan, char *interface_name); + +/*! + \fn int sangoma_span_chan_fromif(char *interface_name, int *span, int *chan) + \brief Convert Interace Name to Span & Chan + \param interface_name pointer to string containing interface name + \param span integer pointer where to write span value + \param chan integer pointer where to write chan value + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_span_chan_fromif(char *interface_name, int *span, int *chan); + + +/*! + \fn int sangoma_interface_wait_up(int span, int chan, int sectimeout) + \brief Wait for a sangoma device to come up (ie: Linux wait for /dev/wanpipex_1 to come up) + \param span span number of the device to wait + \param chan chan number of the device to wait + \param sectimeout how many seconds to wait for the device to come up, -1 to wait forever + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_interface_wait_up(int span, int chan, int sectimeout); + +/*! + \fn int sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, wan_driver_version_t *drv_ver) + \brief Get Device Driver Version Number + \param fd device file descriptor + \param tdm_api tdm api command structure + \param drv_ver driver version structure that will contain the driver version + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, wan_driver_version_t *drv_ver); + +/*! + \fn int sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) + \brief Get Hardware/Firmware Version + \param fd device file descriptor + \param tdm_api tdm api command structure + \param ver hardware/firmware version number + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver); + +/*! + \fn int sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) + \brief Get Hardare/CPLD Version + \param fd device file descriptor + \param tdm_api tdm api command structure + \param ver hardware/cpld version number + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver); + + +/*! + \fn int sangoma_get_stats(sng_fd_t fd, wanpipe_api_t *tdm_api, wanpipe_chan_stats_t *stats) + \brief Get Device Statistics. Statistics will be available in tdm_api->wp_cmd.stats structure. + \param fd device file descriptor + \param tdm_api tdm api command structure + \param stats stats structure will be filled with device stats. (Optional, can be left NULL) + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_get_stats(sng_fd_t fd, wanpipe_api_t *tdm_api, wanpipe_chan_stats_t *stats); + +/*! + \fn int _SAPI_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush/Reset device statistics + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, int rxflashtime) + \brief Set rxflashtime for FXS module Wink-Flash Event + \param fd device file descriptor + \param tdm_api tdm api command structure + \param rxflashtime time value + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, int rxflashtime); + +#ifdef WP_API_FEATURE_RM_GAIN +/*! + \fn int sangoma_set_rm_tx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) + \brief set tx gain for FXO/FXS module + \param fd device file descriptor + \param tdm_api tdm api command structure + \param value txgain (FXO - txgain value ranges from -150 to 120 , FXS - txgain value 35,-35) + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_set_rm_tx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value); + + +/*! + \fn int sangoma_set_rm_rx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) + \brief set rx gain for FXO/FXS module + \param fd device file descriptor + \param tdm_api tdm api command structure + \param value rxgain (FXO - rxgain value ranges from -150 to 120 , FXS -rxgain value 35,-35) + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_set_rm_rx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value); + +#endif /* WP RM GAIN feature */ + +/*! + \fn int sangoma_set_polarity(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) + \brief set polarity on FXS (Analog only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param value 0 fwd, 1 rev + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_tdm_set_polarity(sng_fd_t fd, wanpipe_api_t *tdm_api, int polarity); + +/*! + \fn int sangoma_tdm_txsig_onhooktranfer(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG ON HOOK Tranfer (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _SAPI_CALL sangoma_tdm_txsig_onhooktransfer(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +#ifdef WP_API_FEATURE_LOOP +/*! + \fn int sangoma_tdm_enable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable channel loop: All rx data will be transmitted back out. + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_tdm_enable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable channel loop + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero = error, 0 = ok +*/ +int _SAPI_CALL sangoma_tdm_disable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api); +#endif + +/************************************************************//** + * Device EVENT Function + ***************************************************************/ + + +/*! + \fn int sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Read API Events + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + The TDM API structure will be populated with a TDM API or WAN Event. + This function usually used after wait() function indicated that event + has occured. +*/ +int _SAPI_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api); + +#ifdef WP_API_FEATURE_LOGGER + +sangoma_status_t _SAPI_CALL sangoma_logger_cmd_exec(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_read_event(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Read Wanpipe Logger Events + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error + + The Logger API structure will be populated with a Logger Event. + This function usually used after wait() function indicated that + an event has occured. +*/ +sangoma_status_t _SAPI_CALL sangoma_logger_read_event(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_flush_buffers(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Flush Wanpipe Logger internal buffers + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _SAPI_CALL sangoma_logger_flush_buffers(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_get_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Get Wanpipe Logger statistics + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _SAPI_CALL sangoma_logger_get_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_reset_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Reset Wanpipe Logger statistics + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _SAPI_CALL sangoma_logger_reset_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_get_open_handle_counter(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Get Counter of open Handles/File Descriptors of Wanpipe Logger + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _SAPI_CALL sangoma_logger_get_open_handle_counter(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_get_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Get current level (types of events) of Wanpipe Logger + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _SAPI_CALL sangoma_logger_get_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_set_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Set current level (types of events) of Wanpipe Logger + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _SAPI_CALL sangoma_logger_set_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +#endif /* WP LOGGER FEATURE */ + +#ifndef LIBSANGOMA_LIGHT + +/************************************************************//** + * Device PORT Control Functions + ***************************************************************/ + +/*! + \fn int sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) + \brief Start a Port, create Sangoma Communication interfaces. + \param[in] fd Port Device file descriptor + \param[out] port_mgmnt pointer to a port_management_struct_t structure. + On return, sangoma_driver_port_start() updates operation_status field + of this structure. + \param[in] port_no 1-based Port Number. Port numbers correspond to Port Names. + For example, a 2-Port card will have ports named WANPIPE1 and WANPIPE2. + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_mgmt->operation_status. +*/ +int _SAPI_CALL sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); + + +/*! + \fn int sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) + \brief Start a Port, create Sangoma Communication interfaces. + \param[in] fd Port Device file descriptor + \param[out] port_mgmnt pointer to a port_management_struct_t structure. + On return, sangoma_driver_port_stop() updates operation_status field + of this structure. + \param[in] port_no 1-based Port Number. Port numbers correspond to Port Names. + For example, a 2-Port card will have ports named WANPIPE1 and WANPIPE2. + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_mgmt->operation_status. +*/ +int _SAPI_CALL sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); + + +/*! + \fn int sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no) + \brief Set Port's "Volatile" configuration. The configuration will not persist between system restarts. + Before calling this function please stop the port by calling sangoma_driver_port_stop(). + After calling this function please start the port by calling sangoma_driver_port_start(). + \param[in] fd Port Device file descriptor + \param[in, out] port_cfg pointer to port_cfg_t structure that specifies complete Port configuration. + On return, sangoma_driver_port_set_config() updates operation_status field + of this structure. + \param[in] port_no 1-based Port Number. Port numbers correspond to Port Names. + For example, a 2-Port card will have ports named WANPIPE1 and WANPIPE2. + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_cfg->operation_status. +*/ +int _SAPI_CALL sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no); + + +/*! + \fn int sangoma_driver_port_get_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no) + \brief Retrieve Port's "Volatile" configuration. + \param[in] fd Port Device file descriptor + \param[out] port_cfg pointer to port_cfg_t structure. + On return, sangoma_driver_port_get_config() will copy current Port configuration + into this structure. + \param[in] port_no please see comment of sangoma_driver_port_set_config() + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_cfg->operation_status. +*/ +int _SAPI_CALL sangoma_driver_port_get_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no); + + +/*! + \fn int sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) + \brief Retrieve information about a single instance of Sangoma hardware. + \param[in] fd Port Device file descriptor + \param[out] port_mgmnt pointer to port_management_struct_t structure which will contain hardware_info_t at + it's "data" field, when this function returns. + \param[in] port_no please see comment of sangoma_driver_port_set_config() + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_mgmt->operation_status. +*/ +int _SAPI_CALL sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); + + +/*! + \fn int sangoma_write_port_config_on_persistent_storage(hardware_info_t *hardware_info, port_cfg_t *port_cfg) + \brief Write Port's configuration on the hard disk. + Linux Specific: the "Persistent" configuration of a Port N (e.g. WANPIPE1) is stored in + /etc/wanpipe/wanpipeN.conf (e.g. wanpipe1.conf). + Configuration can be manualy viewed/changed by editing the ".conf" file. + Currently this functionality is not implemented. + Windows Specific: the "Persistent" configuration of a Port (e.g. WANPIPE1) is stored in + Windows Registry. + Configuration can be manualy viewed/changed in the Device Manager. + \param[in] hardware_info pointer to hardware_info_t structure containing information about a + single instance of Sangoma hardware. + \param[in] port_cfg pointer to structure containing complete Port configuration. + \param[in] port_no please see comment of sangoma_driver_port_set_config() + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_write_port_config_on_persistent_storage(hardware_info_t *hardware_info, port_cfg_t *port_cfg, unsigned short port_no); + + +/************************************************************//** + * Device MANAGEMENT Functions + ***************************************************************/ + +/*! + \fn int sangoma_mgmt_cmd(sng_fd_t fd, wan_udp_hdr_t* wan_udp) + \brief Execute Sangoma Management Command + \param fd device file descriptor + \param wan_udp management command structure + \return non-zero: error, 0: ok +*/ +int _SAPI_CALL sangoma_mgmt_cmd(sng_fd_t fd, wan_udp_hdr_t* wan_udp); + + +#endif /* LIBSANGOMA_LIGHT */ + + +/*================================================================ + * DEPRECATED Function Calls - Not to be used any more + * Here for backward compatibility + *================================================================*/ + + +#ifndef LIBSANGOMA_SET_FESTATUS +/*! + \def LIBSANGOMA_SET_FESTATUS + \brief Set Front End Status Feature +*/ +#define LIBSANGOMA_SET_FESTATUS 1 +#endif + +/*! + \fn int sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status) + \brief Get Device Link Status (Connected/Disconnected) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param current_status pointer where result will be filled: 0=Link UP 1=Link Down + \return non-zero: error, 0: ok -> check current_status + + Deprecated - replaced by sangoma_tdm_get_link_status function +*/ +int _SAPI_CALL sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status); + + + +/*! + \fn int sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int codec) + \brief Set TDM Codec per chan + \param fd device file descriptor + \param tdm_api tdm api command structure + \param codec codec to set (ulaw/alaw/slinear) + \return non-zero: error, 0: ok + + Deprecated Function - Here for backward compatibility + Only valid in CHAN Operation Mode +*/ +int _SAPI_CALL sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int codec); + +/*! + \fn int sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Configured TDM Codec per chan + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative: error or configured codec value + + Deprecated Function - Here for backward compatibility + Only valid in CHAN Operation Mode +*/ +int _SAPI_CALL sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn sng_fd_t sangoma_create_socket_by_name(char *device, char *card) + \brief Open a device based on a interface and card name + \param device interface name + \param card card name + \return File Descriptor: -1 error, 0 or positive integer: valid file descriptor + + Deprecated - here for backward compatibility +*/ +sng_fd_t _SAPI_CALL sangoma_create_socket_by_name(char *device, char *card); + +/*! + \fn int sangoma_interface_toi(char *interface_name, int *span, int *chan) + \brief Convert Span & Chan to interface name + \param interface_name pointer to string where interface name will be written + \param span span number starting from 1 to 255 + \param chan chan number starting from 1 to 32 + \return non-zero = error, 0 = ok + Deprecated - here for backward compatibility +*/ +int _SAPI_CALL sangoma_interface_toi(char *interface_name, int *span, int *chan); + + +/*! + \fn int sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, int power) + \brief Set Power Level - so only data matching the power level would be passed up. + \param fd device file descriptor + \param tdm_api tdm api command structure + \param power value of power + \return non-zero: error, 0: ok + + Deprecated - not used/implemented +*/ +int _SAPI_CALL sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, int power); + +/*! + \fn int sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Configured Power Level + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative: error or configured power level + + Deprecated - not used/implemented +*/ +int _SAPI_CALL sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +#ifdef __cplusplus +} +#endif/* __WINDOWS__ */ + + +#if defined(__WINDOWS__) +/*! Windows specific API */ +#ifdef __cplusplus +extern "C" { /* for C++ users */ +#endif + +sangoma_status_t _SAPI_CALL sangoma_unload_driver(); +sangoma_status_t _SAPI_CALL sangoma_load_driver(); + +#ifdef __cplusplus +} +#endif/* __WINDOWS__ */ +#else +/*! Backward compabile defines */ +#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 + +#endif /* _LIBSNAGOMA_H */ + diff --git a/api/libsangoma/.svn/tmp/tempfile.22.tmp b/api/libsangoma/.svn/tmp/tempfile.22.tmp new file mode 100644 index 0000000..faf647a --- /dev/null +++ b/api/libsangoma/.svn/tmp/tempfile.22.tmp @@ -0,0 +1,1963 @@ +/*******************************************************************************//** + * \file libsangoma.h + * \brief Wanpipe API Library header for Sangoma AFT T1/E1/Analog/BRI/Serial Hardware - + * \brief Provides User a Unified/OS Agnostic API to Wanpipe/Sangoma Drivers and Hardware + * + * Author(s): Nenad Corbic + * David Rokhvarg + * Michael Jerris + * Anthony Minessale II + * + * Copyright: (c) 2005-2008 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 Sangoma Technologies 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 Sangoma Technologies ``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 Sangoma Technologies 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 + * =============================================================================== + * v.2.0.0 Nenad Corbic + * Jan 30 2009 + * Added sangoma_get_driver_version, sangoma_get_firmware_version, + * sangoma_get_cpld_version functions,sangoma_get_stats,sangoma_flush_stats + */ + +#ifndef _LIBSNAGOMA_H +#define _LIBSNAGOMA_H + +#ifdef __linux__ +#ifndef __LINUX__ +/* most wanpipe driver headers require __LINUX__ to be defined */ +#define __LINUX__ +#endif +#endif + +#ifdef __cplusplus +extern "C" { /* for C++ users */ +#endif + +#include + + +/*! + \def WANPIPE_TDM_API + \brief Used by compiler and driver to enable TDM API +*/ +#define WANPIPE_TDM_API 1 + +/*TODO: LIBSANGOMA_VERSION_CODE should be generated out of LIBSANGOMA_LT_CURRENT and friends in configure.in */ + +/*! + \def LIBSANGOMA_VERSION + \brief LibSangoma Macro to check the Version Number +*/ +#define LIBSANGOMA_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +/*! + \def LIBSANGOMA_VERSION_CODE + \brief LibSangoma Current Version Number to be checked against the LIBSANGOMA_VERSION Macro +*/ +#define LIBSANGOMA_VERSION_CODE LIBSANGOMA_VERSION(3,3,0) + +/*! + \def LIBSANGOMA_VERSION_STR + \brief LibSangoma Version in string format +*/ +#define LIBSANGOMA_VERSION_STR "3.3.0" + +struct sangoma_wait_obj; +#define sangoma_wait_obj_t struct sangoma_wait_obj + + +/*! + \def SANGOMA_DECLARE_TDM_API_CMD + \brief Instantiate/Declare a tdm api cmd strucure + \def SANGOMA_INIT_TDM_API_CMD + \brief Initialize the tdm api cmd structure. Set to 0. + \def SANGOMA_DECLARE_INIT_TDM_API_CMD + \brief Declare and initialize the tdm api cmd structure. +*/ +#define SANGOMA_DECLARE_TDM_API_CMD(_name_) wanpipe_api_t _name_ +#define SANGOMA_INIT_TDM_API_CMD(_name_) memset(&_name_,0,sizeof(_name_)) +#define SANGOMA_DECLARE_INIT_TDM_API_CMD(_name_) wanpipe_tdm_api_t _name_; SANGOMA_INIT_TDM_API_CMD(_name_) + + +#if defined(WIN32) || defined(WIN64) +#ifndef __WINDOWS__ +#define __WINDOWS__ +#endif +#include +#include +#include +#include +#include + +/*! + \def _LIBSNG_CALL + \brief libsangoma.dll functions exported as __cdecl calling convention +*/ +#ifdef __COMPILING_LIBSANGOMA__ +# define _LIBSNG_CALL __declspec(dllexport) __cdecl +#else +# define _LIBSNG_CALL __declspec(dllimport) __cdecl +#endif + +/*! + \def SANGOMA_INFINITE_API_POLL_WAIT (deprecated, use SANGOMA_WAIT_INFINITE instead) + \brief Infinite poll timeout value +*/ +#define SANGOMA_INFINITE_API_POLL_WAIT INFINITE +#define SANGOMA_WAIT_INFINITE INFINITE + +/*! + \def sangoma_msleep(x) + \brief milisecond sleep function +*/ +#define sangoma_msleep(x) Sleep(x) + +/*! + \def sangoma_ctime(time_val) + \brief Convert a time value to a string + \param time_val pointer to 64-bit time value +*/ +#define sangoma_ctime(time) _ctime64(time) + +#else +/* L I N U X */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*! + \def _LIBSNG_CALL + \brief Not used in Linux +*/ +#define _LIBSNG_CALL + +/*! + \def INVALID_HANDLE_VALUE + \brief Invalid file handle value -1, Ported from Windows +*/ +#define INVALID_HANDLE_VALUE -1 + +/*! + \def SANGOMA_INFINITE_API_POLL_WAIT (deprecated, use SANGOMA_WAIT_INFINITE instead) + \brief Infinite poll timeout value -1, Ported from Windows +*/ +#define SANGOMA_INFINITE_API_POLL_WAIT -1 +#define SANGOMA_WAIT_INFINITE -1 + +/*! + \def __cdecl + \brief Ported from Windows + \typedef BOOL + \brief Boolean type int, Ported from Windows + \typedef DWORD + \brief DWORD type is int, Ported from Windows + \def FALSE + \brief FALSE value is 0, Ported from Windows + \def TRUE + \brief TRUE value is 1, Ported from Windows + \def sangoma_msleep(x) + \brief milisecond sleep function + \def _getch + \brief get character, Ported from Windows + \def Sleep + \brief milisecond sleep function + \typedef HANDLE + \brief file handle type int, Ported from Windows + \typedef TCHAR + \brief TCHAN type mapped to char, Ported from Windows + \typedef ULONG + \brief ULONG type mapped to unsigned long, Ported from Windows + \typedef UCHAR + \brief ULONG type mapped to unsigned char, Ported from Windows + \typedef USHORT + \brief USHORT type mapped to unsigned short, Ported from Windows + \typedef LPSTR + \brief LPSTR type mapped to unsigned char *, Ported from Windows + \typedef PUCHAR + \brief PUCHAR type mapped to unsigned char *, Ported from Windows + \typedef PVOID + \brief PVOID type mapped to void *, Ported from Windows + \typedef LPTHREAD_START_ROUTINE + \brief LPTHREAD_START_ROUTINE type mapped to unsigned char *, Ported from Windows + \def _stricmp + \brief _stricmp type mapped to _stricmp, Ported from Windows + \def _snprintf + \brief _snprintf type mapped to snprintf, Ported from Windows +*/ + +#define __cdecl + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#define sangoma_msleep(x) usleep(x*1000) +#define _getch getchar +#define Sleep sangoma_msleep +#define _stricmp strcmp +#define _snprintf snprintf +#define _vsnprintf vsnprintf + +typedef int HANDLE; +typedef int BOOL; +typedef int DWORD; +typedef char TCHAR; +typedef unsigned char UCHAR; +typedef unsigned long ULONG; +typedef unsigned short USHORT; +typedef unsigned char * LPSTR; +typedef unsigned char * PUCHAR; +typedef void * PVOID; +typedef void * LPTHREAD_START_ROUTINE; +typedef pthread_mutex_t CRITICAL_SECTION; + +#define EnterCriticalSection(arg) pthread_mutex_lock(arg) +#define LeaveCriticalSection(arg) pthread_mutex_unlock(arg) +#define InitializeCriticalSection(arg) pthread_mutex_init(arg, NULL); + +typedef struct tm SYSTEMTIME; +typedef char * LPCTSTR; + +/*! + \def sangoma_ctime(time_val) + \brief Convert a time value to a string + \param time_val pointer to time value +*/ +#define sangoma_ctime(time) ctime((time_t*)time) + +#endif/* WIN32 */ + + +/*! + LIBSANGOMA_LIGHT can be used to enable only IO and EVENT + libsangoma functions. The DRIVER configuration/start/stop + functions are not compiled. + + LIBSANGOMA_LIGHT depends only on 3 header files. Instead + of all wanpipe header files needed for DRIVER management + + LIBSANGMOA_LIGHT is NOT enabled by default. +*/ + +#ifdef LIBSANGOMA_LIGHT +#include "wanpipe_api_iface.h" +#include "wanpipe_api_hdr.h" +#include "sdla_te1.h" +#include "wanpipe_events.h" +#include "wanpipe_api_deprecated.h" +#else +#include "wanpipe_api.h" +#endif + +#ifdef __LINUX__ +#include "wanpipe_kernel.h" +#endif + +/*! + * As of now this typedef maps exactly to SANG_STATUS_T, however that + * is a kernel type, ugly, ugly, uglyyyyy, we should have strictly + * minimum set of shared data structures between kernel and user + * many return codes specified in SANG_STATUS_T are kernel specific + * like FAILED_TO_LOCK_USER_MEMORY or INVALID_IRQL, the libsangoma + * user does not need that much information, and even if ever needs + * it we should provide simpler defaults + * \brief return status from sangoma APIs + */ +typedef int32_t sangoma_status_t; + +/*! + \def FNAME_LEN + \brief string length of a file name + \def FUNC_DBG(x) + \brief function debug print function + \def DBG_PRINT + \brief debug print function +*/ +#define FNAME_LEN 100 +#define LIBSNG_FUNC_DBG() if(0)printf("%s(): line:%d\n", __FUNCTION__, __LINE__) +#define LIBSNG_DBG_PRINT if(0)printf + +/*! + \typedef sangoma_api_hdr_t + \brief Backward comaptible define of wp_api_hdr_t +*/ +typedef wp_api_hdr_t sangoma_api_hdr_t; + +/*! + \enum _sangoma_wait_obj_type_t + \brief Wait object type definition + \typedef sangoma_wait_obj_type_t + \brief Wait object type definition +*/ +typedef enum _sangoma_wait_obj_type +{ + /*! \brief deprecated, use SANGOMA_GENERIC_WAIT_OBJ */ + UNKNOWN_WAIT_OBJ = 0, + /*! \brief Generic object that can be signaled but is not associated to any sangoma device */ + SANGOMA_GENERIC_WAIT_OBJ = 0, + /*! \brief Sangoma object associated to some device which cannot be signaled (cannot call sangoma_wait_obj_signal on it) */ + SANGOMA_DEVICE_WAIT_OBJ, + /*! \brief Sangoma object that is associated to a device AND can be signaled */ + SANGOMA_DEVICE_WAIT_OBJ_SIG, +} sangoma_wait_obj_type_t; + +#define DECODE_SANGOMA_WAIT_OBJECT_TYPE(type)\ + type == SANGOMA_GENERIC_WAIT_OBJ ? "SANGOMA_GENERIC_WAIT_OBJ" :\ + type == SANGOMA_DEVICE_WAIT_OBJ ? "SANGOMA_DEVICE_WAIT_OBJ" :\ + type == SANGOMA_DEVICE_WAIT_OBJ_SIG ? "SANGOMA_DEVICE_WAIT_OBJ_SIG" :\ + "Invalid Wait Object type!" + +/* + * Possible flags for sangoma waitable objects + * this definitions MUST match POLLIN, POLLOUT and POLLPRI + * SANG_WAIT_OBJ_IS_SIGNALED has no posix equivalent though + * Users are encouraged to use this flags instead of the system ones + */ +typedef enum _sangoma_wait_obj_flags { + SANG_WAIT_OBJ_HAS_INPUT = POLLIN, + SANG_WAIT_OBJ_HAS_OUTPUT = POLLOUT, + SANG_WAIT_OBJ_HAS_EVENTS = POLLPRI, + SANG_WAIT_OBJ_IS_SIGNALED = 0x400 /* matches GNU extension POLLMSG */ +} sangoma_wait_obj_flags_t; + +/************************************************************//** + * Device OPEN / CLOSE Functions + ***************************************************************/ + +/*! + \fn sng_fd_t sangoma_open_api_span_chan(int span, int chan) + \brief Open a Device based on Span/Chan values + \param span span number starting from 1 to 255 + \param chan chan number starting from 1 to 32 + \return File Descriptor: -1 error, 0 or positive integer: valid file descriptor + + Restriced open, device will allowed to be open only once. +*/ +sng_fd_t _LIBSNG_CALL sangoma_open_api_span_chan(int span, int chan); + + +/*! + \fn sng_fd_t __sangoma_open_api_span_chan(int span, int chan) + \brief Open a Device based on Span/Chan values + \param span span number starting from 1 to 255 + \param chan chan number starting from 1 to 32 + \return File Descriptor: -1 error, 0 or positive integer: valid file descriptor + + Unrestriced open, allows mutiple open calls on a single device +*/ +sng_fd_t _LIBSNG_CALL __sangoma_open_api_span_chan(int span, int chan); +#define __sangoma_open_tdmapi_span_chan __sangoma_open_api_span_chan + +/*! + \fn sng_fd_t sangoma_open_tdmapi_span(int span) + \brief Open a first available device on a Span + \param span span number starting from 1 to 255 + \return File Descriptor: -1 error, 0 or positive integer: valid file descriptor + + Unrestriced open, allows mutiple open calls on a single device +*/ +sng_fd_t _LIBSNG_CALL sangoma_open_api_span(int span); + + +/*! + \def sangoma_create_socket_intr + \brief Backward compatible open span chan call +*/ + + + +/*! + \def LIBSANGOMA_TDMAPI_CTRL + \brief Global control device feature +*/ +#ifndef LIBSANGOMA_TDMAPI_CTRL +#define LIBSANGOMA_TDMAPI_CTRL 1 +#endif + +/*! + \fn sng_fd_t sangoma_open_api_ctrl(void) + \brief Open a Global Control Device + \return File Descriptor - negative=error 0 or greater = fd + + The global control device receives events for all devices + configured. +*/ +sng_fd_t _LIBSNG_CALL sangoma_open_api_ctrl(void); + +/*! + \fn sng_fd_t sangoma_logger_open(void) + \brief Open a Global Logger Device + \return File Descriptor - negative=error 0 or greater = fd + + The global Logger device receives Logger Events for all devices + configured. +*/ +sng_fd_t _LIBSNG_CALL sangoma_logger_open(void); + +/*! + \fn sng_fd_t sangoma_open_driver_ctrl(int port_no) + \brief Open a Global Driver Control Device + \return File Descriptor - negative=error 0 or greater = fd + + The global control device receives events for all devices + configured. +*/ +sng_fd_t _LIBSNG_CALL sangoma_open_driver_ctrl(int port_no); + + + +/*! + \fn void sangoma_close(sng_fd_t *fd) + \brief Close device file descriptor + \param fd device file descriptor + \return void + +*/ +void _LIBSNG_CALL sangoma_close(sng_fd_t *fd); + + + +/*! + \fn int sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get device open count + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative or 0: error, greater than 1 : open count +*/ + +int _LIBSNG_CALL sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/************************************************************//** + * Device READ / WRITE Functions + ***************************************************************/ + +/*! + \fn int sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, unsigned short datalen, int flag) + \brief Write Data to device + \param fd device file descriptor + \param hdrbuf pointer to header structure wp_api_hdr_t + \param hdrlen size of wp_api_hdr_t + \param databuf pointer to data buffer to be transmitted + \param datalen length of data buffer + \param flag currently not used, set to 0 + \return transmit size, must be equal to datalen, anything else is error + + In case of error return code, one must check the header operation_status + variable to identify the reason of an error. Please refer to the error codes. +*/ + +int _LIBSNG_CALL sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, + void *databuf, unsigned short datalen, int flag); + + +/*! + \fn int sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, int datalen, int flag) + \brief Read Data from device + \param fd device file descriptor + \param hdrbuf pointer to header structure wp_api_hdr_t + \param hdrlen size of wp_api_hdr_t + \param databuf pointer to data buffer to be received + \param datalen length of data buffer + \param flag currently not used, set to 0 + \return received size, must be equal to datalen, anything else is error or busy + + In case of error return code, one must check the header operation_status + variable to identify the reason of error. Please refer to the error codes. +*/ +int _LIBSNG_CALL sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, + void *databuf, int datalen, int flag); + + + + +/************************************************************//** + * Device POLL Functions + ***************************************************************/ + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, int32_t inflags, int32_t *outflags, int32_t timeout) + \brief Wait for a single waitable object + \param sangoma_wait_obj pointer to a wait object previously created with sangoma_wait_obj_create + \param timeout timeout in miliseconds in case of no event + \return SANG_STATUS_APIPOLL_TIMEOUT: timeout, SANG_STATUS_SUCCESS: event occured use sangoma_wait_obj_get_out_flags to check or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, uint32_t inflags, uint32_t *outflags, int32_t timeout); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], int32_t in_flags[], int32_t out_flags[] uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout); + \brief Wait for multiple sangoma waitable objects + \param sangoma_wait_objects pointer to array of wait objects previously created with sangoma_wait_obj_create + \param in_flags array of flags corresponding to the flags the user is interested on for each waitable object + \param out_flags array of flags corresponding to the flags that are ready in the waitable objects + \param number_of_sangoma_wait_objects size of the array of waitable objects and flags + \param system_wait_timeout timeout in miliseconds in case of no event + \return negative: SANG_STATUS_APIPOLL_TIMEOUT: timeout, SANG_STATUS_SUCCESS: event occured check in sangoma_wait_objects, any other return code is some error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], uint32_t in_flags[], uint32_t out_flags[], + uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout); + +/*! + \fn sangoma_status_t sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma_wait_object, sng_fd_t fd, sangoma_wait_obj_type_t object_type) + \brief Create a wait object that will be used with sangoma_waitfor_many() API + \param sangoma_wait_object pointer a single device object + \param fd device file descriptor + \param object_type type of the wait object. see sangoma_wait_obj_type_t for types + \see sangoma_wait_obj_type_t + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma_wait_object, sng_fd_t fd, sangoma_wait_obj_type_t object_type); + +/*! + \fn sangoma_status_t sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object) + \brief De-allocate all resources inside a wait object which were allocated by sangoma_wait_obj_init(). + \param sangoma_wait_object pointer to a pointer to a single device object + \return SANG_STATUS_SUCCESS on success or some sangoma status error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object); + +/*! + \fn void sangoma_wait_obj_signal(sangoma_wait_obj_t *sangoma_wait_object) + \brief Set wait object to a signaled state + \param sangoma_wait_object pointer a single device object that can be signaled + \return sangoma_status_t +*/ +sangoma_status_t _LIBSNG_CALL sangoma_wait_obj_signal(sangoma_wait_obj_t *sangoma_wait_object); + +/*! + \fn sng_fd_t sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sangoma_wait_object) + \brief Get fd device file descriptor which was the 'fd' parameter for sangoma_wait_obj_create(), not useful for generic objects + \param sangoma_wait_object pointer a single device object + \return sng_fd_t - device file descriptor +*/ +sng_fd_t _LIBSNG_CALL sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sangoma_wait_object); + +/*! + \fn void sangoma_wait_obj_set_context(sangoma_wait_obj_t *sangoma_wait_object) + \brief Store the given context into provided sangoma wait object. + \brief This function is useful to associate a context with a sangoma wait object. + \param sangoma_wait_object pointer a single device object + \param context void pointer to user context + \return void +*/ +void _LIBSNG_CALL sangoma_wait_obj_set_context(sangoma_wait_obj_t *sangoma_wait_object, void *context); + +/*! + \fn PVOID sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object) + \brief Retrieve the user context (if any) that was set via sangoma_wait_obj_set_context. + \brief Windows note: must use return type PVOID instead of void* to satisfy WDK compiler. + \param sangoma_wait_object pointer a single device object + \return void* +*/ +PVOID _LIBSNG_CALL sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object); + + +/************************************************************//** + * Device API COMMAND Functions + ***************************************************************/ + +/*! + \fn int sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Execute Sangoma API Command + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Read tdm api device configuration + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, int period) + \brief Set Tx/Rx Period in Milliseconds + \param fd device file descriptor + \param tdm_api tdm api command structure + \param period value in miliseconds (1,2,5,10) + \return non-zero: error, 0: ok + + Only valid in CHAN Operation Mode +*/ +int _LIBSNG_CALL sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, int period); + +/*! + \fn int sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Tx/Rx Period in Milliseconds + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative: error or configured period value +*/ +int _LIBSNG_CALL sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Tx/Rx MTU/MRU in bytes + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative: error or configured mtu/mru in bytes +*/ +int _LIBSNG_CALL sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush all (tx/rx/event) buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _LIBSNG_CALL sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_flush_rx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush only rx buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _LIBSNG_CALL sangoma_flush_rx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); +/*! + \fn int sangoma_flush_tx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush only tx buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _LIBSNG_CALL sangoma_flush_tx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_flush_event_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush only event buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _LIBSNG_CALL sangoma_flush_event_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_tdm_enable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api, int poll_in_sec) + \brief Enable RBS Events on a device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param poll_in_sec driver poll period for rbs events + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_tdm_enable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api, int poll_in_sec); + +/*! + \fn int sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RBS Events for a device + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char rbs) + \brief Write RBS Bits on a device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param channel t1/e1 timeslot + \param rbs rbs bits (ABCD) + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char rbs); + +/*! + \fn int sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char *rbs) + \brief Read RBS Bits on a device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param channel t1/e1 timeslot + \param rbs pointer to rbs bits (ABCD) + \return non-zero: error, 0: ok +*/ + +int _LIBSNG_CALL sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char *rbs); + +/*! + \fn int sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable DTMF Detection on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _LIBSNG_CALL sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable DTMF Detection on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _LIBSNG_CALL sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +#ifdef WP_API_FEATURE_FAX_EVENTS +/*! + \fn int sangoma_tdm_enable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable FAX Detection on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _LIBSNG_CALL sangoma_tdm_enable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable FAX Detection on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _LIBSNG_CALL sangoma_tdm_disable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_get_hw_fax(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get HW FAX Detection State (Enable or Disabled) on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: Disabled, 1: Enabled + + Supported only on cards that have HWEC +*/ +int _LIBSNG_CALL sangoma_tdm_get_hw_fax(sng_fd_t fd, wanpipe_api_t *tdm_api); + +#endif + + +/*! + \fn int sangoma_tdm_enable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable DTMF Detection on Analog/Remora SLIC Chip + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_enable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable DTMF Detection on Analog/Remora SLIC Chip + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable RX HOOK Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RX HOOK Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable RING Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RING Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_tdm_enable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable RING DETECT Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_enable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RING DETECT Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable RING TRIP Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RING TRIP Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api, uint16_t tone_id) + \brief Transmit a TONE on this device (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param tone_id tone type to transmit + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api, uint16_t tone_id); + +/*! + \fn int sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable TONE Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG ON HOOK (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG OFF HOOK (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG START (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG KEWL START (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable HWEC on this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _LIBSNG_CALL sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable HWEC on this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _LIBSNG_CALL sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int _LIBSNG_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms); + \brief Get Front End Alarms (T1/E1 Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param alarms bit map status of T1/E1 alarms + \return non-zero: error, 0: ok + + Supported only on T1/E1 Cards +*/ +int _LIBSNG_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms); + + + +#ifdef WP_API_FEATURE_LINK_STATUS +# ifndef LIBSANGOMA_GET_LINKSTATUS +/*! + \def LIBSANGOMA_GET_LINKSTATUS + \brief Get Link Status feature +*/ +# define LIBSANGOMA_GET_LINKSTATUS 1 +# endif + +/*! + \fn int sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status) + \brief Get Device Link Status (Connected/Disconnected) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param current_status pointer where result will be filled: 0=Link UP 1=Link Down + \return non-zero: error, 0: ok -> check current_status + +*/ +int _LIBSNG_CALL sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status); + +#endif + +/* set current Line Connection state - Connected/Disconnected */ +#ifndef LIBSANGOMA_GET_FESTATUS +/*! + \def LIBSANGOMA_GET_FESTATUS + \brief Get Front End Status feature +*/ +#define LIBSANGOMA_GET_FESTATUS 1 +#endif + +/*! + \fn int sangoma_set_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char new_status) + \brief Set Device Link Status (Connected/Disconnected) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param new_status new status 0=Link UP 1=Link Down + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_set_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char new_status); + + +/*! + \fn int _LIBSNG_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) + \brief Enable BRI Bchannel loopback - used when debugging bri device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param channel bri bchannel 1 or 2 + \return non-zero: error, 0: ok + +*/ +int _LIBSNG_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel); + +/*! + \fn int _LIBSNG_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) + \brief Disable BRI Bchannel loopback - used when debugging bri device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param channel bri bchannel 1 or 2 + \return non-zero: error, 0: ok + +*/ +int _LIBSNG_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel); + + +/*! + \fn int sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Tx Queue Size for this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_set_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) + \brief Get Tx Queue Size for this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \param size tx queue size (minimum value of 1) + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_set_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size); + +/*! + \fn int sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Rx Queue Size for this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) + \brief Get Tx Queue Size for this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \param size rx queue size (minimum value of 1) + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size); + + +#ifndef LIBSANGOMA_GET_HWCODING +/*! + \def LIBSANGOMA_GET_HWCODING + \brief Get HW Coding Feature +*/ +#define LIBSANGOMA_GET_HWCODING 1 +#endif + +/*! + \fn int sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get HW Voice Coding (ulaw/alaw) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + This function will return the low level voice coding + depending on configuration. (ulaw or alaw) +*/ +int _LIBSNG_CALL sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api); + + + +#ifndef LIBSANGOMA_GET_HWDTMF +/*! + \def LIBSANGOMA_GET_HWDTMF + \brief HW DTMF Feature +*/ +#define LIBSANGOMA_GET_HWDTMF 1 +#endif +/*! + \fn int sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Check if hwdtmf support is available + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + This function will check if hw supports HW DTMF. +*/ +int _LIBSNG_CALL sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Check if hw echo cancelation support is available + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + This function will check if hw supports HW EC. +*/ +int _LIBSNG_CALL sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_span_chan_toif(int span, int chan, char *interface_name) + \brief Convert Span & Chan to interface name + \param span span number starting from 1 to 255 + \param chan chan number starting from 1 to 32 + \param interface_name pointer to string where interface name will be written + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_span_chan_toif(int span, int chan, char *interface_name); + +/*! + \fn int sangoma_span_chan_fromif(char *interface_name, int *span, int *chan) + \brief Convert Interace Name to Span & Chan + \param interface_name pointer to string containing interface name + \param span integer pointer where to write span value + \param chan integer pointer where to write chan value + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_span_chan_fromif(char *interface_name, int *span, int *chan); + + +/*! + \fn int sangoma_interface_wait_up(int span, int chan, int sectimeout) + \brief Wait for a sangoma device to come up (ie: Linux wait for /dev/wanpipex_1 to come up) + \param span span number of the device to wait + \param chan chan number of the device to wait + \param sectimeout how many seconds to wait for the device to come up, -1 to wait forever + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_interface_wait_up(int span, int chan, int sectimeout); + +/*! + \fn int sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, wan_driver_version_t *drv_ver) + \brief Get Device Driver Version Number + \param fd device file descriptor + \param tdm_api tdm api command structure + \param drv_ver driver version structure that will contain the driver version + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, wan_driver_version_t *drv_ver); + +/*! + \fn int sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) + \brief Get Hardware/Firmware Version + \param fd device file descriptor + \param tdm_api tdm api command structure + \param ver hardware/firmware version number + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver); + +/*! + \fn int sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) + \brief Get Hardare/CPLD Version + \param fd device file descriptor + \param tdm_api tdm api command structure + \param ver hardware/cpld version number + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver); + + +/*! + \fn int sangoma_get_stats(sng_fd_t fd, wanpipe_api_t *tdm_api, wanpipe_chan_stats_t *stats) + \brief Get Device Statistics. Statistics will be available in tdm_api->wp_cmd.stats structure. + \param fd device file descriptor + \param tdm_api tdm api command structure + \param stats stats structure will be filled with device stats. (Optional, can be left NULL) + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_get_stats(sng_fd_t fd, wanpipe_api_t *tdm_api, wanpipe_chan_stats_t *stats); + +/*! + \fn int _LIBSNG_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush/Reset device statistics + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, int rxflashtime) + \brief Set rxflashtime for FXS module Wink-Flash Event + \param fd device file descriptor + \param tdm_api tdm api command structure + \param rxflashtime time value + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, int rxflashtime); + +#ifdef WP_API_FEATURE_RM_GAIN +/*! + \fn int sangoma_set_rm_tx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) + \brief set tx gain for FXO/FXS module + \param fd device file descriptor + \param tdm_api tdm api command structure + \param value txgain (FXO - txgain value ranges from -150 to 120 , FXS - txgain value 35,-35) + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_set_rm_tx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value); + + +/*! + \fn int sangoma_set_rm_rx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) + \brief set rx gain for FXO/FXS module + \param fd device file descriptor + \param tdm_api tdm api command structure + \param value rxgain (FXO - rxgain value ranges from -150 to 120 , FXS -rxgain value 35,-35) + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_set_rm_rx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value); + +#endif /* WP RM GAIN feature */ + +/*! + \fn int sangoma_set_polarity(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) + \brief set polarity on FXS (Analog only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param value 0 fwd, 1 rev + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_tdm_set_polarity(sng_fd_t fd, wanpipe_api_t *tdm_api, int polarity); + +/*! + \fn int sangoma_tdm_txsig_onhooktranfer(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG ON HOOK Tranfer (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_txsig_onhooktransfer(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +#ifdef WP_API_FEATURE_LOOP +/*! + \fn int sangoma_tdm_enable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable channel loop: All rx data will be transmitted back out. + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_tdm_enable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable channel loop + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_tdm_disable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api); +#endif + +/************************************************************//** + * Device EVENT Function + ***************************************************************/ + + +/*! + \fn int sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Read API Events + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + The TDM API structure will be populated with a TDM API or WAN Event. + This function usually used after wait() function indicated that event + has occured. +*/ +int _LIBSNG_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api); + +#ifdef WP_API_FEATURE_LOGGER + +sangoma_status_t _LIBSNG_CALL sangoma_logger_cmd_exec(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_read_event(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Read Wanpipe Logger Events + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error + + The Logger API structure will be populated with a Logger Event. + This function usually used after wait() function indicated that + an event has occured. +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_read_event(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_flush_buffers(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Flush Wanpipe Logger internal buffers + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_flush_buffers(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_get_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Get Wanpipe Logger statistics + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_get_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_reset_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Reset Wanpipe Logger statistics + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_reset_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_get_open_handle_counter(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Get Counter of open Handles/File Descriptors of Wanpipe Logger + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_get_open_handle_counter(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_get_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Get current level (types of events) of Wanpipe Logger + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_get_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_set_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Set current level (types of events) of Wanpipe Logger + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_set_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +#endif /* WP LOGGER FEATURE */ + +#ifndef LIBSANGOMA_LIGHT + +/************************************************************//** + * Device PORT Control Functions + ***************************************************************/ + +/*! + \fn int sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) + \brief Start a Port, create Sangoma Communication interfaces. + \param[in] fd Port Device file descriptor + \param[out] port_mgmnt pointer to a port_management_struct_t structure. + On return, sangoma_driver_port_start() updates operation_status field + of this structure. + \param[in] port_no 1-based Port Number. Port numbers correspond to Port Names. + For example, a 2-Port card will have ports named WANPIPE1 and WANPIPE2. + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_mgmt->operation_status. +*/ +int _LIBSNG_CALL sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); + + +/*! + \fn int sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) + \brief Start a Port, create Sangoma Communication interfaces. + \param[in] fd Port Device file descriptor + \param[out] port_mgmnt pointer to a port_management_struct_t structure. + On return, sangoma_driver_port_stop() updates operation_status field + of this structure. + \param[in] port_no 1-based Port Number. Port numbers correspond to Port Names. + For example, a 2-Port card will have ports named WANPIPE1 and WANPIPE2. + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_mgmt->operation_status. +*/ +int _LIBSNG_CALL sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); + + +/*! + \fn int sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no) + \brief Set Port's "Volatile" configuration. The configuration will not persist between system restarts. + Before calling this function please stop the port by calling sangoma_driver_port_stop(). + After calling this function please start the port by calling sangoma_driver_port_start(). + \param[in] fd Port Device file descriptor + \param[in, out] port_cfg pointer to port_cfg_t structure that specifies complete Port configuration. + On return, sangoma_driver_port_set_config() updates operation_status field + of this structure. + \param[in] port_no 1-based Port Number. Port numbers correspond to Port Names. + For example, a 2-Port card will have ports named WANPIPE1 and WANPIPE2. + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_cfg->operation_status. +*/ +int _LIBSNG_CALL sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no); + + +/*! + \fn int sangoma_driver_port_get_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no) + \brief Retrieve Port's "Volatile" configuration. + \param[in] fd Port Device file descriptor + \param[out] port_cfg pointer to port_cfg_t structure. + On return, sangoma_driver_port_get_config() will copy current Port configuration + into this structure. + \param[in] port_no please see comment of sangoma_driver_port_set_config() + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_cfg->operation_status. +*/ +int _LIBSNG_CALL sangoma_driver_port_get_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no); + + +/*! + \fn int sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) + \brief Retrieve information about a single instance of Sangoma hardware. + \param[in] fd Port Device file descriptor + \param[out] port_mgmnt pointer to port_management_struct_t structure which will contain hardware_info_t at + it's "data" field, when this function returns. + \param[in] port_no please see comment of sangoma_driver_port_set_config() + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_mgmt->operation_status. +*/ +int _LIBSNG_CALL sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); + + +/*! + \fn int sangoma_write_port_config_on_persistent_storage(hardware_info_t *hardware_info, port_cfg_t *port_cfg) + \brief Write Port's configuration on the hard disk. + Linux Specific: the "Persistent" configuration of a Port N (e.g. WANPIPE1) is stored in + /etc/wanpipe/wanpipeN.conf (e.g. wanpipe1.conf). + Configuration can be manualy viewed/changed by editing the ".conf" file. + Currently this functionality is not implemented. + Windows Specific: the "Persistent" configuration of a Port (e.g. WANPIPE1) is stored in + Windows Registry. + Configuration can be manualy viewed/changed in the Device Manager. + \param[in] hardware_info pointer to hardware_info_t structure containing information about a + single instance of Sangoma hardware. + \param[in] port_cfg pointer to structure containing complete Port configuration. + \param[in] port_no please see comment of sangoma_driver_port_set_config() + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_write_port_config_on_persistent_storage(hardware_info_t *hardware_info, port_cfg_t *port_cfg, unsigned short port_no); + + +/************************************************************//** + * Device MANAGEMENT Functions + ***************************************************************/ + +/*! + \fn int sangoma_mgmt_cmd(sng_fd_t fd, wan_udp_hdr_t* wan_udp) + \brief Execute Sangoma Management Command + \param fd device file descriptor + \param wan_udp management command structure + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_mgmt_cmd(sng_fd_t fd, wan_udp_hdr_t* wan_udp); + + +#endif /* LIBSANGOMA_LIGHT */ + + +/*================================================================ + * DEPRECATED Function Calls - Not to be used any more + * Here for backward compatibility + *================================================================*/ + + +#ifndef LIBSANGOMA_SET_FESTATUS +/*! + \def LIBSANGOMA_SET_FESTATUS + \brief Set Front End Status Feature +*/ +#define LIBSANGOMA_SET_FESTATUS 1 +#endif + +/*! + \fn int sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status) + \brief Get Device Link Status (Connected/Disconnected) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param current_status pointer where result will be filled: 0=Link UP 1=Link Down + \return non-zero: error, 0: ok -> check current_status + + Deprecated - replaced by sangoma_tdm_get_link_status function +*/ +int _LIBSNG_CALL sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status); + + + +/*! + \fn int sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int codec) + \brief Set TDM Codec per chan + \param fd device file descriptor + \param tdm_api tdm api command structure + \param codec codec to set (ulaw/alaw/slinear) + \return non-zero: error, 0: ok + + Deprecated Function - Here for backward compatibility + Only valid in CHAN Operation Mode +*/ +int _LIBSNG_CALL sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int codec); + +/*! + \fn int sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Configured TDM Codec per chan + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative: error or configured codec value + + Deprecated Function - Here for backward compatibility + Only valid in CHAN Operation Mode +*/ +int _LIBSNG_CALL sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn sng_fd_t sangoma_create_socket_by_name(char *device, char *card) + \brief Open a device based on a interface and card name + \param device interface name + \param card card name + \return File Descriptor: -1 error, 0 or positive integer: valid file descriptor + + Deprecated - here for backward compatibility +*/ +sng_fd_t _LIBSNG_CALL sangoma_create_socket_by_name(char *device, char *card); + +/*! + \fn int sangoma_interface_toi(char *interface_name, int *span, int *chan) + \brief Convert Span & Chan to interface name + \param interface_name pointer to string where interface name will be written + \param span span number starting from 1 to 255 + \param chan chan number starting from 1 to 32 + \return non-zero = error, 0 = ok + Deprecated - here for backward compatibility +*/ +int _LIBSNG_CALL sangoma_interface_toi(char *interface_name, int *span, int *chan); + + +/*! + \fn int sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, int power) + \brief Set Power Level - so only data matching the power level would be passed up. + \param fd device file descriptor + \param tdm_api tdm api command structure + \param power value of power + \return non-zero: error, 0: ok + + Deprecated - not used/implemented +*/ +int _LIBSNG_CALL sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, int power); + +/*! + \fn int sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Configured Power Level + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative: error or configured power level + + Deprecated - not used/implemented +*/ +int _LIBSNG_CALL sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +#ifdef WP_API_FEATURE_LIBSNG_HWEC +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_init(char *device_name) + + \brief Load Firmware image onto EC chip. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_init(char *device_name); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_release(char *device_name) + + \brief Reset internal state of HWEC API. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_release(char *device_name); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_operation_mode(char *device_name, int mode, unsigned int fe_chan_map) + + \brief Modify channel operation mode. + + \param mode One of WANEC_API_OPMODE_? values defined in wanpipe_api_iface.h: + WANEC_API_OPMODE_NORMAL, + WANEC_API_OPMODE_HT_FREEZE, + WANEC_API_OPMODE_HT_RESET, + WANEC_API_OPMODE_POWER_DOWN, + WANEC_API_OPMODE_NO_ECHO, + WANEC_API_OPMODE_SPEECH_RECOGNITION. + + \param fe_chan_map Bitmap of channels (timeslots for Digital, + lines for Analog) where the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_operation_mode(char *device_name, int mode, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_on (char *device_name, unsigned int fe_chan_map) + + \brief Set the channel state in the echo canceller to NORMAL/POWER ON. + This enables echo cancelation logic inside the chip. + The action is internal to EC chip itself, not related to AFT FPGA. + This call is slow and should be used only on startup. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, + lines for Analog) where the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_on(char *device_name, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_off (char *device_name, unsigned int fe_chan_map) + + \brief Set the channel state in the echo canceller to POWER OFF. + This disables echo cancellatio logic inside the chip and + data passes unmodified through the ec chip. + The action is internal to EC chip itself, not related + to AFT FPGA. This call is slow and should be used only on startup. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, + lines for Analog) where the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_off(char *device_name, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_enable(char *device_name, unsigned int fe_chan_map) + + \brief Redirect audio stream from AFT FPGA to EC chip. + This command effectively enables echo cancellation since + data is now forced through the EC chip by the FPGA. + Data will be modified by the echo canceller. + This command is recommened for fast enabling of Echo Cancellation. + Note 1: Chip must be configured and in POWER ON state for echo + Chancellation to take place. + Note 2: sangoma_tdm_enable_hwec() function can be use to achive + the same funcitnality based on file descriptor versus + channel map. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_enable(char *device_name, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_disable(char *device_name, unsigned int fe_chan_map) + + \brief Force AFT FPGA to bypass the echo canceller. + This command effectively disables echo cancellation since + data will not flowing through the ec chip. + Data will not be modified by the echo canceller. + This command is recommened for fast disabling of Echo Cancelation. + Note: sangoma_tdm_disable_hwec() function can be use to achive + the same functionality based on file descriptor versus + channel map. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_disable(char *device_name, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_channel_parameters(char *device_name, char *parameter, char *parameter_value, unsigned int channel_map) + + \brief Modify channel configuration parameters. + This is list of Echo Cancellation channel parameters: + + Channel parameter Channel parameter value + ================= ======================= + WANEC_EnableNlp TRUE | FALSE + WANEC_EnableTailDisplacement TRUE | FALSE + WANEC_TailDisplacement 0-896 + WANEC_SoutLevelControl TRUE | FALSE + WANEC_RinAutomaticLevelControl TRUE | FALSE + WANEC_SoutAutomaticLevelControl TRUE | FALSE + WANEC_SoutAdaptiveNoiseReduction TRUE | FALSE + WANEC_RoutNoiseReduction TRUE | FALSE + WANEC_ComfortNoiseMode COMFORT_NOISE_NORMAL + COMFORT_NOISE_FAST_LATCH + COMFORT_NOISE_EXTENDED + COMFORT_NOISE_OFF + WANEC_DtmfToneRemoval TRUE | FALSE + WANEC_AcousticEcho TRUE | FALSE + WANEC_NonLinearityBehaviorA 0-13 + WANEC_NonLinearityBehaviorB 0-8 + WANEC_DoubleTalkBehavior DT_BEH_NORMAL + DT_BEH_LESS_AGGRESSIVE + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_channel_parameters(char *device_name, char *parameter, char *parameter_value, unsigned int channel_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_tone_detection(char *device_name, int tone_id, int enable, unsigned int fe_chan_map, unsigned char port_map) + + \brief Enable/Disable tone detection (such as DTMF) of channels from channel map. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param tone_id See wanpipe_api_iface.h for list of valid tones + + \param enable A flag, if 1 - the specified tone will be detected, + if 0 - specified tone will not be detected. + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \param port_map Port\Direction of tone detection - Rx, Tx. See wanpipe_events.h for + list of valid ports (WAN_EC_CHANNEL_PORT_SOUT...). + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_tone_detection(char *device_name, int tone_id, int enable, unsigned int fe_chan_map, unsigned char port_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_print_statistics(char *device_name, int full, unsigned int fe_chan_map) + + \brief Read and print Chip/Channel statistics from EC chip. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param full Flag to read full statistics, if set to 1. + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will read statistics. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_print_statistics(char *device_name, int full, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_load(char *device_name, char *filename, char pcmlaw, int *out_buffer_id) + + \brief Load audio buffer to EC chip. The buffer can be played out using the sangoma_hwec_audio_buffer_playout() function. + + \param filename name of the audio file (without the extension). + Actual file must have .pcm extension. + Location: + Windows: %SystemRoot%\sang_ec_files (ex: c:\WINDOWS\sang_ec_files) + Linux: /etc/wanpipe/buffers + + \param out_buffer_id when the buffer is loaded on the chip, it is assigned an ID. This ID should + be used when requesting to play out the buffer. + + \return SANG_STATUS_SUCCESS: success, or error status + + */ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_load(char *device_name, char *filename, char pcmlaw, int *out_buffer_id); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_bufferunload(char *device_name, int in_buffer_id) + + \brief Unload/remove an audio buffer from the HWEC chip. + + \param device_name Sangoma wanpipe device name. (ex: wanpipe1 - Linux; wanpipe1_if1 - Windows). + + \param in_buffer_id ID of the buffer which will be unloaded. The ID must be initialized by sangoma_hwec_audio_bufferload(). + + \return SANG_STATUS_SUCCESS - buffer was successfully unloaded/removed, SANG_STATUS_GENERAL_ERROR - error occured +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_unload(char *device_name, int in_buffer_id); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_playout(char *device_name, unsigned int fe_chan_map, + unsigned char port_map, int buffer_id, int start, int repeat_cnt, int duration) + + \brief Start\Stop playing out an audio buffer previously loaded by sangoma_hwec_audio_buffer_load(). + + \param fe_chan_map Bitmap of channels (timeslots for Digital, + lines for Analog) where the call will take effect. + + \param port_map Port\Direction where the buffer will be played out. + This is the channel port on which the buffer will be + played (WAN_EC_CHANNEL_PORT_SOUT or WAN_EC_CHANNEL_PORT_ROUT) + + \param in_buffer_id ID of the buffer which will be unloaded. The ID must be initialized by sangoma_hwec_audio_bufferload(). + + \param start If 1 - start the play out, 0 - stop the play out + + \param repeat_cnt Number of times to play out the same buffer + + \param duration Maximum duration of the playout, in milliseconds. If it takes less then 'duration' to + play out the whole buffer this paramter is ignored. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_playout(char *device_name, unsigned int fe_chan_map, + unsigned char port, int in_buffer_id, int start, + int repeat_cnt, int duration); + +/*! + \fn void _LIBSNG_CALL sangoma_hwec_config_verbosity(int verbosity_level) + + \brief Set Verbosity level of EC API. The level controls amount of data + printed to stdout and wanpipelog.txt for diagnostic purposes. + + \param verbosity_level Valid values are from 0 to 3. + + \return SANG_STATUS_SUCCESS: success - the level was changed to 'verbosity_level', + SANG_STATUS_INVALID_PARAMETER: error - the level was not changed because new level is invalid +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_verbosity(int verbosity_level); + +#endif /* WP_API_FEATURE_LIBSNG_HWEC */ + + +#ifdef __cplusplus +} +#endif/* __WINDOWS__ */ + + +#if defined(__WINDOWS__) +/*! Windows specific API */ +#ifdef __cplusplus +extern "C" { /* for C++ users */ +#endif + +sangoma_status_t _LIBSNG_CALL sangoma_unload_driver(); +sangoma_status_t _LIBSNG_CALL sangoma_load_driver(); +void _LIBSNG_CALL sangoma_reset_port_numbers(); + +#ifdef __cplusplus +} +#endif/* __WINDOWS__ */ + +#else +/*! Backward compabile defines */ +#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 + +#endif /* _LIBSNAGOMA_H */ + diff --git a/api/libsangoma/.svn/tmp/tempfile.23.tmp b/api/libsangoma/.svn/tmp/tempfile.23.tmp new file mode 100644 index 0000000..58fecf9 --- /dev/null +++ b/api/libsangoma/.svn/tmp/tempfile.23.tmp @@ -0,0 +1,1965 @@ +/*******************************************************************************//** + * \file libsangoma.h + * \brief Wanpipe API Library header for Sangoma AFT T1/E1/Analog/BRI/Serial Hardware - + * \brief Provides User a Unified/OS Agnostic API to Wanpipe/Sangoma Drivers and Hardware + * + * Author(s): Nenad Corbic + * David Rokhvarg + * Michael Jerris + * Anthony Minessale II + * + * Copyright: (c) 2005-2008 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 Sangoma Technologies 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 Sangoma Technologies ``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 Sangoma Technologies 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 + * =============================================================================== + * v.2.0.0 Nenad Corbic + * Jan 30 2009 + * Added sangoma_get_driver_version, sangoma_get_firmware_version, + * sangoma_get_cpld_version functions,sangoma_get_stats,sangoma_flush_stats + */ + +#ifndef _LIBSNAGOMA_H +#define _LIBSNAGOMA_H + +#ifdef __linux__ +#ifndef __LINUX__ +/* most wanpipe driver headers require __LINUX__ to be defined */ +#define __LINUX__ +#endif +#endif + +#ifdef __cplusplus +extern "C" { /* for C++ users */ +#endif + +#include + + +/*! + \def WANPIPE_TDM_API + \brief Used by compiler and driver to enable TDM API +*/ +#define WANPIPE_TDM_API 1 + +/*TODO: LIBSANGOMA_VERSION_CODE should be generated out of LIBSANGOMA_LT_CURRENT and friends in configure.in */ + +/*! + \def LIBSANGOMA_VERSION + \brief LibSangoma Macro to check the Version Number +*/ +#define LIBSANGOMA_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +/*! + \def LIBSANGOMA_VERSION_CODE + \brief LibSangoma Current Version Number to be checked against the LIBSANGOMA_VERSION Macro +*/ +#define LIBSANGOMA_VERSION_CODE LIBSANGOMA_VERSION(3,3,0) + +/*! + \def LIBSANGOMA_VERSION_STR + \brief LibSangoma Version in string format +*/ +#define LIBSANGOMA_VERSION_STR "3.3.0" + +struct sangoma_wait_obj; +#define sangoma_wait_obj_t struct sangoma_wait_obj + + +/*! + \def SANGOMA_DECLARE_TDM_API_CMD + \brief Instantiate/Declare a tdm api cmd strucure + \def SANGOMA_INIT_TDM_API_CMD + \brief Initialize the tdm api cmd structure. Set to 0. + \def SANGOMA_DECLARE_INIT_TDM_API_CMD + \brief Declare and initialize the tdm api cmd structure. +*/ +#define SANGOMA_DECLARE_TDM_API_CMD(_name_) wanpipe_api_t _name_ +#define SANGOMA_INIT_TDM_API_CMD(_name_) memset(&_name_,0,sizeof(_name_)) +#define SANGOMA_DECLARE_INIT_TDM_API_CMD(_name_) wanpipe_tdm_api_t _name_; SANGOMA_INIT_TDM_API_CMD(_name_) + + +#if defined(WIN32) || defined(WIN64) +#ifndef __WINDOWS__ +#define __WINDOWS__ +#endif +#include +#include +#include +#include +#include + +/*! + \def _LIBSNG_CALL + \brief libsangoma.dll functions exported as __cdecl calling convention +*/ +#ifdef __COMPILING_LIBSANGOMA__ +# define _LIBSNG_CALL __declspec(dllexport) __cdecl +#else +# define _LIBSNG_CALL __declspec(dllimport) __cdecl +#endif + +/*! + \def SANGOMA_INFINITE_API_POLL_WAIT (deprecated, use SANGOMA_WAIT_INFINITE instead) + \brief Infinite poll timeout value +*/ +#define SANGOMA_INFINITE_API_POLL_WAIT INFINITE +#define SANGOMA_WAIT_INFINITE INFINITE + +/*! + \def sangoma_msleep(x) + \brief milisecond sleep function +*/ +#define sangoma_msleep(x) Sleep(x) + +/*! + \def sangoma_ctime(time_val) + \brief Convert a time value to a string + \param time_val pointer to 64-bit time value +*/ +#define sangoma_ctime(time) _ctime64(time) + +#else +/* L I N U X */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*! + \def _LIBSNG_CALL + \brief Not used in Linux +*/ +#define _LIBSNG_CALL + +/*! + \def INVALID_HANDLE_VALUE + \brief Invalid file handle value -1, Ported from Windows +*/ +#define INVALID_HANDLE_VALUE -1 + +/*! + \def SANGOMA_INFINITE_API_POLL_WAIT (deprecated, use SANGOMA_WAIT_INFINITE instead) + \brief Infinite poll timeout value -1, Ported from Windows +*/ +#define SANGOMA_INFINITE_API_POLL_WAIT -1 +#define SANGOMA_WAIT_INFINITE -1 + +/*! + \def __cdecl + \brief Ported from Windows + \typedef BOOL + \brief Boolean type int, Ported from Windows + \typedef DWORD + \brief DWORD type is int, Ported from Windows + \def FALSE + \brief FALSE value is 0, Ported from Windows + \def TRUE + \brief TRUE value is 1, Ported from Windows + \def sangoma_msleep(x) + \brief milisecond sleep function + \def _getch + \brief get character, Ported from Windows + \def Sleep + \brief milisecond sleep function + \typedef HANDLE + \brief file handle type int, Ported from Windows + \typedef TCHAR + \brief TCHAN type mapped to char, Ported from Windows + \typedef ULONG + \brief ULONG type mapped to unsigned long, Ported from Windows + \typedef UCHAR + \brief ULONG type mapped to unsigned char, Ported from Windows + \typedef USHORT + \brief USHORT type mapped to unsigned short, Ported from Windows + \typedef LPSTR + \brief LPSTR type mapped to unsigned char *, Ported from Windows + \typedef PUCHAR + \brief PUCHAR type mapped to unsigned char *, Ported from Windows + \typedef PVOID + \brief PVOID type mapped to void *, Ported from Windows + \typedef LPTHREAD_START_ROUTINE + \brief LPTHREAD_START_ROUTINE type mapped to unsigned char *, Ported from Windows + \def _stricmp + \brief _stricmp type mapped to _stricmp, Ported from Windows + \def _snprintf + \brief _snprintf type mapped to snprintf, Ported from Windows +*/ + +#define __cdecl + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#define sangoma_msleep(x) usleep(x*1000) +#define _getch getchar +#define Sleep sangoma_msleep +#define _stricmp strcmp +#define _snprintf snprintf +#define _vsnprintf vsnprintf + +typedef int HANDLE; +typedef int BOOL; +typedef int DWORD; +typedef char TCHAR; +typedef unsigned char UCHAR; +typedef unsigned long ULONG; +typedef unsigned short USHORT; +typedef unsigned char * LPSTR; +typedef unsigned char * PUCHAR; +typedef void * PVOID; +typedef void * LPTHREAD_START_ROUTINE; +typedef pthread_mutex_t CRITICAL_SECTION; + +#define EnterCriticalSection(arg) pthread_mutex_lock(arg) +#define LeaveCriticalSection(arg) pthread_mutex_unlock(arg) +#define InitializeCriticalSection(arg) pthread_mutex_init(arg, NULL); + +typedef struct tm SYSTEMTIME; +typedef char * LPCTSTR; + +/*! + \def sangoma_ctime(time_val) + \brief Convert a time value to a string + \param time_val pointer to time value +*/ +#define sangoma_ctime(time) ctime((time_t*)time) + +#endif/* WIN32 */ + + +/*! + LIBSANGOMA_LIGHT can be used to enable only IO and EVENT + libsangoma functions. The DRIVER configuration/start/stop + functions are not compiled. + + LIBSANGOMA_LIGHT depends only on 3 header files. Instead + of all wanpipe header files needed for DRIVER management + + LIBSANGMOA_LIGHT is NOT enabled by default. +*/ + +#ifdef LIBSANGOMA_LIGHT +#include "wanpipe_api_iface.h" +#include "wanpipe_api_hdr.h" +#include "sdla_te1.h" +#include "wanpipe_events.h" +#include "wanpipe_api_deprecated.h" +#else +#include "wanpipe_api.h" +#endif + +#ifdef __LINUX__ +#include "wanpipe_kernel.h" +#endif + +/*! + * As of now this typedef maps exactly to SANG_STATUS_T, however that + * is a kernel type, ugly, ugly, uglyyyyy, we should have strictly + * minimum set of shared data structures between kernel and user + * many return codes specified in SANG_STATUS_T are kernel specific + * like FAILED_TO_LOCK_USER_MEMORY or INVALID_IRQL, the libsangoma + * user does not need that much information, and even if ever needs + * it we should provide simpler defaults + * \brief return status from sangoma APIs + */ +typedef int32_t sangoma_status_t; + +/*! + \def FNAME_LEN + \brief string length of a file name + \def FUNC_DBG(x) + \brief function debug print function + \def DBG_PRINT + \brief debug print function +*/ +#define FNAME_LEN 100 +#define LIBSNG_FUNC_DBG() if(0)printf("%s(): line:%d\n", __FUNCTION__, __LINE__) +#define LIBSNG_DBG_PRINT if(0)printf + +/*! + \typedef sangoma_api_hdr_t + \brief Backward comaptible define of wp_api_hdr_t +*/ +typedef wp_api_hdr_t sangoma_api_hdr_t; + +/*! + \enum _sangoma_wait_obj_type_t + \brief Wait object type definition + \typedef sangoma_wait_obj_type_t + \brief Wait object type definition +*/ +typedef enum _sangoma_wait_obj_type +{ + /*! \brief deprecated, use SANGOMA_GENERIC_WAIT_OBJ */ + UNKNOWN_WAIT_OBJ = 0, + /*! \brief Generic object that can be signaled but is not associated to any sangoma device */ + SANGOMA_GENERIC_WAIT_OBJ = 0, + /*! \brief Sangoma object associated to some device which cannot be signaled (cannot call sangoma_wait_obj_signal on it) */ + SANGOMA_DEVICE_WAIT_OBJ, + /*! \brief Sangoma object that is associated to a device AND can be signaled */ + SANGOMA_DEVICE_WAIT_OBJ_SIG, +} sangoma_wait_obj_type_t; + +#define DECODE_SANGOMA_WAIT_OBJECT_TYPE(type)\ + type == SANGOMA_GENERIC_WAIT_OBJ ? "SANGOMA_GENERIC_WAIT_OBJ" :\ + type == SANGOMA_DEVICE_WAIT_OBJ ? "SANGOMA_DEVICE_WAIT_OBJ" :\ + type == SANGOMA_DEVICE_WAIT_OBJ_SIG ? "SANGOMA_DEVICE_WAIT_OBJ_SIG" :\ + "Invalid Wait Object type!" + +/* + * Possible flags for sangoma waitable objects + * this definitions MUST match POLLIN, POLLOUT and POLLPRI + * SANG_WAIT_OBJ_IS_SIGNALED has no posix equivalent though + * Users are encouraged to use this flags instead of the system ones + */ +typedef enum _sangoma_wait_obj_flags { + SANG_WAIT_OBJ_HAS_INPUT = POLLIN, + SANG_WAIT_OBJ_HAS_OUTPUT = POLLOUT, + SANG_WAIT_OBJ_HAS_EVENTS = POLLPRI, + SANG_WAIT_OBJ_IS_SIGNALED = 0x400 /* matches GNU extension POLLMSG */ +} sangoma_wait_obj_flags_t; + +/************************************************************//** + * Device OPEN / CLOSE Functions + ***************************************************************/ + +/*! + \fn sng_fd_t sangoma_open_api_span_chan(int span, int chan) + \brief Open a Device based on Span/Chan values + \param span span number starting from 1 to 255 + \param chan chan number starting from 1 to 32 + \return File Descriptor: -1 error, 0 or positive integer: valid file descriptor + + Restriced open, device will allowed to be open only once. +*/ +sng_fd_t _LIBSNG_CALL sangoma_open_api_span_chan(int span, int chan); + + +/*! + \fn sng_fd_t __sangoma_open_api_span_chan(int span, int chan) + \brief Open a Device based on Span/Chan values + \param span span number starting from 1 to 255 + \param chan chan number starting from 1 to 32 + \return File Descriptor: -1 error, 0 or positive integer: valid file descriptor + + Unrestriced open, allows mutiple open calls on a single device +*/ +sng_fd_t _LIBSNG_CALL __sangoma_open_api_span_chan(int span, int chan); +#define __sangoma_open_tdmapi_span_chan __sangoma_open_api_span_chan + +/*! + \fn sng_fd_t sangoma_open_tdmapi_span(int span) + \brief Open a first available device on a Span + \param span span number starting from 1 to 255 + \return File Descriptor: -1 error, 0 or positive integer: valid file descriptor + + Unrestriced open, allows mutiple open calls on a single device +*/ +sng_fd_t _LIBSNG_CALL sangoma_open_api_span(int span); + + +/*! + \def sangoma_create_socket_intr + \brief Backward compatible open span chan call +*/ + + + +/*! + \def LIBSANGOMA_TDMAPI_CTRL + \brief Global control device feature +*/ +#ifndef LIBSANGOMA_TDMAPI_CTRL +#define LIBSANGOMA_TDMAPI_CTRL 1 +#endif + +/*! + \fn sng_fd_t sangoma_open_api_ctrl(void) + \brief Open a Global Control Device + \return File Descriptor - negative=error 0 or greater = fd + + The global control device receives events for all devices + configured. +*/ +sng_fd_t _LIBSNG_CALL sangoma_open_api_ctrl(void); + +/*! + \fn sng_fd_t sangoma_logger_open(void) + \brief Open a Global Logger Device + \return File Descriptor - negative=error 0 or greater = fd + + The global Logger device receives Logger Events for all devices + configured. +*/ +sng_fd_t _LIBSNG_CALL sangoma_logger_open(void); + +/*! + \fn sng_fd_t sangoma_open_driver_ctrl(int port_no) + \brief Open a Global Driver Control Device + \return File Descriptor - negative=error 0 or greater = fd + + The global control device receives events for all devices + configured. +*/ +sng_fd_t _LIBSNG_CALL sangoma_open_driver_ctrl(int port_no); + + + +/*! + \fn void sangoma_close(sng_fd_t *fd) + \brief Close device file descriptor + \param fd device file descriptor + \return void + +*/ +void _LIBSNG_CALL sangoma_close(sng_fd_t *fd); + + + +/*! + \fn int sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get device open count + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative or 0: error, greater than 1 : open count +*/ + +int _LIBSNG_CALL sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/************************************************************//** + * Device READ / WRITE Functions + ***************************************************************/ + +/*! + \fn int sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, unsigned short datalen, int flag) + \brief Write Data to device + \param fd device file descriptor + \param hdrbuf pointer to header structure wp_api_hdr_t + \param hdrlen size of wp_api_hdr_t + \param databuf pointer to data buffer to be transmitted + \param datalen length of data buffer + \param flag currently not used, set to 0 + \return transmit size, must be equal to datalen, anything else is error + + In case of error return code, one must check the header operation_status + variable to identify the reason of an error. Please refer to the error codes. +*/ + +int _LIBSNG_CALL sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, + void *databuf, unsigned short datalen, int flag); + + +/*! + \fn int sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, int datalen, int flag) + \brief Read Data from device + \param fd device file descriptor + \param hdrbuf pointer to header structure wp_api_hdr_t + \param hdrlen size of wp_api_hdr_t + \param databuf pointer to data buffer to be received + \param datalen length of data buffer + \param flag currently not used, set to 0 + \return received size, must be equal to datalen, anything else is error or busy + + In case of error return code, one must check the header operation_status + variable to identify the reason of error. Please refer to the error codes. +*/ +int _LIBSNG_CALL sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, + void *databuf, int datalen, int flag); + + + + +/************************************************************//** + * Device POLL Functions + ***************************************************************/ + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, int32_t inflags, int32_t *outflags, int32_t timeout) + \brief Wait for a single waitable object + \param sangoma_wait_obj pointer to a wait object previously created with sangoma_wait_obj_create + \param timeout timeout in miliseconds in case of no event + \return SANG_STATUS_APIPOLL_TIMEOUT: timeout, SANG_STATUS_SUCCESS: event occured use sangoma_wait_obj_get_out_flags to check or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, uint32_t inflags, uint32_t *outflags, int32_t timeout); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], int32_t in_flags[], int32_t out_flags[] uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout); + \brief Wait for multiple sangoma waitable objects + \param sangoma_wait_objects pointer to array of wait objects previously created with sangoma_wait_obj_create + \param in_flags array of flags corresponding to the flags the user is interested on for each waitable object + \param out_flags array of flags corresponding to the flags that are ready in the waitable objects + \param number_of_sangoma_wait_objects size of the array of waitable objects and flags + \param system_wait_timeout timeout in miliseconds in case of no event + \return negative: SANG_STATUS_APIPOLL_TIMEOUT: timeout, SANG_STATUS_SUCCESS: event occured check in sangoma_wait_objects, any other return code is some error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], uint32_t in_flags[], uint32_t out_flags[], + uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout); + +/*! + \fn sangoma_status_t sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma_wait_object, sng_fd_t fd, sangoma_wait_obj_type_t object_type) + \brief Create a wait object that will be used with sangoma_waitfor_many() API + \param sangoma_wait_object pointer a single device object + \param fd device file descriptor + \param object_type type of the wait object. see sangoma_wait_obj_type_t for types + \see sangoma_wait_obj_type_t + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma_wait_object, sng_fd_t fd, sangoma_wait_obj_type_t object_type); + +/*! + \fn sangoma_status_t sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object) + \brief De-allocate all resources inside a wait object which were allocated by sangoma_wait_obj_init(). + \param sangoma_wait_object pointer to a pointer to a single device object + \return SANG_STATUS_SUCCESS on success or some sangoma status error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object); + +/*! + \fn void sangoma_wait_obj_signal(sangoma_wait_obj_t *sangoma_wait_object) + \brief Set wait object to a signaled state + \param sangoma_wait_object pointer a single device object that can be signaled + \return sangoma_status_t +*/ +sangoma_status_t _LIBSNG_CALL sangoma_wait_obj_signal(sangoma_wait_obj_t *sangoma_wait_object); + +/*! + \fn sng_fd_t sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sangoma_wait_object) + \brief Get fd device file descriptor which was the 'fd' parameter for sangoma_wait_obj_create(), not useful for generic objects + \param sangoma_wait_object pointer a single device object + \return sng_fd_t - device file descriptor +*/ +sng_fd_t _LIBSNG_CALL sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sangoma_wait_object); + +/*! + \fn void sangoma_wait_obj_set_context(sangoma_wait_obj_t *sangoma_wait_object) + \brief Store the given context into provided sangoma wait object. + \brief This function is useful to associate a context with a sangoma wait object. + \param sangoma_wait_object pointer a single device object + \param context void pointer to user context + \return void +*/ +void _LIBSNG_CALL sangoma_wait_obj_set_context(sangoma_wait_obj_t *sangoma_wait_object, void *context); + +/*! + \fn PVOID sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object) + \brief Retrieve the user context (if any) that was set via sangoma_wait_obj_set_context. + \brief Windows note: must use return type PVOID instead of void* to satisfy WDK compiler. + \param sangoma_wait_object pointer a single device object + \return void* +*/ +PVOID _LIBSNG_CALL sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object); + + +/************************************************************//** + * Device API COMMAND Functions + ***************************************************************/ + +/*! + \fn int sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Execute Sangoma API Command + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Read tdm api device configuration + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, int period) + \brief Set Tx/Rx Period in Milliseconds + \param fd device file descriptor + \param tdm_api tdm api command structure + \param period value in miliseconds (1,2,5,10) + \return non-zero: error, 0: ok + + Only valid in CHAN Operation Mode +*/ +int _LIBSNG_CALL sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, int period); + +/*! + \fn int sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Tx/Rx Period in Milliseconds + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative: error or configured period value +*/ +int _LIBSNG_CALL sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Tx/Rx MTU/MRU in bytes + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative: error or configured mtu/mru in bytes +*/ +int _LIBSNG_CALL sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush all (tx/rx/event) buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _LIBSNG_CALL sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_flush_rx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush only rx buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _LIBSNG_CALL sangoma_flush_rx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); +/*! + \fn int sangoma_flush_tx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush only tx buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _LIBSNG_CALL sangoma_flush_tx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_flush_event_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush only event buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _LIBSNG_CALL sangoma_flush_event_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_tdm_enable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api, int poll_in_sec) + \brief Enable RBS Events on a device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param poll_in_sec driver poll period for rbs events + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_tdm_enable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api, int poll_in_sec); + +/*! + \fn int sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RBS Events for a device + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char rbs) + \brief Write RBS Bits on a device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param channel t1/e1 timeslot + \param rbs rbs bits (ABCD) + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char rbs); + +/*! + \fn int sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char *rbs) + \brief Read RBS Bits on a device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param channel t1/e1 timeslot + \param rbs pointer to rbs bits (ABCD) + \return non-zero: error, 0: ok +*/ + +int _LIBSNG_CALL sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char *rbs); + +/*! + \fn int sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable DTMF Detection on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _LIBSNG_CALL sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable DTMF Detection on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _LIBSNG_CALL sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +#ifdef WP_API_FEATURE_FAX_EVENTS +/*! + \fn int sangoma_tdm_enable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable FAX Detection on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _LIBSNG_CALL sangoma_tdm_enable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable FAX Detection on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _LIBSNG_CALL sangoma_tdm_disable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_get_hw_fax(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get HW FAX Detection State (Enable or Disabled) on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: Disabled, 1: Enabled + + Supported only on cards that have HWEC +*/ +int _LIBSNG_CALL sangoma_tdm_get_hw_fax(sng_fd_t fd, wanpipe_api_t *tdm_api); + +#endif + + +/*! + \fn int sangoma_tdm_enable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable DTMF Detection on Analog/Remora SLIC Chip + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_enable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable DTMF Detection on Analog/Remora SLIC Chip + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable RX HOOK Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RX HOOK Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable RING Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RING Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_tdm_enable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable RING DETECT Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_enable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RING DETECT Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable RING TRIP Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable RING TRIP Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api, uint16_t tone_id) + \brief Transmit a TONE on this device (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param tone_id tone type to transmit + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api, uint16_t tone_id); + +/*! + \fn int sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable TONE Events (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG ON HOOK (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG OFF HOOK (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG START (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG KEWL START (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable HWEC on this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _LIBSNG_CALL sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable HWEC on this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _LIBSNG_CALL sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int _LIBSNG_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms); + \brief Get Front End Alarms (T1/E1 Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param alarms bit map status of T1/E1 alarms + \return non-zero: error, 0: ok + + Supported only on T1/E1 Cards +*/ +int _LIBSNG_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms); + + + +#ifdef WP_API_FEATURE_LINK_STATUS +# ifndef LIBSANGOMA_GET_LINKSTATUS +/*! + \def LIBSANGOMA_GET_LINKSTATUS + \brief Get Link Status feature +*/ +# define LIBSANGOMA_GET_LINKSTATUS 1 +# endif + +/*! + \fn int sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status) + \brief Get Device Link Status (Connected/Disconnected) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param current_status pointer where result will be filled: 0=Link UP 1=Link Down + \return non-zero: error, 0: ok -> check current_status + +*/ +int _LIBSNG_CALL sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status); + +#endif + +/* set current Line Connection state - Connected/Disconnected */ +#ifndef LIBSANGOMA_GET_FESTATUS +/*! + \def LIBSANGOMA_GET_FESTATUS + \brief Get Front End Status feature +*/ +#define LIBSANGOMA_GET_FESTATUS 1 +#endif + +/*! + \fn int sangoma_set_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char new_status) + \brief Set Device Link Status (Connected/Disconnected) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param new_status new status 0=Link UP 1=Link Down + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_set_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char new_status); + + +/*! + \fn int _LIBSNG_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) + \brief Enable BRI Bchannel loopback - used when debugging bri device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param channel bri bchannel 1 or 2 + \return non-zero: error, 0: ok + +*/ +int _LIBSNG_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel); + +/*! + \fn int _LIBSNG_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) + \brief Disable BRI Bchannel loopback - used when debugging bri device + \param fd device file descriptor + \param tdm_api tdm api command structure + \param channel bri bchannel 1 or 2 + \return non-zero: error, 0: ok + +*/ +int _LIBSNG_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel); + + +/*! + \fn int sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Tx Queue Size for this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn int sangoma_set_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) + \brief Get Tx Queue Size for this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \param size tx queue size (minimum value of 1) + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_set_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size); + +/*! + \fn int sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Rx Queue Size for this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) + \brief Get Tx Queue Size for this channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \param size rx queue size (minimum value of 1) + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size); + + +#ifndef LIBSANGOMA_GET_HWCODING +/*! + \def LIBSANGOMA_GET_HWCODING + \brief Get HW Coding Feature +*/ +#define LIBSANGOMA_GET_HWCODING 1 +#endif + +/*! + \fn int sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get HW Voice Coding (ulaw/alaw) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + This function will return the low level voice coding + depending on configuration. (ulaw or alaw) +*/ +int _LIBSNG_CALL sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api); + + + +#ifndef LIBSANGOMA_GET_HWDTMF +/*! + \def LIBSANGOMA_GET_HWDTMF + \brief HW DTMF Feature +*/ +#define LIBSANGOMA_GET_HWDTMF 1 +#endif +/*! + \fn int sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Check if hwdtmf support is available + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + This function will check if hw supports HW DTMF. +*/ +int _LIBSNG_CALL sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Check if hw echo cancelation support is available + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + This function will check if hw supports HW EC. +*/ +int _LIBSNG_CALL sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_span_chan_toif(int span, int chan, char *interface_name) + \brief Convert Span & Chan to interface name + \param span span number starting from 1 to 255 + \param chan chan number starting from 1 to 32 + \param interface_name pointer to string where interface name will be written + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_span_chan_toif(int span, int chan, char *interface_name); + +/*! + \fn int sangoma_span_chan_fromif(char *interface_name, int *span, int *chan) + \brief Convert Interace Name to Span & Chan + \param interface_name pointer to string containing interface name + \param span integer pointer where to write span value + \param chan integer pointer where to write chan value + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_span_chan_fromif(char *interface_name, int *span, int *chan); + + +/*! + \fn int sangoma_interface_wait_up(int span, int chan, int sectimeout) + \brief Wait for a sangoma device to come up (ie: Linux wait for /dev/wanpipex_1 to come up) + \param span span number of the device to wait + \param chan chan number of the device to wait + \param sectimeout how many seconds to wait for the device to come up, -1 to wait forever + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_interface_wait_up(int span, int chan, int sectimeout); + +/*! + \fn int sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, wan_driver_version_t *drv_ver) + \brief Get Device Driver Version Number + \param fd device file descriptor + \param tdm_api tdm api command structure + \param drv_ver driver version structure that will contain the driver version + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, wan_driver_version_t *drv_ver); + +/*! + \fn int sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) + \brief Get Hardware/Firmware Version + \param fd device file descriptor + \param tdm_api tdm api command structure + \param ver hardware/firmware version number + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver); + +/*! + \fn int sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) + \brief Get Hardare/CPLD Version + \param fd device file descriptor + \param tdm_api tdm api command structure + \param ver hardware/cpld version number + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver); + + +/*! + \fn int sangoma_get_stats(sng_fd_t fd, wanpipe_api_t *tdm_api, wanpipe_chan_stats_t *stats) + \brief Get Device Statistics. Statistics will be available in tdm_api->wp_cmd.stats structure. + \param fd device file descriptor + \param tdm_api tdm api command structure + \param stats stats structure will be filled with device stats. (Optional, can be left NULL) + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_get_stats(sng_fd_t fd, wanpipe_api_t *tdm_api, wanpipe_chan_stats_t *stats); + +/*! + \fn int _LIBSNG_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush/Reset device statistics + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, int rxflashtime) + \brief Set rxflashtime for FXS module Wink-Flash Event + \param fd device file descriptor + \param tdm_api tdm api command structure + \param rxflashtime time value + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, int rxflashtime); + +#ifdef WP_API_FEATURE_RM_GAIN +/*! + \fn int sangoma_set_rm_tx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) + \brief set tx gain for FXO/FXS module + \param fd device file descriptor + \param tdm_api tdm api command structure + \param value txgain (FXO - txgain value ranges from -150 to 120 , FXS - txgain value 35,-35) + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_set_rm_tx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value); + + +/*! + \fn int sangoma_set_rm_rx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) + \brief set rx gain for FXO/FXS module + \param fd device file descriptor + \param tdm_api tdm api command structure + \param value rxgain (FXO - rxgain value ranges from -150 to 120 , FXS -rxgain value 35,-35) + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_set_rm_rx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value); + +#endif /* WP RM GAIN feature */ + +/*! + \fn int sangoma_set_polarity(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) + \brief set polarity on FXS (Analog only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param value 0 fwd, 1 rev + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_tdm_set_polarity(sng_fd_t fd, wanpipe_api_t *tdm_api, int polarity); + +/*! + \fn int sangoma_tdm_txsig_onhooktranfer(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG ON HOOK Tranfer (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_txsig_onhooktransfer(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +#ifdef WP_API_FEATURE_LOOP +/*! + \fn int sangoma_tdm_enable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable channel loop: All rx data will be transmitted back out. + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_tdm_enable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable channel loop + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_tdm_disable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api); +#endif + +/************************************************************//** + * Device EVENT Function + ***************************************************************/ + + +/*! + \fn int sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Read API Events + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + The TDM API structure will be populated with a TDM API or WAN Event. + This function usually used after wait() function indicated that event + has occured. +*/ +int _LIBSNG_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api); + +#ifdef WP_API_FEATURE_LOGGER + +sangoma_status_t _LIBSNG_CALL sangoma_logger_cmd_exec(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_read_event(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Read Wanpipe Logger Events + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error + + The Logger API structure will be populated with a Logger Event. + This function usually used after wait() function indicated that + an event has occured. +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_read_event(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_flush_buffers(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Flush Wanpipe Logger internal buffers + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_flush_buffers(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_get_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Get Wanpipe Logger statistics + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_get_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_reset_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Reset Wanpipe Logger statistics + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_reset_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_get_open_handle_counter(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Get Counter of open Handles/File Descriptors of Wanpipe Logger + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_get_open_handle_counter(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_get_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Get current level (types of events) of Wanpipe Logger + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_get_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_set_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Set current level (types of events) of Wanpipe Logger + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_set_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +#endif /* WP LOGGER FEATURE */ + +#ifndef LIBSANGOMA_LIGHT + +/************************************************************//** + * Device PORT Control Functions + ***************************************************************/ + +/*! + \fn int sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) + \brief Start a Port, create Sangoma Communication interfaces. + \param[in] fd Port Device file descriptor + \param[out] port_mgmnt pointer to a port_management_struct_t structure. + On return, sangoma_driver_port_start() updates operation_status field + of this structure. + \param[in] port_no 1-based Port Number. Port numbers correspond to Port Names. + For example, a 2-Port card will have ports named WANPIPE1 and WANPIPE2. + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_mgmt->operation_status. +*/ +int _LIBSNG_CALL sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); + + +/*! + \fn int sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) + \brief Start a Port, create Sangoma Communication interfaces. + \param[in] fd Port Device file descriptor + \param[out] port_mgmnt pointer to a port_management_struct_t structure. + On return, sangoma_driver_port_stop() updates operation_status field + of this structure. + \param[in] port_no 1-based Port Number. Port numbers correspond to Port Names. + For example, a 2-Port card will have ports named WANPIPE1 and WANPIPE2. + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_mgmt->operation_status. +*/ +int _LIBSNG_CALL sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); + + +/*! + \fn int sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no) + \brief Set Port's "Volatile" configuration. The configuration will not persist between system restarts. + Before calling this function please stop the port by calling sangoma_driver_port_stop(). + After calling this function please start the port by calling sangoma_driver_port_start(). + \param[in] fd Port Device file descriptor + \param[in, out] port_cfg pointer to port_cfg_t structure that specifies complete Port configuration. + On return, sangoma_driver_port_set_config() updates operation_status field + of this structure. + \param[in] port_no 1-based Port Number. Port numbers correspond to Port Names. + For example, a 2-Port card will have ports named WANPIPE1 and WANPIPE2. + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_cfg->operation_status. +*/ +int _LIBSNG_CALL sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no); + + +/*! + \fn int sangoma_driver_port_get_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no) + \brief Retrieve Port's "Volatile" configuration. + \param[in] fd Port Device file descriptor + \param[out] port_cfg pointer to port_cfg_t structure. + On return, sangoma_driver_port_get_config() will copy current Port configuration + into this structure. + \param[in] port_no please see comment of sangoma_driver_port_set_config() + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_cfg->operation_status. +*/ +int _LIBSNG_CALL sangoma_driver_port_get_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no); + + +/*! + \fn int sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) + \brief Retrieve information about a single instance of Sangoma hardware. + \param[in] fd Port Device file descriptor + \param[out] port_mgmnt pointer to port_management_struct_t structure which will contain hardware_info_t at + it's "data" field, when this function returns. + \param[in] port_no please see comment of sangoma_driver_port_set_config() + \return non-zero: system error. Call OS specific code to find cause of the error. + Linux example: strerror(errno) + Windows example: combination of GetLastError()/FormatMessage() + zero: no system error. Check port_mgmt->operation_status. +*/ +int _LIBSNG_CALL sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); + + +/*! + \fn int sangoma_write_port_config_on_persistent_storage(hardware_info_t *hardware_info, port_cfg_t *port_cfg) + \brief Write Port's configuration on the hard disk. + Linux Specific: the "Persistent" configuration of a Port N (e.g. WANPIPE1) is stored in + /etc/wanpipe/wanpipeN.conf (e.g. wanpipe1.conf). + Configuration can be manualy viewed/changed by editing the ".conf" file. + Currently this functionality is not implemented. + Windows Specific: the "Persistent" configuration of a Port (e.g. WANPIPE1) is stored in + Windows Registry. + Configuration can be manualy viewed/changed in the Device Manager. + \param[in] hardware_info pointer to hardware_info_t structure containing information about a + single instance of Sangoma hardware. + \param[in] port_cfg pointer to structure containing complete Port configuration. + \param[in] port_no please see comment of sangoma_driver_port_set_config() + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_write_port_config_on_persistent_storage(hardware_info_t *hardware_info, port_cfg_t *port_cfg, unsigned short port_no); + + +/************************************************************//** + * Device MANAGEMENT Functions + ***************************************************************/ + +/*! + \fn int sangoma_mgmt_cmd(sng_fd_t fd, wan_udp_hdr_t* wan_udp) + \brief Execute Sangoma Management Command + \param fd device file descriptor + \param wan_udp management command structure + \return non-zero: error, 0: ok +*/ +int _LIBSNG_CALL sangoma_mgmt_cmd(sng_fd_t fd, wan_udp_hdr_t* wan_udp); + + +#endif /* LIBSANGOMA_LIGHT */ + + +/*================================================================ + * DEPRECATED Function Calls - Not to be used any more + * Here for backward compatibility + *================================================================*/ + + +#ifndef LIBSANGOMA_SET_FESTATUS +/*! + \def LIBSANGOMA_SET_FESTATUS + \brief Set Front End Status Feature +*/ +#define LIBSANGOMA_SET_FESTATUS 1 +#endif + +/*! + \fn int sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status) + \brief Get Device Link Status (Connected/Disconnected) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param current_status pointer where result will be filled: 0=Link UP 1=Link Down + \return non-zero: error, 0: ok -> check current_status + + Deprecated - replaced by sangoma_tdm_get_link_status function +*/ +int _LIBSNG_CALL sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status); + + + +/*! + \fn int sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int codec) + \brief Set TDM Codec per chan + \param fd device file descriptor + \param tdm_api tdm api command structure + \param codec codec to set (ulaw/alaw/slinear) + \return non-zero: error, 0: ok + + Deprecated Function - Here for backward compatibility + Only valid in CHAN Operation Mode +*/ +int _LIBSNG_CALL sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int codec); + +/*! + \fn int sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Configured TDM Codec per chan + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative: error or configured codec value + + Deprecated Function - Here for backward compatibility + Only valid in CHAN Operation Mode +*/ +int _LIBSNG_CALL sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +/*! + \fn sng_fd_t sangoma_create_socket_by_name(char *device, char *card) + \brief Open a device based on a interface and card name + \param device interface name + \param card card name + \return File Descriptor: -1 error, 0 or positive integer: valid file descriptor + + Deprecated - here for backward compatibility +*/ +sng_fd_t _LIBSNG_CALL sangoma_create_socket_by_name(char *device, char *card); + +/*! + \fn int sangoma_interface_toi(char *interface_name, int *span, int *chan) + \brief Convert Span & Chan to interface name + \param interface_name pointer to string where interface name will be written + \param span span number starting from 1 to 255 + \param chan chan number starting from 1 to 32 + \return non-zero = error, 0 = ok + Deprecated - here for backward compatibility +*/ +int _LIBSNG_CALL sangoma_interface_toi(char *interface_name, int *span, int *chan); + + +/*! + \fn int sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, int power) + \brief Set Power Level - so only data matching the power level would be passed up. + \param fd device file descriptor + \param tdm_api tdm api command structure + \param power value of power + \return non-zero: error, 0: ok + + Deprecated - not used/implemented +*/ +int _LIBSNG_CALL sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, int power); + +/*! + \fn int sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get Configured Power Level + \param fd device file descriptor + \param tdm_api tdm api command structure + \return negative: error or configured power level + + Deprecated - not used/implemented +*/ +int _LIBSNG_CALL sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +#ifdef WP_API_FEATURE_LIBSNG_HWEC +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_init(char *device_name) + + \brief Load Firmware image onto EC chip. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_init(char *device_name); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_release(char *device_name) + + \brief Reset internal state of HWEC API. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_release(char *device_name); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_operation_mode(char *device_name, int mode, unsigned int fe_chan_map) + + \brief Modify channel operation mode. + + \param mode One of WANEC_API_OPMODE_? values defined in wanpipe_api_iface.h: + WANEC_API_OPMODE_NORMAL, + WANEC_API_OPMODE_HT_FREEZE, + WANEC_API_OPMODE_HT_RESET, + WANEC_API_OPMODE_POWER_DOWN, + WANEC_API_OPMODE_NO_ECHO, + WANEC_API_OPMODE_SPEECH_RECOGNITION. + + \param fe_chan_map Bitmap of channels (timeslots for Digital, + lines for Analog) where the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_operation_mode(char *device_name, int mode, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_on (char *device_name, unsigned int fe_chan_map) + + \brief Set the channel state in the echo canceller to NORMAL/POWER ON. + This enables echo cancelation logic inside the chip. + The action is internal to EC chip itself, not related to AFT FPGA. + This call is slow and should be used only on startup. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, + lines for Analog) where the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_on(char *device_name, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_off (char *device_name, unsigned int fe_chan_map) + + \brief Set the channel state in the echo canceller to POWER OFF. + This disables echo cancellatio logic inside the chip and + data passes unmodified through the ec chip. + The action is internal to EC chip itself, not related + to AFT FPGA. This call is slow and should be used only on startup. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, + lines for Analog) where the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_off(char *device_name, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_enable(char *device_name, unsigned int fe_chan_map) + + \brief Redirect audio stream from AFT FPGA to EC chip. + This command effectively enables echo cancellation since + data is now forced through the EC chip by the FPGA. + Data will be modified by the echo canceller. + This command is recommened for fast enabling of Echo Cancellation. + Note 1: Chip must be configured and in POWER ON state for echo + Chancellation to take place. + Note 2: sangoma_tdm_enable_hwec() function can be use to achive + the same funcitnality based on file descriptor versus + channel map. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_enable(char *device_name, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_disable(char *device_name, unsigned int fe_chan_map) + + \brief Force AFT FPGA to bypass the echo canceller. + This command effectively disables echo cancellation since + data will not flowing through the ec chip. + Data will not be modified by the echo canceller. + This command is recommened for fast disabling of Echo Cancelation. + Note: sangoma_tdm_disable_hwec() function can be use to achive + the same functionality based on file descriptor versus + channel map. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_disable(char *device_name, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_channel_parameters(char *device_name, char *parameter, char *parameter_value, unsigned int channel_map) + + \brief Modify channel configuration parameters. + This is list of Echo Cancellation channel parameters: + + Channel parameter Channel parameter value + ================= ======================= + WANEC_EnableNlp TRUE | FALSE + WANEC_EnableTailDisplacement TRUE | FALSE + WANEC_TailDisplacement 0-896 + WANEC_SoutLevelControl TRUE | FALSE + WANEC_RinAutomaticLevelControl TRUE | FALSE + WANEC_SoutAutomaticLevelControl TRUE | FALSE + WANEC_SoutAdaptiveNoiseReduction TRUE | FALSE + WANEC_RoutNoiseReduction TRUE | FALSE + WANEC_ComfortNoiseMode COMFORT_NOISE_NORMAL + COMFORT_NOISE_FAST_LATCH + COMFORT_NOISE_EXTENDED + COMFORT_NOISE_OFF + WANEC_DtmfToneRemoval TRUE | FALSE + WANEC_AcousticEcho TRUE | FALSE + WANEC_NonLinearityBehaviorA 0-13 + WANEC_NonLinearityBehaviorB 0-8 + WANEC_DoubleTalkBehavior DT_BEH_NORMAL + DT_BEH_LESS_AGGRESSIVE + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_channel_parameters(char *device_name, char *parameter, char *parameter_value, unsigned int channel_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_tone_detection(char *device_name, int tone_id, int enable, unsigned int fe_chan_map, unsigned char port_map) + + \brief Enable/Disable tone detection (such as DTMF) of channels from channel map. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param tone_id See wanpipe_api_iface.h for list of valid tones + + \param enable A flag, if 1 - the specified tone will be detected, + if 0 - specified tone will not be detected. + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \param port_map Port\Direction of tone detection - Rx, Tx. See wanpipe_events.h for + list of valid ports (WAN_EC_CHANNEL_PORT_SOUT...). + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_tone_detection(char *device_name, int tone_id, int enable, unsigned int fe_chan_map, unsigned char port_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_print_statistics(char *device_name, int full, unsigned int fe_chan_map) + + \brief Read and print Chip/Channel statistics from EC chip. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param full Flag to read full statistics, if set to 1. + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will read statistics. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_print_statistics(char *device_name, int full, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_load(char *device_name, char *filename, char pcmlaw, int *out_buffer_id) + + \brief Load audio buffer to EC chip. The buffer can be played out using the sangoma_hwec_audio_buffer_playout() function. + + \param filename name of the audio file (without the extension). + Actual file must have .pcm extension. + Location: + Windows: %SystemRoot%\sang_ec_files (ex: c:\WINDOWS\sang_ec_files) + Linux: /etc/wanpipe/buffers + + \param out_buffer_id when the buffer is loaded on the chip, it is assigned an ID. This ID should + be used when requesting to play out the buffer. + + \return SANG_STATUS_SUCCESS: success, or error status + + */ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_load(char *device_name, char *filename, char pcmlaw, int *out_buffer_id); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_bufferunload(char *device_name, int in_buffer_id) + + \brief Unload/remove an audio buffer from the HWEC chip. + + \param device_name Sangoma wanpipe device name. (ex: wanpipe1 - Linux; wanpipe1_if1 - Windows). + + \param in_buffer_id ID of the buffer which will be unloaded. The ID must be initialized by sangoma_hwec_audio_bufferload(). + + \return SANG_STATUS_SUCCESS - buffer was successfully unloaded/removed, SANG_STATUS_GENERAL_ERROR - error occured +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_unload(char *device_name, int in_buffer_id); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_playout(char *device_name, unsigned int fe_chan_map, + unsigned char port_map, int buffer_id, int start, int repeat_cnt, int duration) + + \brief Start\Stop playing out an audio buffer previously loaded by sangoma_hwec_audio_buffer_load(). + + \param fe_chan_map Bitmap of channels (timeslots for Digital, + lines for Analog) where the call will take effect. + + \param port_map Port\Direction where the buffer will be played out. + This is the channel port on which the buffer will be + played (WAN_EC_CHANNEL_PORT_SOUT or WAN_EC_CHANNEL_PORT_ROUT) + + \param in_buffer_id ID of the buffer which will be unloaded. The ID must be initialized by sangoma_hwec_audio_bufferload(). + + \param start If 1 - start the play out, 0 - stop the play out + + \param repeat_cnt Number of times to play out the same buffer + + \param duration Maximum duration of the playout, in milliseconds. If it takes less then 'duration' to + play out the whole buffer this paramter is ignored. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_playout(char *device_name, unsigned int fe_chan_map, + unsigned char port, int in_buffer_id, int start, + int repeat_cnt, int duration); + +/*! + \fn void _LIBSNG_CALL sangoma_hwec_config_verbosity(int verbosity_level) + + \brief Set Verbosity level of EC API. The level controls amount of data + printed to stdout and wanpipelog.txt for diagnostic purposes. + + \param verbosity_level Valid values are from 0 to 3. + + \return SANG_STATUS_SUCCESS: success - the level was changed to 'verbosity_level', + SANG_STATUS_INVALID_PARAMETER: error - the level was not changed because new level is invalid +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_verbosity(int verbosity_level); + +#endif /* WP_API_FEATURE_LIBSNG_HWEC */ + + +#ifdef __cplusplus +} +#endif/* __WINDOWS__ */ + + +#if defined(__WINDOWS__) +/*! Windows specific API */ +#ifdef __cplusplus +extern "C" { /* for C++ users */ +#endif + +sangoma_status_t _LIBSNG_CALL sangoma_unload_driver(); +sangoma_status_t _LIBSNG_CALL sangoma_load_driver(); +void _LIBSNG_CALL sangoma_reset_port_numbers(); +sangoma_status_t _LIBSNG_CALL sangoma_get_driver_version_from_registry(char *out_buffer, int out_buffer_length); +sangoma_status_t _LIBSNG_CALL sangoma_set_driver_mode_of_all_hw_devices(int driver_mode); + +#ifdef __cplusplus +} +#endif/* __WINDOWS__ */ + +#else +/*! Backward compabile defines */ +#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 + +#endif /* _LIBSNAGOMA_H */ + diff --git a/api/libsangoma/Makefile.Windows b/api/libsangoma/Makefile.Windows deleted file mode 100644 index 66f1c8e..0000000 --- a/api/libsangoma/Makefile.Windows +++ /dev/null @@ -1,8 +0,0 @@ -# -# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source -# file to this component. This file merely indirects to the real make file -# that is shared by all the driver components of the Windows NT DDK -# - -!INCLUDE $(NTMAKEENV)\makefile.def - diff --git a/api/libsangoma/cleanup.sh b/api/libsangoma/cleanup.sh index 53556c3..1c8f41c 100755 --- a/api/libsangoma/cleanup.sh +++ b/api/libsangoma/cleanup.sh @@ -1,4 +1,4 @@ #!/bin/sh -svn status | xargs rm -rf +svn status | grep -v "libsangoma.*" | xargs rm -rf rm -rf sample_c/regression diff --git a/api/libsangoma/configure b/api/libsangoma/configure index d50bda6..a3ac102 100755 --- a/api/libsangoma/configure +++ b/api/libsangoma/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.59 for libsangoma 3.2.0. +# Generated by GNU Autoconf 2.59 for libsangoma 3.3.0. # # Report bugs to . # @@ -423,8 +423,8 @@ SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='libsangoma' PACKAGE_TARNAME='libsangoma' -PACKAGE_VERSION='3.2.0' -PACKAGE_STRING='libsangoma 3.2.0' +PACKAGE_VERSION='3.3.0' +PACKAGE_STRING='libsangoma 3.3.0' PACKAGE_BUGREPORT='ncorbic@sangoma.com' # Factoring default headers for most tests. @@ -953,7 +953,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures libsangoma 3.2.0 to adapt to many kinds of systems. +\`configure' configures libsangoma 3.3.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1019,7 +1019,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of libsangoma 3.2.0:";; + short | recursive ) echo "Configuration of libsangoma 3.3.0:";; esac cat <<\_ACEOF @@ -1159,7 +1159,7 @@ fi test -n "$ac_init_help" && exit 0 if $ac_init_version; then cat <<\_ACEOF -libsangoma configure 3.2.0 +libsangoma configure 3.3.0 generated by GNU Autoconf 2.59 Copyright (C) 2003 Free Software Foundation, Inc. @@ -1173,7 +1173,7 @@ cat >&5 <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by libsangoma $as_me 3.2.0, which was +It was created by libsangoma $as_me 3.3.0, which was generated by GNU Autoconf 2.59. Invocation command line was $ $0 $@ @@ -1818,7 +1818,7 @@ fi # Define the identity of the package. PACKAGE='libsangoma' - VERSION='3.2.0' + VERSION='3.3.0' cat >>confdefs.h <<_ACEOF @@ -1950,7 +1950,7 @@ am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -' LIBSANGOMA_LT_CURRENT=3 # interface 3 -LIBSANGOMA_LT_REVISION=2 # first revision of this interface +LIBSANGOMA_LT_REVISION=3 # first revision of this interface LIBSANGOMA_LT_AGE=0 #not backwards compatible (0 previous interfaces are compatible) #AC_CONFIG_MACRO_DIR([m4]) @@ -19648,7 +19648,7 @@ _ASBOX } >&5 cat >&5 <<_CSEOF -This file was extended by libsangoma $as_me 3.2.0, which was +This file was extended by libsangoma $as_me 3.3.0, which was generated by GNU Autoconf 2.59. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -19711,7 +19711,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ -libsangoma config.status 3.2.0 +libsangoma config.status 3.3.0 configured by $0, generated by GNU Autoconf 2.59, with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" diff --git a/api/libsangoma/configure.in b/api/libsangoma/configure.in index ad3e732..3da2963 100644 --- a/api/libsangoma/configure.in +++ b/api/libsangoma/configure.in @@ -15,12 +15,12 @@ # for example, if we ever fix a bug in priserver.c and we want to make a release we can do so # without incrementing libsangoma LT version -AC_INIT([libsangoma],[3.2.0],[ncorbic@sangoma.com]) +AC_INIT([libsangoma],[3.3.0],[ncorbic@sangoma.com]) AC_CONFIG_HEADERS(config.h) AM_INIT_AUTOMAKE LIBSANGOMA_LT_CURRENT=3 # interface 3 -LIBSANGOMA_LT_REVISION=2 # first revision of this interface +LIBSANGOMA_LT_REVISION=3 # first revision of this interface LIBSANGOMA_LT_AGE=0 #not backwards compatible (0 previous interfaces are compatible) #AC_CONFIG_MACRO_DIR([m4]) diff --git a/api/libsangoma/docs/.svn/entries b/api/libsangoma/docs/.svn/entries index 286beb7..1ce9b6f 100644 --- a/api/libsangoma/docs/.svn/entries +++ b/api/libsangoma/docs/.svn/entries @@ -1,7 +1,7 @@ 8 dir -222 +273 https://www.sangomapbx.com/svn/libsangoma/trunk/docs https://www.sangomapbx.com/svn/libsangoma diff --git a/api/libsangoma/docs/doxygen/.svn/entries b/api/libsangoma/docs/doxygen/.svn/entries index 73fd1e2..588b0c6 100644 --- a/api/libsangoma/docs/doxygen/.svn/entries +++ b/api/libsangoma/docs/doxygen/.svn/entries @@ -1,7 +1,7 @@ 8 dir -222 +273 https://www.sangomapbx.com/svn/libsangoma/trunk/docs/doxygen https://www.sangomapbx.com/svn/libsangoma diff --git a/api/libsangoma/examples/.svn/entries b/api/libsangoma/examples/.svn/entries index 17176ad..972034b 100644 --- a/api/libsangoma/examples/.svn/entries +++ b/api/libsangoma/examples/.svn/entries @@ -1,7 +1,7 @@ 8 dir -222 +273 https://www.sangomapbx.com/svn/libsangoma/trunk/examples https://www.sangomapbx.com/svn/libsangoma diff --git a/api/libsangoma/examples/hptdm_api/.svn/entries b/api/libsangoma/examples/hptdm_api/.svn/entries index d5a4178..68f5677 100644 --- a/api/libsangoma/examples/hptdm_api/.svn/entries +++ b/api/libsangoma/examples/hptdm_api/.svn/entries @@ -1,7 +1,7 @@ 8 dir -222 +273 https://www.sangomapbx.com/svn/libsangoma/trunk/examples/hptdm_api https://www.sangomapbx.com/svn/libsangoma @@ -32,7 +32,7 @@ file -2009-02-04T23:12:17.000000Z +2009-08-25T20:44:41.000000Z 81a3f7cc906c1a95c63c8bbb05655f30 2008-08-18T04:26:05.260169Z 17 @@ -45,7 +45,7 @@ file -2009-02-04T23:12:17.000000Z +2009-08-25T20:44:41.000000Z f71df7ce5278594373b87a592d826719 2008-08-18T04:26:05.260169Z 17 @@ -57,7 +57,7 @@ file -2009-02-04T23:12:17.000000Z +2009-08-25T20:44:41.000000Z b8ef9830437ad25ebd95d62434ed9530 2008-09-17T21:25:40.156211Z 22 @@ -72,7 +72,7 @@ file -2009-02-04T23:12:17.000000Z +2009-08-25T20:44:41.000000Z 7f0a6424997adf454bbb3c88fb608622 2008-08-18T04:26:05.260169Z 17 @@ -84,7 +84,7 @@ file -2009-02-04T23:12:17.000000Z +2009-08-25T20:44:41.000000Z 1432ab687de350b6a988bdbc553bdbe7 2008-09-17T21:25:40.156211Z 22 diff --git a/api/libsangoma/examples/hptdm_api/docs/.svn/entries b/api/libsangoma/examples/hptdm_api/docs/.svn/entries index 11a27f2..4e6c49a 100644 --- a/api/libsangoma/examples/hptdm_api/docs/.svn/entries +++ b/api/libsangoma/examples/hptdm_api/docs/.svn/entries @@ -1,7 +1,7 @@ 8 dir -222 +273 https://www.sangomapbx.com/svn/libsangoma/trunk/examples/hptdm_api/docs https://www.sangomapbx.com/svn/libsangoma diff --git a/api/libsangoma/examples/hptdm_api/docs/doxygen/.svn/entries b/api/libsangoma/examples/hptdm_api/docs/doxygen/.svn/entries index 0eb8efc..220257f 100644 --- a/api/libsangoma/examples/hptdm_api/docs/doxygen/.svn/entries +++ b/api/libsangoma/examples/hptdm_api/docs/doxygen/.svn/entries @@ -1,7 +1,7 @@ 8 dir -222 +273 https://www.sangomapbx.com/svn/libsangoma/trunk/examples/hptdm_api/docs/doxygen https://www.sangomapbx.com/svn/libsangoma diff --git a/api/libsangoma/examples/priserver/.svn/entries b/api/libsangoma/examples/priserver/.svn/entries index acd581e..d13c9dc 100644 --- a/api/libsangoma/examples/priserver/.svn/entries +++ b/api/libsangoma/examples/priserver/.svn/entries @@ -1,7 +1,7 @@ 8 dir -222 +273 https://www.sangomapbx.com/svn/libsangoma/trunk/examples/priserver https://www.sangomapbx.com/svn/libsangoma @@ -32,7 +32,7 @@ file -2009-02-04T23:12:16.000000Z +2009-08-25T20:44:41.000000Z da36acc78b83d5047481df0cca63d969 2008-08-18T04:26:05.260169Z 17 @@ -45,7 +45,7 @@ file -2009-06-19T20:55:52.000000Z +2009-08-25T20:44:41.000000Z f30671b0a55df63396f6290c31af2fcb 2009-06-17T15:37:24.869927Z 182 @@ -57,7 +57,7 @@ file -2009-02-04T23:12:17.000000Z +2009-08-25T20:44:41.000000Z 5c222b74d68b1c7df3f937a9d6321d40 2008-08-18T04:26:05.260169Z 17 @@ -69,7 +69,7 @@ file -2009-06-19T20:55:52.000000Z +2009-08-25T20:44:41.000000Z 5afcd0bf7821144218b895150a7951bd 2009-06-17T15:37:24.869927Z 182 @@ -81,7 +81,7 @@ file -2009-02-04T23:12:17.000000Z +2009-08-25T20:44:41.000000Z 40538792592cd766e4941d9b37dbaf56 2008-08-18T04:26:05.260169Z 17 diff --git a/api/libsangoma/libsangoma.c b/api/libsangoma/libsangoma.c index e53ce0d..b4cd35f 100644 --- a/api/libsangoma/libsangoma.c +++ b/api/libsangoma/libsangoma.c @@ -44,6 +44,10 @@ # define MAX_COMP_DESC 2096 # define MAX_FRIENDLY 2096 # define TMP_BUFFER_LEN 256 + +/*!+! jpboily used to tell get_out_flags no objects were signaled */ +# define INVALID_INDEX (uint32_t) -1 +# define WP_ALL_BITS_SET ((uint32_t)-1) #endif static void libsng_dbg(const char * fmt, ...) @@ -63,14 +67,15 @@ static void libsng_dbg(const char * fmt, ...) /*********************************************************************//** * WINDOWS Only Section *************************************************************************/ +static int libsng_dbg_level = 0; -#define DBG_POLL if(0)libsng_dbg -#define DBG_EVNT if(0)libsng_dbg -#define DBG_ERR if(0)libsng_dbg("Error: %s() line: %d : ", __FUNCTION__, __LINE__);if(0)libsng_dbg -#define DBG_INIT if(0)libsng_dbg +#define DBG_POLL if(libsng_dbg_level)libsng_dbg +#define DBG_EVNT if(libsng_dbg_level)libsng_dbg +#define DBG_ERR if(libsng_dbg_level)libsng_dbg("Error: %s() line: %d : ", __FUNCTION__, __LINE__);if(libsng_dbg_level)libsng_dbg +#define DBG_INIT if(libsng_dbg_level)libsng_dbg #if defined(__WINDOWS__) -#define DBG_REGISTRY if(0)libsng_dbg +#define DBG_REGISTRY if(libsng_dbg_level)libsng_dbg /* \fn static void DecodeLastError(LPSTR lpszFunction) @@ -79,7 +84,7 @@ static void libsng_dbg(const char * fmt, ...) Private Windows Only Function */ -static void DecodeLastError(LPSTR lpszFunction) +static void LibSangomaDecodeLastError(LPSTR lpszFunction) { LPVOID lpMsgBuf; DWORD dwLastErr = GetLastError(); @@ -95,7 +100,7 @@ static void DecodeLastError(LPSTR lpszFunction) NULL ); /* Display the string. */ - DBG_POLL("Last Error in %s(): %s (%d)\n", lpszFunction, lpMsgBuf, dwLastErr); + DBG_POLL("Last Error in %s(): %s (%d)\n", lpszFunction, (char*)lpMsgBuf, dwLastErr); /* Free the buffer. */ LocalFree( lpMsgBuf ); } @@ -111,11 +116,10 @@ static u16 handle_device_ioctl_result(int bResult, char *caller_name) { if(bResult == 0){ /*error*/ - DecodeLastError(caller_name); - return 1; - + LibSangomaDecodeLastError(caller_name); + return SANG_STATUS_IO_ERROR; }else{ - return 0; + return SANG_STATUS_SUCCESS; } } @@ -151,14 +155,14 @@ static int UdpManagementCommand(sng_fd_t fd, wan_udp_hdr_t* wan_udp) } /* - \fn static int TdmvApiCommand(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) - \brief Executes Driver TDM API Command + \fn static int tdmv_api_ioctl(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) + \brief Executes Driver TDM API Command Wrapper Function \param fd device file descriptor \param api_cmd tdm_api managemet cmd structure Private Windows Function */ -static int TdmvApiCommand(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) +static int tdmv_api_ioctl(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) { DWORD ln, bIoResult; @@ -176,23 +180,6 @@ static int TdmvApiCommand(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) return handle_device_ioctl_result(bIoResult, __FUNCTION__); } -/* - \fn static int tdmv_api_ioctl(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) - \brief Executes Driver TDM API Command Wrapper Function - \param fd device file descriptor - \param api_cmd tdm_api managemet cmd structure - - Private Windows Function - */ -static int tdmv_api_ioctl(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) -{ - if(TdmvApiCommand(fd, api_cmd)){ - return SANG_STATUS_GENERAL_ERROR; - } - - return api_cmd->result; -} - /* \fn static USHORT DoReadCommand(sng_fd_t fd, RX_DATA_STRUCT * pRx) \brief API READ Function @@ -276,7 +263,33 @@ static USHORT sangoma_poll_fd(sng_fd_t fd, API_POLL_STRUCT *api_poll_ptr) return handle_device_ioctl_result(bIoResult, __FUNCTION__); } -static int CdevCtrlCommand(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) +/* + \fn static int logger_api_ioctl(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Executes Logger API IOCTL + \param fd device file descriptor + \param logger_cmd Logger API command structure + + Private Windows Function + */ +static sangoma_status_t logger_api_ioctl(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + DWORD ln, bIoResult; + + bIoResult = DeviceIoControl( + fd, + IoctlLoggerApiCommand, + (LPVOID)logger_cmd, + sizeof(wp_logger_cmd_t), + (LPVOID)logger_cmd, + sizeof(wp_logger_cmd_t), + (LPDWORD)(&ln), + (LPOVERLAPPED)NULL ); + + return handle_device_ioctl_result(bIoResult, __FUNCTION__); +} + +/* This function is exported only for debugging purposes and NOT a part of API. */ +sangoma_status_t _LIBSNG_CALL sangoma_cdev_ctrl_cmd(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) { DWORD ln, bIoResult; @@ -294,15 +307,6 @@ static int CdevCtrlCommand(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) return handle_device_ioctl_result(bIoResult, __FUNCTION__); } -/* This function is exported only for debugging purposes and NOT a part of API. */ -sangoma_status_t _SAPI_CALL sangoma_cdev_ctrl_cmd(sng_fd_t fd, wanpipe_tdm_api_cmd_t *api_cmd) -{ - if(CdevCtrlCommand(fd, api_cmd)){ - return SANG_STATUS_GENERAL_ERROR; - } - return api_cmd->result; -} - static sangoma_status_t init_sangoma_event_object(sangoma_wait_obj_t *sng_wait_obj, int flags_in, sng_fd_t fd) { int event_index = -1; @@ -356,14 +360,14 @@ static sangoma_status_t init_sangoma_event_object(sangoma_wait_obj_t *sng_wait_o sng_wait_obj->sng_event_objects[event_index] = OpenEvent(EVENT_ALL_ACCESS, TRUE, event_name); if(NULL == sng_wait_obj->sng_event_objects[event_index]){ /* error */ - DecodeLastError(__FUNCTION__); + LibSangomaDecodeLastError(__FUNCTION__); return SANG_STATUS_GENERAL_ERROR; } return SANG_STATUS_SUCCESS; } -static sangoma_status_t _SAPI_CALL sangoma_wait_obj_poll(sangoma_wait_obj_t *sangoma_wait_object, int flags_in, int *flags_out) +static sangoma_status_t sangoma_wait_obj_poll(sangoma_wait_obj_t *sangoma_wait_object, int flags_in, int *flags_out) { int err; sangoma_wait_obj_t *sng_wait_obj = sangoma_wait_object; @@ -390,7 +394,7 @@ static sangoma_status_t _SAPI_CALL sangoma_wait_obj_poll(sangoma_wait_obj_t *san } if(*flags_out == 0){ - DBG_POLL("======%s(): *flags_out: 0x%X, flags_in: 0x%X\n", __FUNCTION__, *flags_out, flags_in); + /*DBG_POLL("======%s(): *flags_out: 0x%X, flags_in: 0x%X\n", __FUNCTION__, *flags_out, flags_in);*/ } return err; } @@ -406,6 +410,7 @@ static int check_number_of_wait_objects(uint32_t number_of_objects, const char * } static sangoma_status_t get_out_flags(IN sangoma_wait_obj_t *sng_wait_objects[], + IN uint32_t first_signaled_obj_index, IN uint32_t in_flags[], OUT uint32_t out_flags[], IN uint32_t number_of_sangoma_wait_objects, OUT BOOL *at_least_one_poll_set_flags_out) @@ -419,7 +424,18 @@ static sangoma_status_t get_out_flags(IN sangoma_wait_obj_t *sng_wait_objects[], for(i = 0; i < number_of_sangoma_wait_objects; i++) { sangoma_wait_obj_t *sangoma_wait_object = sng_wait_objects[i]; + if (!SANGOMA_OBJ_HAS_DEVICE(sangoma_wait_object)) { + + /* This object does not has a device, but, may have been signaled via sangoma_wait_obj_signal() + * test if the object is signaled, if it is, set SANG_WAIT_OBJ_IS_SIGNALED */ + + if((i == first_signaled_obj_index) || (WaitForSingleObject(sangoma_wait_object->generic_event_object, 0) == WAIT_OBJECT_0)) { + /* !+! jpboily : Since WaitForMultipleObjects cleared the status + * of the first signaled object, we make sure that the out_flag + * for this object is set */ + out_flags[i] |= SANG_WAIT_OBJ_IS_SIGNALED; + } continue; } @@ -438,8 +454,14 @@ static sangoma_status_t get_out_flags(IN sangoma_wait_obj_t *sng_wait_objects[], *at_least_one_poll_set_flags_out = TRUE; } } - } - } +#if 0 + /* Check if a device-related object was signalled, if it is, set SANG_WAIT_OBJ_IS_SIGNALED. */ + if (WaitForSingleObject(sangoma_wait_object->sng_event_objects[j], 0) == WAIT_OBJECT_0) { + out_flags[i] |= SANG_WAIT_OBJ_IS_SIGNALED; + } +#endif + }/* for(j = 0; j < LIBSNG_NUMBER_OF_EVENT_OBJECTS; j++) */ + }/* for(i = 0; i < number_of_sangoma_wait_objects; i++) */ return SANG_STATUS_SUCCESS; } @@ -447,48 +469,69 @@ static sangoma_status_t get_out_flags(IN sangoma_wait_obj_t *sng_wait_objects[], /*! \brief Brief description */ -static void registry_get_string_value(HKEY hkey, LPTSTR szKeyname, OUT LPSTR szValue, OUT DWORD *pdwSize) +static LONG registry_get_string_value(HKEY hkey, LPTSTR szKeyname, OUT LPSTR szValue, OUT DWORD *pdwSize) { + LONG iReturnCode; + /* reading twice to set pdwSize to needed value */ RegQueryValueEx(hkey, szKeyname, NULL, NULL, (unsigned char *)szValue, pdwSize); - RegQueryValueEx(hkey, szKeyname, NULL, NULL, (unsigned char *)szValue, pdwSize); - DBG_REGISTRY("%s(): %s: %s\n", __FUNCTION__, szKeyname, szValue); + + iReturnCode = RegQueryValueEx(hkey, szKeyname, NULL, NULL, (unsigned char *)szValue, pdwSize); + if(ERROR_SUCCESS == iReturnCode){ + iReturnCode = 0; + } + DBG_REGISTRY("%s(): %s: %s: iReturnCode: %d\n", __FUNCTION__, szKeyname, szValue, iReturnCode); + return iReturnCode; } /*! \brief Brief description */ -static void registry_set_string_value(HKEY hkey, LPTSTR szKeyname, IN LPSTR szValue) +static LONG registry_set_string_value(HKEY hkey, LPTSTR szKeyname, IN LPSTR szValue) { DWORD dwSize; + LONG iReturnCode; - dwSize = strlen(szValue) + 1; - RegSetValueEx(hkey, szKeyname, 0, REG_SZ, (unsigned char *)szValue, dwSize); + dwSize = (DWORD)strlen(szValue) + 1; + + iReturnCode = RegSetValueEx(hkey, szKeyname, 0, REG_SZ, (unsigned char *)szValue, dwSize); DBG_REGISTRY("%s(): %s: %s\n", __FUNCTION__, szKeyname, szValue); + + if(ERROR_SUCCESS == iReturnCode){ + iReturnCode = 0; + } + return iReturnCode; } /*! \brief Convert an integer (iValue) to string and write it to registry */ -static void registry_set_integer_value(HKEY hkey, LPTSTR szKeyname, IN int iValue) +static LONG registry_set_integer_value(HKEY hkey, LPTSTR szKeyname, IN int iValue) { DWORD dwSize; char szTemp[TMP_BUFFER_LEN]; + LONG iReturnCode; sprintf(szTemp, "%u", iValue); - dwSize = strlen(szTemp) + 1; - RegSetValueEx(hkey, szKeyname, 0, REG_SZ, (unsigned char *)szTemp, dwSize); - DBG_REGISTRY("%s(): %s: %d\n", __FUNCTION__, szKeyname, iValue); + dwSize = (DWORD)strlen(szTemp) + 1; + iReturnCode = RegSetValueEx(hkey, szKeyname, 0, REG_SZ, (unsigned char *)szTemp, dwSize); + + if(ERROR_SUCCESS == iReturnCode){ + iReturnCode = 0; + } + + DBG_REGISTRY("%s(): %s: %d: iReturnCode: %d\n", __FUNCTION__, szKeyname, iValue, iReturnCode); + return iReturnCode; } /*! * \brief Go through the list of ALL "Sangoma Hardware Abstraction Driver" ports installed on the system. - * Read Bus/Slot/Port information for a port and copare with what is searched for. + * Read Bus/Slot/Port information for a port and compare with what is searched for. */ static HKEY registry_open_port_key(hardware_info_t *hardware_info) { - int i; + int i, iRegistryReturnCode; SP_DEVINFO_DATA deid={sizeof(SP_DEVINFO_DATA)}; HDEVINFO hdi = SetupDiGetClassDevs((struct _GUID *)&GUID_DEVCLASS_SANGOMA_ADAPTER, NULL,NULL, DIGCF_PRESENT); char DeviceName[TMP_BUFFER_LEN], PCI_Bus[TMP_BUFFER_LEN], PCI_Slot[TMP_BUFFER_LEN], Port_Number[TMP_BUFFER_LEN]; @@ -496,12 +539,10 @@ static HKEY registry_open_port_key(hardware_info_t *hardware_info) HKEY hKeyTmp = (struct HKEY__ *)INVALID_HANDLE_VALUE; HKEY hPortRegistryKey = (struct HKEY__ *)INVALID_HANDLE_VALUE; - TCHAR name[MAX_FRIENDLY]; char szCompInstanceId[MAX_COMP_INSTID]; TCHAR szCompDescription[MAX_COMP_DESC]; DWORD dwRegType; - - /* Possible Port Names (refer to sdladrv.inf): + /* Possible Sangoma Port Names (refer to sdladrv.inf): Sangoma Hardware Abstraction Driver (Port 1) Sangoma Hardware Abstraction Driver (Port 2) Sangoma Hardware Abstraction Driver (Port 3) @@ -512,23 +553,22 @@ static HKEY registry_open_port_key(hardware_info_t *hardware_info) Sangoma Hardware Abstraction Driver (Port 8) Sangoma Hardware Abstraction Driver (Analog) Sangoma Hardware Abstraction Driver (ISDN BRI) */ + const TCHAR *search_SubStr = "Sangoma Hardware Abstraction Driver"; /* search for all (AFT) ports: */ - sprintf(name," Sangoma Hardware Abstraction Driver"); - for (i = 0; SetupDiEnumDeviceInfo(hdi, i, &deid); i++){ - BOOL fSuccess = SetupDiGetDeviceInstanceId(hdi, &deid, (PSTR)szCompInstanceId,MAX_COMP_INSTID, NULL); + BOOL fSuccess = SetupDiGetDeviceInstanceId(hdi, &deid, (PSTR)szCompInstanceId, MAX_COMP_INSTID, NULL); if (!fSuccess){ continue; } - fSuccess = SetupDiGetDeviceRegistryProperty(hdi, &deid,SPDRP_DEVICEDESC,&dwRegType, (BYTE*)szCompDescription, MAX_COMP_DESC, NULL); + fSuccess = SetupDiGetDeviceRegistryProperty(hdi, &deid, SPDRP_DEVICEDESC, &dwRegType, (BYTE*)szCompDescription, MAX_COMP_DESC, NULL); if (!fSuccess){ continue; } - if (strncmp(szCompDescription, name, strlen(name)) != 0) { /* Windows can add #2 etc - do NOT consider */ + if (!strstr(szCompDescription, search_SubStr)) { /* Windows can add #2 etc - do NOT consider */ /* This is a "Sangoma Card" device, we are interested only in "Sangoma Port" devices. */ continue; } @@ -542,13 +582,22 @@ static HKEY registry_open_port_key(hardware_info_t *hardware_info) } PCI_Bus[0] = '\0'; - registry_get_string_value(hKeyTmp, "PCI_Bus", PCI_Bus, &dwTemp); + iRegistryReturnCode = registry_get_string_value(hKeyTmp, "PCI_Bus", PCI_Bus, &dwTemp); + if(iRegistryReturnCode){ + continue; + } PCI_Slot[0] = '\0'; - registry_get_string_value(hKeyTmp, "PCI_Slot", PCI_Slot, &dwTemp); + iRegistryReturnCode = registry_get_string_value(hKeyTmp, "PCI_Slot", PCI_Slot, &dwTemp); + if(iRegistryReturnCode){ + continue; + } Port_Number[0] = '\0'; - registry_get_string_value(hKeyTmp, "Port_Number", Port_Number, &dwTemp); + iRegistryReturnCode = registry_get_string_value(hKeyTmp, "Port_Number", Port_Number, &dwTemp); + if(iRegistryReturnCode){ + continue; + } if( atoi(PCI_Bus) == hardware_info->pci_bus_number && atoi(PCI_Slot) == hardware_info->pci_slot_number && @@ -556,6 +605,7 @@ static HKEY registry_open_port_key(hardware_info_t *hardware_info) hPortRegistryKey = hKeyTmp; + /* read device name for debugging only */ DeviceName[0] = '\0'; registry_get_string_value(hPortRegistryKey, "DeviceName", DeviceName, &dwTemp); @@ -572,72 +622,16 @@ static HKEY registry_open_port_key(hardware_info_t *hardware_info) return hPortRegistryKey; } -static int registry_write_front_end_cfg(HKEY hPortRegistryKey, port_cfg_t *port_cfg) -{ - wandev_conf_t *wandev_conf = &port_cfg->wandev_conf; - sdla_fe_cfg_t *sdla_fe_cfg = &wandev_conf->fe_cfg; - sdla_te_cfg_t *te_cfg = &sdla_fe_cfg->cfg.te_cfg; - sdla_remora_cfg_t *analog_cfg = &sdla_fe_cfg->cfg.remora; - sdla_bri_cfg_t *bri_cfg = &sdla_fe_cfg->cfg.bri; - - /* set Media specific values. */ - switch(sdla_fe_cfg->media) - { - case WAN_MEDIA_T1: - case WAN_MEDIA_J1: /* the same as T1 */ - case WAN_MEDIA_E1: - /* only T1/E1 Port can change the Media, all other ports ignore this parameter. */ - registry_set_integer_value(hPortRegistryKey, "Media", sdla_fe_cfg->media /*WAN_MEDIA_T1*/); - registry_set_integer_value(hPortRegistryKey, "LDecoding", sdla_fe_cfg->lcode /*WAN_LCODE_B8ZS*/); - registry_set_integer_value(hPortRegistryKey, "Framing", sdla_fe_cfg->frame /*WAN_FR_ESF*/); - - registry_set_integer_value(hPortRegistryKey, "ClkRefPort", te_cfg->te_ref_clock); - registry_set_integer_value(hPortRegistryKey, "TE_IGNORE_YEL", te_cfg->ignore_yel_alarm); - registry_set_integer_value(hPortRegistryKey, "ACTIVE_CH", ENABLE_ALL_CHANNELS /*must be hardcoded*/); - registry_set_integer_value(hPortRegistryKey, "TE_RBS_CH", te_cfg->te_rbs_ch); /*not used by DS chip code, only by PMC */ - - registry_set_integer_value(hPortRegistryKey, "LBO", te_cfg->lbo /*WAN_T1_LBO_0_DB*/); - registry_set_integer_value(hPortRegistryKey, "ClkMode", te_cfg->te_clock /*WAN_NORMAL_CLK*/); - registry_set_integer_value(hPortRegistryKey, "HighImpedanceMode",te_cfg->high_impedance_mode /*WANOPT_NO*/); - registry_set_integer_value(hPortRegistryKey, "TE_RX_SLEVEL", te_cfg->rx_slevel /*WAN_TE1_RX_SLEVEL_12_DB*/); - /* write E1 signalling for both T1 and E1 */ - registry_set_integer_value(hPortRegistryKey, "E1Signalling", te_cfg->sig_mode /*WAN_TE1_SIG_CCS*/); - break; - case WAN_MEDIA_56K: - /* do nothing */ - break; - case WAN_MEDIA_FXOFXS: - /* Analog global (per-card) settings */ - registry_set_string_value(hPortRegistryKey, "remora_fxo_operation_mode_name", analog_cfg->opermode_name); - registry_set_integer_value(hPortRegistryKey, "RM_BATTTHRESH", analog_cfg->battthresh); - registry_set_integer_value(hPortRegistryKey, "RM_BATTDEBOUNCE", analog_cfg->battdebounce); - registry_set_integer_value(hPortRegistryKey, "RM_FXO_TAPPING", analog_cfg->rm_mode); - registry_set_integer_value(hPortRegistryKey, "RM_FXO_TAPPING_OFF_HOOK_THRESHOLD",analog_cfg->ohthresh); - break; - case WAN_MEDIA_BRI: - registry_set_integer_value(hPortRegistryKey, "aft_bri_clock_mode", bri_cfg->clock_mode); - break; - case WAN_MEDIA_SERIAL: - registry_set_integer_value(hPortRegistryKey, "clock_source", wandev_conf->clocking); - registry_set_integer_value(hPortRegistryKey, "BaudRate", wandev_conf->bps); - registry_set_integer_value(hPortRegistryKey, "serial_connection_type", wandev_conf->connection); - registry_set_integer_value(hPortRegistryKey, "serial_line_coding", wandev_conf->line_coding); - registry_set_integer_value(hPortRegistryKey, "serial_line_idle", wandev_conf->line_idle); - break; - default: - DBG_ERR("Invalid Media Type %d!\n", sdla_fe_cfg->media); - return 1; - } - - return 0; -} - static char* timeslot_bitmap_to_string(int bitmap) { int i = 0, range_counter = 0; int start_channel = -1, stop_channel = -1; static char tmp_string[256]; + if( WP_ALL_BITS_SET == bitmap ){ + return "ALL"; /* If all bits are set, use the "ALL" keyword. */ + } + tmp_string[0] = '\0'; /* all ranges between two zeros is what we will look for */ @@ -646,7 +640,7 @@ static char* timeslot_bitmap_to_string(int bitmap) if(start_channel < 0){ /* found a starting one of a range */ if(bitmap & (1 << i)){ - start_channel = i; + start_channel = i + 1; } } @@ -654,14 +648,14 @@ static char* timeslot_bitmap_to_string(int bitmap) if((bitmap & (1 << i)) == 0){ /* we hit a zero, that means the previous channel is one */ - stop_channel = i - 1; + stop_channel = i; }else if(i == (sizeof(bitmap) * 8 - 1)){ /* The most significant bit is set - there will be no delimiting zero. * It will also take care of "all channels" which is a special * case - there is a start but no stop channel because all bits - * are set. result will be 0-31 */ - stop_channel = (sizeof(bitmap) * 8 - 1); + * are set. result will be 1-32 */ + stop_channel = (sizeof(bitmap) * 8); } } @@ -690,34 +684,549 @@ static char* timeslot_bitmap_to_string(int bitmap) return tmp_string; } +static int registry_write_front_end_cfg(HKEY hPortRegistryKey, port_cfg_t *port_cfg) +{ + wandev_conf_t *wandev_conf = &port_cfg->wandev_conf; + sdla_fe_cfg_t *sdla_fe_cfg = &wandev_conf->fe_cfg; + sdla_te_cfg_t *te_cfg = &sdla_fe_cfg->cfg.te_cfg; + sdla_remora_cfg_t *analog_cfg = &sdla_fe_cfg->cfg.remora; + sdla_bri_cfg_t *bri_cfg = &sdla_fe_cfg->cfg.bri; + int iReturnCode = 0; + + /* Set Card/Port-wide values, which are independent of Media type, and + * stored in sdla_fe_cfg_t. */ + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "tdmv_law", FE_TDMV_LAW(sdla_fe_cfg) /* WAN_TDMV_MULAW/WAN_TDMV_ALAW */); + if(iReturnCode){ + return iReturnCode; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "ExternalSynchronization", FE_NETWORK_SYNC(sdla_fe_cfg) /* WANOPT_NO/WANOPT_YES */); + if(iReturnCode){ + return iReturnCode; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "FE_TXTRISTATE", FE_TXTRISTATE(sdla_fe_cfg) /* WANOPT_NO/WANOPT_YES */); + if(iReturnCode){ + return iReturnCode; + } + + /* set Media specific values. */ + switch(sdla_fe_cfg->media) + { + case WAN_MEDIA_T1: + case WAN_MEDIA_J1: /* the same as T1 */ + case WAN_MEDIA_E1: + /* only T1/E1 Port can change the Media, all other ports ignore this parameter. */ + iReturnCode = registry_set_integer_value(hPortRegistryKey, "Media", sdla_fe_cfg->media /*WAN_MEDIA_T1*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "LDecoding", sdla_fe_cfg->lcode /*WAN_LCODE_B8ZS*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "Framing", sdla_fe_cfg->frame /*WAN_FR_ESF*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "ClkRefPort", te_cfg->te_ref_clock); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "TE_IGNORE_YEL", te_cfg->ignore_yel_alarm); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "ACTIVE_CH", ENABLE_ALL_CHANNELS /*must be hardcoded*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "TE_RBS_CH", te_cfg->te_rbs_ch); /*not used by DS chip code, only by PMC */ + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "LBO", te_cfg->lbo /*WAN_T1_LBO_0_DB*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "ClkMode", te_cfg->te_clock /*WAN_NORMAL_CLK*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "HighImpedanceMode",te_cfg->high_impedance_mode /*WANOPT_NO*/); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "TE_RX_SLEVEL", te_cfg->rx_slevel /*WAN_TE1_RX_SLEVEL_12_DB*/); + if(iReturnCode){ + break; + } + + /* write E1 signalling for both T1 and E1 */ + iReturnCode = registry_set_integer_value(hPortRegistryKey, "E1Signalling", te_cfg->sig_mode /*WAN_TE1_SIG_CCS*/); + if(iReturnCode){ + break; + } + break; + + case WAN_MEDIA_56K: + /* do nothing */ + iReturnCode = 0; + break; + + case WAN_MEDIA_FXOFXS: + /* Analog global (per-card) settings */ + iReturnCode = registry_set_string_value(hPortRegistryKey, "remora_fxo_operation_mode_name", analog_cfg->opermode_name); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_BATTTHRESH", analog_cfg->battthresh); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_BATTDEBOUNCE", analog_cfg->battdebounce); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_FXO_TAPPING", analog_cfg->rm_mode); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_FXO_TAPPING_OFF_HOOK_THRESHOLD",analog_cfg->ohthresh); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_LCM", analog_cfg->rm_lcm); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_FXSTXGAIN", analog_cfg->fxs_txgain); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_FXSRXGAIN", analog_cfg->fxs_rxgain); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_FXOTXGAIN", analog_cfg->fxo_txgain); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "RM_FXORXGAIN", analog_cfg->fxo_rxgain); + if(iReturnCode){ + break; + } + break; + + case WAN_MEDIA_BRI: + iReturnCode = registry_set_integer_value(hPortRegistryKey, "aft_bri_clock_mode", bri_cfg->clock_mode); + if(iReturnCode){ + break; + } + break; + + case WAN_MEDIA_SERIAL: + iReturnCode = registry_set_integer_value(hPortRegistryKey, "clock_source", wandev_conf->clocking); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "BaudRate", wandev_conf->bps); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "serial_connection_type", wandev_conf->connection); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "serial_line_coding", wandev_conf->line_coding); + if(iReturnCode){ + break; + } + + iReturnCode = registry_set_integer_value(hPortRegistryKey, "serial_line_idle", wandev_conf->line_idle); + if(iReturnCode){ + break; + } + break; + + default: + DBG_ERR("Invalid Media Type %d!\n", sdla_fe_cfg->media); + iReturnCode = 1; + } + + return iReturnCode; +} + +static int registry_write_wan_tdmv_conf(HKEY hPortRegistryKey, port_cfg_t *port_cfg) +{ + wandev_conf_t *wandev_conf = &port_cfg->wandev_conf; + sdla_fe_cfg_t *sdla_fe_cfg = &wandev_conf->fe_cfg; + wan_tdmv_conf_t *tdmv_conf = &wandev_conf->tdmv_conf; + int iReturnCode = 1; + + /* set Media specific values. */ + switch(sdla_fe_cfg->media) + { + case WAN_MEDIA_T1: + case WAN_MEDIA_J1: /* the same as T1 */ + case WAN_MEDIA_E1: + /* write dchannel bitmap as a string (the same way as active channels) */ + iReturnCode = registry_set_string_value(hPortRegistryKey, "TDMV_DCHAN", timeslot_bitmap_to_string(tdmv_conf->dchan)); + break; + + case WAN_MEDIA_56K: + case WAN_MEDIA_FXOFXS: + case WAN_MEDIA_BRI: + case WAN_MEDIA_SERIAL: + /* do nothing */ + iReturnCode = 0; + break; + + default: + DBG_ERR("Invalid Media Type %d!\n", sdla_fe_cfg->media); + iReturnCode = 2; + break; + } + + return iReturnCode; +} + static int registry_write_channel_group_cfg(HKEY hPortRegistryKey, port_cfg_t *port_cfg, int interface_index, wanif_conf_t wanif_conf) { char szTemp[TMP_BUFFER_LEN]; + int iReturnCode = 0; - // Line mode - _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_line_mode", interface_index); - registry_set_string_value(hPortRegistryKey, szTemp, - (wanif_conf.hdlc_streaming == WANOPT_YES ? MODE_OPTION_HDLC : MODE_OPTION_BITSTRM)); + do{ + // Line mode + _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_line_mode", interface_index); + iReturnCode = registry_set_string_value(hPortRegistryKey, szTemp, + (wanif_conf.hdlc_streaming == WANOPT_YES ? MODE_OPTION_HDLC : MODE_OPTION_BITSTRM)); + if(iReturnCode){ + break; + } - // MTU - _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_mtu", interface_index); - registry_set_integer_value(hPortRegistryKey, szTemp, wanif_conf.mtu); + // MTU + _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_mtu", interface_index); + iReturnCode = registry_set_integer_value(hPortRegistryKey, szTemp, wanif_conf.mtu); + if(iReturnCode){ + break; + } - // Operational mode - _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_operational_mode", interface_index); - registry_set_string_value(hPortRegistryKey, szTemp, wanif_conf.usedby); + // Operational mode + _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_operational_mode", interface_index); + iReturnCode = registry_set_string_value(hPortRegistryKey, szTemp, wanif_conf.usedby); + if(iReturnCode){ + break; + } - // Active Timeslots for the Group - _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_active_ch", interface_index); - registry_set_string_value(hPortRegistryKey, szTemp, timeslot_bitmap_to_string(wanif_conf.active_ch)); + // Active Timeslots for the Group + _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_active_ch", interface_index); + iReturnCode = registry_set_string_value(hPortRegistryKey, szTemp, timeslot_bitmap_to_string(wanif_conf.active_ch)); + if(iReturnCode){ + break; + } - // Idle char. - _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_idle_char", interface_index); - registry_set_integer_value(hPortRegistryKey, szTemp, wanif_conf.u.aft.idle_flag); + // Idle char. + _snprintf(szTemp, TMP_BUFFER_LEN, "aft_logic_channel_%d_idle_char", interface_index); + iReturnCode = registry_set_integer_value(hPortRegistryKey, szTemp, wanif_conf.u.aft.idle_flag); + if(iReturnCode){ + break; + } - return 0; + }while(0); + + return iReturnCode; } +/*! + * \brief Go through the list of ALL Sangoma PCI Adapters (Cards) on the system. + * Enable or Disable each one of them (state change will be visible in the Device Manager). + */ +static sangoma_status_t sangoma_control_cards(DWORD StateChange) +{ + sangoma_status_t rc; + int i; + SP_DEVINFO_DATA deid = {sizeof(SP_DEVINFO_DATA)}; + HDEVINFO hdi; + TCHAR szInstanceId[MAX_COMP_INSTID]; + + const TCHAR *szAftSearchSubStr = "PCI\\VEN_1923"; /* Sangoma PCI Vendor ID is 1923 */ + /*const TCHAR *szS518AdslSearchSubStr = "PCI\\VEN_14BC&DEV_D002";*/ /* Note: S518 is end-of-life */ + + SP_PROPCHANGE_PARAMS pcp; + + hdi = SetupDiGetClassDevs((struct _GUID *)&GUID_DEVCLASS_SANGOMA_ADAPTER, NULL, NULL, DIGCF_PRESENT); + if(INVALID_HANDLE_VALUE == hdi){ + /* no sangoma cards installed on the system */ + return SANG_STATUS_INVALID_DEVICE; + } + + /* search for all AFT Cards: */ + for (i = 0; SetupDiEnumDeviceInfo(hdi, i, &deid); i++){ + + BOOL fSuccess = SetupDiGetDeviceInstanceId(hdi, &deid, (PSTR)szInstanceId, sizeof(szInstanceId), NULL); + if (!fSuccess){ + rc = SANG_STATUS_REGISTRY_ERROR; + break; + } + if(!strstr(szInstanceId, szAftSearchSubStr)){ + /* Found Sangoma Port, but we are interested only Cards. */ + continue; + } + + memset(&pcp, 0x00, sizeof(pcp)); + //Set the PropChangeParams structure. + pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER); + pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE; + pcp.Scope = DICS_FLAG_GLOBAL; /* DICS_FLAG_CONFIGSPECIFIC will enable ALL cards, including + * those explicetly DISABLED by the user in the Device Manager, + * and we don't want to force anything on the user, hence we + * use DICS_FLAG_GLOBAL. */ + pcp.StateChange = StateChange; + + if(!SetupDiSetClassInstallParams(hdi, &deid, (SP_CLASSINSTALL_HEADER *)&pcp, sizeof(pcp))){ + rc = SANG_STATUS_REGISTRY_ERROR; + break; + } + + if(!SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, hdi, &deid)){ + rc = SANG_STATUS_REGISTRY_ERROR; + break; + } + + }/* for() */ + + SetupDiDestroyDeviceInfoList(hdi); + + return SANG_STATUS_SUCCESS; +} + +sangoma_status_t _LIBSNG_CALL sangoma_unload_driver() +{ + return sangoma_control_cards(DICS_DISABLE); +} + +sangoma_status_t _LIBSNG_CALL sangoma_load_driver() +{ + return sangoma_control_cards(DICS_ENABLE); +} + +/*! + * \brief Go through the list of ALL "Sangoma Hardware Abstraction Driver" ports installed on the system, + * and assign sequential numbers to the Ports, starting from 1. This will override the Automatic Wanpipe + * Span numbering. + */ +void _LIBSNG_CALL sangoma_reset_port_numbers() +{ + int i, iRegistryReturnCode, iPortCounter = 0; + SP_DEVINFO_DATA deid={sizeof(SP_DEVINFO_DATA)}; + HDEVINFO hdi = SetupDiGetClassDevs((struct _GUID *)&GUID_DEVCLASS_SANGOMA_ADAPTER, NULL,NULL, DIGCF_PRESENT); + DWORD dwTemp; + HKEY hKeyTmp = (struct HKEY__ *)INVALID_HANDLE_VALUE; + + char szCompInstanceId[MAX_COMP_INSTID]; + TCHAR szCompDescription[MAX_COMP_DESC]; + DWORD dwRegType; + /* Possible Sangoma Port Names (refer to sdladrv.inf): + Sangoma Hardware Abstraction Driver (Port 1) + Sangoma Hardware Abstraction Driver (Port 2) + Sangoma Hardware Abstraction Driver (Port 3) + Sangoma Hardware Abstraction Driver (Port 4) + Sangoma Hardware Abstraction Driver (Port 5) + Sangoma Hardware Abstraction Driver (Port 6) + Sangoma Hardware Abstraction Driver (Port 7) + Sangoma Hardware Abstraction Driver (Port 8) + Sangoma Hardware Abstraction Driver (Analog) + Sangoma Hardware Abstraction Driver (ISDN BRI) */ + const TCHAR *search_SubStr = "Sangoma Hardware Abstraction Driver"; + + /* search for all (AFT) ports: */ + for (i = 0; SetupDiEnumDeviceInfo(hdi, i, &deid); i++){ + + BOOL fSuccess = SetupDiGetDeviceInstanceId(hdi, &deid, (PSTR)szCompInstanceId, MAX_COMP_INSTID, NULL); + if (!fSuccess){ + continue; + } + + fSuccess = SetupDiGetDeviceRegistryProperty(hdi, &deid, SPDRP_DEVICEDESC, &dwRegType, (BYTE*)szCompDescription, MAX_COMP_DESC, NULL); + if (!fSuccess){ + continue; + } + + if (!strstr(szCompDescription, search_SubStr)) { /* Windows can add #2 etc - do NOT consider */ + /* This is a "Sangoma Card" device, we are interested only in "Sangoma Port" devices. */ + continue; + } + + hKeyTmp = SetupDiOpenDevRegKey(hdi, &deid, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ | KEY_SET_VALUE ); + if(hKeyTmp == INVALID_HANDLE_VALUE){ + DBG_REGISTRY("Error: Failed to open Registry key!!\n"); + continue; + } + + iPortCounter++; + + printf("* %s -> Setting Span/Port number: %d\n", szCompDescription, iPortCounter); + + /* TODO: 1. search for installed Sangoma Cards + 2. for each Card, search Ports (Bus/Slot must match) + 3. based on number of installed Ports, set SerialNumbersRange of the Card + + This way the "UserWanpipeNumber" can be set to zero and Span numbers will be + seqencial automatically. + */ + iRegistryReturnCode = registry_set_integer_value(hKeyTmp, "UserWanpipeNumber", iPortCounter); + if(iRegistryReturnCode){ + continue; + } + + RegCloseKey(hKeyTmp); + + }/* for() */ + + SetupDiDestroyDeviceInfoList(hdi); + + return; +} + + +/*! + * \brief Get version of driver from the Windows Registry, as it was written by .inf parser. + * Note that there is a possibility that currently loaded driver has a different + * version (if the original binary file was overwritten). + */ +sangoma_status_t _LIBSNG_CALL sangoma_get_driver_version_from_registry(char *out_buffer, int out_buffer_length) +{ + int i, iRegistryReturnCode; + SP_DEVINFO_DATA deid = {sizeof(SP_DEVINFO_DATA)}; + HDEVINFO hdi = SetupDiGetClassDevs((struct _GUID *)&GUID_DEVCLASS_SANGOMA_ADAPTER, NULL, NULL, DIGCF_PRESENT); + DWORD dwTemp; + HKEY hKeyTmp = (struct HKEY__ *)INVALID_HANDLE_VALUE; + + TCHAR szTmp[MAX_COMP_DESC]; + BOOL fSuccess = FALSE; + + /* search for ANY Sangoma device of class GUID_DEVCLASS_SANGOMA_ADAPTER */ + for (i = 0; SetupDiEnumDeviceInfo(hdi, i, &deid); i++){ + + fSuccess = SetupDiGetDeviceInstanceId(hdi, &deid, (PSTR)szTmp, sizeof(szTmp), NULL); + if (!fSuccess){ + break; + } + DBG_REGISTRY("%s(): Device Instance Id: %s\n", __FUNCTION__, szTmp); + /* Device Instance Id: COMMSADAPTER\SANGOMAADAPTER_AFT_LINE8\5&32E1B75F&0&13 + Device Instance Id: PCI\VEN_1923&DEV_0100&SUBSYS_4113A114&REV_00\4&31B6CD7&0&10F0 */ + + hKeyTmp = SetupDiOpenDevRegKey( hdi, &deid, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ | KEY_SET_VALUE ); + if(hKeyTmp == INVALID_HANDLE_VALUE){ + DBG_REGISTRY("Error: Failed to open Registry key!!\n"); + fSuccess = FALSE; + break; + } + + iRegistryReturnCode = registry_get_string_value(hKeyTmp, "DriverVersion", szTmp, &dwTemp); + if(iRegistryReturnCode){ + fSuccess = FALSE; + break; + } + + wp_snprintf(out_buffer, out_buffer_length, szTmp); + + RegCloseKey(hKeyTmp); + + /* it is enough to read the version only once, so break here */ + break; + }/* for() */ + + SetupDiDestroyDeviceInfoList(hdi); + + if (!fSuccess){ + return SANG_STATUS_REGISTRY_ERROR; + }else{ + return SANG_STATUS_SUCCESS; + } +} + +/*! + * \brief Set the Driver Mode of all Hardware (not virtual) devices. + * The modes are: Normal + * Firmware Update + */ +sangoma_status_t _LIBSNG_CALL sangoma_set_driver_mode_of_all_hw_devices(int driver_mode) +{ + sangoma_status_t rc; + int i, iRegistryReturnCode; + SP_DEVINFO_DATA deid = {sizeof(SP_DEVINFO_DATA)}; + HDEVINFO hdi; + TCHAR szInstanceId[MAX_COMP_INSTID]; + HKEY hKeyTmp = (struct HKEY__ *)INVALID_HANDLE_VALUE; + + const TCHAR *szAftSearchSubStr = "PCI\\VEN_1923"; /* Sangoma PCI Vendor ID is 1923 */ + + hdi = SetupDiGetClassDevs((struct _GUID *)&GUID_DEVCLASS_SANGOMA_ADAPTER, NULL, NULL, DIGCF_PRESENT); + if(INVALID_HANDLE_VALUE == hdi){ + /* no sangoma cards installed on the system */ + return SANG_STATUS_INVALID_DEVICE; + } + + /* search for all AFT Cards: */ + for (i = 0; SetupDiEnumDeviceInfo(hdi, i, &deid); i++){ + + BOOL fSuccess = SetupDiGetDeviceInstanceId(hdi, &deid, (PSTR)szInstanceId, sizeof(szInstanceId), NULL); + if (!fSuccess){ + rc = SANG_STATUS_REGISTRY_ERROR; + break; + } + if(!strstr(szInstanceId, szAftSearchSubStr)){ + /* Found Sangoma Port, but we are interested only Cards. */ + continue; + } + + hKeyTmp = SetupDiOpenDevRegKey(hdi, &deid, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ | KEY_SET_VALUE ); + if(hKeyTmp == INVALID_HANDLE_VALUE){ + rc = SANG_STATUS_REGISTRY_ERROR; + break; + } + + iRegistryReturnCode = registry_set_integer_value(hKeyTmp, "driver_mode", driver_mode); + if(iRegistryReturnCode){ + rc = SANG_STATUS_REGISTRY_ERROR; + break; + } + + RegCloseKey(hKeyTmp); + + }/* for() */ + + SetupDiDestroyDeviceInfoList(hdi); + + return SANG_STATUS_SUCCESS; +} + + #endif /* __WINDOWS__ */ @@ -740,10 +1249,10 @@ static int registry_write_channel_group_cfg(HKEY hPortRegistryKey, port_cfg_t *p \see sangoma_wait_obj_type_t \return SANG_STATUS_SUCCESS: success, or error status */ -sangoma_status_t _SAPI_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma_wait_object, sng_fd_t fd, sangoma_wait_obj_type_t object_type) +sangoma_status_t _LIBSNG_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma_wait_object, sng_fd_t fd, sangoma_wait_obj_type_t object_type) { int err = 0; - sangoma_wait_obj_t *sng_wait_obj; + sangoma_wait_obj_t *sng_wait_obj = NULL; if (!sangoma_wait_object) { return SANG_STATUS_INVALID_DEVICE; @@ -769,7 +1278,8 @@ sangoma_status_t _SAPI_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma if (SANGOMA_OBJ_IS_SIGNALABLE(sng_wait_obj)) { sng_wait_obj->generic_event_object = CreateEvent( NULL, FALSE, FALSE, NULL); if(!sng_wait_obj->generic_event_object){ - return SANG_STATUS_GENERAL_ERROR; + err = SANG_STATUS_GENERAL_ERROR; + goto failed; } } @@ -781,17 +1291,17 @@ sangoma_status_t _SAPI_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma err = init_sangoma_event_object(sng_wait_obj, POLLIN, fd); if(SANG_STATUS_SUCCESS != err){ - return err; + goto failed; } err = init_sangoma_event_object(sng_wait_obj, POLLOUT, fd); if(SANG_STATUS_SUCCESS != err){ - return err; + goto failed; } err = init_sangoma_event_object(sng_wait_obj, POLLPRI, fd); if(SANG_STATUS_SUCCESS != err) { - return err; + goto failed; } DBG_INIT("%s(): returning: %d", __FUNCTION__, err); @@ -802,7 +1312,8 @@ sangoma_status_t _SAPI_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma sng_wait_obj->signal_write_fd = INVALID_HANDLE_VALUE; /* if we want cross-process event notification we can use a named pipe with mkfifo() */ if (pipe(filedes)) { - return -1; + err = SANG_STATUS_GENERAL_ERROR; + goto failed; } sng_wait_obj->signal_read_fd = filedes[0]; sng_wait_obj->signal_write_fd = filedes[1]; @@ -810,6 +1321,12 @@ sangoma_status_t _SAPI_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma #endif *sangoma_wait_object = sng_wait_obj; return err; + +failed: + if (sng_wait_obj) { + sangoma_wait_obj_delete(&sng_wait_obj); + } + return err; } /*! @@ -818,7 +1335,7 @@ sangoma_status_t _SAPI_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma \param sangoma_wait_object pointer to a pointer to a single device object \return SANG_STATUS_SUCCESS on success or some sangoma status error */ -sangoma_status_t _SAPI_CALL sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object) +sangoma_status_t _LIBSNG_CALL sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object) { sangoma_wait_obj_t *sng_wait_obj = *sangoma_wait_object; #if defined(__WINDOWS__) @@ -847,6 +1364,7 @@ sangoma_status_t _SAPI_CALL sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma #endif sng_wait_obj->init_flag = 0; sng_wait_obj->object_type = UNKNOWN_WAIT_OBJ; + free(sng_wait_obj); *sangoma_wait_object = NULL; return SANG_STATUS_SUCCESS; } @@ -857,7 +1375,7 @@ sangoma_status_t _SAPI_CALL sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma \param sangoma_wait_object pointer a single device object \return 0 on success, non-zero on error */ -int _SAPI_CALL sangoma_wait_obj_signal(sangoma_wait_obj_t *sng_wait_obj) +int _LIBSNG_CALL sangoma_wait_obj_signal(sangoma_wait_obj_t *sng_wait_obj) { if (!SANGOMA_OBJ_IS_SIGNALABLE(sng_wait_obj)) { /* even when Windows objects are always signalable for the sake of providing @@ -887,7 +1405,7 @@ int _SAPI_CALL sangoma_wait_obj_signal(sangoma_wait_obj_t *sng_wait_obj) \param sangoma_wait_object pointer a single device object \return fd - device file descriptor */ -sng_fd_t _SAPI_CALL sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sng_wait_obj) +sng_fd_t _LIBSNG_CALL sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sng_wait_obj) { return sng_wait_obj->fd; } @@ -900,24 +1418,24 @@ sng_fd_t _SAPI_CALL sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sng_wait_obj) \param context void pointer to user context \return void */ -void _SAPI_CALL sangoma_wait_obj_set_context(sangoma_wait_obj_t *sng_wait_obj, void *context) +void _LIBSNG_CALL sangoma_wait_obj_set_context(sangoma_wait_obj_t *sng_wait_obj, void *context) { sng_wait_obj->context = context; } /*! - \fn void *sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object) + \fn PVOID sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object) \brief Retrieve the user context (if any) that was set via sangoma_wait_obj_set_context. \param sangoma_wait_object pointer a single device object \return void* */ -void* _SAPI_CALL sangoma_wait_obj_get_context(sangoma_wait_obj_t *sng_wait_obj) +PVOID _LIBSNG_CALL sangoma_wait_obj_get_context(sangoma_wait_obj_t *sng_wait_obj) { return sng_wait_obj->context; } /*! - \fn sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], int32_t in_flags[], int32_t out_flags[] uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout); + \fn sangoma_status_t _LIBSNG_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], int32_t in_flags[], int32_t out_flags[] uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout); \brief Wait for multiple sangoma waitable objects \param sangoma_wait_objects pointer to array of wait objects previously created with sangoma_wait_obj_create \param in_flags array of flags corresponding to the flags the user is interested on for each waitable object @@ -926,11 +1444,13 @@ void* _SAPI_CALL sangoma_wait_obj_get_context(sangoma_wait_obj_t *sng_wait_obj) \param system_wait_timeout timeout in miliseconds in case of no event \return negative: SANG_STATUS_APIPOLL_TIMEOUT: timeout, SANG_STATUS_SUCCESS: event occured check in sangoma_wait_objects, any other return code is some error */ -sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sng_wait_objects[], uint32_t in_flags[], uint32_t out_flags[], +sangoma_status_t _LIBSNG_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sng_wait_objects[], uint32_t in_flags[], uint32_t out_flags[], uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout) { #if defined(__WINDOWS__) HANDLE hEvents[MAXIMUM_WAIT_OBJECTS]; + uint32_t uiMapEventIndexToWaitObjectIndex[MAXIMUM_WAIT_OBJECTS]; + DWORD dwResult; int at_least_one_poll_set_flags_out, number_of_internal_signaling_objects, err; #endif uint32_t i = 0, j = 0; @@ -949,6 +1469,7 @@ sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sng_wait_ob return SANG_STATUS_NO_FREE_BUFFERS; } hEvents[number_of_internal_signaling_objects] = sangoma_wait_object->generic_event_object; + uiMapEventIndexToWaitObjectIndex[number_of_internal_signaling_objects] = i; number_of_internal_signaling_objects++; } @@ -958,11 +1479,12 @@ sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sng_wait_ob ((j == LIBSNG_EVENT_INDEX_POLLOUT) && (in_flags[i] & POLLOUT)) || ((j == LIBSNG_EVENT_INDEX_POLLPRI) && (in_flags[i] & POLLPRI)) ){ - if(check_number_of_wait_objects(number_of_internal_signaling_objects, __FUNCTION__, __LINE__)){ - return SANG_STATUS_NO_FREE_BUFFERS; - } - hEvents[number_of_internal_signaling_objects] = sangoma_wait_object->sng_event_objects[j]; - number_of_internal_signaling_objects++; + if(check_number_of_wait_objects(number_of_internal_signaling_objects, __FUNCTION__, __LINE__)){ + return SANG_STATUS_NO_FREE_BUFFERS; + } + hEvents[number_of_internal_signaling_objects] = sangoma_wait_object->sng_event_objects[j]; + uiMapEventIndexToWaitObjectIndex[number_of_internal_signaling_objects] = i; + number_of_internal_signaling_objects++; } }/* if () */ }/* for() */ @@ -979,7 +1501,7 @@ sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sng_wait_ob /* It is important to get 'out flags' BEFORE the WaitForMultipleObjects() * because it allows to keep API driver's transmit queue full. */ - err = get_out_flags(sng_wait_objects, in_flags, out_flags, number_of_sangoma_wait_objects, &at_least_one_poll_set_flags_out); + err = get_out_flags(sng_wait_objects, INVALID_INDEX, in_flags, out_flags, number_of_sangoma_wait_objects, &at_least_one_poll_set_flags_out); if(SANG_ERROR(err)){ return err; } @@ -989,12 +1511,20 @@ sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sng_wait_ob } /* wait untill at least one of the events is signaled OR a 'system_wait_timeout' occured */ - if (WAIT_TIMEOUT == WaitForMultipleObjects(number_of_internal_signaling_objects, &hEvents[0], FALSE, system_wait_timeout)){ + dwResult = WaitForMultipleObjects(number_of_internal_signaling_objects, &hEvents[0], FALSE, system_wait_timeout); + if (WAIT_TIMEOUT == dwResult){ return SANG_STATUS_APIPOLL_TIMEOUT; } + if( dwResult >= (DWORD)number_of_internal_signaling_objects ) { + return SANG_STATUS_GENERAL_ERROR; + } + /* WaitForMultipleObjects() was waken by a Sangoma or by a non-Sangoma wait object. */ - err = get_out_flags(sng_wait_objects, in_flags, out_flags, number_of_sangoma_wait_objects, NULL); + err = get_out_flags(sng_wait_objects, + uiMapEventIndexToWaitObjectIndex[dwResult],/* This is the index of first signaled object in + * the original sng_wait_objects[] array, not in hEvents[] array. */ + in_flags, out_flags, number_of_sangoma_wait_objects, NULL); if(SANG_ERROR(err)){ return err; } @@ -1028,18 +1558,14 @@ sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sng_wait_ob if (res > 0) { for(i = 0; i < number_of_sangoma_wait_objects; i++){ out_flags[i] = pfds[i].revents; + /* POLLIN, POLLOUT, POLLPRI have same values as SANG_WAIT_OBJ_ HAS_INPUT, CAN_OUTPUT and HAS_EVENTS, no need to set them */ } for(i = 0; i < j; i++){ - /* TODO: we must do something extra for signalled objects like setting a flag. - * current user of the SANGOMA_OBJ_IS_SIGNALABLE feature is Netborder and they dont - * need to know which object was signaled. If we want to know which object was signaled - * we need a new libsangoma API sangoma_wait_obj_is_signaled() where in Windows we can - * use WaitForSingleObject to test the signaled state and in Linux we can set a flag in - * sng_wait_obj - * */ if (pfds[number_of_sangoma_wait_objects+i].revents & POLLIN) { - /* read and discard the signal byte */ + /* read and discard the signal byte */ read(pfds[number_of_sangoma_wait_objects+i].fd, &dummy_buf, 1); + /* set the proper flag so users may know this object was signaled */ + out_flags[i] |= SANG_WAIT_OBJ_IS_SIGNALED; } } } else if (res < 0 && errno == EINTR) { @@ -1058,13 +1584,13 @@ sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sng_wait_ob /*! - \fn sangoma_status_t _SAPI_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, int32_t inflags, int32_t *outflags, int32_t timeout) + \fn sangoma_status_t _LIBSNG_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, int32_t inflags, int32_t *outflags, int32_t timeout) \brief Wait for a single waitable object - \param sangoma_wait_obj pointer to array of file descriptors to wait for + \param sangoma_wait_obj pointer to a wait object previously created with sangoma_wait_obj_create \param timeout timeout in miliseconds in case of no event \return SANG_STATUS_APIPOLL_TIMEOUT: timeout, SANG_STATUS_SUCCESS: event occured use sangoma_wait_obj_get_out_flags to check or error status */ -sangoma_status_t _SAPI_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, uint32_t inflags, uint32_t *outflags, int32_t timeout) +sangoma_status_t _LIBSNG_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, uint32_t inflags, uint32_t *outflags, int32_t timeout) { return sangoma_waitfor_many(&sangoma_wait_obj, &inflags, outflags, 1, timeout); } @@ -1075,18 +1601,18 @@ sangoma_status_t _SAPI_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj ***************************************************************/ -int _SAPI_CALL sangoma_span_chan_toif(int span, int chan, char *interface_name) +int _LIBSNG_CALL sangoma_span_chan_toif(int span, int chan, char *interface_name) { #if defined(__WINDOWS__) -/* FIXME: Not implemented */ - return -1; + /* Form the Interface Name from span and chan number (i.e. wanpipe1_if1). */ + sprintf(interface_name, WP_INTERFACE_NAME_FORM, span, chan); #else sprintf(interface_name,"s%ic%i",span,chan); #endif return 0; } -int _SAPI_CALL sangoma_interface_toi(char *interface_name, int *span, int *chan) +int _LIBSNG_CALL sangoma_interface_toi(char *interface_name, int *span, int *chan) { char *p=NULL, *sp = NULL, *ch = NULL; int ret = 0; @@ -1117,7 +1643,7 @@ int _SAPI_CALL sangoma_interface_toi(char *interface_name, int *span, int *chan) return ret; } -int _SAPI_CALL sangoma_interface_wait_up(int span, int chan, int sectimeout) +int _LIBSNG_CALL sangoma_interface_wait_up(int span, int chan, int sectimeout) { #if defined(__WINDOWS__) /* Windows does not need to wait for interfaces to come up */ @@ -1149,7 +1675,7 @@ int _SAPI_CALL sangoma_interface_wait_up(int span, int chan, int sectimeout) #endif } -int _SAPI_CALL sangoma_span_chan_fromif(char *interface_name, int *span, int *chan) +int _LIBSNG_CALL sangoma_span_chan_fromif(char *interface_name, int *span, int *chan) { char *p = NULL, *sp = NULL, *ch = NULL; int ret = 0; @@ -1180,7 +1706,7 @@ int _SAPI_CALL sangoma_span_chan_fromif(char *interface_name, int *span, int *ch return ret; } -sng_fd_t _SAPI_CALL sangoma_open_api_span_chan(int span, int chan) +sng_fd_t _LIBSNG_CALL sangoma_open_api_span_chan(int span, int chan) { sng_fd_t fd = INVALID_HANDLE_VALUE; wanpipe_api_t tdm_api; @@ -1215,40 +1741,12 @@ sng_fd_t _SAPI_CALL sangoma_open_api_span_chan(int span, int chan) return fd; } -/* no checks done for multiple open */ -sng_fd_t _SAPI_CALL __sangoma_open_api_span_chan(int span, int chan) +static sng_fd_t sangoma_open_dev_by_name(const char *dev_name) { - char fname[FNAME_LEN], tmp_fname[FNAME_LEN]; - - /* Form the Interface Name from span and chan number (i.e. wanpipe1_if1). */ - _snprintf(tmp_fname, DEV_NAME_LEN, WP_INTERFACE_NAME_FORM, span, chan); + char fname[FNAME_LEN]; #if defined(__WINDOWS__) - _snprintf(fname , FNAME_LEN, "\\\\.\\%s", tmp_fname); - return CreateFile( fname, - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - (LPSECURITY_ATTRIBUTES)NULL, - OPEN_EXISTING, - FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, - (HANDLE)NULL - ); -#else - sprintf(fname,"/dev/%s", tmp_fname); - - return open(fname, O_RDWR); -#endif -} - -sng_fd_t _SAPI_CALL sangoma_open_api_ctrl(void) -{ - char fname[FNAME_LEN], tmp_fname[FNAME_LEN]; - - /* Form the Ctrl Device Name. */ - _snprintf(tmp_fname, DEV_NAME_LEN, WP_CTRL_DEV_NAME); - -#if defined(__WINDOWS__) - _snprintf(fname , FNAME_LEN, "\\\\.\\%s", tmp_fname); + _snprintf(fname , FNAME_LEN, "\\\\.\\%s", dev_name); return CreateFile( fname, GENERIC_READ | GENERIC_WRITE, @@ -1259,16 +1757,40 @@ sng_fd_t _SAPI_CALL sangoma_open_api_ctrl(void) (HANDLE)NULL ); #else - sprintf(fname,"/dev/%s", tmp_fname); + sprintf(fname,"/dev/%s", dev_name); return open(fname, O_RDWR); #endif } +/* no checks done for multiple open */ +sng_fd_t _LIBSNG_CALL __sangoma_open_api_span_chan(int span, int chan) +{ + char tmp_fname[FNAME_LEN]; -int _SAPI_CALL sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api) + /* Form the Interface Name from span and chan number (i.e. wanpipe1_if1). */ + _snprintf(tmp_fname, DEV_NAME_LEN, WP_INTERFACE_NAME_FORM, span, chan); + + return sangoma_open_dev_by_name(tmp_fname); +} + +sng_fd_t _LIBSNG_CALL sangoma_open_api_ctrl(void) +{ + return sangoma_open_dev_by_name(WP_CTRL_DEV_NAME); +} + +#ifdef WP_API_FEATURE_LOGGER +sng_fd_t _LIBSNG_CALL sangoma_logger_open(void) +{ + return sangoma_open_dev_by_name(WP_LOGGER_DEV_NAME); +} +#endif + +int _LIBSNG_CALL sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api) { int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_OPEN_CNT; err=sangoma_cmd_exec(fd,tdm_api); @@ -1279,7 +1801,7 @@ int _SAPI_CALL sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api) return tdm_api->wp_cmd.open_cnt; } -sng_fd_t _SAPI_CALL sangoma_create_socket_by_name(char *device, char *card) +sng_fd_t _LIBSNG_CALL sangoma_create_socket_by_name(char *device, char *card) { int span,chan; sangoma_interface_toi(device,&span,&chan); @@ -1288,7 +1810,7 @@ sng_fd_t _SAPI_CALL sangoma_create_socket_by_name(char *device, char *card) } -sng_fd_t _SAPI_CALL sangoma_open_api_span(int span) +sng_fd_t _LIBSNG_CALL sangoma_open_api_span(int span) { int i=0; sng_fd_t fd = INVALID_HANDLE_VALUE; @@ -1319,18 +1841,21 @@ sng_fd_t _SAPI_CALL sangoma_open_api_span(int span) \param fd device file descriptor \return void */ -void _SAPI_CALL sangoma_close(sng_fd_t *fd) +void _LIBSNG_CALL sangoma_close(sng_fd_t *fd) { + if (!fd) { + return; + } #if defined(__WINDOWS__) - if( *fd != INVALID_HANDLE_VALUE){ + if (*fd != INVALID_HANDLE_VALUE){ CloseHandle(*fd); *fd = INVALID_HANDLE_VALUE; } #else - if (*fd >= 0) { + if (*fd >= 0) { close(*fd); *fd = -1; - } + } #endif } @@ -1340,7 +1865,7 @@ void _SAPI_CALL sangoma_close(sng_fd_t *fd) * Device READ / WRITE Functions ***************************************************************/ -int _SAPI_CALL sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, int datalen, int flag) +int _LIBSNG_CALL sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, int datalen, int flag) { int rx_len=0; @@ -1374,7 +1899,7 @@ int _SAPI_CALL sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *data break; default: /* note that SANG_STATUS_NO_DATA_AVAILABLE is NOT an error! */ - if(0)DBG_ERR("Operation Status: %s(%d)\n", + if(libsng_dbg_level)DBG_ERR("Operation Status: %s(%d)\n", SDLA_DECODE_SANG_STATUS(rx_hdr->operation_status), rx_hdr->operation_status); return -5; } @@ -1407,7 +1932,7 @@ int _SAPI_CALL sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *data return rx_len; } -int _SAPI_CALL sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, unsigned short datalen, int flag) +int _LIBSNG_CALL sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, unsigned short datalen, int flag) { int bsent=-1; wp_api_hdr_t *wp_api_hdr = hdrbuf; @@ -1487,7 +2012,7 @@ int _SAPI_CALL sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *dat * Execute TDM command * */ -int _SAPI_CALL sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api) { int err; @@ -1509,10 +2034,11 @@ int _SAPI_CALL sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api) * Get Full TDM API configuration per channel * */ -int _SAPI_CALL sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_FULL_CFG; err=sangoma_cmd_exec(fd,tdm_api); @@ -1563,16 +2089,12 @@ int _SAPI_CALL sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api) * } * */ -int _SAPI_CALL sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int codec) +int _LIBSNG_CALL sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int codec) { - int err; - + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_CODEC; tdm_api->wp_cmd.tdm_codec = codec; - - err=sangoma_cmd_exec(fd,tdm_api); - - return err; + return sangoma_cmd_exec(fd,tdm_api); } /*======================================================== @@ -1587,10 +2109,11 @@ int _SAPI_CALL sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int co * } * */ -int _SAPI_CALL sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_CODEC; err=sangoma_cmd_exec(fd,tdm_api); @@ -1608,16 +2131,12 @@ int _SAPI_CALL sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api) * 10,20,30,40,50 ms * */ -int _SAPI_CALL sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, int period) +int _LIBSNG_CALL sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, int period) { - int err; - + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_USR_PERIOD; tdm_api->wp_cmd.usr_period = period; - - err=sangoma_cmd_exec(fd,tdm_api); - - return err; + return sangoma_cmd_exec(fd,tdm_api); } /*======================================================== @@ -1627,10 +2146,11 @@ int _SAPI_CALL sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, i * 10,20,30,40,50 ms * */ -int _SAPI_CALL sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_USR_PERIOD; err=sangoma_cmd_exec(fd,tdm_api); @@ -1647,15 +2167,17 @@ int _SAPI_CALL sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api) * Coding Format will be ULAW/ALAW based on T1/E1 */ -int _SAPI_CALL sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api) { - int err; - tdm_api->wp_cmd.cmd = WP_API_CMD_GET_HW_CODING; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - return tdm_api->wp_cmd.hw_tdm_coding; + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_HW_CODING; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + return tdm_api->wp_cmd.hw_tdm_coding; } #ifdef WP_API_FEATURE_DTMF_EVENTS @@ -1665,9 +2187,11 @@ int _SAPI_CALL sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api) * Will return true if HW DTMF is enabled on Octasic */ -int _SAPI_CALL sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api) { int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_HW_DTMF; err=sangoma_cmd_exec(fd,tdm_api); if (err){ @@ -1682,9 +2206,11 @@ int _SAPI_CALL sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api) * Will return true if HW EC is enabled */ -int _SAPI_CALL sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api) { int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_HW_EC; err=sangoma_cmd_exec(fd,tdm_api); if (err){ @@ -1700,10 +2226,11 @@ int _SAPI_CALL sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api) * The USER MTU/MRU values will change each time a PERIOD * or CODEC is adjusted. */ -int _SAPI_CALL sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_USR_MTU_MRU; err=sangoma_cmd_exec(fd,tdm_api); @@ -1720,16 +2247,12 @@ int _SAPI_CALL sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api) * This option is not implemented yet * */ -int _SAPI_CALL sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, int power) +int _LIBSNG_CALL sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, int power) { - int err; - + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_POWER_LEVEL; tdm_api->wp_cmd.power_level = power; - - err=sangoma_cmd_exec(fd,tdm_api); - - return err; + return sangoma_cmd_exec(fd,tdm_api); } /*======================================================== @@ -1738,10 +2261,11 @@ int _SAPI_CALL sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, * This option is not implemented yet * */ -int _SAPI_CALL sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_POWER_LEVEL; err=sangoma_cmd_exec(fd,tdm_api); @@ -1752,71 +2276,62 @@ int _SAPI_CALL sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api) return tdm_api->wp_cmd.power_level; } -int _SAPI_CALL sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) { - int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_FLUSH_BUFFERS; - - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_enable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api, int poll_in_sec) { - - int err; - - tdm_api->wp_cmd.cmd = WP_API_CMD_ENABLE_RBS_EVENTS; - tdm_api->wp_cmd.rbs_poll=poll_in_sec; - - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; -} - - -int _SAPI_CALL sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - tdm_api->wp_cmd.cmd = WP_API_CMD_DISABLE_RBS_EVENTS; - - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; -} - -int _SAPI_CALL sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char rbs) +int _LIBSNG_CALL sangoma_flush_rx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - tdm_api->wp_cmd.cmd = WP_API_CMD_WRITE_RBS_BITS; - tdm_api->wp_cmd.chan = (unsigned char)channel; - tdm_api->wp_cmd.rbs_tx_bits=rbs; - - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_FLUSH_RX_BUFFERS; + return sangoma_cmd_exec(fd,tdm_api); +} - return 0; +int _LIBSNG_CALL sangoma_flush_tx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_FLUSH_TX_BUFFERS; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _LIBSNG_CALL sangoma_flush_event_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_FLUSH_EVENT_BUFFERS; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _LIBSNG_CALL sangoma_tdm_enable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api, int poll_in_sec) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_ENABLE_RBS_EVENTS; + tdm_api->wp_cmd.rbs_poll = poll_in_sec; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _LIBSNG_CALL sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_DISABLE_RBS_EVENTS; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _LIBSNG_CALL sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char rbs) +{ + WANPIPE_API_INIT_CHAN(tdm_api, channel); + tdm_api->wp_cmd.cmd = WP_API_CMD_WRITE_RBS_BITS; + tdm_api->wp_cmd.rbs_tx_bits=rbs; + return sangoma_cmd_exec(fd,tdm_api); } - -int _SAPI_CALL sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char *rbs) +int _LIBSNG_CALL sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char *rbs) { - int err; + WANPIPE_API_INIT_CHAN(tdm_api, channel); tdm_api->wp_cmd.cmd = WP_API_CMD_READ_RBS_BITS; - tdm_api->wp_cmd.chan = (unsigned char)channel; tdm_api->wp_cmd.rbs_tx_bits=0; err=sangoma_cmd_exec(fd,tdm_api); @@ -1825,17 +2340,17 @@ int _SAPI_CALL sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int cha } *rbs=(unsigned char)tdm_api->wp_cmd.rbs_rx_bits; - return 0; } -int _SAPI_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api) { #ifdef WP_API_FEATURE_EVENTS wp_api_event_t *rx_event; int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_READ_EVENT; err=sangoma_cmd_exec(fd,tdm_api); @@ -1845,7 +2360,6 @@ int _SAPI_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api) rx_event = &tdm_api->wp_cmd.event; - /* The use of callbacks here is purely optional and is left here for backward compatibility purposes. By default user @@ -1859,7 +2373,6 @@ int _SAPI_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api) if (tdm_api->wp_callback.wp_rbs_event) { tdm_api->wp_callback.wp_rbs_event(fd,rx_event->wp_api_event_rbs_bits); } - break; #ifdef WP_API_FEATURE_DTMF_EVENTS @@ -1920,9 +2433,9 @@ int _SAPI_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api) #endif default: #ifdef __WINDOWS__ - printf("libsangoma: %s fd=0x%p: Unknown TDM event!", __FUNCTION__,fd); + if(0)printf("Warning: libsangoma: %s fd=0x%p: Unknown TDM event!", __FUNCTION__,fd); #else - printf("libsangoma: %s fd=%d: Unknown TDM event!", __FUNCTION__, fd); + if(0)printf("Warning: libsangoma: %s fd=%d: Unknown TDM event!", __FUNCTION__, fd); #endif break; } @@ -1932,314 +2445,283 @@ int _SAPI_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api) printf("Error: Read Event not supported!\n"); return -1; #endif -} +} + + +#ifdef WP_API_FEATURE_LOGGER +/*======================================================== + * Execute Wanpipe Logger command + */ +sangoma_status_t _LIBSNG_CALL sangoma_logger_cmd_exec(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ +#if defined(__WINDOWS__) + return logger_api_ioctl(fd, logger_cmd); +#else + int err = ioctl(fd,WANPIPE_IOCTL_LOGGER_CMD,logger_cmd); + if (err < 0){ + char tmp[50]; + sprintf(tmp,"Logger CMD: %i\n",logger_cmd->cmd); + perror(tmp); + return -1; + } + return err; +#endif +} + +sangoma_status_t _LIBSNG_CALL sangoma_logger_read_event(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_READ_EVENT; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _LIBSNG_CALL sangoma_logger_flush_buffers(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_FLUSH_BUFFERS; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _LIBSNG_CALL sangoma_logger_get_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_GET_STATS; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _LIBSNG_CALL sangoma_logger_reset_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_RESET_STATS; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _LIBSNG_CALL sangoma_logger_get_open_handle_counter(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_OPEN_CNT; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _LIBSNG_CALL sangoma_logger_get_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_GET_LOGGER_LEVEL; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +sangoma_status_t _LIBSNG_CALL sangoma_logger_set_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) +{ + logger_cmd->cmd = WP_API_LOGGER_CMD_SET_LOGGER_LEVEL; + return sangoma_logger_cmd_exec(fd, logger_cmd); +} + +#endif /* WP_API_FEATURE_LOGGER */ + +#ifdef WP_API_FEATURE_FAX_EVENTS +int _LIBSNG_CALL sangoma_tdm_enable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_FAX_DETECT; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _LIBSNG_CALL sangoma_tdm_disable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_FAX_DETECT; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; + return sangoma_cmd_exec(fd,tdm_api); +} + +int _LIBSNG_CALL sangoma_tdm_get_hw_fax(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_GET_HW_FAX_DETECT; + err=sangoma_cmd_exec(fd,tdm_api); + if (err){ + return err; + } + return tdm_api->wp_cmd.hw_fax; +} +#endif #ifdef WP_API_FEATURE_DTMF_EVENTS -int _SAPI_CALL sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - int err; - + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_DTMF; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - int err; - + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_DTMF; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } - -int _SAPI_CALL sangoma_tdm_enable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_enable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - int err; - + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RM_DTMF; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - int err; - + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RM_DTMF; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - int err; - + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RXHOOK; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - int err; - + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RXHOOK; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; - - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } - -int _SAPI_CALL sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; - - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_enable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_enable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING_DETECT; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return err; + return sangoma_cmd_exec(fd,tdm_api); } - -int _SAPI_CALL sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING_DETECT; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING_TRIP_DETECT; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return tdm_api->wp_cmd.rbs_poll; + return sangoma_cmd_exec(fd,tdm_api); } - -int _SAPI_CALL sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; - tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING_DETECT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_RING_TRIP_DETECT; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TXSIG_KEWL; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TXSIG_START; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TXSIG_ONHOOK; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TXSIG_OFFHOOK; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + return sangoma_cmd_exec(fd,tdm_api); } - -int _SAPI_CALL sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api, uint16_t tone_id) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api, uint16_t tone_id) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TONE; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; tdm_api->wp_cmd.event.wp_api_event_tone_type = tone_id; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return tdm_api->wp_cmd.rbs_poll; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api) { - - int err; - +int _LIBSNG_CALL sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_TONE; tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_DISABLE; tdm_api->wp_cmd.event.wp_api_event_tone_type = 0x00; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return tdm_api->wp_cmd.rbs_poll; + return sangoma_cmd_exec(fd,tdm_api); } - #endif -int _SAPI_CALL sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) { - int err; - - tdm_api->wp_cmd.cmd = WP_API_CMD_ENABLE_HWEC; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_ENABLE_HWEC; + return sangoma_cmd_exec(fd,tdm_api); } -int _SAPI_CALL sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) { - int err; - - tdm_api->wp_cmd.cmd = WP_API_CMD_DISABLE_HWEC; - err=sangoma_cmd_exec(fd,tdm_api); - if (err){ - return err; - } - - return 0; + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_DISABLE_HWEC; + return sangoma_cmd_exec(fd,tdm_api); } @@ -2248,10 +2730,11 @@ int _SAPI_CALL sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) * */ #ifdef WP_API_FEATURE_FE_ALARM -int _SAPI_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms) +int _LIBSNG_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_FE_ALARMS; err=sangoma_cmd_exec(fd,tdm_api); @@ -2265,10 +2748,11 @@ int _SAPI_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, un } /* get current Line Connection state - Connected/Disconnected */ -int _SAPI_CALL sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status) +int _LIBSNG_CALL sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_FE_STATUS; err = sangoma_cmd_exec(fd, tdm_api); *current_status = tdm_api->wp_cmd.fe_status; @@ -2279,10 +2763,11 @@ int _SAPI_CALL sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsign /* get current Line Connection state - Connected/Disconnected */ #ifdef WP_API_FEATURE_LINK_STATUS -int _SAPI_CALL sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status) +int _LIBSNG_CALL sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_FE_STATUS; err = sangoma_cmd_exec(fd, tdm_api); *current_status = tdm_api->wp_cmd.fe_status; @@ -2291,17 +2776,18 @@ int _SAPI_CALL sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsi } /* set current Line Connection state - Connected/Disconnected. valid only for ISDN BRI */ -int _SAPI_CALL sangoma_set_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char new_status) +int _LIBSNG_CALL sangoma_set_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char new_status) { + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_FE_STATUS; tdm_api->wp_cmd.fe_status = new_status; - return sangoma_cmd_exec(fd, tdm_api); } #endif -int _SAPI_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) +int _LIBSNG_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) { + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.channel = (unsigned char)channel; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_BRI_CHAN_LOOPBACK; @@ -2309,8 +2795,9 @@ int _SAPI_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *td return sangoma_cmd_exec(fd, tdm_api); } -int _SAPI_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) +int _LIBSNG_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) { + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; tdm_api->wp_cmd.event.channel = (unsigned char)channel; tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_BRI_CHAN_LOOPBACK; @@ -2318,11 +2805,11 @@ int _SAPI_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm return sangoma_cmd_exec(fd, tdm_api); } - -int _SAPI_CALL sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_TX_Q_SIZE; tdm_api->wp_cmd.tx_queue_sz = 0; @@ -2334,22 +2821,22 @@ int _SAPI_CALL sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) return tdm_api->wp_cmd.tx_queue_sz; } -int _SAPI_CALL sangoma_set_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) +int _LIBSNG_CALL sangoma_set_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) { if (size < 0) { return -1; } - + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_TX_Q_SIZE; tdm_api->wp_cmd.tx_queue_sz = size; - return sangoma_cmd_exec(fd, tdm_api); } -int _SAPI_CALL sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_RX_Q_SIZE; tdm_api->wp_cmd.rx_queue_sz = 0; @@ -2362,23 +2849,23 @@ int _SAPI_CALL sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) } -int _SAPI_CALL sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) +int _LIBSNG_CALL sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) { if (size < 0) { return -1; } + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_SET_RX_Q_SIZE; tdm_api->wp_cmd.rx_queue_sz = size; - return sangoma_cmd_exec(fd, tdm_api); - } -int _SAPI_CALL sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, wan_driver_version_t *drv_ver) +int _LIBSNG_CALL sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, wan_driver_version_t *drv_ver) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_DRIVER_VERSION; err = sangoma_cmd_exec(fd, tdm_api); @@ -2395,10 +2882,11 @@ int _SAPI_CALL sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, w return err; } -int _SAPI_CALL sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) +int _LIBSNG_CALL sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_FIRMWARE_VERSION; err = sangoma_cmd_exec(fd, tdm_api); @@ -2413,11 +2901,11 @@ int _SAPI_CALL sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, return err; } - -int _SAPI_CALL sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) +int _LIBSNG_CALL sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_CPLD_VERSION; err = sangoma_cmd_exec(fd, tdm_api); @@ -2432,41 +2920,109 @@ int _SAPI_CALL sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, uns return err; } -int _SAPI_CALL sangoma_get_stats(sng_fd_t fd, wanpipe_api_t *tdm_api, wanpipe_chan_stats_t *stats) +int _LIBSNG_CALL sangoma_get_stats(sng_fd_t fd, wanpipe_api_t *tdm_api, wanpipe_chan_stats_t *stats) { int err; + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_GET_STATS; err = sangoma_cmd_exec(fd, tdm_api); if (err == 0) { if (stats) { - memcpy(stats,&tdm_api->wp_cmd.stats,sizeof(wanpipe_chan_stats_t)); + memcpy(stats, &tdm_api->wp_cmd.stats, sizeof(wanpipe_chan_stats_t)); } } return err; } -int _SAPI_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api) +int _LIBSNG_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api) { + WANPIPE_API_INIT_CHAN(tdm_api, 0); tdm_api->wp_cmd.cmd = WP_API_CMD_RESET_STATS; return sangoma_cmd_exec(fd, tdm_api); } -int _SAPI_CALL sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, int rxflashtime) +int _LIBSNG_CALL sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, int rxflashtime) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_RM_RXFLASHTIME; + tdm_api->wp_cmd.rxflashtime=rxflashtime; + return sangoma_cmd_exec(fd, tdm_api); +} + +#ifdef WP_API_FEATURE_RM_GAIN +int _LIBSNG_CALL sangoma_set_rm_tx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_SET_RM_TX_GAIN; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + tdm_api->wp_cmd.event.wp_api_event_gain_value = value; + return sangoma_cmd_exec(fd, tdm_api); +} + +int _LIBSNG_CALL sangoma_set_rm_rx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_SET_RM_RX_GAIN; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + tdm_api->wp_cmd.event.wp_api_event_gain_value = value; + return sangoma_cmd_exec(fd, tdm_api); +} +#endif /* WP_API_FEATURE_RM_GAIN */ + +int _LIBSNG_CALL sangoma_tdm_set_polarity(sng_fd_t fd, wanpipe_api_t *tdm_api, int polarity) { int err; - tdm_api->wp_cmd.cmd = WP_API_CMD_SET_RM_RXFLASHTIME; - tdm_api->wp_cmd.rxflashtime=rxflashtime; + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_SETPOLARITY; + tdm_api->wp_cmd.event.wp_api_event_polarity = polarity; err = sangoma_cmd_exec(fd, tdm_api); return err; + } -#ifndef LIBSANGOMA_LIGHT +int _LIBSNG_CALL sangoma_tdm_txsig_onhooktransfer(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_SET_EVENT; + tdm_api->wp_cmd.event.wp_api_event_type = WP_API_EVENT_ONHOOKTRANSFER; + tdm_api->wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + return sangoma_cmd_exec(fd,tdm_api); +} +#ifdef WP_API_FEATURE_LOOP + +int _LIBSNG_CALL sangoma_tdm_enable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_ENABLE_LOOP; + err = sangoma_cmd_exec(fd, tdm_api); + return err; +} + +int _LIBSNG_CALL sangoma_tdm_disable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api) +{ + int err; + + WANPIPE_API_INIT_CHAN(tdm_api, 0); + tdm_api->wp_cmd.cmd = WP_API_CMD_DISABLE_LOOP; + err = sangoma_cmd_exec(fd, tdm_api); + return err; +} + +#endif + + +#ifndef LIBSANGOMA_LIGHT /************************************************************//** * Device PORT Control Functions @@ -2487,7 +3043,7 @@ static int sangoma_port_mgmnt_ioctl(sng_fd_t fd, port_management_struct_t *port_ (LPDWORD)(&ln), (LPOVERLAPPED)NULL ) == FALSE){ - /* Call Call OS specific code to find cause of the error and check messages log. */ + /* Call OS specific code to find cause of the error and check messages log. */ DBG_ERR("%s():Error: IoctlPortManagementCommand failed!!\n", __FUNCTION__); err = -1; } @@ -2519,7 +3075,7 @@ static int sangoma_port_cfg_ioctl(sng_fd_t fd, port_cfg_t *port_cfg) (LPDWORD)(&ln), (LPOVERLAPPED)NULL ) == FALSE){ - /* Call Call OS specific code to find cause of the error and check messages log. */ + /* Call OS specific code to find cause of the error and check messages log. */ DBG_ERR("%s():Error: IoctlPortConfigurationCommand failed!!\n", __FUNCTION__); err = -1; } @@ -2537,60 +3093,50 @@ static int sangoma_port_cfg_ioctl(sng_fd_t fd, port_cfg_t *port_cfg) } /* open wanpipe configuration device */ -sng_fd_t _SAPI_CALL sangoma_open_driver_ctrl(int port_no) +sng_fd_t _LIBSNG_CALL sangoma_open_driver_ctrl(int port_no) { - char fname[FNAME_LEN], tmp_fname[FNAME_LEN]; + char tmp_fname[FNAME_LEN]; #if defined(__WINDOWS__) /* Form the Config Device Name (i.e. wanpipe1, wanpipe2,...). */ _snprintf(tmp_fname, DEV_NAME_LEN, WP_PORT_NAME_FORM, port_no); - _snprintf(fname, FNAME_LEN, "\\\\.\\%s", tmp_fname); - - return CreateFile( fname, - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - (LPSECURITY_ATTRIBUTES)NULL, - OPEN_EXISTING, - FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, - (HANDLE)NULL - ); #else /* Form the Config Device Name. ("/dev/wanpipe") */ _snprintf(tmp_fname, DEV_NAME_LEN, WP_CONFIG_DEV_NAME); - - sprintf(fname,"/dev/%s", tmp_fname); - - return open(fname, O_RDWR); #endif + return sangoma_open_dev_by_name(tmp_fname); } - -int _SAPI_CALL sangoma_mgmt_cmd(sng_fd_t fd, wan_udp_hdr_t* wan_udp) +int _LIBSNG_CALL sangoma_mgmt_cmd(sng_fd_t fd, wan_udp_hdr_t* wan_udp) { + int err=0; #if defined(__WINDOWS__) if(UdpManagementCommand(fd, wan_udp)){ - return 1; + err = 1; } #else unsigned char id = 0; - int err=0; + wan_udp->wan_udphdr_request_reply = 0x01; wan_udp->wan_udphdr_id = id; wan_udp->wan_udphdr_return_code = WAN_UDP_TIMEOUT_CMD; err=ioctl(fd,WANPIPE_IOCTL_PIPEMON,wan_udp); if (err < 0) { - return 1; + err = 1; } #endif - - if(wan_udp->wan_udphdr_return_code != WAN_CMD_OK){ - return 2; + if(err){ + /* The ioctl failed. */ + return err; } + + /* The ioctl was successfull. The caller must check + * value of wan_udp->wan_udphdr_return_code. */ return 0; } -int _SAPI_CALL sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) +int _LIBSNG_CALL sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) { int err; port_mgmnt->command_code = START_PORT_VOLATILE_CONFIG; @@ -2605,7 +3151,7 @@ int _SAPI_CALL sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t * return port_mgmnt->operation_status; } -int _SAPI_CALL sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) +int _LIBSNG_CALL sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) { int err; port_mgmnt->command_code = STOP_PORT; @@ -2633,7 +3179,7 @@ int _SAPI_CALL sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *p return err; } -int _SAPI_CALL sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) +int _LIBSNG_CALL sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no) { int err; port_mgmnt->command_code = GET_HARDWARE_INFO; @@ -2647,7 +3193,7 @@ int _SAPI_CALL sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t return port_mgmnt->operation_status; } -int _SAPI_CALL sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no) +int _LIBSNG_CALL sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no) { port_cfg->command_code = SET_PORT_VOLATILE_CONFIG; port_cfg->port_no = port_no; @@ -2655,22 +3201,20 @@ int _SAPI_CALL sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, return sangoma_port_cfg_ioctl(fd, port_cfg); } -int _SAPI_CALL sangoma_driver_port_get_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no) +int _LIBSNG_CALL sangoma_driver_port_get_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no) { - port_cfg->command_code = GET_PORT_VOLATILE_CONFIG; - port_cfg->port_no = port_no; - + port_cfg->port_no = port_no; return sangoma_port_cfg_ioctl(fd, port_cfg); } -int _SAPI_CALL sangoma_write_port_config_on_persistent_storage(hardware_info_t *hardware_info, port_cfg_t *port_cfg, unsigned short port_no) +int _LIBSNG_CALL sangoma_write_port_config_on_persistent_storage(hardware_info_t *hardware_info, port_cfg_t *port_cfg, unsigned short port_no) { int err = 0; #if defined(__WINDOWS__) HKEY hPortRegistryKey = registry_open_port_key(hardware_info); - wandev_conf_t *wandev_conf = &port_cfg->wandev_conf; - sdla_fe_cfg_t *sdla_fe_cfg = &wandev_conf->fe_cfg; +/* wandev_conf_t *wandev_conf = &port_cfg->wandev_conf; + sdla_fe_cfg_t *sdla_fe_cfg = &wandev_conf->fe_cfg;*/ unsigned int ind; if(hPortRegistryKey == INVALID_HANDLE_VALUE){ @@ -2682,9 +3226,17 @@ int _SAPI_CALL sangoma_write_port_config_on_persistent_storage(hardware_info_t * return 2; } - /* write number of groups */ - registry_set_integer_value(hPortRegistryKey, "aft_number_of_logic_channels", port_cfg->num_of_ifs); + /* write TDM Voice configuration */ + if(registry_write_wan_tdmv_conf(hPortRegistryKey, port_cfg)){ + return 3; + } + /* write number of groups */ + err = registry_set_integer_value(hPortRegistryKey, "aft_number_of_logic_channels", port_cfg->num_of_ifs); + if(err){ + return err; + } + /* write configuration of each group */ for(ind = 0; ind < port_cfg->num_of_ifs; ind++){ registry_write_channel_group_cfg(hPortRegistryKey, port_cfg, ind, port_cfg->if_cfg[ind]); @@ -2696,7 +3248,6 @@ int _SAPI_CALL sangoma_write_port_config_on_persistent_storage(hardware_info_t * #endif return err; } - #endif /* #ifndef LIBSANGOMA_LIGHT */ #endif /* WANPIPE_TDM_API */ diff --git a/api/libsangoma/libsangoma.def b/api/libsangoma/libsangoma.def index 88a3fe3..213664f 100644 --- a/api/libsangoma/libsangoma.def +++ b/api/libsangoma/libsangoma.def @@ -1,83 +1,2 @@ LIBRARY libsangoma - EXPORTS - sangoma_open_api_span - sangoma_span_chan_toif - sangoma_span_chan_fromif - sangoma_interface_toi - sangoma_create_socket_by_name - sangoma_open_api_span_chan - sangoma_open_api_span - sangoma_writemsg - sangoma_readmsg - sangoma_close - sangoma_get_full_cfg - sangoma_tdm_set_codec - sangoma_tdm_get_codec - sangoma_tdm_set_usr_period - sangoma_tdm_get_usr_period - sangoma_tdm_get_usr_mtu_mru - sangoma_tdm_set_power_level - sangoma_tdm_get_power_level - sangoma_flush_bufs - sangoma_tdm_enable_rbs_events - sangoma_tdm_disable_rbs_events - sangoma_tdm_write_rbs - sangoma_read_event - sangoma_tdm_enable_dtmf_events - sangoma_tdm_disable_dtmf_events - sangoma_tdm_enable_rm_dtmf_events - sangoma_tdm_disable_rm_dtmf_events - sangoma_tdm_enable_rxhook_events - sangoma_tdm_disable_rxhook_events - sangoma_tdm_enable_ring_events - sangoma_tdm_disable_ring_events - sangoma_tdm_enable_ring_detect_events - sangoma_tdm_disable_ring_detect_events - sangoma_tdm_enable_ring_trip_detect_events - sangoma_tdm_disable_ring_trip_detect_events - sangoma_tdm_enable_tone_events - sangoma_tdm_disable_tone_events - sangoma_tdm_txsig_onhook - sangoma_tdm_txsig_offhook - sangoma_tdm_txsig_start - sangoma_tdm_txsig_kewl - sangoma_get_hw_coding - sangoma_waitfor - sangoma_waitfor_many - sangoma_cmd_exec - sangoma_mgmt_cmd - __sangoma_open_api_span_chan - sangoma_get_tx_queue_sz - sangoma_get_rx_queue_sz - sangoma_get_driver_version - sangoma_get_firmware_version - sangoma_get_cpld_version - sangoma_get_stats - sangoma_flush_stats - sangoma_set_tx_queue_sz - sangoma_set_rx_queue_sz - sangoma_set_rm_rxflashtime - sangoma_open_api_ctrl - sangoma_open_driver_ctrl - sangoma_driver_port_start - sangoma_driver_port_stop - sangoma_driver_port_set_config - sangoma_driver_port_get_config - sangoma_driver_get_hw_info - sangoma_tdm_read_rbs - sangoma_get_open_cnt - sangoma_enable_bri_bchan_loopback - sangoma_disable_bri_bchan_loopback - sangoma_set_fe_status - sangoma_get_fe_status - sangoma_tdm_get_hw_dtmf - sangoma_tdm_get_fe_alarms - sangoma_wait_obj_create - sangoma_wait_obj_delete - sangoma_wait_obj_signal - sangoma_wait_obj_get_fd - sangoma_wait_obj_set_context - sangoma_wait_obj_get_context - sangoma_cdev_ctrl_cmd - sangoma_write_port_config_on_persistent_storage diff --git a/api/libsangoma/libsangoma.h b/api/libsangoma/libsangoma.h index c477710..58fecf9 100644 --- a/api/libsangoma/libsangoma.h +++ b/api/libsangoma/libsangoma.h @@ -3,10 +3,10 @@ * \brief Wanpipe API Library header for Sangoma AFT T1/E1/Analog/BRI/Serial Hardware - * \brief Provides User a Unified/OS Agnostic API to Wanpipe/Sangoma Drivers and Hardware * - * Author(s): Nenad Corbic + * Author(s): Nenad Corbic * David Rokhvarg * Michael Jerris - * Anthony Minessale II + * Anthony Minessale II * * Copyright: (c) 2005-2008 Nenad Corbic * @@ -40,6 +40,13 @@ #ifndef _LIBSNAGOMA_H #define _LIBSNAGOMA_H +#ifdef __linux__ +#ifndef __LINUX__ +/* most wanpipe driver headers require __LINUX__ to be defined */ +#define __LINUX__ +#endif +#endif + #ifdef __cplusplus extern "C" { /* for C++ users */ #endif @@ -65,20 +72,30 @@ extern "C" { /* for C++ users */ \def LIBSANGOMA_VERSION_CODE \brief LibSangoma Current Version Number to be checked against the LIBSANGOMA_VERSION Macro */ -#define LIBSANGOMA_VERSION_CODE LIBSANGOMA_VERSION(3,2,0) +#define LIBSANGOMA_VERSION_CODE LIBSANGOMA_VERSION(3,3,0) /*! \def LIBSANGOMA_VERSION_STR \brief LibSangoma Version in string format */ -#define LIBSANGOMA_VERSION_STR "3.2.0" +#define LIBSANGOMA_VERSION_STR "3.3.0" + +struct sangoma_wait_obj; +#define sangoma_wait_obj_t struct sangoma_wait_obj + + +/*! + \def SANGOMA_DECLARE_TDM_API_CMD + \brief Instantiate/Declare a tdm api cmd strucure + \def SANGOMA_INIT_TDM_API_CMD + \brief Initialize the tdm api cmd structure. Set to 0. + \def SANGOMA_DECLARE_INIT_TDM_API_CMD + \brief Declare and initialize the tdm api cmd structure. +*/ +#define SANGOMA_DECLARE_TDM_API_CMD(_name_) wanpipe_api_t _name_ +#define SANGOMA_INIT_TDM_API_CMD(_name_) memset(&_name_,0,sizeof(_name_)) +#define SANGOMA_DECLARE_INIT_TDM_API_CMD(_name_) wanpipe_tdm_api_t _name_; SANGOMA_INIT_TDM_API_CMD(_name_) -#ifdef __COMPILING_LIBSANGOMA__ - struct sangoma_wait_obj; - #define sangoma_wait_obj_t struct sangoma_wait_obj -#else - typedef void sangoma_wait_obj_t; -#endif #if defined(WIN32) || defined(WIN64) #ifndef __WINDOWS__ @@ -91,10 +108,14 @@ extern "C" { /* for C++ users */ #include /*! - \def _SAPI_CALL + \def _LIBSNG_CALL \brief libsangoma.dll functions exported as __cdecl calling convention */ -#define _SAPI_CALL __cdecl +#ifdef __COMPILING_LIBSANGOMA__ +# define _LIBSNG_CALL __declspec(dllexport) __cdecl +#else +# define _LIBSNG_CALL __declspec(dllimport) __cdecl +#endif /*! \def SANGOMA_INFINITE_API_POLL_WAIT (deprecated, use SANGOMA_WAIT_INFINITE instead) @@ -109,6 +130,13 @@ extern "C" { /* for C++ users */ */ #define sangoma_msleep(x) Sleep(x) +/*! + \def sangoma_ctime(time_val) + \brief Convert a time value to a string + \param time_val pointer to 64-bit time value +*/ +#define sangoma_ctime(time) _ctime64(time) + #else /* L I N U X */ #include @@ -135,10 +163,10 @@ extern "C" { /* for C++ users */ #include /*! - \def _SAPI_CALL + \def _LIBSNG_CALL \brief Not used in Linux */ -#define _SAPI_CALL +#define _LIBSNG_CALL /*! \def INVALID_HANDLE_VALUE @@ -184,6 +212,8 @@ extern "C" { /* for C++ users */ \brief LPSTR type mapped to unsigned char *, Ported from Windows \typedef PUCHAR \brief PUCHAR type mapped to unsigned char *, Ported from Windows + \typedef PVOID + \brief PVOID type mapped to void *, Ported from Windows \typedef LPTHREAD_START_ROUTINE \brief LPTHREAD_START_ROUTINE type mapped to unsigned char *, Ported from Windows \def _stricmp @@ -218,6 +248,7 @@ typedef unsigned long ULONG; typedef unsigned short USHORT; typedef unsigned char * LPSTR; typedef unsigned char * PUCHAR; +typedef void * PVOID; typedef void * LPTHREAD_START_ROUTINE; typedef pthread_mutex_t CRITICAL_SECTION; @@ -227,7 +258,14 @@ typedef pthread_mutex_t CRITICAL_SECTION; typedef struct tm SYSTEMTIME; typedef char * LPCTSTR; - + +/*! + \def sangoma_ctime(time_val) + \brief Convert a time value to a string + \param time_val pointer to time value +*/ +#define sangoma_ctime(time) ctime((time_t*)time) + #endif/* WIN32 */ @@ -277,8 +315,8 @@ typedef int32_t sangoma_status_t; \brief debug print function */ #define FNAME_LEN 100 -#define FUNC_DBG(x) if(0)printf("%s():%d\n", x, __LINE__) -#define DBG_PRINT if(1)printf +#define LIBSNG_FUNC_DBG() if(0)printf("%s(): line:%d\n", __FUNCTION__, __LINE__) +#define LIBSNG_DBG_PRINT if(0)printf /*! \typedef sangoma_api_hdr_t @@ -310,6 +348,19 @@ typedef enum _sangoma_wait_obj_type type == SANGOMA_DEVICE_WAIT_OBJ_SIG ? "SANGOMA_DEVICE_WAIT_OBJ_SIG" :\ "Invalid Wait Object type!" +/* + * Possible flags for sangoma waitable objects + * this definitions MUST match POLLIN, POLLOUT and POLLPRI + * SANG_WAIT_OBJ_IS_SIGNALED has no posix equivalent though + * Users are encouraged to use this flags instead of the system ones + */ +typedef enum _sangoma_wait_obj_flags { + SANG_WAIT_OBJ_HAS_INPUT = POLLIN, + SANG_WAIT_OBJ_HAS_OUTPUT = POLLOUT, + SANG_WAIT_OBJ_HAS_EVENTS = POLLPRI, + SANG_WAIT_OBJ_IS_SIGNALED = 0x400 /* matches GNU extension POLLMSG */ +} sangoma_wait_obj_flags_t; + /************************************************************//** * Device OPEN / CLOSE Functions ***************************************************************/ @@ -323,7 +374,7 @@ typedef enum _sangoma_wait_obj_type Restriced open, device will allowed to be open only once. */ -sng_fd_t _SAPI_CALL sangoma_open_api_span_chan(int span, int chan); +sng_fd_t _LIBSNG_CALL sangoma_open_api_span_chan(int span, int chan); /*! @@ -335,7 +386,7 @@ sng_fd_t _SAPI_CALL sangoma_open_api_span_chan(int span, int chan); Unrestriced open, allows mutiple open calls on a single device */ -sng_fd_t _SAPI_CALL __sangoma_open_api_span_chan(int span, int chan); +sng_fd_t _LIBSNG_CALL __sangoma_open_api_span_chan(int span, int chan); #define __sangoma_open_tdmapi_span_chan __sangoma_open_api_span_chan /*! @@ -346,7 +397,7 @@ sng_fd_t _SAPI_CALL __sangoma_open_api_span_chan(int span, int chan); Unrestriced open, allows mutiple open calls on a single device */ -sng_fd_t _SAPI_CALL sangoma_open_api_span(int span); +sng_fd_t _LIBSNG_CALL sangoma_open_api_span(int span); /*! @@ -372,8 +423,17 @@ sng_fd_t _SAPI_CALL sangoma_open_api_span(int span); The global control device receives events for all devices configured. */ -sng_fd_t _SAPI_CALL sangoma_open_api_ctrl(void); +sng_fd_t _LIBSNG_CALL sangoma_open_api_ctrl(void); +/*! + \fn sng_fd_t sangoma_logger_open(void) + \brief Open a Global Logger Device + \return File Descriptor - negative=error 0 or greater = fd + + The global Logger device receives Logger Events for all devices + configured. +*/ +sng_fd_t _LIBSNG_CALL sangoma_logger_open(void); /*! \fn sng_fd_t sangoma_open_driver_ctrl(int port_no) @@ -383,7 +443,7 @@ sng_fd_t _SAPI_CALL sangoma_open_api_ctrl(void); The global control device receives events for all devices configured. */ -sng_fd_t _SAPI_CALL sangoma_open_driver_ctrl(int port_no); +sng_fd_t _LIBSNG_CALL sangoma_open_driver_ctrl(int port_no); @@ -394,7 +454,7 @@ sng_fd_t _SAPI_CALL sangoma_open_driver_ctrl(int port_no); \return void */ -void _SAPI_CALL sangoma_close(sng_fd_t *fd); +void _LIBSNG_CALL sangoma_close(sng_fd_t *fd); @@ -406,7 +466,7 @@ void _SAPI_CALL sangoma_close(sng_fd_t *fd); \return negative or 0: error, greater than 1 : open count */ -int _SAPI_CALL sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api); /************************************************************//** @@ -428,7 +488,7 @@ int _SAPI_CALL sangoma_get_open_cnt(sng_fd_t fd, wanpipe_api_t *tdm_api); variable to identify the reason of an error. Please refer to the error codes. */ -int _SAPI_CALL sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, +int _LIBSNG_CALL sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, unsigned short datalen, int flag); @@ -446,7 +506,7 @@ int _SAPI_CALL sangoma_writemsg(sng_fd_t fd, void *hdrbuf, int hdrlen, In case of error return code, one must check the header operation_status variable to identify the reason of error. Please refer to the error codes. */ -int _SAPI_CALL sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, +int _LIBSNG_CALL sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, void *databuf, int datalen, int flag); @@ -457,16 +517,16 @@ int _SAPI_CALL sangoma_readmsg(sng_fd_t fd, void *hdrbuf, int hdrlen, ***************************************************************/ /*! - \fn sangoma_status_t _SAPI_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, int32_t inflags, int32_t *outflags, int32_t timeout) + \fn sangoma_status_t _LIBSNG_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, int32_t inflags, int32_t *outflags, int32_t timeout) \brief Wait for a single waitable object - \param sangoma_wait_obj pointer to array of file descriptors to wait for + \param sangoma_wait_obj pointer to a wait object previously created with sangoma_wait_obj_create \param timeout timeout in miliseconds in case of no event \return SANG_STATUS_APIPOLL_TIMEOUT: timeout, SANG_STATUS_SUCCESS: event occured use sangoma_wait_obj_get_out_flags to check or error status */ -sangoma_status_t _SAPI_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, uint32_t inflags, uint32_t *outflags, int32_t timeout); +sangoma_status_t _LIBSNG_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj, uint32_t inflags, uint32_t *outflags, int32_t timeout); /*! - \fn sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], int32_t in_flags[], int32_t out_flags[] uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout); + \fn sangoma_status_t _LIBSNG_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], int32_t in_flags[], int32_t out_flags[] uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout); \brief Wait for multiple sangoma waitable objects \param sangoma_wait_objects pointer to array of wait objects previously created with sangoma_wait_obj_create \param in_flags array of flags corresponding to the flags the user is interested on for each waitable object @@ -475,7 +535,7 @@ sangoma_status_t _SAPI_CALL sangoma_waitfor(sangoma_wait_obj_t *sangoma_wait_obj \param system_wait_timeout timeout in miliseconds in case of no event \return negative: SANG_STATUS_APIPOLL_TIMEOUT: timeout, SANG_STATUS_SUCCESS: event occured check in sangoma_wait_objects, any other return code is some error */ -sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], uint32_t in_flags[], uint32_t out_flags[], +sangoma_status_t _LIBSNG_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wait_objects[], uint32_t in_flags[], uint32_t out_flags[], uint32_t number_of_sangoma_wait_objects, int32_t system_wait_timeout); /*! @@ -487,7 +547,7 @@ sangoma_status_t _SAPI_CALL sangoma_waitfor_many(sangoma_wait_obj_t *sangoma_wai \see sangoma_wait_obj_type_t \return SANG_STATUS_SUCCESS: success, or error status */ -sangoma_status_t _SAPI_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma_wait_object, sng_fd_t fd, sangoma_wait_obj_type_t object_type); +sangoma_status_t _LIBSNG_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma_wait_object, sng_fd_t fd, sangoma_wait_obj_type_t object_type); /*! \fn sangoma_status_t sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object) @@ -495,7 +555,7 @@ sangoma_status_t _SAPI_CALL sangoma_wait_obj_create(sangoma_wait_obj_t **sangoma \param sangoma_wait_object pointer to a pointer to a single device object \return SANG_STATUS_SUCCESS on success or some sangoma status error */ -sangoma_status_t _SAPI_CALL sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object); +sangoma_status_t _LIBSNG_CALL sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma_wait_object); /*! \fn void sangoma_wait_obj_signal(sangoma_wait_obj_t *sangoma_wait_object) @@ -503,7 +563,7 @@ sangoma_status_t _SAPI_CALL sangoma_wait_obj_delete(sangoma_wait_obj_t **sangoma \param sangoma_wait_object pointer a single device object that can be signaled \return sangoma_status_t */ -sangoma_status_t _SAPI_CALL sangoma_wait_obj_signal(sangoma_wait_obj_t *sangoma_wait_object); +sangoma_status_t _LIBSNG_CALL sangoma_wait_obj_signal(sangoma_wait_obj_t *sangoma_wait_object); /*! \fn sng_fd_t sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sangoma_wait_object) @@ -511,7 +571,7 @@ sangoma_status_t _SAPI_CALL sangoma_wait_obj_signal(sangoma_wait_obj_t *sangoma_ \param sangoma_wait_object pointer a single device object \return sng_fd_t - device file descriptor */ -sng_fd_t _SAPI_CALL sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sangoma_wait_object); +sng_fd_t _LIBSNG_CALL sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sangoma_wait_object); /*! \fn void sangoma_wait_obj_set_context(sangoma_wait_obj_t *sangoma_wait_object) @@ -521,15 +581,16 @@ sng_fd_t _SAPI_CALL sangoma_wait_obj_get_fd(sangoma_wait_obj_t *sangoma_wait_obj \param context void pointer to user context \return void */ -void _SAPI_CALL sangoma_wait_obj_set_context(sangoma_wait_obj_t *sangoma_wait_object, void *context); +void _LIBSNG_CALL sangoma_wait_obj_set_context(sangoma_wait_obj_t *sangoma_wait_object, void *context); /*! - \fn void *sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object) + \fn PVOID sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object) \brief Retrieve the user context (if any) that was set via sangoma_wait_obj_set_context. + \brief Windows note: must use return type PVOID instead of void* to satisfy WDK compiler. \param sangoma_wait_object pointer a single device object \return void* */ -void* _SAPI_CALL sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object); +PVOID _LIBSNG_CALL sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_object); /************************************************************//** @@ -543,7 +604,7 @@ void* _SAPI_CALL sangoma_wait_obj_get_context(sangoma_wait_obj_t *sangoma_wait_o \param tdm_api tdm api command structure \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! @@ -553,7 +614,7 @@ int _SAPI_CALL sangoma_cmd_exec(sng_fd_t fd, wanpipe_api_t *tdm_api); \param tdm_api tdm api command structure \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, int period) @@ -565,7 +626,7 @@ int _SAPI_CALL sangoma_get_full_cfg(sng_fd_t fd, wanpipe_api_t *tdm_api); Only valid in CHAN Operation Mode */ -int _SAPI_CALL sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, int period); +int _LIBSNG_CALL sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, int period); /*! \fn int sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -574,7 +635,7 @@ int _SAPI_CALL sangoma_tdm_set_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api, i \param tdm_api tdm api command structure \return negative: error or configured period value */ -int _SAPI_CALL sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -583,18 +644,47 @@ int _SAPI_CALL sangoma_tdm_get_usr_period(sng_fd_t fd, wanpipe_api_t *tdm_api); \param tdm_api tdm api command structure \return negative: error or configured mtu/mru in bytes */ -int _SAPI_CALL sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_get_usr_mtu_mru(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) - \brief Flush buffers from current channel + \brief Flush all (tx/rx/event) buffers from current channel \param fd device file descriptor \param tdm_api tdm api command structure \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_flush_rx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush only rx buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _LIBSNG_CALL sangoma_flush_rx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); +/*! + \fn int sangoma_flush_tx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush only tx buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _LIBSNG_CALL sangoma_flush_tx_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_flush_event_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Flush only event buffers from current channel + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + +*/ +int _LIBSNG_CALL sangoma_flush_event_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! @@ -605,7 +695,7 @@ int _SAPI_CALL sangoma_flush_bufs(sng_fd_t fd, wanpipe_api_t *tdm_api); \param poll_in_sec driver poll period for rbs events \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_tdm_enable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api, int poll_in_sec); +int _LIBSNG_CALL sangoma_tdm_enable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api, int poll_in_sec); /*! \fn int sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -614,7 +704,7 @@ int _SAPI_CALL sangoma_tdm_enable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api \param tdm_api tdm api command structure \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char rbs) @@ -625,7 +715,7 @@ int _SAPI_CALL sangoma_tdm_disable_rbs_events(sng_fd_t fd, wanpipe_api_t *tdm_ap \param rbs rbs bits (ABCD) \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char rbs); +int _LIBSNG_CALL sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char rbs); /*! \fn int sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char *rbs) @@ -637,7 +727,7 @@ int _SAPI_CALL sangoma_tdm_write_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int ch \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char *rbs); +int _LIBSNG_CALL sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel, unsigned char *rbs); /*! \fn int sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -648,7 +738,7 @@ int _SAPI_CALL sangoma_tdm_read_rbs(sng_fd_t fd, wanpipe_api_t *tdm_api, int cha Supported only on cards that have HWEC */ -int _SAPI_CALL sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -659,7 +749,44 @@ int _SAPI_CALL sangoma_tdm_enable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_ap Supported only on cards that have HWEC */ -int _SAPI_CALL sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +#ifdef WP_API_FEATURE_FAX_EVENTS +/*! + \fn int sangoma_tdm_enable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable FAX Detection on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _LIBSNG_CALL sangoma_tdm_enable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable FAX Detection on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on cards that have HWEC +*/ +int _LIBSNG_CALL sangoma_tdm_disable_fax_events(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_get_hw_fax(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Get HW FAX Detection State (Enable or Disabled) on Octasic chip (if hw supports it) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: Disabled, 1: Enabled + + Supported only on cards that have HWEC +*/ +int _LIBSNG_CALL sangoma_tdm_get_hw_fax(sng_fd_t fd, wanpipe_api_t *tdm_api); + +#endif /*! @@ -668,10 +795,10 @@ int _SAPI_CALL sangoma_tdm_disable_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_a \param fd device file descriptor \param tdm_api tdm api command structure \return non-zero: error, 0: ok - + Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_enable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_enable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -682,7 +809,7 @@ int _SAPI_CALL sangoma_tdm_enable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -693,7 +820,7 @@ int _SAPI_CALL sangoma_tdm_disable_rm_dtmf_events(sng_fd_t fd, wanpipe_api_t *td Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -704,7 +831,7 @@ int _SAPI_CALL sangoma_tdm_enable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_ Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -715,7 +842,7 @@ int _SAPI_CALL sangoma_tdm_disable_rxhook_events(sng_fd_t fd, wanpipe_api_t *tdm Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -726,7 +853,7 @@ int _SAPI_CALL sangoma_tdm_enable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_ap Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! @@ -738,7 +865,7 @@ int _SAPI_CALL sangoma_tdm_disable_ring_events(sng_fd_t fd, wanpipe_api_t *tdm_a Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_enable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_enable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -749,7 +876,7 @@ int _SAPI_CALL sangoma_tdm_enable_ring_detect_events(sng_fd_t fd, wanpipe_api_t Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -760,7 +887,7 @@ int _SAPI_CALL sangoma_tdm_disable_ring_detect_events(sng_fd_t fd, wanpipe_api_t Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -771,7 +898,7 @@ int _SAPI_CALL sangoma_tdm_enable_ring_trip_detect_events(sng_fd_t fd, wanpipe_a Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api, uint16_t tone_id) @@ -783,7 +910,7 @@ int _SAPI_CALL sangoma_tdm_disable_ring_trip_detect_events(sng_fd_t fd, wanpipe_ Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api, uint16_t tone_id); +int _LIBSNG_CALL sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api, uint16_t tone_id); /*! \fn int sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -794,7 +921,7 @@ int _SAPI_CALL sangoma_tdm_enable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_ap Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -805,7 +932,7 @@ int _SAPI_CALL sangoma_tdm_disable_tone_events(sng_fd_t fd, wanpipe_api_t *tdm_a Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -816,7 +943,7 @@ int _SAPI_CALL sangoma_tdm_txsig_onhook(sng_fd_t fd, wanpipe_api_t *tdm_api); Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -827,7 +954,7 @@ int _SAPI_CALL sangoma_tdm_txsig_offhook(sng_fd_t fd, wanpipe_api_t *tdm_api); Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -838,7 +965,7 @@ int _SAPI_CALL sangoma_tdm_txsig_start(sng_fd_t fd, wanpipe_api_t *tdm_api); Supported only on Analog Cards */ -int _SAPI_CALL sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -849,7 +976,7 @@ int _SAPI_CALL sangoma_tdm_txsig_kewl(sng_fd_t fd, wanpipe_api_t *tdm_api); Supported only on cards that have HWEC */ -int _SAPI_CALL sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -860,10 +987,10 @@ int _SAPI_CALL sangoma_tdm_enable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api); Supported only on cards that have HWEC */ -int _SAPI_CALL sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! - \fn int _SAPI_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms); + \fn int _LIBSNG_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms); \brief Get Front End Alarms (T1/E1 Only) \param fd device file descriptor \param tdm_api tdm api command structure @@ -872,7 +999,7 @@ int _SAPI_CALL sangoma_tdm_disable_hwec(sng_fd_t fd, wanpipe_api_t *tdm_api); Supported only on T1/E1 Cards */ -int _SAPI_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms); +int _LIBSNG_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned int *alarms); @@ -894,7 +1021,7 @@ int _SAPI_CALL sangoma_tdm_get_fe_alarms(sng_fd_t fd, wanpipe_api_t *tdm_api, un \return non-zero: error, 0: ok -> check current_status */ -int _SAPI_CALL sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status); +int _LIBSNG_CALL sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status); #endif @@ -915,11 +1042,11 @@ int _SAPI_CALL sangoma_get_link_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsi \param new_status new status 0=Link UP 1=Link Down \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_set_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char new_status); +int _LIBSNG_CALL sangoma_set_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char new_status); /*! - \fn int _SAPI_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) + \fn int _LIBSNG_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) \brief Enable BRI Bchannel loopback - used when debugging bri device \param fd device file descriptor \param tdm_api tdm api command structure @@ -927,10 +1054,10 @@ int _SAPI_CALL sangoma_set_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsign \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel); +int _LIBSNG_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel); /*! - \fn int _SAPI_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) + \fn int _LIBSNG_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel) \brief Disable BRI Bchannel loopback - used when debugging bri device \param fd device file descriptor \param tdm_api tdm api command structure @@ -938,7 +1065,7 @@ int _SAPI_CALL sangoma_enable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel); +int _LIBSNG_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *tdm_api, int channel); /*! @@ -948,7 +1075,7 @@ int _SAPI_CALL sangoma_disable_bri_bchan_loopback(sng_fd_t fd, wanpipe_api_t *td \param tdm_api tdm api command structure \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! @@ -959,7 +1086,7 @@ int _SAPI_CALL sangoma_get_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api); \param size tx queue size (minimum value of 1) \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_set_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size); +int _LIBSNG_CALL sangoma_set_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size); /*! \fn int sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -968,7 +1095,7 @@ int _SAPI_CALL sangoma_set_tx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int \param tdm_api tdm api command structure \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size) @@ -978,7 +1105,7 @@ int _SAPI_CALL sangoma_get_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api); \param size rx queue size (minimum value of 1) \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size); +int _LIBSNG_CALL sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int size); #ifndef LIBSANGOMA_GET_HWCODING @@ -999,7 +1126,7 @@ int _SAPI_CALL sangoma_set_rx_queue_sz(sng_fd_t fd, wanpipe_api_t *tdm_api, int This function will return the low level voice coding depending on configuration. (ulaw or alaw) */ -int _SAPI_CALL sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api); @@ -1019,7 +1146,7 @@ int _SAPI_CALL sangoma_get_hw_coding(sng_fd_t fd, wanpipe_api_t *tdm_api); This function will check if hw supports HW DTMF. */ -int _SAPI_CALL sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -1030,7 +1157,7 @@ int _SAPI_CALL sangoma_tdm_get_hw_dtmf(sng_fd_t fd, wanpipe_api_t *tdm_api); This function will check if hw supports HW EC. */ -int _SAPI_CALL sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_span_chan_toif(int span, int chan, char *interface_name) @@ -1040,7 +1167,7 @@ int _SAPI_CALL sangoma_tdm_get_hw_ec(sng_fd_t fd, wanpipe_api_t *tdm_api); \param interface_name pointer to string where interface name will be written \return non-zero = error, 0 = ok */ -int _SAPI_CALL sangoma_span_chan_toif(int span, int chan, char *interface_name); +int _LIBSNG_CALL sangoma_span_chan_toif(int span, int chan, char *interface_name); /*! \fn int sangoma_span_chan_fromif(char *interface_name, int *span, int *chan) @@ -1050,7 +1177,7 @@ int _SAPI_CALL sangoma_span_chan_toif(int span, int chan, char *interface_name); \param chan integer pointer where to write chan value \return non-zero = error, 0 = ok */ -int _SAPI_CALL sangoma_span_chan_fromif(char *interface_name, int *span, int *chan); +int _LIBSNG_CALL sangoma_span_chan_fromif(char *interface_name, int *span, int *chan); /*! @@ -1061,7 +1188,7 @@ int _SAPI_CALL sangoma_span_chan_fromif(char *interface_name, int *span, int *ch \param sectimeout how many seconds to wait for the device to come up, -1 to wait forever \return non-zero = error, 0 = ok */ -int _SAPI_CALL sangoma_interface_wait_up(int span, int chan, int sectimeout); +int _LIBSNG_CALL sangoma_interface_wait_up(int span, int chan, int sectimeout); /*! \fn int sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, wan_driver_version_t *drv_ver) @@ -1071,7 +1198,7 @@ int _SAPI_CALL sangoma_interface_wait_up(int span, int chan, int sectimeout); \param drv_ver driver version structure that will contain the driver version \return non-zero = error, 0 = ok */ -int _SAPI_CALL sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, wan_driver_version_t *drv_ver); +int _LIBSNG_CALL sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, wan_driver_version_t *drv_ver); /*! \fn int sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) @@ -1081,7 +1208,7 @@ int _SAPI_CALL sangoma_get_driver_version(sng_fd_t fd, wanpipe_api_t *tdm_api, w \param ver hardware/firmware version number \return non-zero = error, 0 = ok */ -int _SAPI_CALL sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver); +int _LIBSNG_CALL sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver); /*! \fn int sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver) @@ -1091,7 +1218,7 @@ int _SAPI_CALL sangoma_get_firmware_version(sng_fd_t fd, wanpipe_api_t *tdm_api, \param ver hardware/cpld version number \return non-zero = error, 0 = ok */ -int _SAPI_CALL sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver); +int _LIBSNG_CALL sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *ver); /*! @@ -1102,16 +1229,16 @@ int _SAPI_CALL sangoma_get_cpld_version(sng_fd_t fd, wanpipe_api_t *tdm_api, uns \param stats stats structure will be filled with device stats. (Optional, can be left NULL) \return non-zero = error, 0 = ok */ -int _SAPI_CALL sangoma_get_stats(sng_fd_t fd, wanpipe_api_t *tdm_api, wanpipe_chan_stats_t *stats); +int _LIBSNG_CALL sangoma_get_stats(sng_fd_t fd, wanpipe_api_t *tdm_api, wanpipe_chan_stats_t *stats); /*! - \fn int _SAPI_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api) + \fn int _LIBSNG_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api) \brief Flush/Reset device statistics \param fd device file descriptor \param tdm_api tdm api command structure \return non-zero = error, 0 = ok */ -int _SAPI_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! \fn int sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, int rxflashtime) @@ -1121,9 +1248,73 @@ int _SAPI_CALL sangoma_flush_stats(sng_fd_t fd, wanpipe_api_t *tdm_api); \param rxflashtime time value \return non-zero = error, 0 = ok */ -int _SAPI_CALL sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, int rxflashtime); +int _LIBSNG_CALL sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, int rxflashtime); + +#ifdef WP_API_FEATURE_RM_GAIN +/*! + \fn int sangoma_set_rm_tx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) + \brief set tx gain for FXO/FXS module + \param fd device file descriptor + \param tdm_api tdm api command structure + \param value txgain (FXO - txgain value ranges from -150 to 120 , FXS - txgain value 35,-35) + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_set_rm_tx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value); +/*! + \fn int sangoma_set_rm_rx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) + \brief set rx gain for FXO/FXS module + \param fd device file descriptor + \param tdm_api tdm api command structure + \param value rxgain (FXO - rxgain value ranges from -150 to 120 , FXS -rxgain value 35,-35) + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_set_rm_rx_gain(sng_fd_t fd, wanpipe_api_t *tdm_api, int value); + +#endif /* WP RM GAIN feature */ + +/*! + \fn int sangoma_set_polarity(sng_fd_t fd, wanpipe_api_t *tdm_api, int value) + \brief set polarity on FXS (Analog only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \param value 0 fwd, 1 rev + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_tdm_set_polarity(sng_fd_t fd, wanpipe_api_t *tdm_api, int polarity); + +/*! + \fn int sangoma_tdm_txsig_onhooktranfer(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Tranmsmit TX SIG ON HOOK Tranfer (Analog Only) + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero: error, 0: ok + + Supported only on Analog Cards +*/ +int _LIBSNG_CALL sangoma_tdm_txsig_onhooktransfer(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +#ifdef WP_API_FEATURE_LOOP +/*! + \fn int sangoma_tdm_enable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Enable channel loop: All rx data will be transmitted back out. + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_tdm_enable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api); + +/*! + \fn int sangoma_tdm_disable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api) + \brief Disable channel loop + \param fd device file descriptor + \param tdm_api tdm api command structure + \return non-zero = error, 0 = ok +*/ +int _LIBSNG_CALL sangoma_tdm_disable_loop(sng_fd_t fd, wanpipe_api_t *tdm_api); +#endif /************************************************************//** * Device EVENT Function @@ -1141,9 +1332,80 @@ int _SAPI_CALL sangoma_set_rm_rxflashtime(sng_fd_t fd, wanpipe_api_t *tdm_api, i This function usually used after wait() function indicated that event has occured. */ -int _SAPI_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api); +#ifdef WP_API_FEATURE_LOGGER +sangoma_status_t _LIBSNG_CALL sangoma_logger_cmd_exec(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_read_event(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Read Wanpipe Logger Events + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error + + The Logger API structure will be populated with a Logger Event. + This function usually used after wait() function indicated that + an event has occured. +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_read_event(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_flush_buffers(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Flush Wanpipe Logger internal buffers + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_flush_buffers(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_get_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Get Wanpipe Logger statistics + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_get_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_reset_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Reset Wanpipe Logger statistics + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_reset_statistics(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_get_open_handle_counter(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Get Counter of open Handles/File Descriptors of Wanpipe Logger + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_get_open_handle_counter(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_get_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Get current level (types of events) of Wanpipe Logger + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_get_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +/*! + \fn sangoma_status_t sangoma_logger_set_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd) + \brief Set current level (types of events) of Wanpipe Logger + \param fd device file descriptor + \param logger_cmd Logger API command structure + \return SANG_STATUS_SUCCESS: ok, else: error +*/ +sangoma_status_t _LIBSNG_CALL sangoma_logger_set_logger_level(sng_fd_t fd, wp_logger_cmd_t *logger_cmd); + +#endif /* WP LOGGER FEATURE */ #ifndef LIBSANGOMA_LIGHT @@ -1165,7 +1427,7 @@ int _SAPI_CALL sangoma_read_event(sng_fd_t fd, wanpipe_api_t *tdm_api); Windows example: combination of GetLastError()/FormatMessage() zero: no system error. Check port_mgmt->operation_status. */ -int _SAPI_CALL sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); +int _LIBSNG_CALL sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); /*! @@ -1182,7 +1444,7 @@ int _SAPI_CALL sangoma_driver_port_start(sng_fd_t fd, port_management_struct_t * Windows example: combination of GetLastError()/FormatMessage() zero: no system error. Check port_mgmt->operation_status. */ -int _SAPI_CALL sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); +int _LIBSNG_CALL sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); /*! @@ -1201,7 +1463,7 @@ int _SAPI_CALL sangoma_driver_port_stop(sng_fd_t fd, port_management_struct_t *p Windows example: combination of GetLastError()/FormatMessage() zero: no system error. Check port_cfg->operation_status. */ -int _SAPI_CALL sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no); +int _LIBSNG_CALL sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no); /*! @@ -1217,7 +1479,7 @@ int _SAPI_CALL sangoma_driver_port_set_config(sng_fd_t fd, port_cfg_t *port_cfg, Windows example: combination of GetLastError()/FormatMessage() zero: no system error. Check port_cfg->operation_status. */ -int _SAPI_CALL sangoma_driver_port_get_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no); +int _LIBSNG_CALL sangoma_driver_port_get_config(sng_fd_t fd, port_cfg_t *port_cfg, unsigned short port_no); /*! @@ -1232,7 +1494,7 @@ int _SAPI_CALL sangoma_driver_port_get_config(sng_fd_t fd, port_cfg_t *port_cfg, Windows example: combination of GetLastError()/FormatMessage() zero: no system error. Check port_mgmt->operation_status. */ -int _SAPI_CALL sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); +int _LIBSNG_CALL sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t *port_mgmnt, unsigned short port_no); /*! @@ -1251,7 +1513,7 @@ int _SAPI_CALL sangoma_driver_get_hw_info(sng_fd_t fd, port_management_struct_t \param[in] port_no please see comment of sangoma_driver_port_set_config() \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_write_port_config_on_persistent_storage(hardware_info_t *hardware_info, port_cfg_t *port_cfg, unsigned short port_no); +int _LIBSNG_CALL sangoma_write_port_config_on_persistent_storage(hardware_info_t *hardware_info, port_cfg_t *port_cfg, unsigned short port_no); /************************************************************//** @@ -1265,7 +1527,7 @@ int _SAPI_CALL sangoma_write_port_config_on_persistent_storage(hardware_info_t * \param wan_udp management command structure \return non-zero: error, 0: ok */ -int _SAPI_CALL sangoma_mgmt_cmd(sng_fd_t fd, wan_udp_hdr_t* wan_udp); +int _LIBSNG_CALL sangoma_mgmt_cmd(sng_fd_t fd, wan_udp_hdr_t* wan_udp); #endif /* LIBSANGOMA_LIGHT */ @@ -1295,7 +1557,7 @@ int _SAPI_CALL sangoma_mgmt_cmd(sng_fd_t fd, wan_udp_hdr_t* wan_udp); Deprecated - replaced by sangoma_tdm_get_link_status function */ -int _SAPI_CALL sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status); +int _LIBSNG_CALL sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsigned char *current_status); @@ -1310,7 +1572,7 @@ int _SAPI_CALL sangoma_get_fe_status(sng_fd_t fd, wanpipe_api_t *tdm_api, unsign Deprecated Function - Here for backward compatibility Only valid in CHAN Operation Mode */ -int _SAPI_CALL sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int codec); +int _LIBSNG_CALL sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int codec); /*! \fn int sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -1322,7 +1584,7 @@ int _SAPI_CALL sangoma_tdm_set_codec(sng_fd_t fd, wanpipe_api_t *tdm_api, int co Deprecated Function - Here for backward compatibility Only valid in CHAN Operation Mode */ -int _SAPI_CALL sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api); /*! @@ -1334,7 +1596,7 @@ int _SAPI_CALL sangoma_tdm_get_codec(sng_fd_t fd, wanpipe_api_t *tdm_api); Deprecated - here for backward compatibility */ -sng_fd_t _SAPI_CALL sangoma_create_socket_by_name(char *device, char *card); +sng_fd_t _LIBSNG_CALL sangoma_create_socket_by_name(char *device, char *card); /*! \fn int sangoma_interface_toi(char *interface_name, int *span, int *chan) @@ -1345,7 +1607,7 @@ sng_fd_t _SAPI_CALL sangoma_create_socket_by_name(char *device, char *card); \return non-zero = error, 0 = ok Deprecated - here for backward compatibility */ -int _SAPI_CALL sangoma_interface_toi(char *interface_name, int *span, int *chan); +int _LIBSNG_CALL sangoma_interface_toi(char *interface_name, int *span, int *chan); /*! @@ -1358,7 +1620,7 @@ int _SAPI_CALL sangoma_interface_toi(char *interface_name, int *span, int *chan) Deprecated - not used/implemented */ -int _SAPI_CALL sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, int power); +int _LIBSNG_CALL sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, int power); /*! \fn int sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api) @@ -1369,15 +1631,318 @@ int _SAPI_CALL sangoma_tdm_set_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api, Deprecated - not used/implemented */ -int _SAPI_CALL sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api); +int _LIBSNG_CALL sangoma_tdm_get_power_level(sng_fd_t fd, wanpipe_api_t *tdm_api); + + +#ifdef WP_API_FEATURE_LIBSNG_HWEC +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_init(char *device_name) + + \brief Load Firmware image onto EC chip. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_init(char *device_name); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_release(char *device_name) + + \brief Reset internal state of HWEC API. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_release(char *device_name); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_operation_mode(char *device_name, int mode, unsigned int fe_chan_map) + + \brief Modify channel operation mode. + + \param mode One of WANEC_API_OPMODE_? values defined in wanpipe_api_iface.h: + WANEC_API_OPMODE_NORMAL, + WANEC_API_OPMODE_HT_FREEZE, + WANEC_API_OPMODE_HT_RESET, + WANEC_API_OPMODE_POWER_DOWN, + WANEC_API_OPMODE_NO_ECHO, + WANEC_API_OPMODE_SPEECH_RECOGNITION. + + \param fe_chan_map Bitmap of channels (timeslots for Digital, + lines for Analog) where the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_operation_mode(char *device_name, int mode, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_on (char *device_name, unsigned int fe_chan_map) + + \brief Set the channel state in the echo canceller to NORMAL/POWER ON. + This enables echo cancelation logic inside the chip. + The action is internal to EC chip itself, not related to AFT FPGA. + This call is slow and should be used only on startup. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, + lines for Analog) where the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_on(char *device_name, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_off (char *device_name, unsigned int fe_chan_map) + + \brief Set the channel state in the echo canceller to POWER OFF. + This disables echo cancellatio logic inside the chip and + data passes unmodified through the ec chip. + The action is internal to EC chip itself, not related + to AFT FPGA. This call is slow and should be used only on startup. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, + lines for Analog) where the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_off(char *device_name, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_enable(char *device_name, unsigned int fe_chan_map) + + \brief Redirect audio stream from AFT FPGA to EC chip. + This command effectively enables echo cancellation since + data is now forced through the EC chip by the FPGA. + Data will be modified by the echo canceller. + This command is recommened for fast enabling of Echo Cancellation. + Note 1: Chip must be configured and in POWER ON state for echo + Chancellation to take place. + Note 2: sangoma_tdm_enable_hwec() function can be use to achive + the same funcitnality based on file descriptor versus + channel map. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_enable(char *device_name, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_disable(char *device_name, unsigned int fe_chan_map) + + \brief Force AFT FPGA to bypass the echo canceller. + This command effectively disables echo cancellation since + data will not flowing through the ec chip. + Data will not be modified by the echo canceller. + This command is recommened for fast disabling of Echo Cancelation. + Note: sangoma_tdm_disable_hwec() function can be use to achive + the same functionality based on file descriptor versus + channel map. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_disable(char *device_name, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_channel_parameters(char *device_name, char *parameter, char *parameter_value, unsigned int channel_map) + + \brief Modify channel configuration parameters. + This is list of Echo Cancellation channel parameters: + + Channel parameter Channel parameter value + ================= ======================= + WANEC_EnableNlp TRUE | FALSE + WANEC_EnableTailDisplacement TRUE | FALSE + WANEC_TailDisplacement 0-896 + WANEC_SoutLevelControl TRUE | FALSE + WANEC_RinAutomaticLevelControl TRUE | FALSE + WANEC_SoutAutomaticLevelControl TRUE | FALSE + WANEC_SoutAdaptiveNoiseReduction TRUE | FALSE + WANEC_RoutNoiseReduction TRUE | FALSE + WANEC_ComfortNoiseMode COMFORT_NOISE_NORMAL + COMFORT_NOISE_FAST_LATCH + COMFORT_NOISE_EXTENDED + COMFORT_NOISE_OFF + WANEC_DtmfToneRemoval TRUE | FALSE + WANEC_AcousticEcho TRUE | FALSE + WANEC_NonLinearityBehaviorA 0-13 + WANEC_NonLinearityBehaviorB 0-8 + WANEC_DoubleTalkBehavior DT_BEH_NORMAL + DT_BEH_LESS_AGGRESSIVE + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_channel_parameters(char *device_name, char *parameter, char *parameter_value, unsigned int channel_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_tone_detection(char *device_name, int tone_id, int enable, unsigned int fe_chan_map, unsigned char port_map) + + \brief Enable/Disable tone detection (such as DTMF) of channels from channel map. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param tone_id See wanpipe_api_iface.h for list of valid tones + + \param enable A flag, if 1 - the specified tone will be detected, + if 0 - specified tone will not be detected. + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \param port_map Port\Direction of tone detection - Rx, Tx. See wanpipe_events.h for + list of valid ports (WAN_EC_CHANNEL_PORT_SOUT...). + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_tone_detection(char *device_name, int tone_id, int enable, unsigned int fe_chan_map, unsigned char port_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_print_statistics(char *device_name, int full, unsigned int fe_chan_map) + + \brief Read and print Chip/Channel statistics from EC chip. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param full Flag to read full statistics, if set to 1. + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will read statistics. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_print_statistics(char *device_name, int full, unsigned int fe_chan_map); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_load(char *device_name, char *filename, char pcmlaw, int *out_buffer_id) + + \brief Load audio buffer to EC chip. The buffer can be played out using the sangoma_hwec_audio_buffer_playout() function. + + \param filename name of the audio file (without the extension). + Actual file must have .pcm extension. + Location: + Windows: %SystemRoot%\sang_ec_files (ex: c:\WINDOWS\sang_ec_files) + Linux: /etc/wanpipe/buffers + + \param out_buffer_id when the buffer is loaded on the chip, it is assigned an ID. This ID should + be used when requesting to play out the buffer. + + \return SANG_STATUS_SUCCESS: success, or error status + + */ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_load(char *device_name, char *filename, char pcmlaw, int *out_buffer_id); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_bufferunload(char *device_name, int in_buffer_id) + + \brief Unload/remove an audio buffer from the HWEC chip. + + \param device_name Sangoma wanpipe device name. (ex: wanpipe1 - Linux; wanpipe1_if1 - Windows). + + \param in_buffer_id ID of the buffer which will be unloaded. The ID must be initialized by sangoma_hwec_audio_bufferload(). + + \return SANG_STATUS_SUCCESS - buffer was successfully unloaded/removed, SANG_STATUS_GENERAL_ERROR - error occured +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_unload(char *device_name, int in_buffer_id); + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_playout(char *device_name, unsigned int fe_chan_map, + unsigned char port_map, int buffer_id, int start, int repeat_cnt, int duration) + + \brief Start\Stop playing out an audio buffer previously loaded by sangoma_hwec_audio_buffer_load(). + + \param fe_chan_map Bitmap of channels (timeslots for Digital, + lines for Analog) where the call will take effect. + + \param port_map Port\Direction where the buffer will be played out. + This is the channel port on which the buffer will be + played (WAN_EC_CHANNEL_PORT_SOUT or WAN_EC_CHANNEL_PORT_ROUT) + + \param in_buffer_id ID of the buffer which will be unloaded. The ID must be initialized by sangoma_hwec_audio_bufferload(). + + \param start If 1 - start the play out, 0 - stop the play out + + \param repeat_cnt Number of times to play out the same buffer + + \param duration Maximum duration of the playout, in milliseconds. If it takes less then 'duration' to + play out the whole buffer this paramter is ignored. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_playout(char *device_name, unsigned int fe_chan_map, + unsigned char port, int in_buffer_id, int start, + int repeat_cnt, int duration); + +/*! + \fn void _LIBSNG_CALL sangoma_hwec_config_verbosity(int verbosity_level) + + \brief Set Verbosity level of EC API. The level controls amount of data + printed to stdout and wanpipelog.txt for diagnostic purposes. + + \param verbosity_level Valid values are from 0 to 3. + + \return SANG_STATUS_SUCCESS: success - the level was changed to 'verbosity_level', + SANG_STATUS_INVALID_PARAMETER: error - the level was not changed because new level is invalid +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_verbosity(int verbosity_level); + +#endif /* WP_API_FEATURE_LIBSNG_HWEC */ #ifdef __cplusplus } +#endif/* __WINDOWS__ */ + + +#if defined(__WINDOWS__) +/*! Windows specific API */ +#ifdef __cplusplus +extern "C" { /* for C++ users */ #endif +sangoma_status_t _LIBSNG_CALL sangoma_unload_driver(); +sangoma_status_t _LIBSNG_CALL sangoma_load_driver(); +void _LIBSNG_CALL sangoma_reset_port_numbers(); +sangoma_status_t _LIBSNG_CALL sangoma_get_driver_version_from_registry(char *out_buffer, int out_buffer_length); +sangoma_status_t _LIBSNG_CALL sangoma_set_driver_mode_of_all_hw_devices(int driver_mode); + +#ifdef __cplusplus +} +#endif/* __WINDOWS__ */ + +#else /*! Backward compabile defines */ -#if !defined(__WINDOWS__) #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 diff --git a/api/libsangoma/libsangoma.sln b/api/libsangoma/libsangoma.sln index 26f7387..34d3d31 100644 --- a/api/libsangoma/libsangoma.sln +++ b/api/libsangoma/libsangoma.sln @@ -1,6 +1,6 @@  -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libsangoma", "libsangoma.vcproj", "{7E60CC2D-E818-4712-8F16-C0E6EC64982F}" EndProject Global diff --git a/api/libsangoma/libsangoma.vcproj b/api/libsangoma/libsangoma.vcproj index aac9048..c1e2001 100644 --- a/api/libsangoma/libsangoma.vcproj +++ b/api/libsangoma/libsangoma.vcproj @@ -1,10 +1,11 @@ @@ -215,10 +216,6 @@ RelativePath=".\libsangoma.h" > - - - - - - - - - - - - diff --git a/api/libsangoma/libsangoma_hwec.c b/api/libsangoma/libsangoma_hwec.c new file mode 100644 index 0000000..a1909e7 --- /dev/null +++ b/api/libsangoma/libsangoma_hwec.c @@ -0,0 +1,547 @@ +/*******************************************************************************//** + * \file libsangoma_hwec.c + * \brief Hardware Echo Canceller API Code Library for + * Sangoma AFT T1/E1/Analog/BRI hardware. + * + * Author(s): David Rokhvarg + * + * Copyright: (c) 2005-2010 Sangoma Technologies Corporation + * + * * 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 Sangoma Technologies 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 Sangoma Technologies ``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 Sangoma Technologies 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 + ******************************************************************************* + */ + +#include "libsangoma.h" +#include "libsangoma-pvt.h" +#include "wanpipe_includes.h" + +#ifdef WP_API_FEATURE_LIBSNG_HWEC + +#include "wanpipe_events.h" +#include "wanec_api.h" + +#if defined (__WINDOWS__) +# include "wanpipe_time.h" /* wp_sleep() */ +# pragma comment( lib, "waneclib" ) /* import functions from waneclib.dll */ +#endif/* __WINDOWS__) */ + +/* Fast sequence of commands to HWEC may cause the chip + * enter fatal error state, the workaround is to have + * a guaranteed delay after eache command. */ +#define HWEC_CMD_DELAY() wp_usleep(20000) /* 20ms */ + +static int libsng_hwec_verbosity_level = 0; + +/************************************************************//** + * Private Functions. (Not exported) + ***************************************************************/ + +static sangoma_status_t sangoma_hwec_bypass(char *device_name, int enable, unsigned int fe_chan_map) +{ + sangoma_status_t rc; + wanec_api_hwec_t hwec; + + memset(&hwec, 0, sizeof(wanec_api_hwec_t)); + + hwec.enable = enable; + hwec.fe_chan_map = fe_chan_map; + + /* WAN_EC_API_CMD_HWEC_ENABLE/WAN_EC_API_CMD_HWEC_DISABLE - Controls the "bypass" mode.)*/ + rc = wanec_api_hwec(device_name, libsng_hwec_verbosity_level, &hwec); + if( rc ) { + return rc; + } + HWEC_CMD_DELAY(); + return SANG_STATUS_SUCCESS; +} + + +/************************************************************//** + * Public Functions. (Exported) + ***************************************************************/ + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_init(char *device_name) + + \brief Load Firmware image onto EC chip. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_init(char *device_name) +{ + sangoma_status_t rc; + wan_custom_param_t custom_parms; + wanec_api_config_t config; + + memset(&config, 0x00, sizeof(config)); + memset(&custom_parms, 0x0, sizeof(custom_parms)); + +#if 1 + /* enable acoustic echo cancellation by default */ + strcpy( custom_parms.name, "WANEC_EnableAcousticEcho" ); + strcpy( custom_parms.sValue, "TRUE" ); + + config.conf.param_no = 1; + config.conf.params = &custom_parms; +#endif + + /* Load firmware on EC chip */ + rc = wanec_api_config( device_name, libsng_hwec_verbosity_level, &config ); + if( rc ) { + return rc; + } + HWEC_CMD_DELAY(); + return SANG_STATUS_SUCCESS; +} + + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_release(char *device_name) + + \brief Reset internal state of HWEC API. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_release(char *device_name) +{ + sangoma_status_t rc; + wanec_api_release_t release; + + memset(&release, 0, sizeof(wanec_api_release_t)); + + rc = wanec_api_release( device_name, libsng_hwec_verbosity_level, &release ); + if( rc ) { + return rc; + } + HWEC_CMD_DELAY(); + return SANG_STATUS_SUCCESS; +} + + +/*! + Modify channel operation mode. +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_operation_mode(char *device_name, int mode, unsigned int fe_chan_map) +{ + sangoma_status_t rc; + wanec_api_opmode_t opmode; + + memset(&opmode, 0, sizeof(wanec_api_opmode_t)); + + opmode.mode = mode; + opmode.fe_chan_map = fe_chan_map; + + /* modes are: + WANEC_API_OPMODE_NORMAL, + WANEC_API_OPMODE_HT_FREEZE, + WANEC_API_OPMODE_HT_RESET, + WANEC_API_OPMODE_POWER_DOWN, + WANEC_API_OPMODE_NO_ECHO, + WANEC_API_OPMODE_SPEECH_RECOGNITION. + */ + rc = wanec_api_opmode(device_name, libsng_hwec_verbosity_level, &opmode); + if( rc ) { + return rc; + } + HWEC_CMD_DELAY(); + return SANG_STATUS_SUCCESS; +} + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_on (char *device_name, unsigned int fe_chan_map) + + \brief Set the channel state in the echo canceller to NORMAL/POWER ON. + This enables echo cancelation logic inside the chip. + The action is internal to EC chip itself, not related to AFT FPGA. + This call is slow and should be used only on startup. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, + lines for Analog) where the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_on(char *device_name, unsigned int fe_chan_map) +{ + return sangoma_hwec_config_operation_mode(device_name, WANEC_API_OPMODE_NORMAL, fe_chan_map); +} + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_off (char *device_name, unsigned int fe_chan_map) + + \brief Set the channel state in the echo canceller to POWER OFF. + This disables echo cancellatio logic inside the chip and + data passes unmodified through the ec chip. + The action is internal to EC chip itself, not related + to AFT FPGA. This call is slow and should be used only on startup. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, + lines for Analog) where the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_power_off(char *device_name, unsigned int fe_chan_map) +{ + return sangoma_hwec_config_operation_mode(device_name, WANEC_API_OPMODE_POWER_DOWN, fe_chan_map); +} + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_enable(char *device_name, unsigned int fe_chan_map) + + \brief Redirect audio stream from AFT FPGA to EC chip. + This command effectively enables echo cancellation since + data is now forced through the EC chip by the FPGA. + Data will be modified by the echo canceller. + This command is recommened for fast enabling of Echo Cancellation. + Note 1: Chip must be configured and in POWER ON state for echo + Chancellation to take place. + Note 2: sangoma_tdm_enable_hwec() function can be use to achive + the same funcitnality based on file descriptor versus + channel map. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_enable(char *device_name, unsigned int fe_chan_map) +{ + return sangoma_hwec_bypass(device_name, 1 /* enable */, fe_chan_map); +} + + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_disable(char *device_name, unsigned int fe_chan_map) + + \brief Force AFT FPGA to bypass the echo canceller. + This command effectively disables echo cancellation since + data will not flowing through the ec chip. + Data will not be modified by the echo canceller. + This command is recommened for fast disabling of Echo Cancelation. + Note: sangoma_tdm_disable_hwec() function can be use to achive + the same functionality based on file descriptor versus + channel map. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_disable(char *device_name, unsigned int fe_chan_map) +{ + return sangoma_hwec_bypass(device_name, 0 /* disable */, fe_chan_map);; +} + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_channel_parameters(char *device_name, char *parameter, char *parameter_value, unsigned int channel_map) + + \brief Modify channel configuration parameters. + This is list of Echo Cancellation channel parameters: + + Channel parameter Channel parameter value + ================= ======================= + WANEC_EnableNlp TRUE | FALSE + WANEC_EnableTailDisplacement TRUE | FALSE + WANEC_TailDisplacement 0-896 + WANEC_SoutLevelControl TRUE | FALSE + WANEC_RinAutomaticLevelControl TRUE | FALSE + WANEC_SoutAutomaticLevelControl TRUE | FALSE + WANEC_SoutAdaptiveNoiseReduction TRUE | FALSE + WANEC_RoutNoiseReduction TRUE | FALSE + WANEC_ComfortNoiseMode COMFORT_NOISE_NORMAL + COMFORT_NOISE_FAST_LATCH + COMFORT_NOISE_EXTENDED + COMFORT_NOISE_OFF + WANEC_DtmfToneRemoval TRUE | FALSE + WANEC_AcousticEcho TRUE | FALSE + WANEC_NonLinearityBehaviorA 0-13 + WANEC_NonLinearityBehaviorB 0-8 + WANEC_DoubleTalkBehavior DT_BEH_NORMAL + DT_BEH_LESS_AGGRESSIVE + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_channel_parameters(char *device_name, char *parameter, char *parameter_value, unsigned int channel_map) +{ + sangoma_status_t rc; + wanec_api_modify_t channelModify; + wan_custom_param_t aParms; + + memset(&channelModify, 0x00, sizeof(channelModify)); + memset(&aParms, 0x00, sizeof(aParms)); + + channelModify.fe_chan_map = channel_map; + channelModify.conf.param_no = 1; + channelModify.conf.params = &aParms; + + strcpy( aParms.name, parameter); + strcpy( aParms.sValue, parameter_value); + + rc = wanec_api_modify( device_name, libsng_hwec_verbosity_level, &channelModify ); + if( rc ){ + return rc; + } + HWEC_CMD_DELAY(); + return SANG_STATUS_SUCCESS; +} + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_tone_detection(char *device_name, int tone_id, int enable, unsigned int fe_chan_map, unsigned char port_map) + + \brief Enable/Disable tone detection (such as DTMF) of channels from channel map. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param tone_id See wanpipe_api_iface.h for list of valid tones + + \param enable A flag, if 1 - the specified tone will be detected, + if 0 - specified tone will not be detected. + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will take effect. + + \param port_map Port\Direction of tone detection - Rx, Tx. See wanpipe_events.h for + list of valid ports (WAN_EC_CHANNEL_PORT_SOUT...). + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_tone_detection(char *device_name, int tone_id, int enable, unsigned int fe_chan_map, unsigned char port_map) +{ + sangoma_status_t rc; + wanec_api_tone_t tone; + + memset(&tone, 0, sizeof(wanec_api_tone_t)); + + tone.id = tone_id; + tone.enable = enable; + tone.fe_chan_map = fe_chan_map; + tone.port_map = port_map; + tone.type_map = WAN_EC_TONE_PRESENT | WAN_EC_TONE_STOP; + + rc = wanec_api_tone( device_name, libsng_hwec_verbosity_level, &tone ); + if( rc ) { + return rc; + } + HWEC_CMD_DELAY(); + return SANG_STATUS_SUCCESS; +} + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_print_statistics(char *device_name, int full, unsigned int fe_chan_map) + + \brief Read and print Chip/Channel statistics from EC chip. + + \param device_name Sangoma API device name. + Windows: wanpipe1_if1, wanpipe2_if1... + Linux: wanpipe1, wanpipe2... + + \param full Flag to read full statistics, if set to 1. + + \param fe_chan_map Bitmap of channels (timeslots for Digital, lines for Analog) where + the call will read statistics. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_print_statistics(char *device_name, int full, unsigned int fe_chan_map) +{ + sangoma_status_t rc; + wanec_api_stats_t stats; + + memset(&stats, 0, sizeof(wanec_api_stats_t)); + + stats.full = full; + stats.fe_chan = fe_chan_map; + stats.reset = 0; /* do not reset */ + + rc = wanec_api_stats( device_name, libsng_hwec_verbosity_level, &stats ); + if( rc ) { + return rc; + } + HWEC_CMD_DELAY(); + return SANG_STATUS_SUCCESS; +} + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_load(char *device_name, char *filename, char pcmlaw, int *out_buffer_id) + + \brief Load audio buffer to EC chip. The buffer can be played out using the sangoma_hwec_audio_buffer_playout() function. + + \param filename name of the audio file (without the extension). + Actual file must have .pcm extension. + Location: + Windows: %SystemRoot%\sang_ec_files (ex: c:\WINDOWS\sang_ec_files) + Linux: /etc/wanpipe/buffers + + \param out_buffer_id when the buffer is loaded on the chip, it is assigned an ID. This ID should + be used when requesting to play out the buffer. + + \return SANG_STATUS_SUCCESS: success, or error status + + */ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_load(char *device_name, char *filename, char pcmlaw, int *out_buffer_id) +{ + sangoma_status_t rc; + wanec_api_bufferload_t bufferload; + + memset(&bufferload, 0, sizeof(wanec_api_bufferload_t)); + *out_buffer_id = -1; + + bufferload.buffer = filename; + bufferload.pcmlaw = pcmlaw; + + rc = wanec_api_buffer_load( device_name, libsng_hwec_verbosity_level, &bufferload ); + if( rc ) { + return rc; + } + + *out_buffer_id = bufferload.buffer_id; + + HWEC_CMD_DELAY(); + return SANG_STATUS_SUCCESS; +} + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_bufferunload(char *device_name, int in_buffer_id) + + \brief Unload/remove an audio buffer from the HWEC chip. + + \param device_name Sangoma wanpipe device name. (ex: wanpipe1 - Linux; wanpipe1_if1 - Windows). + + \param in_buffer_id ID of the buffer which will be unloaded. The ID must be initialized by sangoma_hwec_audio_bufferload(). + + \return SANG_STATUS_SUCCESS - buffer was successfully unloaded/removed, SANG_STATUS_GENERAL_ERROR - error occured +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_unload(char *device_name, int in_buffer_id) +{ + sangoma_status_t rc; + wanec_api_bufferunload_t bufferunload; + + memset(&bufferunload, 0, sizeof(wanec_api_bufferunload_t)); + + bufferunload.buffer_id = (unsigned int)in_buffer_id; + + rc = wanec_api_buffer_unload( device_name, libsng_hwec_verbosity_level, &bufferunload); + if( rc ) { + return rc; + } + HWEC_CMD_DELAY(); + return SANG_STATUS_SUCCESS; +} + +/*! + \fn sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_playout(char *device_name, unsigned int fe_chan_map, + unsigned char port_map, int buffer_id, int start, int repeat_cnt, int duration) + + \brief Start\Stop playing out an audio buffer previously loaded by sangoma_hwec_audio_buffer_load(). + + \param fe_chan_map Bitmap of channels (timeslots for Digital, + lines for Analog) where the call will take effect. + + \param port_map Port\Direction where the buffer will be played out. + This is the channel port on which the buffer will be + played (WAN_EC_CHANNEL_PORT_SOUT or WAN_EC_CHANNEL_PORT_ROUT) + + \param in_buffer_id ID of the buffer which will be unloaded. The ID must be initialized by sangoma_hwec_audio_bufferload(). + + \param start If 1 - start the play out, 0 - stop the play out + + \param repeat_cnt Number of times to play out the same buffer + + \param duration Maximum duration of the playout, in milliseconds. If it takes less then 'duration' to + play out the whole buffer this paramter is ignored. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_audio_buffer_playout(char *device_name, unsigned int fe_chan_map, + unsigned char port, int in_buffer_id, int start, + int repeat_cnt, int duration) +{ + sangoma_status_t rc; + wanec_api_playout_t playout; + + memset(&playout, 0, sizeof(wanec_api_playout_t)); + + playout.start = start; + playout.fe_chan = fe_chan_map; + playout.buffer_id = in_buffer_id; + playout.port = port; + playout.notifyonstop = 1; + playout.user_event_id = 0xA5; /* dummy value */ + playout.repeat_cnt = repeat_cnt; + playout.duration = (duration) ? duration : 5000; /* default is 5s */ + + rc = wanec_api_playout( device_name, libsng_hwec_verbosity_level, &playout); + if( rc ) { + return rc; + } + HWEC_CMD_DELAY(); + return SANG_STATUS_SUCCESS; +} + +/*! + \fn void _LIBSNG_CALL sangoma_hwec_config_verbosity(int verbosity_level) + + \brief Set Verbosity level of EC API. The level controls amount of data + printed to stdout and wanpipelog.txt for diagnostic purposes. + + \param verbosity_level Valid values are from 0 to 3. + + \return SANG_STATUS_SUCCESS: success, or error status +*/ +sangoma_status_t _LIBSNG_CALL sangoma_hwec_config_verbosity(int verbosity_level) +{ + if (verbosity_level >= 0 || verbosity_level <= 3) { + libsng_hwec_verbosity_level = verbosity_level; + return SANG_STATUS_SUCCESS; + } + return SANG_STATUS_INVALID_PARAMETER; +} + +#endif /* WP_API_FEATURE_LIBSNG_HWEC */ diff --git a/api/libsangoma/sample_c/.svn/all-wcprops b/api/libsangoma/sample_c/.svn/all-wcprops index 48350af..47dd1cf 100644 --- a/api/libsangoma/sample_c/.svn/all-wcprops +++ b/api/libsangoma/sample_c/.svn/all-wcprops @@ -1,7 +1,7 @@ K 25 svn:wc:ra_dav:version-url V 43 -/svn/libsangoma/!svn/ver/194/trunk/sample_c +/svn/libsangoma/!svn/ver/248/trunk/sample_c END lib_api.c K 25 @@ -19,7 +19,7 @@ sample.c K 25 svn:wc:ra_dav:version-url V 52 -/svn/libsangoma/!svn/ver/194/trunk/sample_c/sample.c +/svn/libsangoma/!svn/ver/234/trunk/sample_c/sample.c END sources K 25 @@ -33,12 +33,6 @@ svn:wc:ra_dav:version-url V 53 /svn/libsangoma/!svn/ver/139/trunk/sample_c/lib_api.h END -Makefile.Windows -K 25 -svn:wc:ra_dav:version-url -V 59 -/svn/libsangoma/!svn/ver/29/trunk/sample_c/Makefile.Windows -END Makefile.Linux K 25 svn:wc:ra_dav:version-url diff --git a/api/libsangoma/sample_c/.svn/dir-prop-base b/api/libsangoma/sample_c/.svn/dir-prop-base new file mode 100644 index 0000000..93d1e81 --- /dev/null +++ b/api/libsangoma/sample_c/.svn/dir-prop-base @@ -0,0 +1,6 @@ +K 10 +svn:ignore +V 17 +Makefile.Windows + +END diff --git a/api/libsangoma/sample_c/.svn/entries b/api/libsangoma/sample_c/.svn/entries index 3591602..24fff89 100644 --- a/api/libsangoma/sample_c/.svn/entries +++ b/api/libsangoma/sample_c/.svn/entries @@ -1,16 +1,16 @@ 8 dir -222 +273 https://www.sangomapbx.com/svn/libsangoma/trunk/sample_c https://www.sangomapbx.com/svn/libsangoma -2009-06-23T21:07:59.480026Z -194 +2010-01-22T21:30:47.609627Z +248 davidr - +has-props svn:special svn:externals svn:needs-lock @@ -32,7 +32,7 @@ file -2009-04-07T22:02:49.000000Z +2009-08-25T20:44:41.000000Z df0de3e86155c3e69e9d289a7d923f68 2009-04-07T01:30:57.044470Z 139 @@ -44,25 +44,22 @@ file -2009-02-04T23:12:14.000000Z +2009-08-25T20:44:41.000000Z dbd3173073165a3786ec4e0ef1234db6 2008-12-17T22:14:25.604781Z 57 ncorbic -regression -dir - sample.c file -2009-06-23T20:48:15.000000Z -ac2fe0c0a868d754e3450cb5fb2827a7 -2009-06-23T21:07:59.480026Z -194 +2009-12-04T21:21:35.000000Z +17f4ef3cc598f84b869bbcc38d2e12aa +2009-11-19T22:00:29.815332Z +234 davidr sources @@ -71,7 +68,7 @@ file -2009-05-01T15:10:42.000000Z +2009-08-25T20:44:41.000000Z 9c0fe43fbf63eb82e89e4aceb96f9bfc 2009-04-22T20:41:57.677943Z 146 @@ -83,31 +80,19 @@ file -2009-04-07T22:02:49.000000Z +2009-08-25T20:44:41.000000Z 80b2a1bced5f696771e5a92163a9967e 2009-04-07T01:30:57.044470Z 139 ncorbic -Makefile.Windows -file - - - - -2009-02-04T23:12:15.000000Z -cebc214b51868b9cdca9356c0d87d074 -2008-11-27T23:39:11.660385Z -29 -ncorbic - Makefile.Linux file -2009-04-09T17:49:27.000000Z +2009-08-25T20:44:41.000000Z ba64d71a9d128b0eb31f6663ae763a68 2009-04-08T00:23:42.359135Z 141 @@ -119,7 +104,7 @@ file -2009-02-04T23:12:15.000000Z +2009-08-25T20:44:41.000000Z 5b82f62f87e4700af9b7e48c658b2cfc 2008-11-28T23:21:04.815499Z 32 diff --git a/api/libsangoma/sample_c/.svn/text-base/Makefile.Windows.svn-base b/api/libsangoma/sample_c/.svn/text-base/Makefile.Windows.svn-base deleted file mode 100644 index 66f1c8e..0000000 --- a/api/libsangoma/sample_c/.svn/text-base/Makefile.Windows.svn-base +++ /dev/null @@ -1,8 +0,0 @@ -# -# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source -# file to this component. This file merely indirects to the real make file -# that is shared by all the driver components of the Windows NT DDK -# - -!INCLUDE $(NTMAKEENV)\makefile.def - diff --git a/api/libsangoma/sample_c/.svn/text-base/sample.c.svn-base b/api/libsangoma/sample_c/.svn/text-base/sample.c.svn-base index c7fa626..6e9d17d 100644 --- a/api/libsangoma/sample_c/.svn/text-base/sample.c.svn-base +++ b/api/libsangoma/sample_c/.svn/text-base/sample.c.svn-base @@ -49,7 +49,7 @@ static u_int32_t poll_events_bitmap = 0; */ #define TEST_NUMBER_OF_OBJECTS 1 -static void *sangoma_wait_objects[TEST_NUMBER_OF_OBJECTS]; +static sangoma_wait_obj_t *sangoma_wait_objects[TEST_NUMBER_OF_OBJECTS]; /* This example application has only a single execution thread - it is safe * to use a global buffer for received data and for data to be transmitted. */ @@ -87,7 +87,7 @@ int write_data_to_file(unsigned char *data, unsigned int data_length); void cleanup(void); #ifdef __WINDOWS__ -BOOL TerminateHandler(DWORD dwCtrlType); +BOOL WINAPI TerminateHandler(DWORD dwCtrlType); #else void TerminateHandler(int); #endif diff --git a/api/libsangoma/sample_c/Makefile.Windows b/api/libsangoma/sample_c/Makefile.Windows deleted file mode 100644 index 66f1c8e..0000000 --- a/api/libsangoma/sample_c/Makefile.Windows +++ /dev/null @@ -1,8 +0,0 @@ -# -# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source -# file to this component. This file merely indirects to the real make file -# that is shared by all the driver components of the Windows NT DDK -# - -!INCLUDE $(NTMAKEENV)\makefile.def - diff --git a/api/libsangoma/sample_c/sample.c b/api/libsangoma/sample_c/sample.c index c7fa626..6e9d17d 100644 --- a/api/libsangoma/sample_c/sample.c +++ b/api/libsangoma/sample_c/sample.c @@ -49,7 +49,7 @@ static u_int32_t poll_events_bitmap = 0; */ #define TEST_NUMBER_OF_OBJECTS 1 -static void *sangoma_wait_objects[TEST_NUMBER_OF_OBJECTS]; +static sangoma_wait_obj_t *sangoma_wait_objects[TEST_NUMBER_OF_OBJECTS]; /* This example application has only a single execution thread - it is safe * to use a global buffer for received data and for data to be transmitted. */ @@ -87,7 +87,7 @@ int write_data_to_file(unsigned char *data, unsigned int data_length); void cleanup(void); #ifdef __WINDOWS__ -BOOL TerminateHandler(DWORD dwCtrlType); +BOOL WINAPI TerminateHandler(DWORD dwCtrlType); #else void TerminateHandler(int); #endif diff --git a/api/libsangoma/sample_cpp/.svn/all-wcprops b/api/libsangoma/sample_cpp/.svn/all-wcprops index 47a96f9..3606ccf 100644 --- a/api/libsangoma/sample_cpp/.svn/all-wcprops +++ b/api/libsangoma/sample_cpp/.svn/all-wcprops @@ -1,31 +1,31 @@ K 25 svn:wc:ra_dav:version-url V 45 -/svn/libsangoma/!svn/ver/221/trunk/sample_cpp +/svn/libsangoma/!svn/ver/270/trunk/sample_cpp END sangoma_port.h K 25 svn:wc:ra_dav:version-url V 60 -/svn/libsangoma/!svn/ver/127/trunk/sample_cpp/sangoma_port.h -END -sources -K 25 -svn:wc:ra_dav:version-url -V 53 -/svn/libsangoma/!svn/ver/149/trunk/sample_cpp/sources +/svn/libsangoma/!svn/ver/248/trunk/sample_cpp/sangoma_port.h END sample.vcproj K 25 svn:wc:ra_dav:version-url V 59 -/svn/libsangoma/!svn/ver/149/trunk/sample_cpp/sample.vcproj +/svn/libsangoma/!svn/ver/248/trunk/sample_cpp/sample.vcproj +END +sources +K 25 +svn:wc:ra_dav:version-url +V 53 +/svn/libsangoma/!svn/ver/248/trunk/sample_cpp/sources END sangoma_interface.cpp K 25 svn:wc:ra_dav:version-url V 67 -/svn/libsangoma/!svn/ver/221/trunk/sample_cpp/sangoma_interface.cpp +/svn/libsangoma/!svn/ver/270/trunk/sample_cpp/sangoma_interface.cpp END sample_linux_compat.h K 25 @@ -37,13 +37,13 @@ sangoma_interface.h K 25 svn:wc:ra_dav:version-url V 65 -/svn/libsangoma/!svn/ver/209/trunk/sample_cpp/sangoma_interface.h +/svn/libsangoma/!svn/ver/270/trunk/sample_cpp/sangoma_interface.h END sangoma_port_configurator.cpp K 25 svn:wc:ra_dav:version-url V 75 -/svn/libsangoma/!svn/ver/221/trunk/sample_cpp/sangoma_port_configurator.cpp +/svn/libsangoma/!svn/ver/268/trunk/sample_cpp/sangoma_port_configurator.cpp END compile.bat K 25 @@ -61,13 +61,13 @@ sangoma_port_configurator.h K 25 svn:wc:ra_dav:version-url V 73 -/svn/libsangoma/!svn/ver/221/trunk/sample_cpp/sangoma_port_configurator.h +/svn/libsangoma/!svn/ver/268/trunk/sample_cpp/sangoma_port_configurator.h END sample.cpp K 25 svn:wc:ra_dav:version-url V 56 -/svn/libsangoma/!svn/ver/221/trunk/sample_cpp/sample.cpp +/svn/libsangoma/!svn/ver/270/trunk/sample_cpp/sample.cpp END sangoma_cthread.h K 25 @@ -75,23 +75,17 @@ svn:wc:ra_dav:version-url V 63 /svn/libsangoma/!svn/ver/133/trunk/sample_cpp/sangoma_cthread.h END -Makefile.Windows -K 25 -svn:wc:ra_dav:version-url -V 61 -/svn/libsangoma/!svn/ver/36/trunk/sample_cpp/Makefile.Windows -END sample.h K 25 svn:wc:ra_dav:version-url V 54 -/svn/libsangoma/!svn/ver/209/trunk/sample_cpp/sample.h +/svn/libsangoma/!svn/ver/269/trunk/sample_cpp/sample.h END sangoma_port.cpp K 25 svn:wc:ra_dav:version-url V 62 -/svn/libsangoma/!svn/ver/149/trunk/sample_cpp/sangoma_port.cpp +/svn/libsangoma/!svn/ver/248/trunk/sample_cpp/sangoma_port.cpp END Makefile.Linux K 25 @@ -102,6 +96,6 @@ END sample.sln K 25 svn:wc:ra_dav:version-url -V 55 -/svn/libsangoma/!svn/ver/65/trunk/sample_cpp/sample.sln +V 56 +/svn/libsangoma/!svn/ver/235/trunk/sample_cpp/sample.sln END diff --git a/api/libsangoma/sample_cpp/.svn/dir-prop-base b/api/libsangoma/sample_cpp/.svn/dir-prop-base index f619725..35cbabd 100644 --- a/api/libsangoma/sample_cpp/.svn/dir-prop-base +++ b/api/libsangoma/sample_cpp/.svn/dir-prop-base @@ -1,6 +1,7 @@ K 10 svn:ignore -V 45 +V 62 +Makefile.Windows sample.suo sample.vcproj.DAVIDRNEW.User.user diff --git a/api/libsangoma/sample_cpp/.svn/entries b/api/libsangoma/sample_cpp/.svn/entries index 64b83f2..47c3c07 100644 --- a/api/libsangoma/sample_cpp/.svn/entries +++ b/api/libsangoma/sample_cpp/.svn/entries @@ -1,14 +1,14 @@ 8 dir -222 +273 https://www.sangomapbx.com/svn/libsangoma/trunk/sample_cpp https://www.sangomapbx.com/svn/libsangoma -2009-07-31T21:19:58.950307Z -221 +2010-03-17T17:19:58.104959Z +270 davidr has-props @@ -32,22 +32,10 @@ file -2009-03-17T21:26:17.000000Z -8dd41c459e1a0cc8d8aedc7d60c4f468 -2009-03-17T22:51:09.315324Z -127 -ncorbic - -sources -file - - - - -2009-05-01T15:10:42.000000Z -6c664b4b011761bc7e801eea4d46ce22 -2009-04-29T21:06:58.775163Z -149 +2010-02-10T23:05:43.000000Z +64b4431c40f821db3c3ec77054f084c0 +2010-01-22T21:30:47.609627Z +248 davidr sample.vcproj @@ -56,23 +44,35 @@ file -2009-05-01T15:10:42.000000Z -fcfbf781e9bf08474ea8ae31cc4eb5ed -2009-04-29T21:06:58.775163Z -149 +2010-02-10T23:05:43.000000Z +3c2655474848e163999341285dc2412e +2010-01-22T21:30:47.609627Z +248 davidr has-props +sources +file + + + + +2010-02-10T23:05:43.000000Z +3bfbd303967164dde107c6accf8d9f84 +2010-01-22T21:30:47.609627Z +248 +davidr + sangoma_interface.cpp file -2009-08-12T17:51:19.000000Z -b7d6e8d02328af549d418df55362e9de -2009-07-31T21:19:58.950307Z -221 +2010-03-23T16:14:34.000000Z +380486fdf6a516f0415b3d0dc8c58f78 +2010-03-17T17:19:58.104959Z +270 davidr sample_linux_compat.h @@ -81,7 +81,7 @@ file -2009-02-27T22:47:06.000000Z +2009-08-25T20:44:41.000000Z 46ae76a473c75513d3b253bd96524f17 2009-03-04T17:25:18.587182Z 109 @@ -93,10 +93,10 @@ file -2009-07-09T16:10:23.000000Z -590894465af0fe4521a74cce3807bcfd -2009-07-09T15:05:43.234603Z -209 +2010-03-23T16:14:34.000000Z +72a11cbee60d7ac08fe0e09d18d34b43 +2010-03-17T17:19:58.104959Z +270 davidr sangoma_port_configurator.cpp @@ -105,11 +105,11 @@ file -2009-08-12T17:51:19.000000Z -01af22c918c1d485ad32f37b1900c84d -2009-07-31T21:19:58.950307Z -221 -davidr +2010-03-23T16:14:34.000000Z +dd259c7c57987730dc55b0c6b67e9497 +2010-03-10T21:56:50.328276Z +268 +jpatel compile.bat file @@ -117,7 +117,7 @@ file -2009-03-11T22:40:27.000000Z +2009-08-25T20:44:41.000000Z 6e4285eeda3d888a8e689516b028327a 2009-03-06T22:48:37.915855Z 120 @@ -129,7 +129,7 @@ file -2009-04-01T22:43:03.000000Z +2009-08-25T20:44:41.000000Z f6d802e64eba57d4a2eed52a86ed6749 2009-03-31T20:38:44.891800Z 133 @@ -141,11 +141,11 @@ file -2009-08-12T17:51:19.000000Z -360507fc313b6e75dc1526c4e661d3e2 -2009-07-31T21:19:58.950307Z -221 -davidr +2010-03-23T16:14:34.000000Z +946fd40742fd52ca8775b538372956af +2010-03-10T21:56:50.328276Z +268 +jpatel sample.cpp file @@ -153,10 +153,10 @@ file -2009-08-12T17:51:19.000000Z -e130dea0b7419e7133d5ccfee92d5f0d -2009-07-31T21:19:58.950307Z -221 +2010-03-23T16:14:34.000000Z +78341087eea6b46442f6bdd047eba2a8 +2010-03-17T17:19:58.104959Z +270 davidr sangoma_cthread.h @@ -165,34 +165,22 @@ file -2009-04-01T22:43:03.000000Z +2009-08-25T20:44:41.000000Z 0f69bcec99210ad26c7391e53a88255e 2009-03-31T20:38:44.891800Z 133 davidr -Makefile.Windows -file - - - - -2009-02-04T23:12:15.000000Z -cebc214b51868b9cdca9356c0d87d074 -2008-12-03T18:36:50.128544Z -36 -davidr - sample.h file -2009-07-09T16:10:23.000000Z -c1835df029ede3c0f8680ab3f157ae96 -2009-07-09T15:05:43.234603Z -209 +2010-03-23T16:14:34.000000Z +c7026dce798297ddf34b7d592f7c5ac1 +2010-03-15T20:12:36.300714Z +269 davidr sangoma_port.cpp @@ -201,10 +189,10 @@ file -2009-05-01T15:10:42.000000Z -bef1d645d075acfae9d9a4a436ef7bb5 -2009-04-29T21:06:58.775163Z -149 +2010-02-10T23:05:43.000000Z +e7715c5d2deb029f1abef3bce2b71aba +2010-01-22T21:30:47.609627Z +248 davidr Makefile.Linux @@ -213,7 +201,7 @@ file -2009-02-27T22:09:44.000000Z +2009-08-25T20:44:41.000000Z 93f1e9c6d533615e6b2dfc7aba2fd0a2 2009-02-27T23:27:29.300080Z 105 @@ -225,10 +213,10 @@ file -2009-02-04T23:12:15.000000Z -e3c5c4b8ac32567bc393629bc4c6f77f -2008-12-23T23:24:52.992798Z -65 +2009-12-04T21:21:35.000000Z +2f4e833b9fd409d8f1c07dded92a32de +2009-12-01T20:38:57.462038Z +235 davidr has-props diff --git a/api/libsangoma/sample_cpp/.svn/text-base/Makefile.Windows.svn-base b/api/libsangoma/sample_cpp/.svn/text-base/Makefile.Windows.svn-base deleted file mode 100644 index 66f1c8e..0000000 --- a/api/libsangoma/sample_cpp/.svn/text-base/Makefile.Windows.svn-base +++ /dev/null @@ -1,8 +0,0 @@ -# -# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source -# file to this component. This file merely indirects to the real make file -# that is shared by all the driver components of the Windows NT DDK -# - -!INCLUDE $(NTMAKEENV)\makefile.def - diff --git a/api/libsangoma/sample_cpp/.svn/text-base/sample.cpp.svn-base b/api/libsangoma/sample_cpp/.svn/text-base/sample.cpp.svn-base index 837ac25..06a149a 100644 --- a/api/libsangoma/sample_cpp/.svn/text-base/sample.cpp.svn-base +++ b/api/libsangoma/sample_cpp/.svn/text-base/sample.cpp.svn-base @@ -37,9 +37,9 @@ #include "sangoma_interface.h" #if defined(__LINUX__) -#include "sample_linux_compat.h" +# include "sample_linux_compat.h" #else -#include +# include #endif #ifndef MAX_PATH @@ -55,13 +55,15 @@ wp_program_settings_t program_settings; callback_functions_t callback_functions; - /***************************************************************** * Prototypes & Defines *****************************************************************/ static int got_rx_data(void *sang_if_ptr, void *rx_data); -static void got_TdmApiEvent(void *sang_if_ptr, void *event_data); +static void got_tdm_api_event(void *sang_if_ptr, void *event_data); +#if USE_WP_LOGGER +static void got_logger_event(void *sang_if_ptr, wp_logger_event_t *logger_event); +#endif typedef struct{ void *sang_if_ptr; @@ -116,16 +118,21 @@ static int set_port_configuration(); sangoma_interface* init(int wanpipe_number, int interface_number) { sangoma_interface *sang_if = NULL; - DBG_MAIN("init()\n"); + DBG_MAIN("%s()\n", __FUNCTION__); + if(program_settings.use_ctrl_dev == 1){ sang_if = new sangoma_api_ctrl_dev(); + }else if(program_settings.use_logger_dev == 1){ + sang_if = new sangoma_api_logger_dev(); }else{ sang_if = new sangoma_interface(wanpipe_number, interface_number); } + if(sang_if->init(&callback_functions)){ delete sang_if; return NULL; } + DBG_MAIN("init(): OK\n"); return sang_if; } @@ -187,11 +194,15 @@ void PrintRxData(wp_api_hdr_t *hdr, void *pdata) rx_counter++; if(program_settings.silent){ if((rx_counter % 1000) == 0){ - INFO_MAIN("Rx counter:%d, Rx datlen : %d\n", rx_counter, datlen); + INFO_MAIN("Rx counter: %d, Rx datlen: %d\n", rx_counter, datlen); +#if 0 + INFO_MAIN("Timestamp: Seconds: %d, Microseconds: %d\n", + hdr->wp_api_hdr_time_stamp_sec, hdr->wp_api_hdr_time_stamp_use); +#endif } return; }else{ - INFO_MAIN("Rx counter:%d, Rx datlen : %d. Data :\n", rx_counter, datlen); + INFO_MAIN("Rx counter: %d, Rx datlen: %d. Data:\n", rx_counter, datlen); } #if 1 @@ -250,7 +261,7 @@ static int got_rx_data(void *sang_if_ptr, void *rxhdr, void *rx_data) } /*! - \fn static void got_TdmApiEvent(void *sang_if_ptr, void *event_data) + \fn static void got_tdm_api_event(void *sang_if_ptr, void *event_data) \brief Callback function indicating Event is pending. \param sang_if_ptr sangoma interface pointer \param event_data API event element strcutre containt header + data @@ -259,7 +270,7 @@ static int got_rx_data(void *sang_if_ptr, void *rxhdr, void *rx_data) Currently Windows launches a thread to handle the event, where Linux handles the event directly. Implementation is left to the user. */ -static void got_TdmApiEvent(void *sang_if_ptr, void *event_data) +static void got_tdm_api_event(void *sang_if_ptr, void *event_data) { TDM_API_EVENT_THREAD_PARAM *param = (TDM_API_EVENT_THREAD_PARAM*)malloc(sizeof(TDM_API_EVENT_THREAD_PARAM)); @@ -369,12 +380,12 @@ void *TdmApiEventThreadFunc(void *lpdwParam) DBG_MAIN("New Alarm State: 0x%X\n", wp_tdm_api_event->wp_api_event_alarm); break; - case WP_API_EVENT_POLARITY_REVERSE: - /* This event may have different meaning on different Telco lines. - * For example, it indicates "Network Initiated Clearing", - * on a British Telecom line. But on some lines it means - * "Start of Caller ID transmission". Please consult with your Telco - * for exact meaning of event. */ + case WP_API_EVENT_POLARITY_REVERSE: + /* This event may have different meaning on different Telco lines. + * For example, it indicates "Network Initiated Clearing", + * on a British Telecom line. But on some lines it means + * "Start of Caller ID transmission". Please consult with your Telco + * for exact meaning of event. */ DBG_MAIN("Polarity Reversal Event: %s\n", WP_API_EVENT_POLARITY_REVERSE_DECODE(wp_tdm_api_event->wp_api_event_polarity_reverse)); break; @@ -390,6 +401,27 @@ void *TdmApiEventThreadFunc(void *lpdwParam) return 0; } +#if USE_WP_LOGGER +/*! + \fn static void got_logger_event(void *sang_if_ptr, wp_logger_event_t *logger_event) + \brief Callback function indicating Logger Event is pending. + \param sang_if_ptr sangoma interface pointer + \param logger_event API Logger Event structure + \return 0 - ok non-zero - Error +*/ +static void got_logger_event(void *sang_if_ptr, wp_logger_event_t *logger_event) +{ + char *timestamp_str = sangoma_ctime( &logger_event->time_stamp_sec ); + + INFO_MAIN("Logger Event Type:\t%s (Logger:%d BitMap: 0x%08X)\n", + wp_decode_logger_event_type(logger_event->logger_type, logger_event->event_type), + logger_event->logger_type, logger_event->event_type); + /* Display Logger Event Timestamp as UNIX-style Date string. */ + INFO_MAIN("Time and Date:\t\t%s\n", + (timestamp_str == NULL ? "Invalid Timestamp" : timestamp_str)); + INFO_MAIN("Logger Event Data: %s\n\n", logger_event->data); +} +#endif /*! \fn int tx_file(sangoma_interface *sang_if) @@ -492,34 +524,48 @@ static int get_user_hex_number() \param argc number of arguments \param argv argument list \return 0 - ok non-zero - Error - */ static int parse_command_line_args(int argc, char* argv[]) { int i; const char *USAGE_STR = "\n" -"Usage: sample [-c] [-i] [-silent]\n" +"Usage: sample [-c] [-i] [options]\n" "\n" -"Options:\n" -"\t-c number Wanpipe number: 1,2,3...\n" -"\t-i number Interface number 0,1,2,3,....\n" -"\t-driver_config configure start/stop driver using volatile....\n" +"\t-c number Wanpipe (Port/Span) number: 1,2,3...\n" +"\t-i number Interface number 1,2,3,....\n" +"options:\n" "\t-silent Disable display of Rx data\n" -"\t-rx2tx All received data automatically transmitted on the SAME interface\n" -"\t-txlength\tnumber\tLength of data frames to be transmitted when 't' key is pressed\n" -"\t-txcount\tnumber Number of test data frames to be transmitted when 't' key is pressed\n" +"\t-driver_config \tStop/Set Configuration/Start a Port....\n" +"\t-rx2tx All received data automatically transmitted on\n" +"\t the SAME interface\n" +"\t-txlength\tnumber\tLength of data frames to be transmitted when 't'\n" +"\t \t \tkey is pressed\n" +"\t-txcount\tnumber Number of test data frames to be transmitted when 't'\n" +"\t \t \tkey is pressed\n" "\t-tx_file_name\tstring\tFile to be transmitted when 't' key is pressed\n" #if USE_STELEPHONY_API -"\t-decode_fsk_cid\t\tDecode FSK Caller ID on an Analog line. For Voice data only.\n" -"\t-encode_fsk_cid\t\tEncode FSK Caller ID on an Analog line. For Voice data only.\n" +"\t-decode_fsk_cid\t\tDecode FSK Caller ID on an Analog line.\n" +"\t \t\tFor Voice data only.\n" +"\t-encode_fsk_cid\t\tEncode FSK Caller ID on an Analog line.\n" +"\t \t\tFor Voice data only.\n" "\t-encode_sw_dtmf\t\tEncode SW DTMF on an line. For Voice data only.\n" "\t-sw_dtmf Enable Sangoma Software DTMF decoder. For Voice data only.\n" "\t-decode_q931 Enable Sangoma Q931 decoder. For HDLC (Dchannel) data only.\n" "\t-alaw\t\t Use Alaw codec instead of default MuLaw codec for Voice data.\n" +"\t-rm_txgain\t Set txgain for FXS/FXO modules.\n" +"\t \t\tFXO range from -150 to 120, FXS 35 or -35\n" +"\t-rm_rxgain\t Set txgain for FXS/FXO modules.\n" +"\t \t\tFXO range from -150 to 120, FXS 35 or -35\n" #endif #if 0 -"\t-use_ctrl_dev Use the global 'wptdm_ctrl' device to Get events and to Control device.\n" +"\t-use_ctrl_dev \tUse the global 'wptdm_ctrl' device to Get events from\n" +"\t \tall API devices.\n" +#endif +"\t-use_logger_dev \tUse the global Logger device to Get Log Messages\n" +"\t \tfrom API driver.\n" +#ifdef WP_API_FEATURE_LIBSNG_HWEC +"\t-use_hwec \tInitialize/Configure/Use the Hardware Echo Canceller\n" #endif "\n" "Example: sample -c 1 -i 1\n"; @@ -527,8 +573,10 @@ static int parse_command_line_args(int argc, char* argv[]) memset(&program_settings, 0, sizeof(wp_program_settings_t)); program_settings.wanpipe_number = 1; program_settings.interface_number = 1; - program_settings.txlength = 128; + program_settings.txlength = 80; program_settings.txcount = 1; + program_settings.rxgain = 0xFFFF; //FXO/FXS rx gain unchanged + program_settings.txgain = 0xFFFF; //FXO/FXS tx gain unchanged for(i = 1; i < argc;){ @@ -618,6 +666,29 @@ static int parse_command_line_args(int argc, char* argv[]) }else if(_stricmp(argv[i], "-use_ctrl_dev") == 0){ INFO_MAIN("Using ctrl_dev...\n"); program_settings.use_ctrl_dev = 1; + }else if(_stricmp(argv[i], "-use_logger_dev") == 0){ + INFO_MAIN("Using logger_dev...\n"); + program_settings.use_logger_dev = 1; + }else if(_stricmp(argv[i], "-rm_txgain") == 0){ + if (i+1 > argc-1){ + INFO_MAIN("No txgain provided!\n"); + return 1; + } + program_settings.txgain = atoi(argv[i+1]); + i++; + INFO_MAIN("Setting txgain to %d.\n", program_settings.txgain); + }else if(_stricmp(argv[i], "-rm_rxgain") == 0){ + if (i+1 > argc-1){ + INFO_MAIN("No rxgain provided!\n"); + return 1; + } + program_settings.rxgain = atoi(argv[i+1]); + i++; + INFO_MAIN("Setting rxgain to %d.\n", program_settings.txgain); + }else if(_stricmp(argv[i], "-use_hwec") == 0){ + + INFO_MAIN("Using hardware echo canceller...\n"); + program_settings.use_hardware_echo_canceller = 1; }else{ INFO_MAIN("Error: Invalid Argument %s\n",argv[i]); return 1; @@ -654,8 +725,10 @@ int __cdecl main(int argc, char* argv[]) //////////////////////////////////////////////////////////////////////////// memset(&callback_functions, 0x00, sizeof(callback_functions)); callback_functions.got_rx_data = got_rx_data; - callback_functions.got_TdmApiEvent = got_TdmApiEvent; - + callback_functions.got_tdm_api_event = got_tdm_api_event; +#if USE_WP_LOGGER + callback_functions.got_logger_event = got_logger_event; +#endif //////////////////////////////////////////////////////////////////////////// if(parse_command_line_args(argc, argv)){ return 1; @@ -693,65 +766,92 @@ int __cdecl main(int argc, char* argv[]) cleanup(sang_if); return rc; } + if(program_settings.txgain != 0xFFFF) { + INFO_MAIN("Applying txgain...\n"); + if (sang_if->tdm_control_rm_txgain(program_settings.txgain)){ + INFO_MAIN("Failed to apply txgain!\n"); + } + } + + /* FXS cannot detect if phone is connected or not when the card is started + therefore tranmit following two events for Set Polarity to work */ + if(sang_if->get_adapter_type() == WAN_MEDIA_FXOFXS && sang_if->get_sub_media() == MOD_TYPE_FXS) { + INFO_MAIN("Setting Proper hookstates on FXS\n"); + sang_if->tdm_txsig_offhook(); + sang_if->tdm_txsig_onhook(); + } + if(program_settings.rxgain != 0xFFFF) { + INFO_MAIN("Applying rxgain...\n"); + if (sang_if->tdm_control_rm_rxgain(program_settings.rxgain)){ + INFO_MAIN("Failed to apply rxgain!\n"); + } + } do{ EnterCriticalSection(&PrintCriticalSection); + INFO_MAIN("Press 'q' to quit the program.\n"); - INFO_MAIN("Press 't' to transmit data.\n"); INFO_MAIN("Press 's' to get Operational Statistics.\n"); INFO_MAIN("Press 'f' to reset (flush) Operational Statistics.\n"); - INFO_MAIN("Press 'v' to get API driver version.\n"); - if(sang_if->get_adapter_type() == WAN_MEDIA_T1 || sang_if->get_adapter_type() == WAN_MEDIA_E1){ - INFO_MAIN("Press 'a' to get T1/E1 alarms.\n"); - //RBS (CAS) commands - INFO_MAIN("Press 'g' to get RBS bits.\n"); - INFO_MAIN("Press 'r' to set RBS bits.\n"); - INFO_MAIN("Press '1' to read FE register. Warning: used by Sangoma Techsupport only!\n"); - INFO_MAIN("Press '2' to write FE register. Warning: used by Sangoma Techsupport only!\n"); - } - INFO_MAIN("Press 'i' to set Tx idle data buffer (BitStream only).\n"); - switch(sang_if->get_adapter_type()) - { - case WAN_MEDIA_T1: - //those commands valid only for T1 - INFO_MAIN("Press 'l' to send 'activate remote loop back' signal.\n"); - INFO_MAIN("Press 'd' to send 'deactivate remote loop back' signal.\n"); - break; - case WAN_MEDIA_FXOFXS: - switch(sang_if->get_sub_media()) + if(program_settings.use_logger_dev != 1){ + /* these options valid for non-logger api devices */ + INFO_MAIN("Press 't' to transmit data.\n"); + INFO_MAIN("Press 'v' to get API driver version.\n"); + + if(sang_if->get_adapter_type() == WAN_MEDIA_T1 || sang_if->get_adapter_type() == WAN_MEDIA_E1){ + INFO_MAIN("Press 'a' to get T1/E1 alarms.\n"); + //RBS (CAS) commands + INFO_MAIN("Press 'g' to get RBS bits.\n"); + INFO_MAIN("Press 'r' to set RBS bits.\n"); + INFO_MAIN("Press '1' to read FE register. Warning: used by Sangoma Techsupport only!\n"); + INFO_MAIN("Press '2' to write FE register. Warning: used by Sangoma Techsupport only!\n"); + } + INFO_MAIN("Press 'i' to set Tx idle data buffer (BitStream only).\n"); + switch(sang_if->get_adapter_type()) { - case MOD_TYPE_FXS: - INFO_MAIN("Press 'e' to listen to test tones on a phone connected to the A200-FXS\n"); - INFO_MAIN("Press 'c' to ring/stop ring phone connected to the A200-FXS\n"); - INFO_MAIN("Press 'n' to enable/disable reception of ON/OFF Hook events on A200-FXS\n"); - INFO_MAIN("Press 'm' to enable DTMF events (on SLIC chip) on A200-FXS\n"); - INFO_MAIN("Press 'j' to enable/disable reception of Ring Trip events on A200-FXS\n"); + case WAN_MEDIA_T1: + //those commands valid only for T1 + INFO_MAIN("Press 'l' to send 'activate remote loop back' signal.\n"); + INFO_MAIN("Press 'd' to send 'deactivate remote loop back' signal.\n"); break; + case WAN_MEDIA_FXOFXS: + switch(sang_if->get_sub_media()) + { + case MOD_TYPE_FXS: + INFO_MAIN("Press 'e' to listen to test tones on a phone connected to the A200-FXS\n"); + INFO_MAIN("Press 'c' to ring/stop ring phone connected to the A200-FXS\n"); + INFO_MAIN("Press 'n' to enable/disable reception of ON/OFF Hook events on A200-FXS\n"); + INFO_MAIN("Press 'm' to enable DTMF events (on SLIC chip) on A200-FXS\n"); + INFO_MAIN("Press 'j 'to enable/disable reception of Ring Trip events on A200-FXS\n"); + INFO_MAIN("Press 'k' to transmit kewl - drop line voltage on the line connected to the A200-FXS\n"); + INFO_MAIN("Press 'h' to set polarity on the line connected to the A200-fXS\n"); + INFO_MAIN("Press 'u' to transmit onhooktransfer on the line connected to the A200-FXS\n"); + break; - case MOD_TYPE_FXO: - INFO_MAIN("Press 'u' to enable/disable reception of Ring Detect events on A200-FXO\n"); - INFO_MAIN("Press 'h' to transmit ON/OFF hook signals on A200-FXO\n"); - INFO_MAIN("Press 'a' to get Line Status (Connected/Disconnected)\n"); + case MOD_TYPE_FXO: + INFO_MAIN("Press 'u' to enable/disable reception of Ring Detect events on A200-FXO\n"); + INFO_MAIN("Press 'h' to transmit ON/OFF hook signals on A200-FXO\n"); + INFO_MAIN("Press 'a' to get Line Status (Connected/Disconnected)\n"); + break; + } + break; + case WAN_MEDIA_BRI: + INFO_MAIN("Press 'k' to Activate/Deactivate ISDN BRI line\n"); + INFO_MAIN("Press 'l' to enable bri bchan loopback\n"); + INFO_MAIN("Press 'd' to disable bri bchan loopback\n"); break; } - break; - case WAN_MEDIA_BRI: - INFO_MAIN("Press 'k' to Activate/Deactivate ISDN BRI line\n"); - INFO_MAIN("Press 'l' to enable bri bchan loopback\n"); - INFO_MAIN("Press 'd' to disable bri bchan loopback\n"); - break; - } - INFO_MAIN("Press 'o' to enable DTMF events (on Octasic chip)\n"); - if (program_settings.encode_sw_dtmf) { - INFO_MAIN("Press 'x' to send software DTMF\n"); - } - if (program_settings.encode_fsk_cid) { - INFO_MAIN("Press 'z' to send software FSK Caller ID\n"); - } + INFO_MAIN("Press 'o' to enable DTMF events (on Octasic chip)\n"); + if (program_settings.encode_sw_dtmf) { + INFO_MAIN("Press 'x' to send software DTMF\n"); + } + if (program_settings.encode_fsk_cid) { + INFO_MAIN("Press 'z' to send software FSK Caller ID\n"); + } + }//if(program_settings.use_logger_dev != 1) LeaveCriticalSection(&PrintCriticalSection); - user_selection = tolower(_getch()); switch(user_selection) { @@ -772,7 +872,12 @@ int __cdecl main(int argc, char* argv[]) } break; case 's': - { + if(program_settings.use_logger_dev == 1){ + wp_logger_stats_t stats; + /* Warning: for demonstration purposes only, it is assumed, + * that 'sang_if' is 'sangoma_api_logger_dev'. */ + ((sangoma_api_logger_dev*)sang_if)->get_logger_dev_operational_stats(&stats); + }else{ wanpipe_chan_stats_t stats; sang_if->get_operational_stats(&stats); } @@ -1035,16 +1140,20 @@ user_retry_ring_e_d: break; case 'u': //Enable/Disable Ring Detect events on FXO. - INFO_MAIN("Press 'e' to ENABLE Rx Ring Detect Events, 'd' to DISABLE Rx Ring Detect Events.\n"); - INFO_MAIN("\n"); - switch(tolower(_getch())) - { - case 'e': - sang_if->tdm_enable_ring_detect_events(); - break; - case 'd': - default: - sang_if->tdm_disable_ring_detect_events(); + if(sang_if->get_sub_media()==MOD_TYPE_FXO){ + INFO_MAIN("Press 'e' to ENABLE Rx Ring Detect Events, 'd' to DISABLE Rx Ring Detect Events.\n"); + INFO_MAIN("\n"); + switch(tolower(_getch())) + { + case 'e': + sang_if->tdm_enable_ring_detect_events(); + break; + case 'd': + default: + sang_if->tdm_disable_ring_detect_events(); + } + }else if(sang_if->get_sub_media()==MOD_TYPE_FXS){ + sang_if->tdm_txsig_onhooktransfer(); } break; case 'j': @@ -1062,29 +1171,56 @@ user_retry_ring_e_d: } break; case 'h': - INFO_MAIN("Press 'e' to transmit OFF hook signal, 'd' to transmit ON hook signal.\n"); - INFO_MAIN("\n"); - switch(tolower(_getch())) - { - case 'e': - sang_if->fxo_go_off_hook(); - break; - case 'd': - default: - sang_if->fxo_go_on_hook(); + if(sang_if->get_sub_media()==MOD_TYPE_FXO) { + INFO_MAIN("Press 'e' to transmit OFF hook signal, 'd' to transmit ON hook signal.\n"); + INFO_MAIN("\n"); + switch(tolower(_getch())) + { + case 'e': + sang_if->fxo_go_off_hook(); + break; + case 'd': + default: + sang_if->fxo_go_on_hook(); + } + }else if(sang_if->get_sub_media()==MOD_TYPE_FXS ) { + INFO_MAIN("Press 'f' for forward, 'r' to for reverse.\n"); + INFO_MAIN("\n"); + switch(tolower(_getch())) + { + case 'f': + sang_if->tdm_set_rm_polarity(0); + break; + case 'r': + sang_if->tdm_set_rm_polarity(1); + break; + default: + //toggle it + sang_if->tdm_set_rm_polarity(1); + sang_if->tdm_set_rm_polarity(0); + } } break; case 'k': - INFO_MAIN("Press 'e' to Activate, 'd' to De-Activate line.\n"); - INFO_MAIN("\n"); - switch(tolower(_getch())) - { - case 'e': - sang_if->tdm_front_end_activate(); - break; - case 'd': - default: - sang_if->tdm_front_end_deactivate(); + if( sang_if->get_adapter_type() == WAN_MEDIA_BRI ) { + INFO_MAIN("Press 'e' to Activate, 'd' to De-Activate line.\n"); + INFO_MAIN("\n"); + switch(tolower(_getch())) + { + case 'e': + sang_if->tdm_front_end_activate(); + break; + case 'd': + default: + sang_if->tdm_front_end_deactivate(); + } + }else if(sang_if->get_adapter_type()== WAN_MEDIA_FXOFXS) { + if(sang_if->get_sub_media()==MOD_TYPE_FXS) { + sang_if->tdm_txsig_kewl(); + sangoma_msleep(5000); + //to restore line current after txsig kewl + sang_if->tdm_txsig_offhook(); + } } break; case 'p': @@ -1127,8 +1263,18 @@ user_retry_ring_e_d: break; case 'z': { - INFO_MAIN("Sending CallerID.\n"); - sang_if->sendCallerID("Sangoma Rocks", "9054741990"); + if(WAN_MEDIA_FXOFXS == sang_if->get_adapter_type() && MOD_TYPE_FXS == sang_if->get_sub_media() ){ + //Ring the line + sang_if->start_ringing_phone(); + sangoma_msleep(2000); + //txsig offhook + sang_if->fxs_txsig_offhook(); + INFO_MAIN("Sending CallerID.\n"); + sang_if->sendCallerID("Sangoma Rocks", "9054741990"); + }else{ + INFO_MAIN("Sending CallerID.\n"); + sang_if->sendCallerID("Sangoma Rocks", "9054741990"); + } } break; #endif @@ -1184,7 +1330,8 @@ user_retry_ring_e_d: static int set_port_configuration() { - int rc = 0, is_te1_card = 0, user_selection; + int rc = 0, user_selection; + int is_te1_card = 0, is_analog_card = 0; hardware_info_t hardware_info; port_cfg_t port_cfg; @@ -1214,15 +1361,6 @@ static int set_port_configuration() return 3; } -#if 0 -defined(__WINDOWS__) - rc = sng_port_cfg_obj->open_port_registry_key(&hardware_info); - if(rc != SANG_STATUS_SUCCESS){ - delete sng_port_cfg_obj; - return 3; - } -#endif - memset(&port_cfg, 0x00, sizeof(port_cfg_t)); switch(hardware_info.card_model) @@ -1233,17 +1371,21 @@ defined(__WINDOWS__) case A108_ADPTR_8TE1: is_te1_card = 1; break; + case A200_ADPTR_ANALOG: + case A400_ADPTR_ANALOG: + is_analog_card = 1; + break; + case AFT_ADPTR_FLEXBRI: + //B700, a hybrid card - may have both ISDN BRI and Analog ports + break; } if(is_te1_card){ - INFO_MAIN("\n"); INFO_MAIN("Press 't' to set T1 configration.\n"); INFO_MAIN("Press 'e' to set E1 configration.\n"); - try_again: user_selection = tolower(_getch()); - switch(user_selection) { case 't'://T1 @@ -1254,27 +1396,38 @@ try_again: rc=sng_port_cfg_obj->initialize_e1_tdm_span_voice_api_configration_structure(&port_cfg,&hardware_info,program_settings.wanpipe_number); break; + case 'q'://quit the application + rc = 1; + break; + default: INFO_MAIN("Invalid command %c.\n",user_selection); goto try_again; break; }//switch(user_selection) + }//if(is_te1_card) - } else { //if(is_te1_card) -#if 0 + if(is_analog_card){ //read current configuration: if(sng_port_cfg_obj->get_configration(&port_cfg)){ rc = 1; }else{ //print the current configuration: sng_port_cfg_obj->print_port_cfg_structure(&port_cfg); - //as an EXAMPLE, set the same configration as the current one: - //rc = sng_port_cfg_obj->set_default_configuration(&port_cfg); - } -#else - INFO_MAIN("Unsupported Card %i\n",hardware_info.card_model); - rc = 1; +#if 0 + //as an EXAMPLE, enable Loop Current Monitoring for Analog FXO: + rc=sng_port_cfg_obj->control_analog_rm_lcm(&port_cfg, 1); #endif +#if 0 + //as an EXAMPLE, set Operation mode for FXO: + rc=sng_port_cfg_obj->set_analog_opermode(&port_cfg, "TBR21"); +#endif + } + }//if(is_analog_card) + + if(!is_te1_card && !is_analog_card){ + INFO_MAIN("Unsupported Card %d\n", hardware_info.card_model); + rc = 1; } do{ @@ -1282,7 +1435,6 @@ try_again: ERR_MAIN("Failed to Initialize Port Configuratoin structure!\n"); break; } - #if 1 INFO_MAIN("Stopping PORT for re-configuration!\n"); if ((rc = sng_port_cfg_obj->stop_port())) { @@ -1316,7 +1468,7 @@ try_again: delete sng_port_cfg_obj; } - sangoma_msleep(2000);//wait a little (2 seconds) for initialization to complete + sangoma_msleep(2000);//wait 2 seconds for initialization to complete return rc; } @@ -1334,7 +1486,7 @@ static void FSKCallerIDEvent(void *callback_context, if(Name){ INFO_MAIN("Name: %s\n", Name); -#if 1 +#if 0 printf("caller name in SINGLE byte hex:\n"); for(unsigned int ind = 0; ind < strlen(Name); ind++){ printf("Name[%02d]: 0x%02X\n", ind, Name[ind]); @@ -1345,9 +1497,11 @@ static void FSKCallerIDEvent(void *callback_context, for(unsigned int ind = 0; ind < strlen(Name); ind += 2){ printf("Name[%02d]: 0x%04X\n", ind, *(unsigned short*)&Name[ind]); } - printf("\n"); #endif + printf("\n"); } + + if(CallerNumber){ INFO_MAIN("CallerNumber: %s\n", CallerNumber); } @@ -1445,7 +1599,7 @@ static void FSKCallerIDTransmit (void *callback_context, void *FskCidBuffer) /* FSK CID buffer can be big (~8000 bytes), we don't want to block the calling thread, so start a new thread to transmit FSK CID. */ - sang_if->CreateSwDtmfTxThread(FskCidBuffer); + sang_if->CreateFskCidTxThread(FskCidBuffer); } #if 0 diff --git a/api/libsangoma/sample_cpp/.svn/text-base/sample.h.svn-base b/api/libsangoma/sample_cpp/.svn/text-base/sample.h.svn-base index 934c344..d563360 100644 --- a/api/libsangoma/sample_cpp/.svn/text-base/sample.h.svn-base +++ b/api/libsangoma/sample_cpp/.svn/text-base/sample.h.svn-base @@ -9,6 +9,7 @@ #include #endif +#define USE_WP_LOGGER 1 #define SAMPLE_CPP_MAX_PATH 1024 typedef struct{ @@ -27,6 +28,10 @@ typedef struct{ unsigned int txcount; unsigned char driver_config; unsigned char use_ctrl_dev; + unsigned char use_logger_dev; + int txgain; + int rxgain; + unsigned char use_hardware_echo_canceller; }wp_program_settings_t; #define DEV_NAME_LEN 100 @@ -38,7 +43,7 @@ typedef struct{ //Recieved data int (*got_rx_data)(void *sang_if_ptr, void *rxhdr, void *rx_data); //TDM events - void (*got_TdmApiEvent)(void *sang_if_ptr, void *event_data); + void (*got_tdm_api_event)(void *sang_if_ptr, void *event_data); #if USE_STELEPHONY_API //FSK Caller ID detected void (*FSKCallerIDEvent)(void *sang_if_ptr, char * Name, char * CallerNumber, char * CalledNumber, char * DateTime); @@ -51,6 +56,11 @@ typedef struct{ //DTMF buffer ready for transmission events void (*SwDtmfTransmit)(void *callback_context, void* buffer); #endif + +#if USE_WP_LOGGER + void (*got_logger_event)(void *sang_if_ptr, wp_logger_event_t *logger_event); +#endif + }callback_functions_t; static void DecodeLastError(LPSTR lpszFunction) @@ -70,7 +80,7 @@ static void DecodeLastError(LPSTR lpszFunction) NULL ); // Display the string. - printf("Last Error: %s (GetLastError() returned: %d)\n", lpMsgBuf, dwLastErr); + printf("Last Error: %s (GetLastError() returned: %d)\n", (char*)lpMsgBuf, dwLastErr); // Free the buffer. LocalFree( lpMsgBuf ); #endif diff --git a/api/libsangoma/sample_cpp/.svn/text-base/sample.sln.svn-base b/api/libsangoma/sample_cpp/.svn/text-base/sample.sln.svn-base index 8e45cb9..6493e1d 100644 --- a/api/libsangoma/sample_cpp/.svn/text-base/sample.sln.svn-base +++ b/api/libsangoma/sample_cpp/.svn/text-base/sample.sln.svn-base @@ -1,7 +1,7 @@  -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample", "sample.vcproj", "{20071FBB-88EE-41D9-A728-68A883BFC68B}" +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample_cpp", "sample.vcproj", "{20071FBB-88EE-41D9-A728-68A883BFC68B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/api/libsangoma/sample_cpp/.svn/text-base/sample.vcproj.svn-base b/api/libsangoma/sample_cpp/.svn/text-base/sample.vcproj.svn-base index 6f9ae4e..37e16da 100644 --- a/api/libsangoma/sample_cpp/.svn/text-base/sample.vcproj.svn-base +++ b/api/libsangoma/sample_cpp/.svn/text-base/sample.vcproj.svn-base @@ -1,10 +1,11 @@ - @@ -168,6 +168,8 @@ SuppressStartupBanner="true" ProgramDatabaseFile=".\Release/sample.pdb" SubSystem="1" + RandomizedBaseAddress="1" + DataExecutionPrevention="0" TargetMachine="1" /> - diff --git a/api/libsangoma/sample_cpp/.svn/text-base/sangoma_interface.cpp.svn-base b/api/libsangoma/sample_cpp/.svn/text-base/sangoma_interface.cpp.svn-base index f7a0a59..50265f0 100644 --- a/api/libsangoma/sample_cpp/.svn/text-base/sangoma_interface.cpp.svn-base +++ b/api/libsangoma/sample_cpp/.svn/text-base/sangoma_interface.cpp.svn-base @@ -5,7 +5,6 @@ ////////////////////////////////////////////////////////////////////// #include "sangoma_interface.h" -#include #define DBG_IFACE if(1)printf #define INFO_IFACE if(1)printf @@ -18,13 +17,22 @@ #define NUMBER_OF_WAIT_OBJECTS 1 +#ifdef WP_API_FEATURE_LIBSNG_HWEC /* defined in wanpipe_api_iface.h */ +# include "wanpipe_events.h" +# include "wanec_api.h" +sangoma_status_t config_hwec(char *strDeviceName, unsigned long in_ulChannelMap); +#endif + extern wp_program_settings_t program_settings; sangoma_interface::sangoma_interface(int wanpipe_number, int interface_number) { - DBG_IFACE( "sangoma_interface::sangoma_interface()\n"); + DBG_IFACE("%s()\n", __FUNCTION__); memset(device_name, 0x00, DEV_NAME_LEN); + memset(&wan_udp, 0x00, sizeof(wan_udp)); + memset(&tdm_api_cmd, 0x00, sizeof(tdm_api_cmd)); + memset(&wp_api, 0x00, sizeof(wp_api)); WanpipeNumber = wanpipe_number; InterfaceNumber = interface_number; @@ -39,7 +47,6 @@ sangoma_interface::sangoma_interface(int wanpipe_number, int interface_number) terminate_tx_rx_threads = 0; is_rbs_monitoring_enabled = 0; - memset(&wp_api, 0, sizeof(wp_api)); sng_wait_obj = NULL; ////////////////////////////////////////////////////////////////// @@ -60,6 +67,7 @@ sangoma_interface::sangoma_interface(int wanpipe_number, int interface_number) protocol_cb_size = sizeof(wan_mgmt_t)+sizeof(wan_cmd_t)+1; wan_protocol = 0; adapter_type = 0; + is_logger_dev = 0; #if USE_STELEPHONY_API stelObj = DtmfBuffer = FskCidBuffer = NULL; @@ -77,13 +85,13 @@ sangoma_interface::sangoma_interface(int wanpipe_number, int interface_number) sangoma_interface::~sangoma_interface() { - DBG_IFACE( "sangoma_interface::~sangoma_interface()\n"); + DBG_IFACE("%s()\n", __FUNCTION__); cleanup(); } int sangoma_interface::init(callback_functions_t *callback_functions_ptr) { - DBG_IFACE("sangoma_interface::init()\n"); + DBG_IFACE("%s()\n", __FUNCTION__); memcpy(&callback_functions, callback_functions_ptr, sizeof(callback_functions_t)); @@ -226,7 +234,7 @@ int sangoma_interface::get_wan_config() DO_COMMAND(wan_udp); if (wan_udp.wan_udphdr_return_code){ - ERR_IFACE( "Error: Command WANPIPEMON_GET_PROTOCOL failed! return code: 0x%X", + ERR_IFACE( "Error: Command WAN_GET_PROTOCOL failed! return code: 0x%X\n", wan_udp.wan_udphdr_return_code); return WAN_FALSE; } @@ -341,146 +349,146 @@ void sangoma_interface::get_te1_56k_stat(void) } fe_stats = (sdla_fe_stats_t*)get_wan_udphdr_data_ptr(0); - - if (adapter_type == WAN_MEDIA_T1 || adapter_type == WAN_MEDIA_E1){ - INFO_IFACE("***** %s: %s Alarms (Framer) *****\n\n", - device_name, (adapter_type == WAN_MEDIA_T1) ? "T1" : "E1"); - INFO_IFACE("ALOS:\t%s\t| LOS:\t%s\n", - WAN_TE_PRN_ALARM_ALOS(fe_stats->alarms), - WAN_TE_PRN_ALARM_LOS(fe_stats->alarms)); - INFO_IFACE("RED:\t%s\t| AIS:\t%s\n", - WAN_TE_PRN_ALARM_RED(fe_stats->alarms), - WAN_TE_PRN_ALARM_AIS(fe_stats->alarms)); - INFO_IFACE("LOF:\t%s\t| RAI:\t%s\n", - WAN_TE_PRN_ALARM_LOF(fe_stats->alarms), - WAN_TE_PRN_ALARM_RAI(fe_stats->alarms)); - - if (fe_stats->alarms & WAN_TE_ALARM_LIU){ - INFO_IFACE("\n***** %s: %s Alarms (LIU) *****\n\n", - device_name, (adapter_type == WAN_MEDIA_T1) ? "T1" : "E1"); - INFO_IFACE("Short Circuit:\t%s\n", - WAN_TE_PRN_ALARM_LIU_SC(fe_stats->alarms)); - INFO_IFACE("Open Circuit:\t%s\n", - WAN_TE_PRN_ALARM_LIU_OC(fe_stats->alarms)); - INFO_IFACE("Loss of Signal:\t%s\n", - WAN_TE_PRN_ALARM_LIU_LOS(fe_stats->alarms)); - } - - }else if (adapter_type == WAN_MEDIA_DS3 || adapter_type == WAN_MEDIA_E3){ - INFO_IFACE("***** %s: %s Alarms *****\n\n", - device_name, (adapter_type == WAN_MEDIA_DS3) ? "DS3" : "E3"); - - if (adapter_type == WAN_MEDIA_DS3){ - INFO_IFACE("AIS:\t%s\t| LOS:\t%s\n", - WAN_TE3_AIS_ALARM(fe_stats->alarms), - WAN_TE3_LOS_ALARM(fe_stats->alarms)); - - INFO_IFACE("OOF:\t%s\t| YEL:\t%s\n", - WAN_TE3_OOF_ALARM(fe_stats->alarms), - WAN_TE3_YEL_ALARM(fe_stats->alarms)); - }else{ - INFO_IFACE("AIS:\t%s\t| LOS:\t%s\n", - WAN_TE3_AIS_ALARM(fe_stats->alarms), - WAN_TE3_LOS_ALARM(fe_stats->alarms)); - - INFO_IFACE("OOF:\t%s\t| YEL:\t%s\n", - WAN_TE3_OOF_ALARM(fe_stats->alarms), - WAN_TE3_YEL_ALARM(fe_stats->alarms)); - - INFO_IFACE("LOF:\t%s\t\n", - WAN_TE3_LOF_ALARM(fe_stats->alarms)); - } - - }else if (adapter_type == WAN_MEDIA_56K){ - INFO_IFACE("***** %s: 56K CSU/DSU Alarms *****\n\n\n", device_name); - INFO_IFACE("In Service:\t\t%s\tData mode idle:\t\t%s\n", - INS_ALARM_56K(fe_stats->alarms), - DMI_ALARM_56K(fe_stats->alarms)); - - INFO_IFACE("Zero supp. code:\t%s\tCtrl mode idle:\t\t%s\n", - ZCS_ALARM_56K(fe_stats->alarms), - CMI_ALARM_56K(fe_stats->alarms)); - - INFO_IFACE("Out of service code:\t%s\tOut of frame code:\t%s\n", - OOS_ALARM_56K(fe_stats->alarms), - OOF_ALARM_56K(fe_stats->alarms)); - - INFO_IFACE("Valid DSU NL loopback:\t%s\tUnsigned mux code:\t%s\n", - DLP_ALARM_56K(fe_stats->alarms), - UMC_ALARM_56K(fe_stats->alarms)); - - INFO_IFACE("Rx loss of signal:\t%s\t\n", - RLOS_ALARM_56K(fe_stats->alarms)); - - }else{ - INFO_IFACE("***** %s: Unknown Front End 0x%X *****\n\n", - device_name, adapter_type); - } - - if (adapter_type == WAN_MEDIA_T1 || adapter_type == WAN_MEDIA_E1){ - sdla_te_pmon_t* pmon = &fe_stats->te_pmon; - - INFO_IFACE("\n\n***** %s: %s Performance Monitoring Counters *****\n\n", - device_name, (adapter_type == WAN_MEDIA_T1) ? "T1" : "E1"); - if (pmon->mask & WAN_TE_BIT_PMON_LCV){ - INFO_IFACE("Line Code Violation\t: %d\n", - pmon->lcv_errors); - } - if (pmon->mask & WAN_TE_BIT_PMON_BEE){ - INFO_IFACE("Bit Errors (CRC6/Ft/Fs)\t: %d\n", - pmon->bee_errors); - } - if (pmon->mask & WAN_TE_BIT_PMON_OOF){ - INFO_IFACE("Out of Frame Errors\t: %d\n", - pmon->oof_errors); - } - if (pmon->mask & WAN_TE_BIT_PMON_FEB){ - INFO_IFACE("Far End Block Errors\t: %d\n", - pmon->feb_errors); - } - if (pmon->mask & WAN_TE_BIT_PMON_CRC4){ - INFO_IFACE("CRC4 Errors\t\t: %d\n", - pmon->crc4_errors); - } - if (pmon->mask & WAN_TE_BIT_PMON_FER){ - INFO_IFACE("Framing Bit Errors\t: %d\n", - pmon->fer_errors); - } - if (pmon->mask & WAN_TE_BIT_PMON_FAS){ - INFO_IFACE("FAS Errors\t\t: %d\n", - pmon->fas_errors); - } - } - - if (adapter_type == WAN_MEDIA_DS3 || adapter_type == WAN_MEDIA_E3){ - sdla_te3_pmon_t* pmon = &fe_stats->u.te3_pmon; - - INFO_IFACE("\n\n***** %s: %s Performance Monitoring Counters *****\n\n", - device_name, (adapter_type == WAN_MEDIA_DS3) ? "DS3" : "E3"); - - INFO_IFACE("Framing Bit Error:\t%d\tLine Code Violation:\t%d\n", - pmon->pmon_framing, - pmon->pmon_lcv); - - if (adapter_type == WAN_MEDIA_DS3){ - INFO_IFACE("Parity Error:\t\t%d\n", - pmon->pmon_parity); - INFO_IFACE("CP-Bit Error Event:\t%d\tFEBE Event:\t\t%d\n", - pmon->pmon_cpbit, - pmon->pmon_febe); - }else{ - INFO_IFACE("Parity Error:\t%d\tFEBE Event:\t\t%d\n", - pmon->pmon_parity, - pmon->pmon_febe); - } - } - - if (adapter_type == WAN_MEDIA_T1 || adapter_type == WAN_MEDIA_E1){ - if (strlen(fe_stats->u.te1_stats.rxlevel)){ - INFO_IFACE("\n\nRx Level\t: %s\n", - fe_stats->u.te1_stats.rxlevel); - } - } + + if (adapter_type == WAN_MEDIA_T1 || adapter_type == WAN_MEDIA_E1){ + INFO_IFACE("***** %s: %s Alarms (Framer) *****\n\n", + device_name, (adapter_type == WAN_MEDIA_T1) ? "T1" : "E1"); + INFO_IFACE("ALOS:\t%s\t| LOS:\t%s\n", + WAN_TE_PRN_ALARM_ALOS(fe_stats->alarms), + WAN_TE_PRN_ALARM_LOS(fe_stats->alarms)); + INFO_IFACE("RED:\t%s\t| AIS:\t%s\n", + WAN_TE_PRN_ALARM_RED(fe_stats->alarms), + WAN_TE_PRN_ALARM_AIS(fe_stats->alarms)); + INFO_IFACE("LOF:\t%s\t| RAI:\t%s\n", + WAN_TE_PRN_ALARM_LOF(fe_stats->alarms), + WAN_TE_PRN_ALARM_RAI(fe_stats->alarms)); + + if (fe_stats->alarms & WAN_TE_ALARM_LIU){ + INFO_IFACE("\n***** %s: %s Alarms (LIU) *****\n\n", + device_name, (adapter_type == WAN_MEDIA_T1) ? "T1" : "E1"); + INFO_IFACE("Short Circuit:\t%s\n", + WAN_TE_PRN_ALARM_LIU_SC(fe_stats->alarms)); + INFO_IFACE("Open Circuit:\t%s\n", + WAN_TE_PRN_ALARM_LIU_OC(fe_stats->alarms)); + INFO_IFACE("Loss of Signal:\t%s\n", + WAN_TE_PRN_ALARM_LIU_LOS(fe_stats->alarms)); + } + + }else if (adapter_type == WAN_MEDIA_DS3 || adapter_type == WAN_MEDIA_E3){ + INFO_IFACE("***** %s: %s Alarms *****\n\n", + device_name, (adapter_type == WAN_MEDIA_DS3) ? "DS3" : "E3"); + + if (adapter_type == WAN_MEDIA_DS3){ + INFO_IFACE("AIS:\t%s\t| LOS:\t%s\n", + WAN_TE3_AIS_ALARM(fe_stats->alarms), + WAN_TE3_LOS_ALARM(fe_stats->alarms)); + + INFO_IFACE("OOF:\t%s\t| YEL:\t%s\n", + WAN_TE3_OOF_ALARM(fe_stats->alarms), + WAN_TE3_YEL_ALARM(fe_stats->alarms)); + }else{ + INFO_IFACE("AIS:\t%s\t| LOS:\t%s\n", + WAN_TE3_AIS_ALARM(fe_stats->alarms), + WAN_TE3_LOS_ALARM(fe_stats->alarms)); + + INFO_IFACE("OOF:\t%s\t| YEL:\t%s\n", + WAN_TE3_OOF_ALARM(fe_stats->alarms), + WAN_TE3_YEL_ALARM(fe_stats->alarms)); + + INFO_IFACE("LOF:\t%s\t\n", + WAN_TE3_LOF_ALARM(fe_stats->alarms)); + } + + }else if (adapter_type == WAN_MEDIA_56K){ + INFO_IFACE("***** %s: 56K CSU/DSU Alarms *****\n\n\n", device_name); + INFO_IFACE("In Service:\t\t%s\tData mode idle:\t\t%s\n", + INS_ALARM_56K(fe_stats->alarms), + DMI_ALARM_56K(fe_stats->alarms)); + + INFO_IFACE("Zero supp. code:\t%s\tCtrl mode idle:\t\t%s\n", + ZCS_ALARM_56K(fe_stats->alarms), + CMI_ALARM_56K(fe_stats->alarms)); + + INFO_IFACE("Out of service code:\t%s\tOut of frame code:\t%s\n", + OOS_ALARM_56K(fe_stats->alarms), + OOF_ALARM_56K(fe_stats->alarms)); + + INFO_IFACE("Valid DSU NL loopback:\t%s\tUnsigned mux code:\t%s\n", + DLP_ALARM_56K(fe_stats->alarms), + UMC_ALARM_56K(fe_stats->alarms)); + + INFO_IFACE("Rx loss of signal:\t%s\t\n", + RLOS_ALARM_56K(fe_stats->alarms)); + + }else{ + INFO_IFACE("***** %s: Unknown Front End 0x%X *****\n\n", + device_name, adapter_type); + } + + if (adapter_type == WAN_MEDIA_T1 || adapter_type == WAN_MEDIA_E1){ + sdla_te_pmon_t* pmon = &fe_stats->te_pmon; + + INFO_IFACE("\n\n***** %s: %s Performance Monitoring Counters *****\n\n", + device_name, (adapter_type == WAN_MEDIA_T1) ? "T1" : "E1"); + if (pmon->mask & WAN_TE_BIT_PMON_LCV){ + INFO_IFACE("Line Code Violation\t: %d\n", + pmon->lcv_errors); + } + if (pmon->mask & WAN_TE_BIT_PMON_BEE){ + INFO_IFACE("Bit Errors (CRC6/Ft/Fs)\t: %d\n", + pmon->bee_errors); + } + if (pmon->mask & WAN_TE_BIT_PMON_OOF){ + INFO_IFACE("Out of Frame Errors\t: %d\n", + pmon->oof_errors); + } + if (pmon->mask & WAN_TE_BIT_PMON_FEB){ + INFO_IFACE("Far End Block Errors\t: %d\n", + pmon->feb_errors); + } + if (pmon->mask & WAN_TE_BIT_PMON_CRC4){ + INFO_IFACE("CRC4 Errors\t\t: %d\n", + pmon->crc4_errors); + } + if (pmon->mask & WAN_TE_BIT_PMON_FER){ + INFO_IFACE("Framing Bit Errors\t: %d\n", + pmon->fer_errors); + } + if (pmon->mask & WAN_TE_BIT_PMON_FAS){ + INFO_IFACE("FAS Errors\t\t: %d\n", + pmon->fas_errors); + } + } + + if (adapter_type == WAN_MEDIA_DS3 || adapter_type == WAN_MEDIA_E3){ + sdla_te3_pmon_t* pmon = &fe_stats->u.te3_pmon; + + INFO_IFACE("\n\n***** %s: %s Performance Monitoring Counters *****\n\n", + device_name, (adapter_type == WAN_MEDIA_DS3) ? "DS3" : "E3"); + + INFO_IFACE("Framing Bit Error:\t%d\tLine Code Violation:\t%d\n", + pmon->pmon_framing, + pmon->pmon_lcv); + + if (adapter_type == WAN_MEDIA_DS3){ + INFO_IFACE("Parity Error:\t\t%d\n", + pmon->pmon_parity); + INFO_IFACE("CP-Bit Error Event:\t%d\tFEBE Event:\t\t%d\n", + pmon->pmon_cpbit, + pmon->pmon_febe); + }else{ + INFO_IFACE("Parity Error:\t%d\tFEBE Event:\t\t%d\n", + pmon->pmon_parity, + pmon->pmon_febe); + } + } + + if (adapter_type == WAN_MEDIA_T1 || adapter_type == WAN_MEDIA_E1){ + if (strlen(fe_stats->u.te1_stats.rxlevel)){ + INFO_IFACE("\n\nRx Level\t: %s\n", + fe_stats->u.te1_stats.rxlevel); + } + } return; } @@ -582,21 +590,20 @@ int sangoma_interface::get_operational_stats(wanpipe_chan_stats_t *stats) int err; unsigned char firm_ver, cpld_ver; - err=sangoma_get_stats(sangoma_dev,&wp_api, NULL); + err=sangoma_get_stats(sangoma_dev, &wp_api, stats); if (err) { + ERR_IFACE("sangoma_get_stats(() failed (err: %d (0x%X))!\n", err, err); return 1; } - memcpy(stats, &wp_api.wp_cmd.stats, sizeof(wanpipe_chan_stats_t)); - INFO_IFACE( "******* OPERATIONAL_STATS *******\n"); INFO_IFACE("\trx_packets\t: %u\n", stats->rx_packets); INFO_IFACE("\ttx_packets\t: %u\n", stats->tx_packets); INFO_IFACE("\trx_bytes\t: %u\n", stats->rx_bytes); INFO_IFACE("\ttx_bytes\t: %u\n", stats->tx_bytes); - INFO_IFACE("\trx_errors\t: %u\n", stats->rx_errors); - INFO_IFACE("\ttx_errors\t: %u\n", stats->tx_errors); + INFO_IFACE("\trx_errors\t: %u\n", stats->rx_errors); //Total number of Rx errors + INFO_IFACE("\ttx_errors\t: %u\n", stats->tx_errors); //Total number of Tx errors INFO_IFACE("\trx_dropped\t: %u\n", stats->rx_dropped); INFO_IFACE("\ttx_dropped\t: %u\n", stats->tx_dropped); INFO_IFACE("\tmulticast\t: %u\n", stats->multicast); @@ -604,12 +611,12 @@ int sangoma_interface::get_operational_stats(wanpipe_chan_stats_t *stats) INFO_IFACE("\trx_length_errors: %u\n", stats->rx_length_errors); INFO_IFACE("\trx_over_errors\t: %u\n", stats->rx_over_errors); - INFO_IFACE("\trx_crc_errors\t: %u\n", stats->rx_crc_errors); - INFO_IFACE("\trx_frame_errors\t: %u\n", stats->rx_frame_errors); + INFO_IFACE("\trx_crc_errors\t: %u\n", stats->rx_crc_errors); //HDLC CRC mismatch + INFO_IFACE("\trx_frame_errors\t: %u\n", stats->rx_frame_errors);//HDLC "abort" occured INFO_IFACE("\trx_fifo_errors\t: %u\n", stats->rx_fifo_errors); INFO_IFACE("\trx_missed_errors: %u\n", stats->rx_missed_errors); - INFO_IFACE("\ttx_aborted_errors: %u\n", stats->tx_aborted_errors); + INFO_IFACE("\ttx_aborted_errors: %u\n", stats->tx_aborted_errors); INFO_IFACE("\tTx Idle Data\t: %u\n", stats->tx_carrier_errors); INFO_IFACE("\ttx_fifo_errors\t: %u\n", stats->tx_fifo_errors); @@ -622,12 +629,12 @@ int sangoma_interface::get_operational_stats(wanpipe_chan_stats_t *stats) INFO_IFACE("\n\trx_packets_in_q: %u\n", stats->current_number_of_frames_in_rx_queue); INFO_IFACE("\trx_queue_size: %u\n", stats->max_rx_queue_length); - INFO_IFACE("\n\trx_events_in_q: %u\n", stats->current_number_of_events_in_event_queue); - INFO_IFACE("\trx_event_queue_size: %u\n", stats->max_event_queue_length); - INFO_IFACE("\trx_events: %u\n", stats->rx_events); + INFO_IFACE("\n\trx_events_in_q: %u\n", stats->current_number_of_events_in_event_queue); + INFO_IFACE("\trx_event_queue_size: %u\n", stats->max_event_queue_length); + INFO_IFACE("\trx_events: %u\n", stats->rx_events); INFO_IFACE("\trx_events_dropped: %u\n", stats->rx_events_dropped); - INFO_IFACE("\tHWEC tone (DTMF) events counter: %u\n", stats->rx_events_tone); + INFO_IFACE("\tHWEC tone (DTMF) events counter: %u\n", stats->rx_events_tone); INFO_IFACE( "*********************************\n"); err=sangoma_get_driver_version(sangoma_dev,&wp_api, NULL); @@ -640,14 +647,14 @@ int sangoma_interface::get_operational_stats(wanpipe_chan_stats_t *stats) wp_api.wp_cmd.version.minor1, wp_api.wp_cmd.version.minor2); - err=sangoma_get_firmware_version(sangoma_dev,&wp_api, &firm_ver); + err=sangoma_get_firmware_version(sangoma_dev, &wp_api, &firm_ver); if (err) { return 1; } INFO_IFACE("\tFirmware Version: %X\n", firm_ver); - err=sangoma_get_cpld_version(sangoma_dev,&wp_api, &cpld_ver); + err=sangoma_get_cpld_version(sangoma_dev, &wp_api, &cpld_ver); if (err) { return 1; } @@ -703,10 +710,9 @@ int sangoma_interface::set_tx_idle_flag(unsigned char new_idle_flag) //get RBS being received char sangoma_interface::get_rbs(rbs_management_t *rbs_management_ptr) { - int err; - err=sangoma_tdm_read_rbs(sangoma_dev, &wp_api, rbs_management_ptr->channel,&rbs_management_ptr->ABCD_bits); + err=sangoma_tdm_read_rbs(sangoma_dev, &wp_api, rbs_management_ptr->channel, &rbs_management_ptr->ABCD_bits); if (err) { ERR_IFACE( "Error: command WANPIPEMON_GET_RBS_BITS failed!\n"); return 1; @@ -728,7 +734,9 @@ char sangoma_interface::get_rbs(rbs_management_t *rbs_management_ptr) //set RBS to be transmitted char sangoma_interface::set_rbs(rbs_management_t *rbs_management_ptr) { - int err=sangoma_tdm_write_rbs(sangoma_dev,&wp_api, rbs_management_ptr->channel, rbs_management_ptr->ABCD_bits); + int err; + + err=sangoma_tdm_write_rbs(sangoma_dev, &wp_api, rbs_management_ptr->channel, rbs_management_ptr->ABCD_bits); if (err) { return 1; } @@ -764,7 +772,7 @@ int sangoma_interface::get_interface_configuration(if_cfg_t *wanif_conf_ptr) INFO_IFACE( "Operational Mode\t: %s (%d)\n", SDLA_DECODE_USEDBY_FIELD(wanif_conf_ptr->usedby), wanif_conf_ptr->usedby); INFO_IFACE( "Configued Active Channels \t: 0x%08X\n", wanif_conf_ptr->active_ch); INFO_IFACE( "Echo Canceller Channels\t\t: 0x%08X\n", wanif_conf_ptr->ec_active_ch); - INFO_IFACE( "User Specified Channels during Port Config\t\t: 0x%08X\n", wanif_conf_ptr->cfg_active_ch); + INFO_IFACE( "User Specified Channels during Port Config\t: 0x%08X\n", wanif_conf_ptr->cfg_active_ch); INFO_IFACE( "Interface Number\t: %u\n", wanif_conf_ptr->interface_number); INFO_IFACE( "Media type\t\t: %u\n", wanif_conf_ptr->media); INFO_IFACE( "Line Mode\t\t: %s\n", wanif_conf_ptr->line_mode); @@ -776,6 +784,8 @@ int sangoma_interface::get_interface_configuration(if_cfg_t *wanif_conf_ptr) case TDM_SPAN_VOICE_API: case TDM_CHAN_VOICE_API: case API: + case TDM_VOICE_DCHAN: + case STACK: //ok break; default: @@ -831,7 +841,7 @@ void sangoma_interface::get_api_driver_version (PDRIVER_VERSION version) int err; - err=sangoma_get_driver_version(sangoma_dev,&wp_api, version); + err=sangoma_get_driver_version(sangoma_dev, &wp_api, version); if(err){ ERR_IFACE("Error: command READ_CODE_VERSION failed!\n"); return; @@ -912,14 +922,15 @@ int sangoma_interface::DoManagementCommand(sng_fd_t fd, wan_udp_hdr_t* wan_udp) int sangoma_interface::tdmv_api_ioctl(wanpipe_api_cmd_t *api_cmd) { - wanpipe_api_t tmp; int err; - memcpy(&tmp.wp_cmd, api_cmd, sizeof(wanpipe_api_cmd_t)); + memset(&wp_api, 0x00, sizeof(wp_api)); - err = sangoma_cmd_exec(sangoma_dev, &tmp); + memcpy(&wp_api.wp_cmd, api_cmd, sizeof(wanpipe_api_cmd_t)); - memcpy(api_cmd, &tmp.wp_cmd, sizeof(wanpipe_api_cmd_t)); + err = sangoma_cmd_exec(sangoma_dev, &wp_api); + + memcpy(api_cmd, &wp_api.wp_cmd, sizeof(wanpipe_api_cmd_t)); return err; } @@ -934,103 +945,125 @@ int sangoma_interface::tdm_disable_ring_detect_events() return sangoma_tdm_disable_ring_detect_events(sangoma_dev,&wp_api); } - /* To enable flash event set rxflashtime to 1250. - 1250 is most used value. To disable flash event set rxflashtime to 0 */ +/* To enable flash event set rxflashtime to 1250. + * 1250 is most used value. To disable flash event set rxflashtime to 0 */ int sangoma_interface::tdm_control_flash_events(int rxflashtime) { - DBG_IFACE("%s()\n", __FUNCTION__); - - int err; - - err=sangoma_set_rm_rxflashtime(sangoma_dev,&wp_api, rxflashtime); - if (err) { - return 1; - } - return 0; + return sangoma_set_rm_rxflashtime(sangoma_dev, &wp_api, rxflashtime); +} + +int sangoma_interface::tdm_control_rm_txgain(int txgain) +{ +#if WP_API_FEATURE_RM_GAIN + return sangoma_set_rm_tx_gain(sangoma_dev,&wp_api, txgain); +#else + return SANG_STATUS_UNSUPPORTED_FUNCTION; +#endif +} + +int sangoma_interface::tdm_control_rm_rxgain(int rxgain) +{ +#if WP_API_FEATURE_RM_GAIN + return sangoma_set_rm_rx_gain(sangoma_dev, &wp_api, rxgain); +#else + return SANG_STATUS_UNSUPPORTED_FUNCTION; +#endif } int sangoma_interface::tdm_enable_ring_trip_detect_events() { - return sangoma_tdm_enable_ring_trip_detect_events(sangoma_dev,&wp_api); + return sangoma_tdm_enable_ring_trip_detect_events(sangoma_dev, &wp_api); } int sangoma_interface::tdm_disable_ring_trip_detect_events() { - return sangoma_tdm_disable_ring_trip_detect_events(sangoma_dev,&wp_api); + return sangoma_tdm_disable_ring_trip_detect_events(sangoma_dev, &wp_api); } int sangoma_interface::tdm_enable_rm_dtmf_events() { - DBG_IFACE("%s()\n", __FUNCTION__); - - return sangoma_tdm_enable_rm_dtmf_events(sangoma_dev,&wp_api); + return sangoma_tdm_enable_rm_dtmf_events(sangoma_dev, &wp_api); } int sangoma_interface::tdm_disable_rm_dtmf_events() { - return sangoma_tdm_disable_rm_dtmf_events(sangoma_dev,&wp_api); + return sangoma_tdm_disable_rm_dtmf_events(sangoma_dev, &wp_api); } int sangoma_interface::tdm_enable_dtmf_events(uint8_t channel) { - return sangoma_tdm_enable_dtmf_events(sangoma_dev,&wp_api); + return sangoma_tdm_enable_dtmf_events(sangoma_dev, &wp_api); } int sangoma_interface::tdm_disable_dtmf_events(uint8_t channel) { - return sangoma_tdm_disable_dtmf_events(sangoma_dev,&wp_api); + return sangoma_tdm_disable_dtmf_events(sangoma_dev, &wp_api); } int sangoma_interface::tdm_enable_rxhook_events() { - DBG_IFACE("%s()\n", __FUNCTION__); - return sangoma_tdm_enable_rxhook_events(sangoma_dev,&wp_api); + return sangoma_tdm_enable_rxhook_events(sangoma_dev, &wp_api); } int sangoma_interface::tdm_disable_rxhook_events() { - return sangoma_tdm_disable_rxhook_events(sangoma_dev,&wp_api); + return sangoma_tdm_disable_rxhook_events(sangoma_dev, &wp_api); } int sangoma_interface::tdm_enable_ring_events() { - return sangoma_tdm_enable_ring_events(sangoma_dev,&wp_api); + return sangoma_tdm_enable_ring_events(sangoma_dev, &wp_api); } int sangoma_interface::tdm_disable_ring_events() { - return sangoma_tdm_disable_ring_events(sangoma_dev,&wp_api); + return sangoma_tdm_disable_ring_events(sangoma_dev, &wp_api); } int sangoma_interface::tdm_txsig_onhook() { - return sangoma_tdm_txsig_onhook(sangoma_dev,&wp_api); + return sangoma_tdm_txsig_onhook(sangoma_dev, &wp_api); +} + +int sangoma_interface::tdm_txsig_kewl() +{ + return sangoma_tdm_txsig_kewl(sangoma_dev, &wp_api); +} + +int sangoma_interface::tdm_txsig_onhooktransfer() +{ + return sangoma_tdm_txsig_onhooktransfer(sangoma_dev, &wp_api); +} + +int sangoma_interface::tdm_set_rm_polarity(int polarity) +{ + return sangoma_tdm_set_polarity(sangoma_dev, &wp_api, polarity); } int sangoma_interface::tdm_txsig_offhook() { - return sangoma_tdm_txsig_offhook(sangoma_dev,&wp_api); + return sangoma_tdm_txsig_offhook(sangoma_dev, &wp_api); } int sangoma_interface::tdm_enable_tone_events(uint16_t tone_id) { - return sangoma_tdm_enable_tone_events(sangoma_dev,&wp_api, tone_id); + return sangoma_tdm_enable_tone_events(sangoma_dev, &wp_api, tone_id); } int sangoma_interface::tdm_disable_tone_events() { - return sangoma_tdm_disable_tone_events(sangoma_dev,&wp_api); + return sangoma_tdm_disable_tone_events(sangoma_dev, &wp_api); } int sangoma_interface::tdm_enable_bri_bchan_loopback(u_int8_t channel) { - return sangoma_enable_bri_bchan_loopback(sangoma_dev,&wp_api, channel); + return sangoma_enable_bri_bchan_loopback(sangoma_dev, &wp_api, channel); } int sangoma_interface::tdm_disable_bri_bchan_loopback(u_int8_t channel) { - return sangoma_disable_bri_bchan_loopback(sangoma_dev,&wp_api, channel); + return sangoma_disable_bri_bchan_loopback(sangoma_dev, &wp_api, channel); } /* 1. Enable 'writing' of RBS bits on card. @@ -1040,45 +1073,44 @@ int sangoma_interface::tdm_disable_bri_bchan_loopback(u_int8_t channel) Valid values are between 20 and 100 (including). */ int sangoma_interface::tdm_enable_rbs_events(int polls_per_second) { - return sangoma_tdm_enable_rbs_events(sangoma_dev,&wp_api,polls_per_second); + return sangoma_tdm_enable_rbs_events(sangoma_dev, &wp_api,polls_per_second); } /* Stop monitoring change in state of RBS bits */ int sangoma_interface::tdm_disable_rbs_events() { - return sangoma_tdm_disable_rbs_events(sangoma_dev,&wp_api); + return sangoma_tdm_disable_rbs_events(sangoma_dev, &wp_api); } /* Activate ISDN BRI line. */ int sangoma_interface::tdm_front_end_activate() { - return sangoma_set_fe_status(sangoma_dev,&wp_api,WAN_FE_CONNECTED); + return sangoma_set_fe_status(sangoma_dev, &wp_api,WAN_FE_CONNECTED); } /* De-activate ISDN BRI line. */ int sangoma_interface::tdm_front_end_deactivate() { - return sangoma_set_fe_status(sangoma_dev,&wp_api,WAN_FE_DISCONNECTED); + return sangoma_set_fe_status(sangoma_dev, &wp_api, WAN_FE_DISCONNECTED); } /* get current state of the line - is it Connected or Disconnected */ int sangoma_interface::tdm_get_front_end_status(unsigned char *status) { - return sangoma_get_fe_status(sangoma_dev,&wp_api,status); + return sangoma_get_fe_status(sangoma_dev, &wp_api, status); } /* Milliseconds interval between receive of Voice Data */ int sangoma_interface::tdm_set_user_period(unsigned int usr_period) { - return sangoma_tdm_set_usr_period(sangoma_dev,&wp_api,usr_period); + return sangoma_tdm_set_usr_period(sangoma_dev, &wp_api, usr_period); } int sangoma_interface::stop() { int wait_counter = 0; - DBG_IFACE("sangoma_interface::stop()\n"); - - INFO_IFACE( "Stopping."); + DBG_IFACE("%s()\n", __FUNCTION__); + INFO_IFACE("Stopping."); terminate_tx_rx_threads = 1; @@ -1131,6 +1163,35 @@ int sangoma_interface::run() break; } +#ifdef WP_API_FEATURE_LIBSNG_HWEC + if (program_settings.use_hardware_echo_canceller == 1) { + + char strDeviceName[DEV_NAME_LEN]; + if_cfg_t wanif_conf; + + memset(strDeviceName, 0x00, DEV_NAME_LEN); + + //Form the Interface Name from Wanpipe Number and Interface Index (i.e. wanpipe1_if1). + sangoma_span_chan_toif(WanpipeNumber, InterfaceNumber, strDeviceName); + INFO_IFACE("Interface Name for HWEC: %s\n", strDeviceName); + + if(get_interface_configuration(&wanif_conf)){ + ERR_IFACE("%s(): get_interface_configuration() failed!", __FUNCTION__); + return 1; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // It is assumed that this interface represents Voice Channel(s), not the d-channel, + // therefore 'wanif_conf.ec_active_ch' bitmap contains only the Voice Channel(s). + ////////////////////////////////////////////////////////////////////////////////////////// + if(SANG_STATUS_SUCCESS != config_hwec(strDeviceName, wanif_conf.ec_active_ch)){ + ERR_IFACE("%s(): config_hwec() failed!", __FUNCTION__); + return 2; + } + DBG_IFACE("%s(): HWEC initialization/configuraion was successful.\n", __FUNCTION__); + } +#endif + //////////////////////////////////////////////////////////////////////////// //Start a thread for receiving data only if(this->CreateThread(1) == false){ @@ -1257,7 +1318,7 @@ int sangoma_interface::sendCallerID(char * name, char * number) #endif strncpy(my_caller_id.calling_name, name, sizeof(my_caller_id.calling_name)-2); - strncpy(my_caller_id.calling_number, number, sizeof(my_caller_id.calling_name)-2); + strncpy(my_caller_id.calling_number, number, sizeof(my_caller_id.calling_number)-2); my_caller_id.calling_name[sizeof(my_caller_id.calling_name)-1] = '\0'; my_caller_id.calling_number[sizeof(my_caller_id.calling_number)-1] = '\0'; @@ -1338,7 +1399,6 @@ void sangoma_interface::RxThreadFunc() } if(output_flags[0] & POLLPRI){ - /* event */ if(read_event()){ ERR_IFACE("Error in read_event()!\n"); } @@ -1352,7 +1412,7 @@ void sangoma_interface::RxThreadFunc() default: /* error */ ERR_IFACE("iResult: %s (%d)\n", SDLA_DECODE_SANG_STATUS(iResult), iResult); - return; + sangoma_msleep(2000);/* don't exit, but put a delay to avoid busy loop */ }//switch(iResult) }//while() @@ -1369,17 +1429,18 @@ void sangoma_interface::RxThreadFunc() int sangoma_interface::read_event() { int err; - wp_api_event_t *rx_event = &wp_api.wp_cmd.event; memset(&wp_api, 0, sizeof(wp_api)); err = sangoma_read_event(sangoma_dev, &wp_api); - if(err){ return err; } - callback_functions.got_TdmApiEvent(this, rx_event); + /* an event - such as DTMF, On/Off Hook, Line Connect/Disconnect... */ + wp_api_event_t *rx_event = &wp_api.wp_cmd.event; + + callback_functions.got_tdm_api_event(this, rx_event); return 0; } @@ -1516,6 +1577,11 @@ int sangoma_interface::transmit(wp_api_hdr_t *hdr, void *data) default: /* error */ ERR_IFACE("iResult: %s (%d)\n", SDLA_DECODE_SANG_STATUS(iResult), iResult); + if(SANG_STATUS_FAILED_ALLOCATE_MEMORY == iResult){ + INFO_IFACE("Fatal Error. Press any key to exit the application.\n"); + _getch(); + exit(1); + } }//switch(iResult) return iResult; @@ -1524,61 +1590,68 @@ int sangoma_interface::transmit(wp_api_hdr_t *hdr, void *data) /////////////////////////////////////////////////////////////////////// int sangoma_interface::start_ring_tone() { - DBG_IFACE( "%s:start_ring_tone()\n", device_name); - return sangoma_tdm_enable_tone_events(sangoma_dev,&wp_api, WP_API_EVENT_TONE_RING); + DBG_IFACE("%s:%s()\n", device_name, __FUNCTION__); + return sangoma_tdm_enable_tone_events(sangoma_dev, &wp_api, WP_API_EVENT_TONE_RING); } /////////////////////////////////////////////////////////////////////// int sangoma_interface::start_congestion_tone() { - DBG_IFACE( "%s:start_congestion_tone()\n", device_name); - return sangoma_tdm_enable_tone_events(sangoma_dev,&wp_api,WP_API_EVENT_TONE_CONGESTION); + DBG_IFACE("%s:%s()\n", device_name, __FUNCTION__); + return sangoma_tdm_enable_tone_events(sangoma_dev, &wp_api, WP_API_EVENT_TONE_CONGESTION); } /////////////////////////////////////////////////////////////////////// int sangoma_interface::start_busy_tone() { - DBG_IFACE( "%s:start_busy_tone()\n", device_name); - return sangoma_tdm_enable_tone_events(sangoma_dev,&wp_api,WP_API_EVENT_TONE_BUSY); + DBG_IFACE("%s:%s()\n", device_name, __FUNCTION__); + return sangoma_tdm_enable_tone_events(sangoma_dev, &wp_api, WP_API_EVENT_TONE_BUSY); } /////////////////////////////////////////////////////////////////////// int sangoma_interface::start_dial_tone() { - DBG_IFACE( "%s:start_dial_tone()\n", device_name); - return sangoma_tdm_enable_tone_events(sangoma_dev,&wp_api,WP_API_EVENT_TONE_DIAL); + DBG_IFACE("%s:%s()\n", device_name, __FUNCTION__); + return sangoma_tdm_enable_tone_events(sangoma_dev, &wp_api, WP_API_EVENT_TONE_DIAL); } /////////////////////////////////////////////////////////////////////// int sangoma_interface::stop_all_tones() { - DBG_IFACE( "%s:stop_all_tones()\n", device_name); - return sangoma_tdm_disable_tone_events(sangoma_dev,&wp_api); + DBG_IFACE("%s:%s()\n", device_name, __FUNCTION__); + return sangoma_tdm_disable_tone_events(sangoma_dev, &wp_api); } /////////////////////////////////////////////////////////////////////// int sangoma_interface::start_ringing_phone() { - DBG_IFACE( "%s:start_ringing_phone()\n", device_name); - return sangoma_tdm_enable_ring_events(sangoma_dev,&wp_api); + DBG_IFACE("%s:%s()\n", device_name, __FUNCTION__); + return sangoma_tdm_enable_ring_events(sangoma_dev, &wp_api); } int sangoma_interface::stop_ringing_phone() { - DBG_IFACE( "%s:stop_ringing_phone()\n", device_name); - return sangoma_tdm_disable_ring_events(sangoma_dev,&wp_api); + DBG_IFACE("%s:%s()\n", device_name, __FUNCTION__); + return sangoma_tdm_disable_ring_events(sangoma_dev, &wp_api); } /////////////////////////////////////////////////////////////////////// int sangoma_interface::fxo_go_off_hook() { - DBG_IFACE( "%s:fxo_go_off_hook()\n", device_name); + DBG_IFACE("%s:%s()\n", device_name, __FUNCTION__); if(wanif_conf_struct.sub_media != MOD_TYPE_FXO){ ERR_IFACE( "%s: The 'Go Off Hook' command valid only for FXO!\n", device_name); return 1; } + return sangoma_tdm_txsig_offhook(sangoma_dev, &wp_api); +} + +/////////////////////////////////////////////////////////////////////// +int sangoma_interface::fxs_txsig_offhook() +{ + DBG_IFACE("%s:%s()\n", device_name, __FUNCTION__); return sangoma_tdm_txsig_offhook(sangoma_dev,&wp_api); } int sangoma_interface::fxo_go_on_hook() { - DBG_IFACE( "%s:fxo_go_on_hook()\n", device_name); + DBG_IFACE("%s:%s()\n", device_name, __FUNCTION__); if(wanif_conf_struct.sub_media != MOD_TYPE_FXO){ ERR_IFACE( "%s: The 'Go On Hook' command valid only for FXO!\n", device_name); @@ -1590,7 +1663,7 @@ int sangoma_interface::fxo_go_on_hook() //set a Telephony interface to it's default state int sangoma_interface::reset_interface_state() { - DBG_IFACE("%s:reset_interface_state()\n", device_name); + DBG_IFACE("%s:%s()\n", device_name, __FUNCTION__); switch(wanif_conf_struct.media) { @@ -1627,7 +1700,8 @@ void sangoma_interface::set_fe_debug_mode(sdla_fe_debug_t *fe_debug) DO_COMMAND(wan_udp); if (wan_udp.wan_udphdr_return_code){ - ERR_IFACE( "Error: WAN_FE_SET_DEBUG_MODE failed! return code: 0x%X", wan_udp.wan_udphdr_return_code); + ERR_IFACE( "Error: WAN_FE_SET_DEBUG_MODE failed! return code: 0x%X", + wan_udp.wan_udphdr_return_code); return; } @@ -1639,7 +1713,7 @@ repeat_read_reg: wan_udp.wan_udphdr_return_code = 0xaa; fe_debug->fe_debug_reg.read = 2; memcpy(data, (unsigned char*)fe_debug, sizeof(sdla_fe_debug_t)); - usleep(100000); + wp_usleep(100000); err = DO_COMMAND(wan_udp); if (err || wan_udp.wan_udphdr_return_code != 0){ if (cnt < 5){ @@ -1690,16 +1764,13 @@ repeat_read_reg: return; } - - - +/////////////////////////////////////////////////////////////////////////// +// Ctrl Device Class implementation sangoma_api_ctrl_dev::sangoma_api_ctrl_dev():sangoma_interface(0, 0) { IFACE_FUNC(); - //Initialize Device Name (for debugging only). _snprintf(device_name, DEV_NAME_LEN, WP_CTRL_DEV_NAME); - INFO_IFACE("Using Device Name: %s\n", device_name); } @@ -1715,7 +1786,7 @@ int sangoma_api_ctrl_dev::init(callback_functions_t *callback_functions_ptr) memcpy(&callback_functions, callback_functions_ptr, sizeof(callback_functions_t)); //////////////////////////////////////////////////////////////////////////// - //open handle for reading and writing data, for events reception and other commands + //open handle for events reception and other commands sangoma_dev = sangoma_open_api_ctrl(); if (sangoma_dev == INVALID_HANDLE_VALUE){ ERR_IFACE("Unable to open Ctrl Dev!\n"); @@ -1726,6 +1797,204 @@ int sangoma_api_ctrl_dev::init(callback_functions_t *callback_functions_ptr) ERR_IFACE("Failed to create 'sangoma_wait_object' for %s\n", device_name); return 1; } + return 0; +} + +/////////////////////////////////////////////////////////////////////////// +// Logger Device Class implementation +sangoma_api_logger_dev::sangoma_api_logger_dev():sangoma_interface(0, 0) +{ + IFACE_FUNC(); +#if USE_WP_LOGGER + //Initialize Device Name (for debugging only). + _snprintf(device_name, DEV_NAME_LEN, WP_LOGGER_DEV_NAME); + INFO_IFACE("Using Device Name: %s\n", device_name); +#endif +} + +sangoma_api_logger_dev::~sangoma_api_logger_dev() +{ + IFACE_FUNC(); +} + +int sangoma_api_logger_dev::init(callback_functions_t *callback_functions_ptr) +{ + IFACE_FUNC(); +#if USE_WP_LOGGER + is_logger_dev = 1; + + memcpy(&callback_functions, callback_functions_ptr, sizeof(callback_functions_t)); + + //////////////////////////////////////////////////////////////////////////// + //open handle for Logger Events reception and other commands + sangoma_dev = sangoma_logger_open(); + if (sangoma_dev == INVALID_HANDLE_VALUE){ + ERR_IFACE("Unable to open Ctrl Dev!\n"); + return 1; + } + + if(SANG_ERROR(sangoma_wait_obj_create(&sng_wait_obj, sangoma_dev, SANGOMA_DEVICE_WAIT_OBJ_SIG))){ + ERR_IFACE("Failed to create 'sangoma_wait_object' for %s\n", device_name); + return 1; + } + + //////////////////////////////////////////////////////////////////////////// + //get Current Level of Default Logger + memset(&logger_cmd, 0x00, sizeof(logger_cmd)); + logger_cmd.logger_level_ctrl.logger_type = WAN_LOGGER_DEFAULT; + if(sangoma_logger_get_logger_level(sangoma_dev, &logger_cmd)){ + ERR_IFACE("WP_API_CMD_GET_LOGGER_LEVEL failed for %s!\n", device_name); + return 1; + }else{ + INFO_IFACE("%s: Current logger level: 0x%08X\n", + device_name, logger_cmd.logger_level_ctrl.logger_level); + } + + //////////////////////////////////////////////////////////////////////////// + //set New Level for Default Logger. + logger_cmd.logger_level_ctrl.logger_type = WAN_LOGGER_DEFAULT; + logger_cmd.logger_level_ctrl.logger_level = + (SANG_LOGGER_INFORMATION | SANG_LOGGER_WARNING | SANG_LOGGER_ERROR); + if(sangoma_logger_set_logger_level(sangoma_dev, &logger_cmd)){ + ERR_IFACE("WP_API_CMD_SET_LOGGER_LEVEL failed for %s!\n", device_name); + return 1; + } + +#if 0 + //enable advanced debugging messages for T1/E1 + memset(&logger_cmd, 0x00, sizeof(logger_cmd)); + logger_cmd.logger_level_ctrl.logger_type = WAN_LOGGER_TE1; + logger_cmd.logger_level_ctrl.logger_level = (SANG_LOGGER_TE1_DEFAULT); + if(sangoma_logger_set_logger_level(sangoma_dev, &logger_cmd)){ + ERR_IFACE("WP_API_CMD_SET_LOGGER_LEVEL failed for %s!\n", device_name); + return 1; + } +#endif + +#if 0 + //enable advanced debugging messages for Hardware Echo Canceller + memset(&logger_cmd, 0x00, sizeof(logger_cmd)); + logger_cmd.logger_level_ctrl.logger_type = WAN_LOGGER_HWEC; + logger_cmd.logger_level_ctrl.logger_level = (SANG_LOGGER_HWEC_DEFAULT); + if(sangoma_logger_set_logger_level(sangoma_dev, &logger_cmd)){ + ERR_IFACE("WP_API_CMD_SET_LOGGER_LEVEL failed for %s!\n", device_name); + return 1; + } +#endif + +#endif//USE_WP_LOGGER + return 0; +} + +int sangoma_api_logger_dev::read_event() +{ + int err=1; + + memset(&logger_cmd, 0, sizeof(logger_cmd)); + +#if USE_WP_LOGGER + err = sangoma_logger_read_event(sangoma_dev, &logger_cmd); +#endif + if(err){ + return err; + } + + /* Wanpipe Logger Event */ +#if USE_WP_LOGGER + wp_logger_event_t *logger_event = &logger_cmd.logger_event; + callback_functions.got_logger_event(this, logger_event); +#endif return 0; } + +int sangoma_api_logger_dev::flush_operational_stats(void) +{ +#if USE_WP_LOGGER + return sangoma_logger_reset_statistics(sangoma_dev, &logger_cmd); +#else + return 1; +#endif +} + +int sangoma_api_logger_dev::get_logger_dev_operational_stats(wp_logger_stats_t *stats) +{ + int err=1; +#if USE_WP_LOGGER + err = sangoma_logger_get_statistics(sangoma_dev, &logger_cmd); + if(err){ + ERR_IFACE("sangoma_logger_get_statistics() failed (err: %d (0x%X))!\n", err, err); + return err; + } + + memcpy(stats, &logger_cmd.stats, sizeof(*stats)); + + INFO_IFACE("*** Logger Device Statistics ***\n"); + INFO_IFACE("\trx_events\t: %u\n", stats->rx_events); + INFO_IFACE("\trx_events_dropped\t: %u\n", stats->rx_events_dropped); + INFO_IFACE("\tmax_event_queue_length\t: %u\n", stats->max_event_queue_length); + INFO_IFACE("\tcurrent_number_of_events_in_event_queue\t: %u\n", stats->current_number_of_events_in_event_queue); + INFO_IFACE("\n"); +#endif + return err; +} + +#ifdef WP_API_FEATURE_LIBSNG_HWEC +/* NOTE: HWEC must NOT be enabled on a d-channel. The 'in_ulChannelMap' should be + * the bitmap of voice channels only. */ +sangoma_status_t config_hwec(char *strDeviceName, unsigned long in_ulChannelMap) +{ + sangoma_status_t rc; + + DBG_IFACE("%s(): strDeviceName: %s, in_ulChannelMap: 0x%X\n", __FUNCTION__, strDeviceName, in_ulChannelMap); + + // Initialize the echo canceller (done once per-physical card) + rc = sangoma_hwec_config_init(strDeviceName); + if (SANG_STATUS_SUCCESS != rc) { + ERR_IFACE( "Failed to initialize echo canceller. rc: %d\n", rc); + return rc; + } + + // Enable DTMF detection + rc = sangoma_hwec_config_tone_detection(strDeviceName, + WP_API_EVENT_TONE_DTMF, + 1 /* enable */, + in_ulChannelMap, + WAN_EC_CHANNEL_PORT_SOUT //(detection of the DTMF coming from the PSTN only Sin -> Sout path) + ); + if (SANG_STATUS_SUCCESS != rc) { + ERR_IFACE( "Failed to enable DTMF detection on echo canceller device.\n" ); + return rc; + } + + // Enable FAX detection + // When FAX is detected, a DTMF event will occur, and the detected digit will be 'f'. + rc = sangoma_hwec_config_tone_detection(strDeviceName, + WP_API_EVENT_TONE_FAXCALLED,//incoming fax detection OR + //WP_API_EVENT_TONE_FAXCALLING - outgoing fax detection + 1 /* enable */, + in_ulChannelMap, + WAN_EC_CHANNEL_PORT_SOUT // (detection of the fax tone coming from the PSTN only Sin -> Sout path) + ); + if (SANG_STATUS_SUCCESS != rc) { + ERR_IFACE( "Failed to enable FAX detection on echo canceller device.\n" ); + return rc; + } + +#if 0 +/* "WANEC_SoutAdaptiveNoiseReduction", "WANEC_TailDisplacement", "WANEC_DtmfToneRemoval", "WANEC_AcousticEcho"...*/ + + // Optionally, Enable noise reduction from PSTN + rc = sangoma_hwec_config_channel_parameters(strDeviceName, + "WANEC_SoutAdaptiveNoiseReduction", + "TRUE", + in_ulChannelMap ); + if (SANG_STATUS_SUCCESS != rc) { + ERR_IFACE( "Failed to enable noise reduction.\n" ); + return rc; + } +#endif + + return SANG_STATUS_SUCCESS; +} +#endif// WP_API_FEATURE_LIBSNG_HWEC diff --git a/api/libsangoma/sample_cpp/.svn/text-base/sangoma_interface.h.svn-base b/api/libsangoma/sample_cpp/.svn/text-base/sangoma_interface.h.svn-base index f22fc5e..9d23857 100644 --- a/api/libsangoma/sample_cpp/.svn/text-base/sangoma_interface.h.svn-base +++ b/api/libsangoma/sample_cpp/.svn/text-base/sangoma_interface.h.svn-base @@ -45,7 +45,7 @@ # include # include # include "bit_win.h" -# include "wanpipe_time.h" //for usleep() +# include "wanpipe_time.h" //for wp_usleep() #elif defined(__LINUX__) @@ -78,16 +78,16 @@ * API may provide input from a SINGLE or from MULTIPLE timeslots. */ #define USE_STELEPHONY_API 1 /* set to zero if don't need to compile - libstelephony.dll function calls */ + function calls to libstelephony.dll */ #include "wanpipe_api.h" #include "sangoma_cthread.h" #include "sample.h" -#include +#include "libsangoma.h" #if USE_STELEPHONY_API -# include +# include "libstelephony.h" #endif /*! @@ -101,7 +101,7 @@ protected: sng_fd_t sangoma_dev; /*! wait object device for an IO device */ - void *sng_wait_obj; + sangoma_wait_obj_t *sng_wait_obj; ////////////////////////////////////////////////////////////////// //receive stuff @@ -156,19 +156,16 @@ protected: /*! Tx Thread function */ void TxThreadFunc(); - /*! Rx data call back function handler */ + /*! Rx Data handler function */ int read_data(); - /*! Rx event call back function handler */ - int read_event(); + /*! Rx Event handler function */ + virtual int read_event(); int write_data(wp_api_hdr_t *hdr, void *tx_buffer); /*! Shutdown function to cleanup the class */ void cleanup(); - /*! deprecated: Developmnet API structure used to read write low level memory */ - wan_cmd_api_t wanpipe_api_cmd; - /*! Get device span configuration */ int get_wan_config(); @@ -233,8 +230,9 @@ protected: virtual unsigned long threadFunction(struct ThreadParam& thParam); public: - char device_name[DEV_NAME_LEN]; + char device_name[DEV_NAME_LEN]; + char is_logger_dev; ////////////////////////////////////////////////////////////////// //methods sangoma_interface(int wanpipe_number, int interface_number); @@ -259,7 +257,7 @@ public: int loopback_command(u_int8_t type, u_int8_t mode, u_int32_t chan_map); int get_operational_stats(wanpipe_chan_stats_t *stats); - int flush_operational_stats (void); + virtual int flush_operational_stats (void); int CreateSwDtmfTxThread(void *buffer); int CreateFskCidTxThread(void *buffer); @@ -317,6 +315,11 @@ public: int tdm_txsig_onhook(); int tdm_txsig_offhook(); + int tdm_txsig_kewl(); + /*To transmit data while FXS is on-hook */ + /* Example: To transmit FSK Message Wait Indication (MWI) + to phone connected with FXS */ + int tdm_txsig_onhooktransfer(); int tdm_enable_tone_events(uint16_t tone_id); int tdm_disable_tone_events(); @@ -325,6 +328,20 @@ public: int tdm_front_end_deactivate(); int tdm_control_flash_events(int rxflashtime); + + /* To set tx/rx gain for analog FXS/FXO modules + Gain in dB = gainvalue / 10 + For FXS: txgain/rxgain value could be -35 or 35 + FXO: txgain/rxgain value ranges from -150 to 120 + FXO/FXS: Set txgain/rxgain value 0 for default setting*/ + + int tdm_control_rm_txgain(int txgain); + int tdm_control_rm_rxgain(int rxgain); + + /* Only valid for FXS module to Set Polarity on the line + polarity 0: Forward Polarity + 1: Reverse Polarity */ + int tdm_set_rm_polarity(int polarity); /* get current state of the line - is it Connected or Disconnected */ int tdm_get_front_end_status(unsigned char *status); @@ -356,6 +373,8 @@ public: int fxo_go_off_hook(); int fxo_go_on_hook(); + int fxs_txsig_offhook(); + //BRI only: int tdm_enable_bri_bchan_loopback(u_int8_t channel); int tdm_disable_bri_bchan_loopback(u_int8_t channel); @@ -370,18 +389,19 @@ public: virtual int init(callback_functions_t *callback_functions_ptr); }; +class sangoma_api_logger_dev : public sangoma_interface +{ + wp_logger_cmd_t logger_cmd; -#if defined(__WINDOWS__) -#define HANDLE_DEVICE_IOCTL_RESULT(bResult)\ -{ \ - if(bResult == 0){ \ - /* check message log */ \ - printf("%s(): Line: %d: Error!!\n", __FUNCTION__, __LINE__); \ - DecodeLastError(__FUNCTION__); \ - return 1; \ - } \ -} -#endif /*__WINDOWS__*/ +public: + sangoma_api_logger_dev(void); + ~sangoma_api_logger_dev(void); + virtual int init(callback_functions_t *callback_functions_ptr); + /*! Logger Event handler function */ + virtual int read_event(); + virtual int flush_operational_stats (void); + int get_logger_dev_operational_stats(wp_logger_stats_t *stats); +}; #endif//SANGOMA_INTERFACE_H diff --git a/api/libsangoma/sample_cpp/.svn/text-base/sangoma_port.cpp.svn-base b/api/libsangoma/sample_cpp/.svn/text-base/sangoma_port.cpp.svn-base index 0b2f858..61ff638 100644 --- a/api/libsangoma/sample_cpp/.svn/text-base/sangoma_port.cpp.svn-base +++ b/api/libsangoma/sample_cpp/.svn/text-base/sangoma_port.cpp.svn-base @@ -51,7 +51,7 @@ int sangoma_port::init(uint16_t wanpipe_number) wp_number = wanpipe_number; wp_handle = sangoma_open_driver_ctrl(wanpipe_number); if(wp_handle == INVALID_HANDLE_VALUE){ - ERR_CFG("Error: failed to open %s!!\n", wanpipe_name_str); + ERR_CFG("Error: failed to open wanpipe%d!!\n", wp_number); return 1; } return 0; @@ -63,7 +63,7 @@ int sangoma_port::get_hardware_info(hardware_info_t *hardware_info) int err=sangoma_driver_get_hw_info(wp_handle,&port_management, wp_number); if (err) { - ERR_CFG("%s: Error: failed to get hw info!\n",wanpipe_name_str); + ERR_CFG("Error: failed to get hw info for wanpipe%d!\n", wp_number); err=1; return err; } diff --git a/api/libsangoma/sample_cpp/.svn/text-base/sangoma_port.h.svn-base b/api/libsangoma/sample_cpp/.svn/text-base/sangoma_port.h.svn-base index db85ae2..a4c4fc0 100644 --- a/api/libsangoma/sample_cpp/.svn/text-base/sangoma_port.h.svn-base +++ b/api/libsangoma/sample_cpp/.svn/text-base/sangoma_port.h.svn-base @@ -82,7 +82,6 @@ public: protected: uint16_t wp_number; - char wanpipe_name_str[WAN_DRVNAME_SZ*2]; HANDLE wp_handle; int push_a_card_into_wanpipe_info_array(wanpipe_instance_info_t *wanpipe_info_array, diff --git a/api/libsangoma/sample_cpp/.svn/text-base/sangoma_port_configurator.cpp.svn-base b/api/libsangoma/sample_cpp/.svn/text-base/sangoma_port_configurator.cpp.svn-base index a0c19db..fc94b4f 100644 --- a/api/libsangoma/sample_cpp/.svn/text-base/sangoma_port_configurator.cpp.svn-base +++ b/api/libsangoma/sample_cpp/.svn/text-base/sangoma_port_configurator.cpp.svn-base @@ -337,7 +337,7 @@ int sangoma_port_configurator::set_volatile_configration(port_cfg_t *port_cfg) switch(port_cfg->operation_status) { case SANG_STATUS_DEVICE_BUSY: - INFO_CFG("Error: open handles exist for '%s_IF\?\?' interfaces!\n", wanpipe_name_str); + INFO_CFG("Error: open handles exist for 'wanpipe%d_if\?\?' interfaces!\n", wp_number); return port_cfg->operation_status; case SANG_STATUS_SUCCESS: //OK @@ -360,7 +360,7 @@ int sangoma_port_configurator::initialize_t1_tdm_span_voice_api_configration_str { wandev_conf_t *wandev_conf = &port_cfg->wandev_conf; sdla_fe_cfg_t *sdla_fe_cfg = &wandev_conf->fe_cfg; - sdla_te_cfg_t *te_cfg = &sdla_fe_cfg->cfg.te_cfg; + //sdla_te_cfg_t *te_cfg = &sdla_fe_cfg->cfg.te_cfg; wan_tdmv_conf_t *tdmv_cfg = &wandev_conf->tdmv_conf; wanif_conf_t *wanif_cfg = &port_cfg->if_cfg[0]; @@ -369,15 +369,15 @@ int sangoma_port_configurator::initialize_t1_tdm_span_voice_api_configration_str FE_LCODE(sdla_fe_cfg) = WAN_LCODE_B8ZS; FE_FRAME(sdla_fe_cfg) = WAN_FR_ESF; FE_LINENO(sdla_fe_cfg) = hardware_info->port_number; - FE_TDMV_LAW(sdla_fe_cfg) = WAN_TDMV_MULAW; - FE_NETWORK_SYNC(sdla_fe_cfg) = 0; + FE_TDMV_LAW(sdla_fe_cfg) = WAN_TDMV_MULAW; + FE_NETWORK_SYNC(sdla_fe_cfg) = 0; + + FE_LBO(sdla_fe_cfg) = WAN_T1_0_110; + FE_REFCLK(sdla_fe_cfg) = 0; + FE_HIMPEDANCE_MODE(sdla_fe_cfg) = 0; + FE_SIG_MODE(sdla_fe_cfg) = WAN_TE1_SIG_CCS; + FE_RX_SLEVEL(sdla_fe_cfg) = WAN_TE1_RX_SLEVEL_12_DB; - FE_LBO(sdla_fe_cfg) = WAN_T1_0_110; - FE_REFCLK(sdla_fe_cfg) = 0; - FE_HIMPEDANCE_MODE(sdla_fe_cfg) = 0; - FE_SIG_MODE(sdla_fe_cfg) = WAN_TE1_SIG_CCS; - FE_RX_SLEVEL(sdla_fe_cfg) = WAN_TE1_RX_SLEVEL_12_DB; - #if 1 FE_CLK(sdla_fe_cfg) = WAN_NORMAL_CLK; #else @@ -396,7 +396,7 @@ int sangoma_port_configurator::initialize_t1_tdm_span_voice_api_configration_str wandev_conf->card_type = WANOPT_AFT; //m_DeviceInfoData.card_model; wanif_cfg->magic = ROUTER_MAGIC; - wanif_cfg->active_ch = 0x01FFFFFE;// channels 1-24 + wanif_cfg->active_ch = 0x00FFFFFF;//channels 1-24 (starting from bit zero) sprintf(wanif_cfg->usedby,"TDM_SPAN_VOICE_API"); wanif_cfg->u.aft.idle_flag=0xFF; wanif_cfg->mtu = 160; @@ -417,7 +417,7 @@ int sangoma_port_configurator::initialize_t1_tdm_span_voice_api_configration_str switch(FE_MEDIA(sdla_fe_cfg)) { case WAN_MEDIA_T1: - tdmv_cfg->dchan = (1<<23);/* channel 24 */ + tdmv_cfg->dchan = (1<<23);/* Channel 24. This is a bitmap, not a channel number. */ break; default: printf("%s(): Error: invalid media type!\n", __FUNCTION__); @@ -442,14 +442,14 @@ int sangoma_port_configurator::initialize_e1_tdm_span_voice_api_configration_str FE_LCODE(sdla_fe_cfg) = WAN_LCODE_HDB3; FE_FRAME(sdla_fe_cfg) = WAN_FR_CRC4; FE_LINENO(sdla_fe_cfg) = hardware_info->port_number; - FE_TDMV_LAW(sdla_fe_cfg) = WAN_TDMV_MULAW; - FE_NETWORK_SYNC(sdla_fe_cfg) = 0; + FE_TDMV_LAW(sdla_fe_cfg) = WAN_TDMV_MULAW; + FE_NETWORK_SYNC(sdla_fe_cfg) = 0; - FE_LBO(sdla_fe_cfg) = WAN_E1_120; - FE_REFCLK(sdla_fe_cfg) = 0; - FE_HIMPEDANCE_MODE(sdla_fe_cfg) = 0; - FE_SIG_MODE(sdla_fe_cfg) = WAN_TE1_SIG_CCS; - FE_RX_SLEVEL(sdla_fe_cfg) = WAN_TE1_RX_SLEVEL_12_DB; + FE_LBO(sdla_fe_cfg) = WAN_E1_120; + FE_REFCLK(sdla_fe_cfg) = 0; + FE_HIMPEDANCE_MODE(sdla_fe_cfg) = 0; + FE_SIG_MODE(sdla_fe_cfg) = WAN_TE1_SIG_CCS; + FE_RX_SLEVEL(sdla_fe_cfg) = WAN_TE1_RX_SLEVEL_12_DB; #if 1 FE_CLK(sdla_fe_cfg) = WAN_NORMAL_CLK; @@ -469,7 +469,7 @@ int sangoma_port_configurator::initialize_e1_tdm_span_voice_api_configration_str wandev_conf->card_type = WANOPT_AFT; //m_DeviceInfoData.card_model; wanif_cfg->magic = ROUTER_MAGIC; - wanif_cfg->active_ch = 0xFFFFFFFE;// channels 1-31 + wanif_cfg->active_ch = 0x7FFFFFFF;// channels 1-31 (starting from bit zero) sprintf(wanif_cfg->usedby,"TDM_SPAN_VOICE_API"); wanif_cfg->u.aft.idle_flag=0xFF; wanif_cfg->mtu = 160; @@ -490,7 +490,7 @@ int sangoma_port_configurator::initialize_e1_tdm_span_voice_api_configration_str switch(FE_MEDIA(sdla_fe_cfg)) { case WAN_MEDIA_E1: - tdmv_cfg->dchan = (1<<15);/* channel 16 */ + tdmv_cfg->dchan = (1<<15);/* Channel 16. This is a bitmap, not a channel number. */ break; default: printf("%s(): Error: invalid media type!\n", __FUNCTION__); @@ -507,3 +507,34 @@ int sangoma_port_configurator::write_configration_on_persistent_storage(port_cfg return sangoma_write_port_config_on_persistent_storage(hardware_info, port_cfg, (unsigned short)span); } +int sangoma_port_configurator::control_analog_rm_lcm(port_cfg_t *port_cfg, int control_val) +{ + wandev_conf_t *wandev_conf = &port_cfg->wandev_conf; + sdla_fe_cfg_t *sdla_fe_cfg = &wandev_conf->fe_cfg; + if(wandev_conf->card_type == WANOPT_AFT_ANALOG){ //Only valid for Analog cards + if(control_val == 1){ + sdla_fe_cfg->cfg.remora.rm_lcm = 1; + }else if(control_val == 0){ + sdla_fe_cfg->cfg.remora.rm_lcm = 0; + }else{ + printf("%s(): Error: invalid parameter!\n", __FUNCTION__); + return -EINVAL; + } + } else{ + return -EINVAL; + } + return 0; +} +int sangoma_port_configurator::set_analog_opermode(port_cfg_t *port_cfg, char *opermode) +{ + wandev_conf_t *wandev_conf = &port_cfg->wandev_conf; + sdla_fe_cfg_t *sdla_fe_cfg = &wandev_conf->fe_cfg; + if(wandev_conf->card_type == WANOPT_AFT_ANALOG){ //Only valid for Analog cards + if(sizeof(sdla_fe_cfg->cfg.remora.opermode_name) < strlen(opermode)) + return -EINVAL; + strcpy(sdla_fe_cfg->cfg.remora.opermode_name,opermode); + } else{ + return -EINVAL; + } + return 0; +} diff --git a/api/libsangoma/sample_cpp/.svn/text-base/sangoma_port_configurator.h.svn-base b/api/libsangoma/sample_cpp/.svn/text-base/sangoma_port_configurator.h.svn-base index 542a7c2..f327ba6 100644 --- a/api/libsangoma/sample_cpp/.svn/text-base/sangoma_port_configurator.h.svn-base +++ b/api/libsangoma/sample_cpp/.svn/text-base/sangoma_port_configurator.h.svn-base @@ -44,12 +44,18 @@ public: sangoma_port_configurator(); virtual ~sangoma_port_configurator(); - int open_port_registry_key(hardware_info_t *hardware_info); - int get_configration(port_cfg_t *port_cfg); int set_default_configuration(port_cfg_t *port_cfg); + //Function to set Loop Current Measure (LCM) for analog FXO + //control_val 1: enable,0: disable + int control_analog_rm_lcm(port_cfg_t *port_cfg, int control_val); + + //Function to set Operation mode for analog FXO + //opermode valid country name supported by Analog FXO + int set_analog_opermode(port_cfg_t *port_cfg, char *opermode); + //Function to check correctness of 'port_cfg_t' structure. int check_port_cfg_structure(port_cfg_t *port_cfg); diff --git a/api/libsangoma/sample_cpp/.svn/text-base/sources.svn-base b/api/libsangoma/sample_cpp/.svn/text-base/sources.svn-base index 384521e..1e48e55 100644 --- a/api/libsangoma/sample_cpp/.svn/text-base/sources.svn-base +++ b/api/libsangoma/sample_cpp/.svn/text-base/sources.svn-base @@ -17,6 +17,7 @@ INCLUDES=$(DDK_INC_PATH);\ $(SANG_WP_DEVEL)\wanpipe_common\include;\ $(SANG_WP_DEVEL)\wanpipe_common\include\aft_core;\ $(SANG_WP_DEVEL)\wanpipe_windows\include;\ +$(SANG_WP_DEVEL)\wanpipe_common\wantools\wanec_apilib;\ $(SANG_WP_DEVEL)\libsangoma;\ $(SANG_WP_DEVEL)\stelephony;\ $(SANG_WP_DEVEL)\stelephony\stel_tone @@ -34,8 +35,9 @@ $(SDK_LIB_PATH)\oleaut32.lib \ $(SDK_LIB_PATH)\uuid.lib \ $(SDK_LIB_PATH)\comctl32.lib \ $(SDK_LIB_PATH)\Setupapi.lib \ -$(SANG_WP_DEVEL)\libsangoma\$(OBJ_DIR)\libsangoma.lib \ -$(SANG_WP_DEVEL)\stelephony\$(OBJ_DIR)\stelephony.lib +$(SANG_WP_DEVEL)\libsangoma\$(O)\libsangoma.lib \ +$(SANG_WP_DEVEL)\stelephony\$(O)\stelephony.lib \ +$(SANG_WP_DEVEL)\wanpipe_common\wantools\wanec_apilib\$(O)\waneclib.lib SOURCES=sample.cpp \ diff --git a/api/libsangoma/sample_cpp/Makefile.Windows b/api/libsangoma/sample_cpp/Makefile.Windows deleted file mode 100644 index 66f1c8e..0000000 --- a/api/libsangoma/sample_cpp/Makefile.Windows +++ /dev/null @@ -1,8 +0,0 @@ -# -# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source -# file to this component. This file merely indirects to the real make file -# that is shared by all the driver components of the Windows NT DDK -# - -!INCLUDE $(NTMAKEENV)\makefile.def - diff --git a/api/libsangoma/sample_cpp/sample.cpp b/api/libsangoma/sample_cpp/sample.cpp index 837ac25..06a149a 100644 --- a/api/libsangoma/sample_cpp/sample.cpp +++ b/api/libsangoma/sample_cpp/sample.cpp @@ -37,9 +37,9 @@ #include "sangoma_interface.h" #if defined(__LINUX__) -#include "sample_linux_compat.h" +# include "sample_linux_compat.h" #else -#include +# include #endif #ifndef MAX_PATH @@ -55,13 +55,15 @@ wp_program_settings_t program_settings; callback_functions_t callback_functions; - /***************************************************************** * Prototypes & Defines *****************************************************************/ static int got_rx_data(void *sang_if_ptr, void *rx_data); -static void got_TdmApiEvent(void *sang_if_ptr, void *event_data); +static void got_tdm_api_event(void *sang_if_ptr, void *event_data); +#if USE_WP_LOGGER +static void got_logger_event(void *sang_if_ptr, wp_logger_event_t *logger_event); +#endif typedef struct{ void *sang_if_ptr; @@ -116,16 +118,21 @@ static int set_port_configuration(); sangoma_interface* init(int wanpipe_number, int interface_number) { sangoma_interface *sang_if = NULL; - DBG_MAIN("init()\n"); + DBG_MAIN("%s()\n", __FUNCTION__); + if(program_settings.use_ctrl_dev == 1){ sang_if = new sangoma_api_ctrl_dev(); + }else if(program_settings.use_logger_dev == 1){ + sang_if = new sangoma_api_logger_dev(); }else{ sang_if = new sangoma_interface(wanpipe_number, interface_number); } + if(sang_if->init(&callback_functions)){ delete sang_if; return NULL; } + DBG_MAIN("init(): OK\n"); return sang_if; } @@ -187,11 +194,15 @@ void PrintRxData(wp_api_hdr_t *hdr, void *pdata) rx_counter++; if(program_settings.silent){ if((rx_counter % 1000) == 0){ - INFO_MAIN("Rx counter:%d, Rx datlen : %d\n", rx_counter, datlen); + INFO_MAIN("Rx counter: %d, Rx datlen: %d\n", rx_counter, datlen); +#if 0 + INFO_MAIN("Timestamp: Seconds: %d, Microseconds: %d\n", + hdr->wp_api_hdr_time_stamp_sec, hdr->wp_api_hdr_time_stamp_use); +#endif } return; }else{ - INFO_MAIN("Rx counter:%d, Rx datlen : %d. Data :\n", rx_counter, datlen); + INFO_MAIN("Rx counter: %d, Rx datlen: %d. Data:\n", rx_counter, datlen); } #if 1 @@ -250,7 +261,7 @@ static int got_rx_data(void *sang_if_ptr, void *rxhdr, void *rx_data) } /*! - \fn static void got_TdmApiEvent(void *sang_if_ptr, void *event_data) + \fn static void got_tdm_api_event(void *sang_if_ptr, void *event_data) \brief Callback function indicating Event is pending. \param sang_if_ptr sangoma interface pointer \param event_data API event element strcutre containt header + data @@ -259,7 +270,7 @@ static int got_rx_data(void *sang_if_ptr, void *rxhdr, void *rx_data) Currently Windows launches a thread to handle the event, where Linux handles the event directly. Implementation is left to the user. */ -static void got_TdmApiEvent(void *sang_if_ptr, void *event_data) +static void got_tdm_api_event(void *sang_if_ptr, void *event_data) { TDM_API_EVENT_THREAD_PARAM *param = (TDM_API_EVENT_THREAD_PARAM*)malloc(sizeof(TDM_API_EVENT_THREAD_PARAM)); @@ -369,12 +380,12 @@ void *TdmApiEventThreadFunc(void *lpdwParam) DBG_MAIN("New Alarm State: 0x%X\n", wp_tdm_api_event->wp_api_event_alarm); break; - case WP_API_EVENT_POLARITY_REVERSE: - /* This event may have different meaning on different Telco lines. - * For example, it indicates "Network Initiated Clearing", - * on a British Telecom line. But on some lines it means - * "Start of Caller ID transmission". Please consult with your Telco - * for exact meaning of event. */ + case WP_API_EVENT_POLARITY_REVERSE: + /* This event may have different meaning on different Telco lines. + * For example, it indicates "Network Initiated Clearing", + * on a British Telecom line. But on some lines it means + * "Start of Caller ID transmission". Please consult with your Telco + * for exact meaning of event. */ DBG_MAIN("Polarity Reversal Event: %s\n", WP_API_EVENT_POLARITY_REVERSE_DECODE(wp_tdm_api_event->wp_api_event_polarity_reverse)); break; @@ -390,6 +401,27 @@ void *TdmApiEventThreadFunc(void *lpdwParam) return 0; } +#if USE_WP_LOGGER +/*! + \fn static void got_logger_event(void *sang_if_ptr, wp_logger_event_t *logger_event) + \brief Callback function indicating Logger Event is pending. + \param sang_if_ptr sangoma interface pointer + \param logger_event API Logger Event structure + \return 0 - ok non-zero - Error +*/ +static void got_logger_event(void *sang_if_ptr, wp_logger_event_t *logger_event) +{ + char *timestamp_str = sangoma_ctime( &logger_event->time_stamp_sec ); + + INFO_MAIN("Logger Event Type:\t%s (Logger:%d BitMap: 0x%08X)\n", + wp_decode_logger_event_type(logger_event->logger_type, logger_event->event_type), + logger_event->logger_type, logger_event->event_type); + /* Display Logger Event Timestamp as UNIX-style Date string. */ + INFO_MAIN("Time and Date:\t\t%s\n", + (timestamp_str == NULL ? "Invalid Timestamp" : timestamp_str)); + INFO_MAIN("Logger Event Data: %s\n\n", logger_event->data); +} +#endif /*! \fn int tx_file(sangoma_interface *sang_if) @@ -492,34 +524,48 @@ static int get_user_hex_number() \param argc number of arguments \param argv argument list \return 0 - ok non-zero - Error - */ static int parse_command_line_args(int argc, char* argv[]) { int i; const char *USAGE_STR = "\n" -"Usage: sample [-c] [-i] [-silent]\n" +"Usage: sample [-c] [-i] [options]\n" "\n" -"Options:\n" -"\t-c number Wanpipe number: 1,2,3...\n" -"\t-i number Interface number 0,1,2,3,....\n" -"\t-driver_config configure start/stop driver using volatile....\n" +"\t-c number Wanpipe (Port/Span) number: 1,2,3...\n" +"\t-i number Interface number 1,2,3,....\n" +"options:\n" "\t-silent Disable display of Rx data\n" -"\t-rx2tx All received data automatically transmitted on the SAME interface\n" -"\t-txlength\tnumber\tLength of data frames to be transmitted when 't' key is pressed\n" -"\t-txcount\tnumber Number of test data frames to be transmitted when 't' key is pressed\n" +"\t-driver_config \tStop/Set Configuration/Start a Port....\n" +"\t-rx2tx All received data automatically transmitted on\n" +"\t the SAME interface\n" +"\t-txlength\tnumber\tLength of data frames to be transmitted when 't'\n" +"\t \t \tkey is pressed\n" +"\t-txcount\tnumber Number of test data frames to be transmitted when 't'\n" +"\t \t \tkey is pressed\n" "\t-tx_file_name\tstring\tFile to be transmitted when 't' key is pressed\n" #if USE_STELEPHONY_API -"\t-decode_fsk_cid\t\tDecode FSK Caller ID on an Analog line. For Voice data only.\n" -"\t-encode_fsk_cid\t\tEncode FSK Caller ID on an Analog line. For Voice data only.\n" +"\t-decode_fsk_cid\t\tDecode FSK Caller ID on an Analog line.\n" +"\t \t\tFor Voice data only.\n" +"\t-encode_fsk_cid\t\tEncode FSK Caller ID on an Analog line.\n" +"\t \t\tFor Voice data only.\n" "\t-encode_sw_dtmf\t\tEncode SW DTMF on an line. For Voice data only.\n" "\t-sw_dtmf Enable Sangoma Software DTMF decoder. For Voice data only.\n" "\t-decode_q931 Enable Sangoma Q931 decoder. For HDLC (Dchannel) data only.\n" "\t-alaw\t\t Use Alaw codec instead of default MuLaw codec for Voice data.\n" +"\t-rm_txgain\t Set txgain for FXS/FXO modules.\n" +"\t \t\tFXO range from -150 to 120, FXS 35 or -35\n" +"\t-rm_rxgain\t Set txgain for FXS/FXO modules.\n" +"\t \t\tFXO range from -150 to 120, FXS 35 or -35\n" #endif #if 0 -"\t-use_ctrl_dev Use the global 'wptdm_ctrl' device to Get events and to Control device.\n" +"\t-use_ctrl_dev \tUse the global 'wptdm_ctrl' device to Get events from\n" +"\t \tall API devices.\n" +#endif +"\t-use_logger_dev \tUse the global Logger device to Get Log Messages\n" +"\t \tfrom API driver.\n" +#ifdef WP_API_FEATURE_LIBSNG_HWEC +"\t-use_hwec \tInitialize/Configure/Use the Hardware Echo Canceller\n" #endif "\n" "Example: sample -c 1 -i 1\n"; @@ -527,8 +573,10 @@ static int parse_command_line_args(int argc, char* argv[]) memset(&program_settings, 0, sizeof(wp_program_settings_t)); program_settings.wanpipe_number = 1; program_settings.interface_number = 1; - program_settings.txlength = 128; + program_settings.txlength = 80; program_settings.txcount = 1; + program_settings.rxgain = 0xFFFF; //FXO/FXS rx gain unchanged + program_settings.txgain = 0xFFFF; //FXO/FXS tx gain unchanged for(i = 1; i < argc;){ @@ -618,6 +666,29 @@ static int parse_command_line_args(int argc, char* argv[]) }else if(_stricmp(argv[i], "-use_ctrl_dev") == 0){ INFO_MAIN("Using ctrl_dev...\n"); program_settings.use_ctrl_dev = 1; + }else if(_stricmp(argv[i], "-use_logger_dev") == 0){ + INFO_MAIN("Using logger_dev...\n"); + program_settings.use_logger_dev = 1; + }else if(_stricmp(argv[i], "-rm_txgain") == 0){ + if (i+1 > argc-1){ + INFO_MAIN("No txgain provided!\n"); + return 1; + } + program_settings.txgain = atoi(argv[i+1]); + i++; + INFO_MAIN("Setting txgain to %d.\n", program_settings.txgain); + }else if(_stricmp(argv[i], "-rm_rxgain") == 0){ + if (i+1 > argc-1){ + INFO_MAIN("No rxgain provided!\n"); + return 1; + } + program_settings.rxgain = atoi(argv[i+1]); + i++; + INFO_MAIN("Setting rxgain to %d.\n", program_settings.txgain); + }else if(_stricmp(argv[i], "-use_hwec") == 0){ + + INFO_MAIN("Using hardware echo canceller...\n"); + program_settings.use_hardware_echo_canceller = 1; }else{ INFO_MAIN("Error: Invalid Argument %s\n",argv[i]); return 1; @@ -654,8 +725,10 @@ int __cdecl main(int argc, char* argv[]) //////////////////////////////////////////////////////////////////////////// memset(&callback_functions, 0x00, sizeof(callback_functions)); callback_functions.got_rx_data = got_rx_data; - callback_functions.got_TdmApiEvent = got_TdmApiEvent; - + callback_functions.got_tdm_api_event = got_tdm_api_event; +#if USE_WP_LOGGER + callback_functions.got_logger_event = got_logger_event; +#endif //////////////////////////////////////////////////////////////////////////// if(parse_command_line_args(argc, argv)){ return 1; @@ -693,65 +766,92 @@ int __cdecl main(int argc, char* argv[]) cleanup(sang_if); return rc; } + if(program_settings.txgain != 0xFFFF) { + INFO_MAIN("Applying txgain...\n"); + if (sang_if->tdm_control_rm_txgain(program_settings.txgain)){ + INFO_MAIN("Failed to apply txgain!\n"); + } + } + + /* FXS cannot detect if phone is connected or not when the card is started + therefore tranmit following two events for Set Polarity to work */ + if(sang_if->get_adapter_type() == WAN_MEDIA_FXOFXS && sang_if->get_sub_media() == MOD_TYPE_FXS) { + INFO_MAIN("Setting Proper hookstates on FXS\n"); + sang_if->tdm_txsig_offhook(); + sang_if->tdm_txsig_onhook(); + } + if(program_settings.rxgain != 0xFFFF) { + INFO_MAIN("Applying rxgain...\n"); + if (sang_if->tdm_control_rm_rxgain(program_settings.rxgain)){ + INFO_MAIN("Failed to apply rxgain!\n"); + } + } do{ EnterCriticalSection(&PrintCriticalSection); + INFO_MAIN("Press 'q' to quit the program.\n"); - INFO_MAIN("Press 't' to transmit data.\n"); INFO_MAIN("Press 's' to get Operational Statistics.\n"); INFO_MAIN("Press 'f' to reset (flush) Operational Statistics.\n"); - INFO_MAIN("Press 'v' to get API driver version.\n"); - if(sang_if->get_adapter_type() == WAN_MEDIA_T1 || sang_if->get_adapter_type() == WAN_MEDIA_E1){ - INFO_MAIN("Press 'a' to get T1/E1 alarms.\n"); - //RBS (CAS) commands - INFO_MAIN("Press 'g' to get RBS bits.\n"); - INFO_MAIN("Press 'r' to set RBS bits.\n"); - INFO_MAIN("Press '1' to read FE register. Warning: used by Sangoma Techsupport only!\n"); - INFO_MAIN("Press '2' to write FE register. Warning: used by Sangoma Techsupport only!\n"); - } - INFO_MAIN("Press 'i' to set Tx idle data buffer (BitStream only).\n"); - switch(sang_if->get_adapter_type()) - { - case WAN_MEDIA_T1: - //those commands valid only for T1 - INFO_MAIN("Press 'l' to send 'activate remote loop back' signal.\n"); - INFO_MAIN("Press 'd' to send 'deactivate remote loop back' signal.\n"); - break; - case WAN_MEDIA_FXOFXS: - switch(sang_if->get_sub_media()) + if(program_settings.use_logger_dev != 1){ + /* these options valid for non-logger api devices */ + INFO_MAIN("Press 't' to transmit data.\n"); + INFO_MAIN("Press 'v' to get API driver version.\n"); + + if(sang_if->get_adapter_type() == WAN_MEDIA_T1 || sang_if->get_adapter_type() == WAN_MEDIA_E1){ + INFO_MAIN("Press 'a' to get T1/E1 alarms.\n"); + //RBS (CAS) commands + INFO_MAIN("Press 'g' to get RBS bits.\n"); + INFO_MAIN("Press 'r' to set RBS bits.\n"); + INFO_MAIN("Press '1' to read FE register. Warning: used by Sangoma Techsupport only!\n"); + INFO_MAIN("Press '2' to write FE register. Warning: used by Sangoma Techsupport only!\n"); + } + INFO_MAIN("Press 'i' to set Tx idle data buffer (BitStream only).\n"); + switch(sang_if->get_adapter_type()) { - case MOD_TYPE_FXS: - INFO_MAIN("Press 'e' to listen to test tones on a phone connected to the A200-FXS\n"); - INFO_MAIN("Press 'c' to ring/stop ring phone connected to the A200-FXS\n"); - INFO_MAIN("Press 'n' to enable/disable reception of ON/OFF Hook events on A200-FXS\n"); - INFO_MAIN("Press 'm' to enable DTMF events (on SLIC chip) on A200-FXS\n"); - INFO_MAIN("Press 'j' to enable/disable reception of Ring Trip events on A200-FXS\n"); + case WAN_MEDIA_T1: + //those commands valid only for T1 + INFO_MAIN("Press 'l' to send 'activate remote loop back' signal.\n"); + INFO_MAIN("Press 'd' to send 'deactivate remote loop back' signal.\n"); break; + case WAN_MEDIA_FXOFXS: + switch(sang_if->get_sub_media()) + { + case MOD_TYPE_FXS: + INFO_MAIN("Press 'e' to listen to test tones on a phone connected to the A200-FXS\n"); + INFO_MAIN("Press 'c' to ring/stop ring phone connected to the A200-FXS\n"); + INFO_MAIN("Press 'n' to enable/disable reception of ON/OFF Hook events on A200-FXS\n"); + INFO_MAIN("Press 'm' to enable DTMF events (on SLIC chip) on A200-FXS\n"); + INFO_MAIN("Press 'j 'to enable/disable reception of Ring Trip events on A200-FXS\n"); + INFO_MAIN("Press 'k' to transmit kewl - drop line voltage on the line connected to the A200-FXS\n"); + INFO_MAIN("Press 'h' to set polarity on the line connected to the A200-fXS\n"); + INFO_MAIN("Press 'u' to transmit onhooktransfer on the line connected to the A200-FXS\n"); + break; - case MOD_TYPE_FXO: - INFO_MAIN("Press 'u' to enable/disable reception of Ring Detect events on A200-FXO\n"); - INFO_MAIN("Press 'h' to transmit ON/OFF hook signals on A200-FXO\n"); - INFO_MAIN("Press 'a' to get Line Status (Connected/Disconnected)\n"); + case MOD_TYPE_FXO: + INFO_MAIN("Press 'u' to enable/disable reception of Ring Detect events on A200-FXO\n"); + INFO_MAIN("Press 'h' to transmit ON/OFF hook signals on A200-FXO\n"); + INFO_MAIN("Press 'a' to get Line Status (Connected/Disconnected)\n"); + break; + } + break; + case WAN_MEDIA_BRI: + INFO_MAIN("Press 'k' to Activate/Deactivate ISDN BRI line\n"); + INFO_MAIN("Press 'l' to enable bri bchan loopback\n"); + INFO_MAIN("Press 'd' to disable bri bchan loopback\n"); break; } - break; - case WAN_MEDIA_BRI: - INFO_MAIN("Press 'k' to Activate/Deactivate ISDN BRI line\n"); - INFO_MAIN("Press 'l' to enable bri bchan loopback\n"); - INFO_MAIN("Press 'd' to disable bri bchan loopback\n"); - break; - } - INFO_MAIN("Press 'o' to enable DTMF events (on Octasic chip)\n"); - if (program_settings.encode_sw_dtmf) { - INFO_MAIN("Press 'x' to send software DTMF\n"); - } - if (program_settings.encode_fsk_cid) { - INFO_MAIN("Press 'z' to send software FSK Caller ID\n"); - } + INFO_MAIN("Press 'o' to enable DTMF events (on Octasic chip)\n"); + if (program_settings.encode_sw_dtmf) { + INFO_MAIN("Press 'x' to send software DTMF\n"); + } + if (program_settings.encode_fsk_cid) { + INFO_MAIN("Press 'z' to send software FSK Caller ID\n"); + } + }//if(program_settings.use_logger_dev != 1) LeaveCriticalSection(&PrintCriticalSection); - user_selection = tolower(_getch()); switch(user_selection) { @@ -772,7 +872,12 @@ int __cdecl main(int argc, char* argv[]) } break; case 's': - { + if(program_settings.use_logger_dev == 1){ + wp_logger_stats_t stats; + /* Warning: for demonstration purposes only, it is assumed, + * that 'sang_if' is 'sangoma_api_logger_dev'. */ + ((sangoma_api_logger_dev*)sang_if)->get_logger_dev_operational_stats(&stats); + }else{ wanpipe_chan_stats_t stats; sang_if->get_operational_stats(&stats); } @@ -1035,16 +1140,20 @@ user_retry_ring_e_d: break; case 'u': //Enable/Disable Ring Detect events on FXO. - INFO_MAIN("Press 'e' to ENABLE Rx Ring Detect Events, 'd' to DISABLE Rx Ring Detect Events.\n"); - INFO_MAIN("\n"); - switch(tolower(_getch())) - { - case 'e': - sang_if->tdm_enable_ring_detect_events(); - break; - case 'd': - default: - sang_if->tdm_disable_ring_detect_events(); + if(sang_if->get_sub_media()==MOD_TYPE_FXO){ + INFO_MAIN("Press 'e' to ENABLE Rx Ring Detect Events, 'd' to DISABLE Rx Ring Detect Events.\n"); + INFO_MAIN("\n"); + switch(tolower(_getch())) + { + case 'e': + sang_if->tdm_enable_ring_detect_events(); + break; + case 'd': + default: + sang_if->tdm_disable_ring_detect_events(); + } + }else if(sang_if->get_sub_media()==MOD_TYPE_FXS){ + sang_if->tdm_txsig_onhooktransfer(); } break; case 'j': @@ -1062,29 +1171,56 @@ user_retry_ring_e_d: } break; case 'h': - INFO_MAIN("Press 'e' to transmit OFF hook signal, 'd' to transmit ON hook signal.\n"); - INFO_MAIN("\n"); - switch(tolower(_getch())) - { - case 'e': - sang_if->fxo_go_off_hook(); - break; - case 'd': - default: - sang_if->fxo_go_on_hook(); + if(sang_if->get_sub_media()==MOD_TYPE_FXO) { + INFO_MAIN("Press 'e' to transmit OFF hook signal, 'd' to transmit ON hook signal.\n"); + INFO_MAIN("\n"); + switch(tolower(_getch())) + { + case 'e': + sang_if->fxo_go_off_hook(); + break; + case 'd': + default: + sang_if->fxo_go_on_hook(); + } + }else if(sang_if->get_sub_media()==MOD_TYPE_FXS ) { + INFO_MAIN("Press 'f' for forward, 'r' to for reverse.\n"); + INFO_MAIN("\n"); + switch(tolower(_getch())) + { + case 'f': + sang_if->tdm_set_rm_polarity(0); + break; + case 'r': + sang_if->tdm_set_rm_polarity(1); + break; + default: + //toggle it + sang_if->tdm_set_rm_polarity(1); + sang_if->tdm_set_rm_polarity(0); + } } break; case 'k': - INFO_MAIN("Press 'e' to Activate, 'd' to De-Activate line.\n"); - INFO_MAIN("\n"); - switch(tolower(_getch())) - { - case 'e': - sang_if->tdm_front_end_activate(); - break; - case 'd': - default: - sang_if->tdm_front_end_deactivate(); + if( sang_if->get_adapter_type() == WAN_MEDIA_BRI ) { + INFO_MAIN("Press 'e' to Activate, 'd' to De-Activate line.\n"); + INFO_MAIN("\n"); + switch(tolower(_getch())) + { + case 'e': + sang_if->tdm_front_end_activate(); + break; + case 'd': + default: + sang_if->tdm_front_end_deactivate(); + } + }else if(sang_if->get_adapter_type()== WAN_MEDIA_FXOFXS) { + if(sang_if->get_sub_media()==MOD_TYPE_FXS) { + sang_if->tdm_txsig_kewl(); + sangoma_msleep(5000); + //to restore line current after txsig kewl + sang_if->tdm_txsig_offhook(); + } } break; case 'p': @@ -1127,8 +1263,18 @@ user_retry_ring_e_d: break; case 'z': { - INFO_MAIN("Sending CallerID.\n"); - sang_if->sendCallerID("Sangoma Rocks", "9054741990"); + if(WAN_MEDIA_FXOFXS == sang_if->get_adapter_type() && MOD_TYPE_FXS == sang_if->get_sub_media() ){ + //Ring the line + sang_if->start_ringing_phone(); + sangoma_msleep(2000); + //txsig offhook + sang_if->fxs_txsig_offhook(); + INFO_MAIN("Sending CallerID.\n"); + sang_if->sendCallerID("Sangoma Rocks", "9054741990"); + }else{ + INFO_MAIN("Sending CallerID.\n"); + sang_if->sendCallerID("Sangoma Rocks", "9054741990"); + } } break; #endif @@ -1184,7 +1330,8 @@ user_retry_ring_e_d: static int set_port_configuration() { - int rc = 0, is_te1_card = 0, user_selection; + int rc = 0, user_selection; + int is_te1_card = 0, is_analog_card = 0; hardware_info_t hardware_info; port_cfg_t port_cfg; @@ -1214,15 +1361,6 @@ static int set_port_configuration() return 3; } -#if 0 -defined(__WINDOWS__) - rc = sng_port_cfg_obj->open_port_registry_key(&hardware_info); - if(rc != SANG_STATUS_SUCCESS){ - delete sng_port_cfg_obj; - return 3; - } -#endif - memset(&port_cfg, 0x00, sizeof(port_cfg_t)); switch(hardware_info.card_model) @@ -1233,17 +1371,21 @@ defined(__WINDOWS__) case A108_ADPTR_8TE1: is_te1_card = 1; break; + case A200_ADPTR_ANALOG: + case A400_ADPTR_ANALOG: + is_analog_card = 1; + break; + case AFT_ADPTR_FLEXBRI: + //B700, a hybrid card - may have both ISDN BRI and Analog ports + break; } if(is_te1_card){ - INFO_MAIN("\n"); INFO_MAIN("Press 't' to set T1 configration.\n"); INFO_MAIN("Press 'e' to set E1 configration.\n"); - try_again: user_selection = tolower(_getch()); - switch(user_selection) { case 't'://T1 @@ -1254,27 +1396,38 @@ try_again: rc=sng_port_cfg_obj->initialize_e1_tdm_span_voice_api_configration_structure(&port_cfg,&hardware_info,program_settings.wanpipe_number); break; + case 'q'://quit the application + rc = 1; + break; + default: INFO_MAIN("Invalid command %c.\n",user_selection); goto try_again; break; }//switch(user_selection) + }//if(is_te1_card) - } else { //if(is_te1_card) -#if 0 + if(is_analog_card){ //read current configuration: if(sng_port_cfg_obj->get_configration(&port_cfg)){ rc = 1; }else{ //print the current configuration: sng_port_cfg_obj->print_port_cfg_structure(&port_cfg); - //as an EXAMPLE, set the same configration as the current one: - //rc = sng_port_cfg_obj->set_default_configuration(&port_cfg); - } -#else - INFO_MAIN("Unsupported Card %i\n",hardware_info.card_model); - rc = 1; +#if 0 + //as an EXAMPLE, enable Loop Current Monitoring for Analog FXO: + rc=sng_port_cfg_obj->control_analog_rm_lcm(&port_cfg, 1); #endif +#if 0 + //as an EXAMPLE, set Operation mode for FXO: + rc=sng_port_cfg_obj->set_analog_opermode(&port_cfg, "TBR21"); +#endif + } + }//if(is_analog_card) + + if(!is_te1_card && !is_analog_card){ + INFO_MAIN("Unsupported Card %d\n", hardware_info.card_model); + rc = 1; } do{ @@ -1282,7 +1435,6 @@ try_again: ERR_MAIN("Failed to Initialize Port Configuratoin structure!\n"); break; } - #if 1 INFO_MAIN("Stopping PORT for re-configuration!\n"); if ((rc = sng_port_cfg_obj->stop_port())) { @@ -1316,7 +1468,7 @@ try_again: delete sng_port_cfg_obj; } - sangoma_msleep(2000);//wait a little (2 seconds) for initialization to complete + sangoma_msleep(2000);//wait 2 seconds for initialization to complete return rc; } @@ -1334,7 +1486,7 @@ static void FSKCallerIDEvent(void *callback_context, if(Name){ INFO_MAIN("Name: %s\n", Name); -#if 1 +#if 0 printf("caller name in SINGLE byte hex:\n"); for(unsigned int ind = 0; ind < strlen(Name); ind++){ printf("Name[%02d]: 0x%02X\n", ind, Name[ind]); @@ -1345,9 +1497,11 @@ static void FSKCallerIDEvent(void *callback_context, for(unsigned int ind = 0; ind < strlen(Name); ind += 2){ printf("Name[%02d]: 0x%04X\n", ind, *(unsigned short*)&Name[ind]); } - printf("\n"); #endif + printf("\n"); } + + if(CallerNumber){ INFO_MAIN("CallerNumber: %s\n", CallerNumber); } @@ -1445,7 +1599,7 @@ static void FSKCallerIDTransmit (void *callback_context, void *FskCidBuffer) /* FSK CID buffer can be big (~8000 bytes), we don't want to block the calling thread, so start a new thread to transmit FSK CID. */ - sang_if->CreateSwDtmfTxThread(FskCidBuffer); + sang_if->CreateFskCidTxThread(FskCidBuffer); } #if 0 diff --git a/api/libsangoma/sample_cpp/sample.h b/api/libsangoma/sample_cpp/sample.h index 934c344..d563360 100644 --- a/api/libsangoma/sample_cpp/sample.h +++ b/api/libsangoma/sample_cpp/sample.h @@ -9,6 +9,7 @@ #include #endif +#define USE_WP_LOGGER 1 #define SAMPLE_CPP_MAX_PATH 1024 typedef struct{ @@ -27,6 +28,10 @@ typedef struct{ unsigned int txcount; unsigned char driver_config; unsigned char use_ctrl_dev; + unsigned char use_logger_dev; + int txgain; + int rxgain; + unsigned char use_hardware_echo_canceller; }wp_program_settings_t; #define DEV_NAME_LEN 100 @@ -38,7 +43,7 @@ typedef struct{ //Recieved data int (*got_rx_data)(void *sang_if_ptr, void *rxhdr, void *rx_data); //TDM events - void (*got_TdmApiEvent)(void *sang_if_ptr, void *event_data); + void (*got_tdm_api_event)(void *sang_if_ptr, void *event_data); #if USE_STELEPHONY_API //FSK Caller ID detected void (*FSKCallerIDEvent)(void *sang_if_ptr, char * Name, char * CallerNumber, char * CalledNumber, char * DateTime); @@ -51,6 +56,11 @@ typedef struct{ //DTMF buffer ready for transmission events void (*SwDtmfTransmit)(void *callback_context, void* buffer); #endif + +#if USE_WP_LOGGER + void (*got_logger_event)(void *sang_if_ptr, wp_logger_event_t *logger_event); +#endif + }callback_functions_t; static void DecodeLastError(LPSTR lpszFunction) @@ -70,7 +80,7 @@ static void DecodeLastError(LPSTR lpszFunction) NULL ); // Display the string. - printf("Last Error: %s (GetLastError() returned: %d)\n", lpMsgBuf, dwLastErr); + printf("Last Error: %s (GetLastError() returned: %d)\n", (char*)lpMsgBuf, dwLastErr); // Free the buffer. LocalFree( lpMsgBuf ); #endif diff --git a/api/libsangoma/sample_cpp/sample.sln b/api/libsangoma/sample_cpp/sample.sln index 8e45cb9..6493e1d 100644 --- a/api/libsangoma/sample_cpp/sample.sln +++ b/api/libsangoma/sample_cpp/sample.sln @@ -1,7 +1,7 @@  -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample", "sample.vcproj", "{20071FBB-88EE-41D9-A728-68A883BFC68B}" +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample_cpp", "sample.vcproj", "{20071FBB-88EE-41D9-A728-68A883BFC68B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/api/libsangoma/sample_cpp/sample.vcproj b/api/libsangoma/sample_cpp/sample.vcproj index 6f9ae4e..37e16da 100644 --- a/api/libsangoma/sample_cpp/sample.vcproj +++ b/api/libsangoma/sample_cpp/sample.vcproj @@ -1,10 +1,11 @@ - @@ -168,6 +168,8 @@ SuppressStartupBanner="true" ProgramDatabaseFile=".\Release/sample.pdb" SubSystem="1" + RandomizedBaseAddress="1" + DataExecutionPrevention="0" TargetMachine="1" /> - diff --git a/api/libsangoma/sample_cpp/sangoma_interface.cpp b/api/libsangoma/sample_cpp/sangoma_interface.cpp index f7a0a59..50265f0 100644 --- a/api/libsangoma/sample_cpp/sangoma_interface.cpp +++ b/api/libsangoma/sample_cpp/sangoma_interface.cpp @@ -5,7 +5,6 @@ ////////////////////////////////////////////////////////////////////// #include "sangoma_interface.h" -#include #define DBG_IFACE if(1)printf #define INFO_IFACE if(1)printf @@ -18,13 +17,22 @@ #define NUMBER_OF_WAIT_OBJECTS 1 +#ifdef WP_API_FEATURE_LIBSNG_HWEC /* defined in wanpipe_api_iface.h */ +# include "wanpipe_events.h" +# include "wanec_api.h" +sangoma_status_t config_hwec(char *strDeviceName, unsigned long in_ulChannelMap); +#endif + extern wp_program_settings_t program_settings; sangoma_interface::sangoma_interface(int wanpipe_number, int interface_number) { - DBG_IFACE( "sangoma_interface::sangoma_interface()\n"); + DBG_IFACE("%s()\n", __FUNCTION__); memset(device_name, 0x00, DEV_NAME_LEN); + memset(&wan_udp, 0x00, sizeof(wan_udp)); + memset(&tdm_api_cmd, 0x00, sizeof(tdm_api_cmd)); + memset(&wp_api, 0x00, sizeof(wp_api)); WanpipeNumber = wanpipe_number; InterfaceNumber = interface_number; @@ -39,7 +47,6 @@ sangoma_interface::sangoma_interface(int wanpipe_number, int interface_number) terminate_tx_rx_threads = 0; is_rbs_monitoring_enabled = 0; - memset(&wp_api, 0, sizeof(wp_api)); sng_wait_obj = NULL; ////////////////////////////////////////////////////////////////// @@ -60,6 +67,7 @@ sangoma_interface::sangoma_interface(int wanpipe_number, int interface_number) protocol_cb_size = sizeof(wan_mgmt_t)+sizeof(wan_cmd_t)+1; wan_protocol = 0; adapter_type = 0; + is_logger_dev = 0; #if USE_STELEPHONY_API stelObj = DtmfBuffer = FskCidBuffer = NULL; @@ -77,13 +85,13 @@ sangoma_interface::sangoma_interface(int wanpipe_number, int interface_number) sangoma_interface::~sangoma_interface() { - DBG_IFACE( "sangoma_interface::~sangoma_interface()\n"); + DBG_IFACE("%s()\n", __FUNCTION__); cleanup(); } int sangoma_interface::init(callback_functions_t *callback_functions_ptr) { - DBG_IFACE("sangoma_interface::init()\n"); + DBG_IFACE("%s()\n", __FUNCTION__); memcpy(&callback_functions, callback_functions_ptr, sizeof(callback_functions_t)); @@ -226,7 +234,7 @@ int sangoma_interface::get_wan_config() DO_COMMAND(wan_udp); if (wan_udp.wan_udphdr_return_code){ - ERR_IFACE( "Error: Command WANPIPEMON_GET_PROTOCOL failed! return code: 0x%X", + ERR_IFACE( "Error: Command WAN_GET_PROTOCOL failed! return code: 0x%X\n", wan_udp.wan_udphdr_return_code); return WAN_FALSE; } @@ -341,146 +349,146 @@ void sangoma_interface::get_te1_56k_stat(void) } fe_stats = (sdla_fe_stats_t*)get_wan_udphdr_data_ptr(0); - - if (adapter_type == WAN_MEDIA_T1 || adapter_type == WAN_MEDIA_E1){ - INFO_IFACE("***** %s: %s Alarms (Framer) *****\n\n", - device_name, (adapter_type == WAN_MEDIA_T1) ? "T1" : "E1"); - INFO_IFACE("ALOS:\t%s\t| LOS:\t%s\n", - WAN_TE_PRN_ALARM_ALOS(fe_stats->alarms), - WAN_TE_PRN_ALARM_LOS(fe_stats->alarms)); - INFO_IFACE("RED:\t%s\t| AIS:\t%s\n", - WAN_TE_PRN_ALARM_RED(fe_stats->alarms), - WAN_TE_PRN_ALARM_AIS(fe_stats->alarms)); - INFO_IFACE("LOF:\t%s\t| RAI:\t%s\n", - WAN_TE_PRN_ALARM_LOF(fe_stats->alarms), - WAN_TE_PRN_ALARM_RAI(fe_stats->alarms)); - - if (fe_stats->alarms & WAN_TE_ALARM_LIU){ - INFO_IFACE("\n***** %s: %s Alarms (LIU) *****\n\n", - device_name, (adapter_type == WAN_MEDIA_T1) ? "T1" : "E1"); - INFO_IFACE("Short Circuit:\t%s\n", - WAN_TE_PRN_ALARM_LIU_SC(fe_stats->alarms)); - INFO_IFACE("Open Circuit:\t%s\n", - WAN_TE_PRN_ALARM_LIU_OC(fe_stats->alarms)); - INFO_IFACE("Loss of Signal:\t%s\n", - WAN_TE_PRN_ALARM_LIU_LOS(fe_stats->alarms)); - } - - }else if (adapter_type == WAN_MEDIA_DS3 || adapter_type == WAN_MEDIA_E3){ - INFO_IFACE("***** %s: %s Alarms *****\n\n", - device_name, (adapter_type == WAN_MEDIA_DS3) ? "DS3" : "E3"); - - if (adapter_type == WAN_MEDIA_DS3){ - INFO_IFACE("AIS:\t%s\t| LOS:\t%s\n", - WAN_TE3_AIS_ALARM(fe_stats->alarms), - WAN_TE3_LOS_ALARM(fe_stats->alarms)); - - INFO_IFACE("OOF:\t%s\t| YEL:\t%s\n", - WAN_TE3_OOF_ALARM(fe_stats->alarms), - WAN_TE3_YEL_ALARM(fe_stats->alarms)); - }else{ - INFO_IFACE("AIS:\t%s\t| LOS:\t%s\n", - WAN_TE3_AIS_ALARM(fe_stats->alarms), - WAN_TE3_LOS_ALARM(fe_stats->alarms)); - - INFO_IFACE("OOF:\t%s\t| YEL:\t%s\n", - WAN_TE3_OOF_ALARM(fe_stats->alarms), - WAN_TE3_YEL_ALARM(fe_stats->alarms)); - - INFO_IFACE("LOF:\t%s\t\n", - WAN_TE3_LOF_ALARM(fe_stats->alarms)); - } - - }else if (adapter_type == WAN_MEDIA_56K){ - INFO_IFACE("***** %s: 56K CSU/DSU Alarms *****\n\n\n", device_name); - INFO_IFACE("In Service:\t\t%s\tData mode idle:\t\t%s\n", - INS_ALARM_56K(fe_stats->alarms), - DMI_ALARM_56K(fe_stats->alarms)); - - INFO_IFACE("Zero supp. code:\t%s\tCtrl mode idle:\t\t%s\n", - ZCS_ALARM_56K(fe_stats->alarms), - CMI_ALARM_56K(fe_stats->alarms)); - - INFO_IFACE("Out of service code:\t%s\tOut of frame code:\t%s\n", - OOS_ALARM_56K(fe_stats->alarms), - OOF_ALARM_56K(fe_stats->alarms)); - - INFO_IFACE("Valid DSU NL loopback:\t%s\tUnsigned mux code:\t%s\n", - DLP_ALARM_56K(fe_stats->alarms), - UMC_ALARM_56K(fe_stats->alarms)); - - INFO_IFACE("Rx loss of signal:\t%s\t\n", - RLOS_ALARM_56K(fe_stats->alarms)); - - }else{ - INFO_IFACE("***** %s: Unknown Front End 0x%X *****\n\n", - device_name, adapter_type); - } - - if (adapter_type == WAN_MEDIA_T1 || adapter_type == WAN_MEDIA_E1){ - sdla_te_pmon_t* pmon = &fe_stats->te_pmon; - - INFO_IFACE("\n\n***** %s: %s Performance Monitoring Counters *****\n\n", - device_name, (adapter_type == WAN_MEDIA_T1) ? "T1" : "E1"); - if (pmon->mask & WAN_TE_BIT_PMON_LCV){ - INFO_IFACE("Line Code Violation\t: %d\n", - pmon->lcv_errors); - } - if (pmon->mask & WAN_TE_BIT_PMON_BEE){ - INFO_IFACE("Bit Errors (CRC6/Ft/Fs)\t: %d\n", - pmon->bee_errors); - } - if (pmon->mask & WAN_TE_BIT_PMON_OOF){ - INFO_IFACE("Out of Frame Errors\t: %d\n", - pmon->oof_errors); - } - if (pmon->mask & WAN_TE_BIT_PMON_FEB){ - INFO_IFACE("Far End Block Errors\t: %d\n", - pmon->feb_errors); - } - if (pmon->mask & WAN_TE_BIT_PMON_CRC4){ - INFO_IFACE("CRC4 Errors\t\t: %d\n", - pmon->crc4_errors); - } - if (pmon->mask & WAN_TE_BIT_PMON_FER){ - INFO_IFACE("Framing Bit Errors\t: %d\n", - pmon->fer_errors); - } - if (pmon->mask & WAN_TE_BIT_PMON_FAS){ - INFO_IFACE("FAS Errors\t\t: %d\n", - pmon->fas_errors); - } - } - - if (adapter_type == WAN_MEDIA_DS3 || adapter_type == WAN_MEDIA_E3){ - sdla_te3_pmon_t* pmon = &fe_stats->u.te3_pmon; - - INFO_IFACE("\n\n***** %s: %s Performance Monitoring Counters *****\n\n", - device_name, (adapter_type == WAN_MEDIA_DS3) ? "DS3" : "E3"); - - INFO_IFACE("Framing Bit Error:\t%d\tLine Code Violation:\t%d\n", - pmon->pmon_framing, - pmon->pmon_lcv); - - if (adapter_type == WAN_MEDIA_DS3){ - INFO_IFACE("Parity Error:\t\t%d\n", - pmon->pmon_parity); - INFO_IFACE("CP-Bit Error Event:\t%d\tFEBE Event:\t\t%d\n", - pmon->pmon_cpbit, - pmon->pmon_febe); - }else{ - INFO_IFACE("Parity Error:\t%d\tFEBE Event:\t\t%d\n", - pmon->pmon_parity, - pmon->pmon_febe); - } - } - - if (adapter_type == WAN_MEDIA_T1 || adapter_type == WAN_MEDIA_E1){ - if (strlen(fe_stats->u.te1_stats.rxlevel)){ - INFO_IFACE("\n\nRx Level\t: %s\n", - fe_stats->u.te1_stats.rxlevel); - } - } + + if (adapter_type == WAN_MEDIA_T1 || adapter_type == WAN_MEDIA_E1){ + INFO_IFACE("***** %s: %s Alarms (Framer) *****\n\n", + device_name, (adapter_type == WAN_MEDIA_T1) ? "T1" : "E1"); + INFO_IFACE("ALOS:\t%s\t| LOS:\t%s\n", + WAN_TE_PRN_ALARM_ALOS(fe_stats->alarms), + WAN_TE_PRN_ALARM_LOS(fe_stats->alarms)); + INFO_IFACE("RED:\t%s\t| AIS:\t%s\n", + WAN_TE_PRN_ALARM_RED(fe_stats->alarms), + WAN_TE_PRN_ALARM_AIS(fe_stats->alarms)); + INFO_IFACE("LOF:\t%s\t| RAI:\t%s\n", + WAN_TE_PRN_ALARM_LOF(fe_stats->alarms), + WAN_TE_PRN_ALARM_RAI(fe_stats->alarms)); + + if (fe_stats->alarms & WAN_TE_ALARM_LIU){ + INFO_IFACE("\n***** %s: %s Alarms (LIU) *****\n\n", + device_name, (adapter_type == WAN_MEDIA_T1) ? "T1" : "E1"); + INFO_IFACE("Short Circuit:\t%s\n", + WAN_TE_PRN_ALARM_LIU_SC(fe_stats->alarms)); + INFO_IFACE("Open Circuit:\t%s\n", + WAN_TE_PRN_ALARM_LIU_OC(fe_stats->alarms)); + INFO_IFACE("Loss of Signal:\t%s\n", + WAN_TE_PRN_ALARM_LIU_LOS(fe_stats->alarms)); + } + + }else if (adapter_type == WAN_MEDIA_DS3 || adapter_type == WAN_MEDIA_E3){ + INFO_IFACE("***** %s: %s Alarms *****\n\n", + device_name, (adapter_type == WAN_MEDIA_DS3) ? "DS3" : "E3"); + + if (adapter_type == WAN_MEDIA_DS3){ + INFO_IFACE("AIS:\t%s\t| LOS:\t%s\n", + WAN_TE3_AIS_ALARM(fe_stats->alarms), + WAN_TE3_LOS_ALARM(fe_stats->alarms)); + + INFO_IFACE("OOF:\t%s\t| YEL:\t%s\n", + WAN_TE3_OOF_ALARM(fe_stats->alarms), + WAN_TE3_YEL_ALARM(fe_stats->alarms)); + }else{ + INFO_IFACE("AIS:\t%s\t| LOS:\t%s\n", + WAN_TE3_AIS_ALARM(fe_stats->alarms), + WAN_TE3_LOS_ALARM(fe_stats->alarms)); + + INFO_IFACE("OOF:\t%s\t| YEL:\t%s\n", + WAN_TE3_OOF_ALARM(fe_stats->alarms), + WAN_TE3_YEL_ALARM(fe_stats->alarms)); + + INFO_IFACE("LOF:\t%s\t\n", + WAN_TE3_LOF_ALARM(fe_stats->alarms)); + } + + }else if (adapter_type == WAN_MEDIA_56K){ + INFO_IFACE("***** %s: 56K CSU/DSU Alarms *****\n\n\n", device_name); + INFO_IFACE("In Service:\t\t%s\tData mode idle:\t\t%s\n", + INS_ALARM_56K(fe_stats->alarms), + DMI_ALARM_56K(fe_stats->alarms)); + + INFO_IFACE("Zero supp. code:\t%s\tCtrl mode idle:\t\t%s\n", + ZCS_ALARM_56K(fe_stats->alarms), + CMI_ALARM_56K(fe_stats->alarms)); + + INFO_IFACE("Out of service code:\t%s\tOut of frame code:\t%s\n", + OOS_ALARM_56K(fe_stats->alarms), + OOF_ALARM_56K(fe_stats->alarms)); + + INFO_IFACE("Valid DSU NL loopback:\t%s\tUnsigned mux code:\t%s\n", + DLP_ALARM_56K(fe_stats->alarms), + UMC_ALARM_56K(fe_stats->alarms)); + + INFO_IFACE("Rx loss of signal:\t%s\t\n", + RLOS_ALARM_56K(fe_stats->alarms)); + + }else{ + INFO_IFACE("***** %s: Unknown Front End 0x%X *****\n\n", + device_name, adapter_type); + } + + if (adapter_type == WAN_MEDIA_T1 || adapter_type == WAN_MEDIA_E1){ + sdla_te_pmon_t* pmon = &fe_stats->te_pmon; + + INFO_IFACE("\n\n***** %s: %s Performance Monitoring Counters *****\n\n", + device_name, (adapter_type == WAN_MEDIA_T1) ? "T1" : "E1"); + if (pmon->mask & WAN_TE_BIT_PMON_LCV){ + INFO_IFACE("Line Code Violation\t: %d\n", + pmon->lcv_errors); + } + if (pmon->mask & WAN_TE_BIT_PMON_BEE){ + INFO_IFACE("Bit Errors (CRC6/Ft/Fs)\t: %d\n", + pmon->bee_errors); + } + if (pmon->mask & WAN_TE_BIT_PMON_OOF){ + INFO_IFACE("Out of Frame Errors\t: %d\n", + pmon->oof_errors); + } + if (pmon->mask & WAN_TE_BIT_PMON_FEB){ + INFO_IFACE("Far End Block Errors\t: %d\n", + pmon->feb_errors); + } + if (pmon->mask & WAN_TE_BIT_PMON_CRC4){ + INFO_IFACE("CRC4 Errors\t\t: %d\n", + pmon->crc4_errors); + } + if (pmon->mask & WAN_TE_BIT_PMON_FER){ + INFO_IFACE("Framing Bit Errors\t: %d\n", + pmon->fer_errors); + } + if (pmon->mask & WAN_TE_BIT_PMON_FAS){ + INFO_IFACE("FAS Errors\t\t: %d\n", + pmon->fas_errors); + } + } + + if (adapter_type == WAN_MEDIA_DS3 || adapter_type == WAN_MEDIA_E3){ + sdla_te3_pmon_t* pmon = &fe_stats->u.te3_pmon; + + INFO_IFACE("\n\n***** %s: %s Performance Monitoring Counters *****\n\n", + device_name, (adapter_type == WAN_MEDIA_DS3) ? "DS3" : "E3"); + + INFO_IFACE("Framing Bit Error:\t%d\tLine Code Violation:\t%d\n", + pmon->pmon_framing, + pmon->pmon_lcv); + + if (adapter_type == WAN_MEDIA_DS3){ + INFO_IFACE("Parity Error:\t\t%d\n", + pmon->pmon_parity); + INFO_IFACE("CP-Bit Error Event:\t%d\tFEBE Event:\t\t%d\n", + pmon->pmon_cpbit, + pmon->pmon_febe); + }else{ + INFO_IFACE("Parity Error:\t%d\tFEBE Event:\t\t%d\n", + pmon->pmon_parity, + pmon->pmon_febe); + } + } + + if (adapter_type == WAN_MEDIA_T1 || adapter_type == WAN_MEDIA_E1){ + if (strlen(fe_stats->u.te1_stats.rxlevel)){ + INFO_IFACE("\n\nRx Level\t: %s\n", + fe_stats->u.te1_stats.rxlevel); + } + } return; } @@ -582,21 +590,20 @@ int sangoma_interface::get_operational_stats(wanpipe_chan_stats_t *stats) int err; unsigned char firm_ver, cpld_ver; - err=sangoma_get_stats(sangoma_dev,&wp_api, NULL); + err=sangoma_get_stats(sangoma_dev, &wp_api, stats); if (err) { + ERR_IFACE("sangoma_get_stats(() failed (err: %d (0x%X))!\n", err, err); return 1; } - memcpy(stats, &wp_api.wp_cmd.stats, sizeof(wanpipe_chan_stats_t)); - INFO_IFACE( "******* OPERATIONAL_STATS *******\n"); INFO_IFACE("\trx_packets\t: %u\n", stats->rx_packets); INFO_IFACE("\ttx_packets\t: %u\n", stats->tx_packets); INFO_IFACE("\trx_bytes\t: %u\n", stats->rx_bytes); INFO_IFACE("\ttx_bytes\t: %u\n", stats->tx_bytes); - INFO_IFACE("\trx_errors\t: %u\n", stats->rx_errors); - INFO_IFACE("\ttx_errors\t: %u\n", stats->tx_errors); + INFO_IFACE("\trx_errors\t: %u\n", stats->rx_errors); //Total number of Rx errors + INFO_IFACE("\ttx_errors\t: %u\n", stats->tx_errors); //Total number of Tx errors INFO_IFACE("\trx_dropped\t: %u\n", stats->rx_dropped); INFO_IFACE("\ttx_dropped\t: %u\n", stats->tx_dropped); INFO_IFACE("\tmulticast\t: %u\n", stats->multicast); @@ -604,12 +611,12 @@ int sangoma_interface::get_operational_stats(wanpipe_chan_stats_t *stats) INFO_IFACE("\trx_length_errors: %u\n", stats->rx_length_errors); INFO_IFACE("\trx_over_errors\t: %u\n", stats->rx_over_errors); - INFO_IFACE("\trx_crc_errors\t: %u\n", stats->rx_crc_errors); - INFO_IFACE("\trx_frame_errors\t: %u\n", stats->rx_frame_errors); + INFO_IFACE("\trx_crc_errors\t: %u\n", stats->rx_crc_errors); //HDLC CRC mismatch + INFO_IFACE("\trx_frame_errors\t: %u\n", stats->rx_frame_errors);//HDLC "abort" occured INFO_IFACE("\trx_fifo_errors\t: %u\n", stats->rx_fifo_errors); INFO_IFACE("\trx_missed_errors: %u\n", stats->rx_missed_errors); - INFO_IFACE("\ttx_aborted_errors: %u\n", stats->tx_aborted_errors); + INFO_IFACE("\ttx_aborted_errors: %u\n", stats->tx_aborted_errors); INFO_IFACE("\tTx Idle Data\t: %u\n", stats->tx_carrier_errors); INFO_IFACE("\ttx_fifo_errors\t: %u\n", stats->tx_fifo_errors); @@ -622,12 +629,12 @@ int sangoma_interface::get_operational_stats(wanpipe_chan_stats_t *stats) INFO_IFACE("\n\trx_packets_in_q: %u\n", stats->current_number_of_frames_in_rx_queue); INFO_IFACE("\trx_queue_size: %u\n", stats->max_rx_queue_length); - INFO_IFACE("\n\trx_events_in_q: %u\n", stats->current_number_of_events_in_event_queue); - INFO_IFACE("\trx_event_queue_size: %u\n", stats->max_event_queue_length); - INFO_IFACE("\trx_events: %u\n", stats->rx_events); + INFO_IFACE("\n\trx_events_in_q: %u\n", stats->current_number_of_events_in_event_queue); + INFO_IFACE("\trx_event_queue_size: %u\n", stats->max_event_queue_length); + INFO_IFACE("\trx_events: %u\n", stats->rx_events); INFO_IFACE("\trx_events_dropped: %u\n", stats->rx_events_dropped); - INFO_IFACE("\tHWEC tone (DTMF) events counter: %u\n", stats->rx_events_tone); + INFO_IFACE("\tHWEC tone (DTMF) events counter: %u\n", stats->rx_events_tone); INFO_IFACE( "*********************************\n"); err=sangoma_get_driver_version(sangoma_dev,&wp_api, NULL); @@ -640,14 +647,14 @@ int sangoma_interface::get_operational_stats(wanpipe_chan_stats_t *stats) wp_api.wp_cmd.version.minor1, wp_api.wp_cmd.version.minor2); - err=sangoma_get_firmware_version(sangoma_dev,&wp_api, &firm_ver); + err=sangoma_get_firmware_version(sangoma_dev, &wp_api, &firm_ver); if (err) { return 1; } INFO_IFACE("\tFirmware Version: %X\n", firm_ver); - err=sangoma_get_cpld_version(sangoma_dev,&wp_api, &cpld_ver); + err=sangoma_get_cpld_version(sangoma_dev, &wp_api, &cpld_ver); if (err) { return 1; } @@ -703,10 +710,9 @@ int sangoma_interface::set_tx_idle_flag(unsigned char new_idle_flag) //get RBS being received char sangoma_interface::get_rbs(rbs_management_t *rbs_management_ptr) { - int err; - err=sangoma_tdm_read_rbs(sangoma_dev, &wp_api, rbs_management_ptr->channel,&rbs_management_ptr->ABCD_bits); + err=sangoma_tdm_read_rbs(sangoma_dev, &wp_api, rbs_management_ptr->channel, &rbs_management_ptr->ABCD_bits); if (err) { ERR_IFACE( "Error: command WANPIPEMON_GET_RBS_BITS failed!\n"); return 1; @@ -728,7 +734,9 @@ char sangoma_interface::get_rbs(rbs_management_t *rbs_management_ptr) //set RBS to be transmitted char sangoma_interface::set_rbs(rbs_management_t *rbs_management_ptr) { - int err=sangoma_tdm_write_rbs(sangoma_dev,&wp_api, rbs_management_ptr->channel, rbs_management_ptr->ABCD_bits); + int err; + + err=sangoma_tdm_write_rbs(sangoma_dev, &wp_api, rbs_management_ptr->channel, rbs_management_ptr->ABCD_bits); if (err) { return 1; } @@ -764,7 +772,7 @@ int sangoma_interface::get_interface_configuration(if_cfg_t *wanif_conf_ptr) INFO_IFACE( "Operational Mode\t: %s (%d)\n", SDLA_DECODE_USEDBY_FIELD(wanif_conf_ptr->usedby), wanif_conf_ptr->usedby); INFO_IFACE( "Configued Active Channels \t: 0x%08X\n", wanif_conf_ptr->active_ch); INFO_IFACE( "Echo Canceller Channels\t\t: 0x%08X\n", wanif_conf_ptr->ec_active_ch); - INFO_IFACE( "User Specified Channels during Port Config\t\t: 0x%08X\n", wanif_conf_ptr->cfg_active_ch); + INFO_IFACE( "User Specified Channels during Port Config\t: 0x%08X\n", wanif_conf_ptr->cfg_active_ch); INFO_IFACE( "Interface Number\t: %u\n", wanif_conf_ptr->interface_number); INFO_IFACE( "Media type\t\t: %u\n", wanif_conf_ptr->media); INFO_IFACE( "Line Mode\t\t: %s\n", wanif_conf_ptr->line_mode); @@ -776,6 +784,8 @@ int sangoma_interface::get_interface_configuration(if_cfg_t *wanif_conf_ptr) case TDM_SPAN_VOICE_API: case TDM_CHAN_VOICE_API: case API: + case TDM_VOICE_DCHAN: + case STACK: //ok break; default: @@ -831,7 +841,7 @@ void sangoma_interface::get_api_driver_version (PDRIVER_VERSION version) int err; - err=sangoma_get_driver_version(sangoma_dev,&wp_api, version); + err=sangoma_get_driver_version(sangoma_dev, &wp_api, version); if(err){ ERR_IFACE("Error: command READ_CODE_VERSION failed!\n"); return; @@ -912,14 +922,15 @@ int sangoma_interface::DoManagementCommand(sng_fd_t fd, wan_udp_hdr_t* wan_udp) int sangoma_interface::tdmv_api_ioctl(wanpipe_api_cmd_t *api_cmd) { - wanpipe_api_t tmp; int err; - memcpy(&tmp.wp_cmd, api_cmd, sizeof(wanpipe_api_cmd_t)); + memset(&wp_api, 0x00, sizeof(wp_api)); - err = sangoma_cmd_exec(sangoma_dev, &tmp); + memcpy(&wp_api.wp_cmd, api_cmd, sizeof(wanpipe_api_cmd_t)); - memcpy(api_cmd, &tmp.wp_cmd, sizeof(wanpipe_api_cmd_t)); + err = sangoma_cmd_exec(sangoma_dev, &wp_api); + + memcpy(api_cmd, &wp_api.wp_cmd, sizeof(wanpipe_api_cmd_t)); return err; } @@ -934,103 +945,125 @@ int sangoma_interface::tdm_disable_ring_detect_events() return sangoma_tdm_disable_ring_detect_events(sangoma_dev,&wp_api); } - /* To enable flash event set rxflashtime to 1250. - 1250 is most used value. To disable flash event set rxflashtime to 0 */ +/* To enable flash event set rxflashtime to 1250. + * 1250 is most used value. To disable flash event set rxflashtime to 0 */ int sangoma_interface::tdm_control_flash_events(int rxflashtime) { - DBG_IFACE("%s()\n", __FUNCTION__); - - int err; - - err=sangoma_set_rm_rxflashtime(sangoma_dev,&wp_api, rxflashtime); - if (err) { - return 1; - } - return 0; + return sangoma_set_rm_rxflashtime(sangoma_dev, &wp_api, rxflashtime); +} + +int sangoma_interface::tdm_control_rm_txgain(int txgain) +{ +#if WP_API_FEATURE_RM_GAIN + return sangoma_set_rm_tx_gain(sangoma_dev,&wp_api, txgain); +#else + return SANG_STATUS_UNSUPPORTED_FUNCTION; +#endif +} + +int sangoma_interface::tdm_control_rm_rxgain(int rxgain) +{ +#if WP_API_FEATURE_RM_GAIN + return sangoma_set_rm_rx_gain(sangoma_dev, &wp_api, rxgain); +#else + return SANG_STATUS_UNSUPPORTED_FUNCTION; +#endif } int sangoma_interface::tdm_enable_ring_trip_detect_events() { - return sangoma_tdm_enable_ring_trip_detect_events(sangoma_dev,&wp_api); + return sangoma_tdm_enable_ring_trip_detect_events(sangoma_dev, &wp_api); } int sangoma_interface::tdm_disable_ring_trip_detect_events() { - return sangoma_tdm_disable_ring_trip_detect_events(sangoma_dev,&wp_api); + return sangoma_tdm_disable_ring_trip_detect_events(sangoma_dev, &wp_api); } int sangoma_interface::tdm_enable_rm_dtmf_events() { - DBG_IFACE("%s()\n", __FUNCTION__); - - return sangoma_tdm_enable_rm_dtmf_events(sangoma_dev,&wp_api); + return sangoma_tdm_enable_rm_dtmf_events(sangoma_dev, &wp_api); } int sangoma_interface::tdm_disable_rm_dtmf_events() { - return sangoma_tdm_disable_rm_dtmf_events(sangoma_dev,&wp_api); + return sangoma_tdm_disable_rm_dtmf_events(sangoma_dev, &wp_api); } int sangoma_interface::tdm_enable_dtmf_events(uint8_t channel) { - return sangoma_tdm_enable_dtmf_events(sangoma_dev,&wp_api); + return sangoma_tdm_enable_dtmf_events(sangoma_dev, &wp_api); } int sangoma_interface::tdm_disable_dtmf_events(uint8_t channel) { - return sangoma_tdm_disable_dtmf_events(sangoma_dev,&wp_api); + return sangoma_tdm_disable_dtmf_events(sangoma_dev, &wp_api); } int sangoma_interface::tdm_enable_rxhook_events() { - DBG_IFACE("%s()\n", __FUNCTION__); - return sangoma_tdm_enable_rxhook_events(sangoma_dev,&wp_api); + return sangoma_tdm_enable_rxhook_events(sangoma_dev, &wp_api); } int sangoma_interface::tdm_disable_rxhook_events() { - return sangoma_tdm_disable_rxhook_events(sangoma_dev,&wp_api); + return sangoma_tdm_disable_rxhook_events(sangoma_dev, &wp_api); } int sangoma_interface::tdm_enable_ring_events() { - return sangoma_tdm_enable_ring_events(sangoma_dev,&wp_api); + return sangoma_tdm_enable_ring_events(sangoma_dev, &wp_api); } int sangoma_interface::tdm_disable_ring_events() { - return sangoma_tdm_disable_ring_events(sangoma_dev,&wp_api); + return sangoma_tdm_disable_ring_events(sangoma_dev, &wp_api); } int sangoma_interface::tdm_txsig_onhook() { - return sangoma_tdm_txsig_onhook(sangoma_dev,&wp_api); + return sangoma_tdm_txsig_onhook(sangoma_dev, &wp_api); +} + +int sangoma_interface::tdm_txsig_kewl() +{ + return sangoma_tdm_txsig_kewl(sangoma_dev, &wp_api); +} + +int sangoma_interface::tdm_txsig_onhooktransfer() +{ + return sangoma_tdm_txsig_onhooktransfer(sangoma_dev, &wp_api); +} + +int sangoma_interface::tdm_set_rm_polarity(int polarity) +{ + return sangoma_tdm_set_polarity(sangoma_dev, &wp_api, polarity); } int sangoma_interface::tdm_txsig_offhook() { - return sangoma_tdm_txsig_offhook(sangoma_dev,&wp_api); + return sangoma_tdm_txsig_offhook(sangoma_dev, &wp_api); } int sangoma_interface::tdm_enable_tone_events(uint16_t tone_id) { - return sangoma_tdm_enable_tone_events(sangoma_dev,&wp_api, tone_id); + return sangoma_tdm_enable_tone_events(sangoma_dev, &wp_api, tone_id); } int sangoma_interface::tdm_disable_tone_events() { - return sangoma_tdm_disable_tone_events(sangoma_dev,&wp_api); + return sangoma_tdm_disable_tone_events(sangoma_dev, &wp_api); } int sangoma_interface::tdm_enable_bri_bchan_loopback(u_int8_t channel) { - return sangoma_enable_bri_bchan_loopback(sangoma_dev,&wp_api, channel); + return sangoma_enable_bri_bchan_loopback(sangoma_dev, &wp_api, channel); } int sangoma_interface::tdm_disable_bri_bchan_loopback(u_int8_t channel) { - return sangoma_disable_bri_bchan_loopback(sangoma_dev,&wp_api, channel); + return sangoma_disable_bri_bchan_loopback(sangoma_dev, &wp_api, channel); } /* 1. Enable 'writing' of RBS bits on card. @@ -1040,45 +1073,44 @@ int sangoma_interface::tdm_disable_bri_bchan_loopback(u_int8_t channel) Valid values are between 20 and 100 (including). */ int sangoma_interface::tdm_enable_rbs_events(int polls_per_second) { - return sangoma_tdm_enable_rbs_events(sangoma_dev,&wp_api,polls_per_second); + return sangoma_tdm_enable_rbs_events(sangoma_dev, &wp_api,polls_per_second); } /* Stop monitoring change in state of RBS bits */ int sangoma_interface::tdm_disable_rbs_events() { - return sangoma_tdm_disable_rbs_events(sangoma_dev,&wp_api); + return sangoma_tdm_disable_rbs_events(sangoma_dev, &wp_api); } /* Activate ISDN BRI line. */ int sangoma_interface::tdm_front_end_activate() { - return sangoma_set_fe_status(sangoma_dev,&wp_api,WAN_FE_CONNECTED); + return sangoma_set_fe_status(sangoma_dev, &wp_api,WAN_FE_CONNECTED); } /* De-activate ISDN BRI line. */ int sangoma_interface::tdm_front_end_deactivate() { - return sangoma_set_fe_status(sangoma_dev,&wp_api,WAN_FE_DISCONNECTED); + return sangoma_set_fe_status(sangoma_dev, &wp_api, WAN_FE_DISCONNECTED); } /* get current state of the line - is it Connected or Disconnected */ int sangoma_interface::tdm_get_front_end_status(unsigned char *status) { - return sangoma_get_fe_status(sangoma_dev,&wp_api,status); + return sangoma_get_fe_status(sangoma_dev, &wp_api, status); } /* Milliseconds interval between receive of Voice Data */ int sangoma_interface::tdm_set_user_period(unsigned int usr_period) { - return sangoma_tdm_set_usr_period(sangoma_dev,&wp_api,usr_period); + return sangoma_tdm_set_usr_period(sangoma_dev, &wp_api, usr_period); } int sangoma_interface::stop() { int wait_counter = 0; - DBG_IFACE("sangoma_interface::stop()\n"); - - INFO_IFACE( "Stopping."); + DBG_IFACE("%s()\n", __FUNCTION__); + INFO_IFACE("Stopping."); terminate_tx_rx_threads = 1; @@ -1131,6 +1163,35 @@ int sangoma_interface::run() break; } +#ifdef WP_API_FEATURE_LIBSNG_HWEC + if (program_settings.use_hardware_echo_canceller == 1) { + + char strDeviceName[DEV_NAME_LEN]; + if_cfg_t wanif_conf; + + memset(strDeviceName, 0x00, DEV_NAME_LEN); + + //Form the Interface Name from Wanpipe Number and Interface Index (i.e. wanpipe1_if1). + sangoma_span_chan_toif(WanpipeNumber, InterfaceNumber, strDeviceName); + INFO_IFACE("Interface Name for HWEC: %s\n", strDeviceName); + + if(get_interface_configuration(&wanif_conf)){ + ERR_IFACE("%s(): get_interface_configuration() failed!", __FUNCTION__); + return 1; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // It is assumed that this interface represents Voice Channel(s), not the d-channel, + // therefore 'wanif_conf.ec_active_ch' bitmap contains only the Voice Channel(s). + ////////////////////////////////////////////////////////////////////////////////////////// + if(SANG_STATUS_SUCCESS != config_hwec(strDeviceName, wanif_conf.ec_active_ch)){ + ERR_IFACE("%s(): config_hwec() failed!", __FUNCTION__); + return 2; + } + DBG_IFACE("%s(): HWEC initialization/configuraion was successful.\n", __FUNCTION__); + } +#endif + //////////////////////////////////////////////////////////////////////////// //Start a thread for receiving data only if(this->CreateThread(1) == false){ @@ -1257,7 +1318,7 @@ int sangoma_interface::sendCallerID(char * name, char * number) #endif strncpy(my_caller_id.calling_name, name, sizeof(my_caller_id.calling_name)-2); - strncpy(my_caller_id.calling_number, number, sizeof(my_caller_id.calling_name)-2); + strncpy(my_caller_id.calling_number, number, sizeof(my_caller_id.calling_number)-2); my_caller_id.calling_name[sizeof(my_caller_id.calling_name)-1] = '\0'; my_caller_id.calling_number[sizeof(my_caller_id.calling_number)-1] = '\0'; @@ -1338,7 +1399,6 @@ void sangoma_interface::RxThreadFunc() } if(output_flags[0] & POLLPRI){ - /* event */ if(read_event()){ ERR_IFACE("Error in read_event()!\n"); } @@ -1352,7 +1412,7 @@ void sangoma_interface::RxThreadFunc() default: /* error */ ERR_IFACE("iResult: %s (%d)\n", SDLA_DECODE_SANG_STATUS(iResult), iResult); - return; + sangoma_msleep(2000);/* don't exit, but put a delay to avoid busy loop */ }//switch(iResult) }//while() @@ -1369,17 +1429,18 @@ void sangoma_interface::RxThreadFunc() int sangoma_interface::read_event() { int err; - wp_api_event_t *rx_event = &wp_api.wp_cmd.event; memset(&wp_api, 0, sizeof(wp_api)); err = sangoma_read_event(sangoma_dev, &wp_api); - if(err){ return err; } - callback_functions.got_TdmApiEvent(this, rx_event); + /* an event - such as DTMF, On/Off Hook, Line Connect/Disconnect... */ + wp_api_event_t *rx_event = &wp_api.wp_cmd.event; + + callback_functions.got_tdm_api_event(this, rx_event); return 0; } @@ -1516,6 +1577,11 @@ int sangoma_interface::transmit(wp_api_hdr_t *hdr, void *data) default: /* error */ ERR_IFACE("iResult: %s (%d)\n", SDLA_DECODE_SANG_STATUS(iResult), iResult); + if(SANG_STATUS_FAILED_ALLOCATE_MEMORY == iResult){ + INFO_IFACE("Fatal Error. Press any key to exit the application.\n"); + _getch(); + exit(1); + } }//switch(iResult) return iResult; @@ -1524,61 +1590,68 @@ int sangoma_interface::transmit(wp_api_hdr_t *hdr, void *data) /////////////////////////////////////////////////////////////////////// int sangoma_interface::start_ring_tone() { - DBG_IFACE( "%s:start_ring_tone()\n", device_name); - return sangoma_tdm_enable_tone_events(sangoma_dev,&wp_api, WP_API_EVENT_TONE_RING); + DBG_IFACE("%s:%s()\n", device_name, __FUNCTION__); + return sangoma_tdm_enable_tone_events(sangoma_dev, &wp_api, WP_API_EVENT_TONE_RING); } /////////////////////////////////////////////////////////////////////// int sangoma_interface::start_congestion_tone() { - DBG_IFACE( "%s:start_congestion_tone()\n", device_name); - return sangoma_tdm_enable_tone_events(sangoma_dev,&wp_api,WP_API_EVENT_TONE_CONGESTION); + DBG_IFACE("%s:%s()\n", device_name, __FUNCTION__); + return sangoma_tdm_enable_tone_events(sangoma_dev, &wp_api, WP_API_EVENT_TONE_CONGESTION); } /////////////////////////////////////////////////////////////////////// int sangoma_interface::start_busy_tone() { - DBG_IFACE( "%s:start_busy_tone()\n", device_name); - return sangoma_tdm_enable_tone_events(sangoma_dev,&wp_api,WP_API_EVENT_TONE_BUSY); + DBG_IFACE("%s:%s()\n", device_name, __FUNCTION__); + return sangoma_tdm_enable_tone_events(sangoma_dev, &wp_api, WP_API_EVENT_TONE_BUSY); } /////////////////////////////////////////////////////////////////////// int sangoma_interface::start_dial_tone() { - DBG_IFACE( "%s:start_dial_tone()\n", device_name); - return sangoma_tdm_enable_tone_events(sangoma_dev,&wp_api,WP_API_EVENT_TONE_DIAL); + DBG_IFACE("%s:%s()\n", device_name, __FUNCTION__); + return sangoma_tdm_enable_tone_events(sangoma_dev, &wp_api, WP_API_EVENT_TONE_DIAL); } /////////////////////////////////////////////////////////////////////// int sangoma_interface::stop_all_tones() { - DBG_IFACE( "%s:stop_all_tones()\n", device_name); - return sangoma_tdm_disable_tone_events(sangoma_dev,&wp_api); + DBG_IFACE("%s:%s()\n", device_name, __FUNCTION__); + return sangoma_tdm_disable_tone_events(sangoma_dev, &wp_api); } /////////////////////////////////////////////////////////////////////// int sangoma_interface::start_ringing_phone() { - DBG_IFACE( "%s:start_ringing_phone()\n", device_name); - return sangoma_tdm_enable_ring_events(sangoma_dev,&wp_api); + DBG_IFACE("%s:%s()\n", device_name, __FUNCTION__); + return sangoma_tdm_enable_ring_events(sangoma_dev, &wp_api); } int sangoma_interface::stop_ringing_phone() { - DBG_IFACE( "%s:stop_ringing_phone()\n", device_name); - return sangoma_tdm_disable_ring_events(sangoma_dev,&wp_api); + DBG_IFACE("%s:%s()\n", device_name, __FUNCTION__); + return sangoma_tdm_disable_ring_events(sangoma_dev, &wp_api); } /////////////////////////////////////////////////////////////////////// int sangoma_interface::fxo_go_off_hook() { - DBG_IFACE( "%s:fxo_go_off_hook()\n", device_name); + DBG_IFACE("%s:%s()\n", device_name, __FUNCTION__); if(wanif_conf_struct.sub_media != MOD_TYPE_FXO){ ERR_IFACE( "%s: The 'Go Off Hook' command valid only for FXO!\n", device_name); return 1; } + return sangoma_tdm_txsig_offhook(sangoma_dev, &wp_api); +} + +/////////////////////////////////////////////////////////////////////// +int sangoma_interface::fxs_txsig_offhook() +{ + DBG_IFACE("%s:%s()\n", device_name, __FUNCTION__); return sangoma_tdm_txsig_offhook(sangoma_dev,&wp_api); } int sangoma_interface::fxo_go_on_hook() { - DBG_IFACE( "%s:fxo_go_on_hook()\n", device_name); + DBG_IFACE("%s:%s()\n", device_name, __FUNCTION__); if(wanif_conf_struct.sub_media != MOD_TYPE_FXO){ ERR_IFACE( "%s: The 'Go On Hook' command valid only for FXO!\n", device_name); @@ -1590,7 +1663,7 @@ int sangoma_interface::fxo_go_on_hook() //set a Telephony interface to it's default state int sangoma_interface::reset_interface_state() { - DBG_IFACE("%s:reset_interface_state()\n", device_name); + DBG_IFACE("%s:%s()\n", device_name, __FUNCTION__); switch(wanif_conf_struct.media) { @@ -1627,7 +1700,8 @@ void sangoma_interface::set_fe_debug_mode(sdla_fe_debug_t *fe_debug) DO_COMMAND(wan_udp); if (wan_udp.wan_udphdr_return_code){ - ERR_IFACE( "Error: WAN_FE_SET_DEBUG_MODE failed! return code: 0x%X", wan_udp.wan_udphdr_return_code); + ERR_IFACE( "Error: WAN_FE_SET_DEBUG_MODE failed! return code: 0x%X", + wan_udp.wan_udphdr_return_code); return; } @@ -1639,7 +1713,7 @@ repeat_read_reg: wan_udp.wan_udphdr_return_code = 0xaa; fe_debug->fe_debug_reg.read = 2; memcpy(data, (unsigned char*)fe_debug, sizeof(sdla_fe_debug_t)); - usleep(100000); + wp_usleep(100000); err = DO_COMMAND(wan_udp); if (err || wan_udp.wan_udphdr_return_code != 0){ if (cnt < 5){ @@ -1690,16 +1764,13 @@ repeat_read_reg: return; } - - - +/////////////////////////////////////////////////////////////////////////// +// Ctrl Device Class implementation sangoma_api_ctrl_dev::sangoma_api_ctrl_dev():sangoma_interface(0, 0) { IFACE_FUNC(); - //Initialize Device Name (for debugging only). _snprintf(device_name, DEV_NAME_LEN, WP_CTRL_DEV_NAME); - INFO_IFACE("Using Device Name: %s\n", device_name); } @@ -1715,7 +1786,7 @@ int sangoma_api_ctrl_dev::init(callback_functions_t *callback_functions_ptr) memcpy(&callback_functions, callback_functions_ptr, sizeof(callback_functions_t)); //////////////////////////////////////////////////////////////////////////// - //open handle for reading and writing data, for events reception and other commands + //open handle for events reception and other commands sangoma_dev = sangoma_open_api_ctrl(); if (sangoma_dev == INVALID_HANDLE_VALUE){ ERR_IFACE("Unable to open Ctrl Dev!\n"); @@ -1726,6 +1797,204 @@ int sangoma_api_ctrl_dev::init(callback_functions_t *callback_functions_ptr) ERR_IFACE("Failed to create 'sangoma_wait_object' for %s\n", device_name); return 1; } + return 0; +} + +/////////////////////////////////////////////////////////////////////////// +// Logger Device Class implementation +sangoma_api_logger_dev::sangoma_api_logger_dev():sangoma_interface(0, 0) +{ + IFACE_FUNC(); +#if USE_WP_LOGGER + //Initialize Device Name (for debugging only). + _snprintf(device_name, DEV_NAME_LEN, WP_LOGGER_DEV_NAME); + INFO_IFACE("Using Device Name: %s\n", device_name); +#endif +} + +sangoma_api_logger_dev::~sangoma_api_logger_dev() +{ + IFACE_FUNC(); +} + +int sangoma_api_logger_dev::init(callback_functions_t *callback_functions_ptr) +{ + IFACE_FUNC(); +#if USE_WP_LOGGER + is_logger_dev = 1; + + memcpy(&callback_functions, callback_functions_ptr, sizeof(callback_functions_t)); + + //////////////////////////////////////////////////////////////////////////// + //open handle for Logger Events reception and other commands + sangoma_dev = sangoma_logger_open(); + if (sangoma_dev == INVALID_HANDLE_VALUE){ + ERR_IFACE("Unable to open Ctrl Dev!\n"); + return 1; + } + + if(SANG_ERROR(sangoma_wait_obj_create(&sng_wait_obj, sangoma_dev, SANGOMA_DEVICE_WAIT_OBJ_SIG))){ + ERR_IFACE("Failed to create 'sangoma_wait_object' for %s\n", device_name); + return 1; + } + + //////////////////////////////////////////////////////////////////////////// + //get Current Level of Default Logger + memset(&logger_cmd, 0x00, sizeof(logger_cmd)); + logger_cmd.logger_level_ctrl.logger_type = WAN_LOGGER_DEFAULT; + if(sangoma_logger_get_logger_level(sangoma_dev, &logger_cmd)){ + ERR_IFACE("WP_API_CMD_GET_LOGGER_LEVEL failed for %s!\n", device_name); + return 1; + }else{ + INFO_IFACE("%s: Current logger level: 0x%08X\n", + device_name, logger_cmd.logger_level_ctrl.logger_level); + } + + //////////////////////////////////////////////////////////////////////////// + //set New Level for Default Logger. + logger_cmd.logger_level_ctrl.logger_type = WAN_LOGGER_DEFAULT; + logger_cmd.logger_level_ctrl.logger_level = + (SANG_LOGGER_INFORMATION | SANG_LOGGER_WARNING | SANG_LOGGER_ERROR); + if(sangoma_logger_set_logger_level(sangoma_dev, &logger_cmd)){ + ERR_IFACE("WP_API_CMD_SET_LOGGER_LEVEL failed for %s!\n", device_name); + return 1; + } + +#if 0 + //enable advanced debugging messages for T1/E1 + memset(&logger_cmd, 0x00, sizeof(logger_cmd)); + logger_cmd.logger_level_ctrl.logger_type = WAN_LOGGER_TE1; + logger_cmd.logger_level_ctrl.logger_level = (SANG_LOGGER_TE1_DEFAULT); + if(sangoma_logger_set_logger_level(sangoma_dev, &logger_cmd)){ + ERR_IFACE("WP_API_CMD_SET_LOGGER_LEVEL failed for %s!\n", device_name); + return 1; + } +#endif + +#if 0 + //enable advanced debugging messages for Hardware Echo Canceller + memset(&logger_cmd, 0x00, sizeof(logger_cmd)); + logger_cmd.logger_level_ctrl.logger_type = WAN_LOGGER_HWEC; + logger_cmd.logger_level_ctrl.logger_level = (SANG_LOGGER_HWEC_DEFAULT); + if(sangoma_logger_set_logger_level(sangoma_dev, &logger_cmd)){ + ERR_IFACE("WP_API_CMD_SET_LOGGER_LEVEL failed for %s!\n", device_name); + return 1; + } +#endif + +#endif//USE_WP_LOGGER + return 0; +} + +int sangoma_api_logger_dev::read_event() +{ + int err=1; + + memset(&logger_cmd, 0, sizeof(logger_cmd)); + +#if USE_WP_LOGGER + err = sangoma_logger_read_event(sangoma_dev, &logger_cmd); +#endif + if(err){ + return err; + } + + /* Wanpipe Logger Event */ +#if USE_WP_LOGGER + wp_logger_event_t *logger_event = &logger_cmd.logger_event; + callback_functions.got_logger_event(this, logger_event); +#endif return 0; } + +int sangoma_api_logger_dev::flush_operational_stats(void) +{ +#if USE_WP_LOGGER + return sangoma_logger_reset_statistics(sangoma_dev, &logger_cmd); +#else + return 1; +#endif +} + +int sangoma_api_logger_dev::get_logger_dev_operational_stats(wp_logger_stats_t *stats) +{ + int err=1; +#if USE_WP_LOGGER + err = sangoma_logger_get_statistics(sangoma_dev, &logger_cmd); + if(err){ + ERR_IFACE("sangoma_logger_get_statistics() failed (err: %d (0x%X))!\n", err, err); + return err; + } + + memcpy(stats, &logger_cmd.stats, sizeof(*stats)); + + INFO_IFACE("*** Logger Device Statistics ***\n"); + INFO_IFACE("\trx_events\t: %u\n", stats->rx_events); + INFO_IFACE("\trx_events_dropped\t: %u\n", stats->rx_events_dropped); + INFO_IFACE("\tmax_event_queue_length\t: %u\n", stats->max_event_queue_length); + INFO_IFACE("\tcurrent_number_of_events_in_event_queue\t: %u\n", stats->current_number_of_events_in_event_queue); + INFO_IFACE("\n"); +#endif + return err; +} + +#ifdef WP_API_FEATURE_LIBSNG_HWEC +/* NOTE: HWEC must NOT be enabled on a d-channel. The 'in_ulChannelMap' should be + * the bitmap of voice channels only. */ +sangoma_status_t config_hwec(char *strDeviceName, unsigned long in_ulChannelMap) +{ + sangoma_status_t rc; + + DBG_IFACE("%s(): strDeviceName: %s, in_ulChannelMap: 0x%X\n", __FUNCTION__, strDeviceName, in_ulChannelMap); + + // Initialize the echo canceller (done once per-physical card) + rc = sangoma_hwec_config_init(strDeviceName); + if (SANG_STATUS_SUCCESS != rc) { + ERR_IFACE( "Failed to initialize echo canceller. rc: %d\n", rc); + return rc; + } + + // Enable DTMF detection + rc = sangoma_hwec_config_tone_detection(strDeviceName, + WP_API_EVENT_TONE_DTMF, + 1 /* enable */, + in_ulChannelMap, + WAN_EC_CHANNEL_PORT_SOUT //(detection of the DTMF coming from the PSTN only Sin -> Sout path) + ); + if (SANG_STATUS_SUCCESS != rc) { + ERR_IFACE( "Failed to enable DTMF detection on echo canceller device.\n" ); + return rc; + } + + // Enable FAX detection + // When FAX is detected, a DTMF event will occur, and the detected digit will be 'f'. + rc = sangoma_hwec_config_tone_detection(strDeviceName, + WP_API_EVENT_TONE_FAXCALLED,//incoming fax detection OR + //WP_API_EVENT_TONE_FAXCALLING - outgoing fax detection + 1 /* enable */, + in_ulChannelMap, + WAN_EC_CHANNEL_PORT_SOUT // (detection of the fax tone coming from the PSTN only Sin -> Sout path) + ); + if (SANG_STATUS_SUCCESS != rc) { + ERR_IFACE( "Failed to enable FAX detection on echo canceller device.\n" ); + return rc; + } + +#if 0 +/* "WANEC_SoutAdaptiveNoiseReduction", "WANEC_TailDisplacement", "WANEC_DtmfToneRemoval", "WANEC_AcousticEcho"...*/ + + // Optionally, Enable noise reduction from PSTN + rc = sangoma_hwec_config_channel_parameters(strDeviceName, + "WANEC_SoutAdaptiveNoiseReduction", + "TRUE", + in_ulChannelMap ); + if (SANG_STATUS_SUCCESS != rc) { + ERR_IFACE( "Failed to enable noise reduction.\n" ); + return rc; + } +#endif + + return SANG_STATUS_SUCCESS; +} +#endif// WP_API_FEATURE_LIBSNG_HWEC diff --git a/api/libsangoma/sample_cpp/sangoma_interface.h b/api/libsangoma/sample_cpp/sangoma_interface.h index f22fc5e..9d23857 100644 --- a/api/libsangoma/sample_cpp/sangoma_interface.h +++ b/api/libsangoma/sample_cpp/sangoma_interface.h @@ -45,7 +45,7 @@ # include # include # include "bit_win.h" -# include "wanpipe_time.h" //for usleep() +# include "wanpipe_time.h" //for wp_usleep() #elif defined(__LINUX__) @@ -78,16 +78,16 @@ * API may provide input from a SINGLE or from MULTIPLE timeslots. */ #define USE_STELEPHONY_API 1 /* set to zero if don't need to compile - libstelephony.dll function calls */ + function calls to libstelephony.dll */ #include "wanpipe_api.h" #include "sangoma_cthread.h" #include "sample.h" -#include +#include "libsangoma.h" #if USE_STELEPHONY_API -# include +# include "libstelephony.h" #endif /*! @@ -101,7 +101,7 @@ protected: sng_fd_t sangoma_dev; /*! wait object device for an IO device */ - void *sng_wait_obj; + sangoma_wait_obj_t *sng_wait_obj; ////////////////////////////////////////////////////////////////// //receive stuff @@ -156,19 +156,16 @@ protected: /*! Tx Thread function */ void TxThreadFunc(); - /*! Rx data call back function handler */ + /*! Rx Data handler function */ int read_data(); - /*! Rx event call back function handler */ - int read_event(); + /*! Rx Event handler function */ + virtual int read_event(); int write_data(wp_api_hdr_t *hdr, void *tx_buffer); /*! Shutdown function to cleanup the class */ void cleanup(); - /*! deprecated: Developmnet API structure used to read write low level memory */ - wan_cmd_api_t wanpipe_api_cmd; - /*! Get device span configuration */ int get_wan_config(); @@ -233,8 +230,9 @@ protected: virtual unsigned long threadFunction(struct ThreadParam& thParam); public: - char device_name[DEV_NAME_LEN]; + char device_name[DEV_NAME_LEN]; + char is_logger_dev; ////////////////////////////////////////////////////////////////// //methods sangoma_interface(int wanpipe_number, int interface_number); @@ -259,7 +257,7 @@ public: int loopback_command(u_int8_t type, u_int8_t mode, u_int32_t chan_map); int get_operational_stats(wanpipe_chan_stats_t *stats); - int flush_operational_stats (void); + virtual int flush_operational_stats (void); int CreateSwDtmfTxThread(void *buffer); int CreateFskCidTxThread(void *buffer); @@ -317,6 +315,11 @@ public: int tdm_txsig_onhook(); int tdm_txsig_offhook(); + int tdm_txsig_kewl(); + /*To transmit data while FXS is on-hook */ + /* Example: To transmit FSK Message Wait Indication (MWI) + to phone connected with FXS */ + int tdm_txsig_onhooktransfer(); int tdm_enable_tone_events(uint16_t tone_id); int tdm_disable_tone_events(); @@ -325,6 +328,20 @@ public: int tdm_front_end_deactivate(); int tdm_control_flash_events(int rxflashtime); + + /* To set tx/rx gain for analog FXS/FXO modules + Gain in dB = gainvalue / 10 + For FXS: txgain/rxgain value could be -35 or 35 + FXO: txgain/rxgain value ranges from -150 to 120 + FXO/FXS: Set txgain/rxgain value 0 for default setting*/ + + int tdm_control_rm_txgain(int txgain); + int tdm_control_rm_rxgain(int rxgain); + + /* Only valid for FXS module to Set Polarity on the line + polarity 0: Forward Polarity + 1: Reverse Polarity */ + int tdm_set_rm_polarity(int polarity); /* get current state of the line - is it Connected or Disconnected */ int tdm_get_front_end_status(unsigned char *status); @@ -356,6 +373,8 @@ public: int fxo_go_off_hook(); int fxo_go_on_hook(); + int fxs_txsig_offhook(); + //BRI only: int tdm_enable_bri_bchan_loopback(u_int8_t channel); int tdm_disable_bri_bchan_loopback(u_int8_t channel); @@ -370,18 +389,19 @@ public: virtual int init(callback_functions_t *callback_functions_ptr); }; +class sangoma_api_logger_dev : public sangoma_interface +{ + wp_logger_cmd_t logger_cmd; -#if defined(__WINDOWS__) -#define HANDLE_DEVICE_IOCTL_RESULT(bResult)\ -{ \ - if(bResult == 0){ \ - /* check message log */ \ - printf("%s(): Line: %d: Error!!\n", __FUNCTION__, __LINE__); \ - DecodeLastError(__FUNCTION__); \ - return 1; \ - } \ -} -#endif /*__WINDOWS__*/ +public: + sangoma_api_logger_dev(void); + ~sangoma_api_logger_dev(void); + virtual int init(callback_functions_t *callback_functions_ptr); + /*! Logger Event handler function */ + virtual int read_event(); + virtual int flush_operational_stats (void); + int get_logger_dev_operational_stats(wp_logger_stats_t *stats); +}; #endif//SANGOMA_INTERFACE_H diff --git a/api/libsangoma/sample_cpp/sangoma_port.cpp b/api/libsangoma/sample_cpp/sangoma_port.cpp index 0b2f858..61ff638 100644 --- a/api/libsangoma/sample_cpp/sangoma_port.cpp +++ b/api/libsangoma/sample_cpp/sangoma_port.cpp @@ -51,7 +51,7 @@ int sangoma_port::init(uint16_t wanpipe_number) wp_number = wanpipe_number; wp_handle = sangoma_open_driver_ctrl(wanpipe_number); if(wp_handle == INVALID_HANDLE_VALUE){ - ERR_CFG("Error: failed to open %s!!\n", wanpipe_name_str); + ERR_CFG("Error: failed to open wanpipe%d!!\n", wp_number); return 1; } return 0; @@ -63,7 +63,7 @@ int sangoma_port::get_hardware_info(hardware_info_t *hardware_info) int err=sangoma_driver_get_hw_info(wp_handle,&port_management, wp_number); if (err) { - ERR_CFG("%s: Error: failed to get hw info!\n",wanpipe_name_str); + ERR_CFG("Error: failed to get hw info for wanpipe%d!\n", wp_number); err=1; return err; } diff --git a/api/libsangoma/sample_cpp/sangoma_port.h b/api/libsangoma/sample_cpp/sangoma_port.h index db85ae2..a4c4fc0 100644 --- a/api/libsangoma/sample_cpp/sangoma_port.h +++ b/api/libsangoma/sample_cpp/sangoma_port.h @@ -82,7 +82,6 @@ public: protected: uint16_t wp_number; - char wanpipe_name_str[WAN_DRVNAME_SZ*2]; HANDLE wp_handle; int push_a_card_into_wanpipe_info_array(wanpipe_instance_info_t *wanpipe_info_array, diff --git a/api/libsangoma/sample_cpp/sangoma_port_configurator.cpp b/api/libsangoma/sample_cpp/sangoma_port_configurator.cpp index a0c19db..fc94b4f 100644 --- a/api/libsangoma/sample_cpp/sangoma_port_configurator.cpp +++ b/api/libsangoma/sample_cpp/sangoma_port_configurator.cpp @@ -337,7 +337,7 @@ int sangoma_port_configurator::set_volatile_configration(port_cfg_t *port_cfg) switch(port_cfg->operation_status) { case SANG_STATUS_DEVICE_BUSY: - INFO_CFG("Error: open handles exist for '%s_IF\?\?' interfaces!\n", wanpipe_name_str); + INFO_CFG("Error: open handles exist for 'wanpipe%d_if\?\?' interfaces!\n", wp_number); return port_cfg->operation_status; case SANG_STATUS_SUCCESS: //OK @@ -360,7 +360,7 @@ int sangoma_port_configurator::initialize_t1_tdm_span_voice_api_configration_str { wandev_conf_t *wandev_conf = &port_cfg->wandev_conf; sdla_fe_cfg_t *sdla_fe_cfg = &wandev_conf->fe_cfg; - sdla_te_cfg_t *te_cfg = &sdla_fe_cfg->cfg.te_cfg; + //sdla_te_cfg_t *te_cfg = &sdla_fe_cfg->cfg.te_cfg; wan_tdmv_conf_t *tdmv_cfg = &wandev_conf->tdmv_conf; wanif_conf_t *wanif_cfg = &port_cfg->if_cfg[0]; @@ -369,15 +369,15 @@ int sangoma_port_configurator::initialize_t1_tdm_span_voice_api_configration_str FE_LCODE(sdla_fe_cfg) = WAN_LCODE_B8ZS; FE_FRAME(sdla_fe_cfg) = WAN_FR_ESF; FE_LINENO(sdla_fe_cfg) = hardware_info->port_number; - FE_TDMV_LAW(sdla_fe_cfg) = WAN_TDMV_MULAW; - FE_NETWORK_SYNC(sdla_fe_cfg) = 0; + FE_TDMV_LAW(sdla_fe_cfg) = WAN_TDMV_MULAW; + FE_NETWORK_SYNC(sdla_fe_cfg) = 0; + + FE_LBO(sdla_fe_cfg) = WAN_T1_0_110; + FE_REFCLK(sdla_fe_cfg) = 0; + FE_HIMPEDANCE_MODE(sdla_fe_cfg) = 0; + FE_SIG_MODE(sdla_fe_cfg) = WAN_TE1_SIG_CCS; + FE_RX_SLEVEL(sdla_fe_cfg) = WAN_TE1_RX_SLEVEL_12_DB; - FE_LBO(sdla_fe_cfg) = WAN_T1_0_110; - FE_REFCLK(sdla_fe_cfg) = 0; - FE_HIMPEDANCE_MODE(sdla_fe_cfg) = 0; - FE_SIG_MODE(sdla_fe_cfg) = WAN_TE1_SIG_CCS; - FE_RX_SLEVEL(sdla_fe_cfg) = WAN_TE1_RX_SLEVEL_12_DB; - #if 1 FE_CLK(sdla_fe_cfg) = WAN_NORMAL_CLK; #else @@ -396,7 +396,7 @@ int sangoma_port_configurator::initialize_t1_tdm_span_voice_api_configration_str wandev_conf->card_type = WANOPT_AFT; //m_DeviceInfoData.card_model; wanif_cfg->magic = ROUTER_MAGIC; - wanif_cfg->active_ch = 0x01FFFFFE;// channels 1-24 + wanif_cfg->active_ch = 0x00FFFFFF;//channels 1-24 (starting from bit zero) sprintf(wanif_cfg->usedby,"TDM_SPAN_VOICE_API"); wanif_cfg->u.aft.idle_flag=0xFF; wanif_cfg->mtu = 160; @@ -417,7 +417,7 @@ int sangoma_port_configurator::initialize_t1_tdm_span_voice_api_configration_str switch(FE_MEDIA(sdla_fe_cfg)) { case WAN_MEDIA_T1: - tdmv_cfg->dchan = (1<<23);/* channel 24 */ + tdmv_cfg->dchan = (1<<23);/* Channel 24. This is a bitmap, not a channel number. */ break; default: printf("%s(): Error: invalid media type!\n", __FUNCTION__); @@ -442,14 +442,14 @@ int sangoma_port_configurator::initialize_e1_tdm_span_voice_api_configration_str FE_LCODE(sdla_fe_cfg) = WAN_LCODE_HDB3; FE_FRAME(sdla_fe_cfg) = WAN_FR_CRC4; FE_LINENO(sdla_fe_cfg) = hardware_info->port_number; - FE_TDMV_LAW(sdla_fe_cfg) = WAN_TDMV_MULAW; - FE_NETWORK_SYNC(sdla_fe_cfg) = 0; + FE_TDMV_LAW(sdla_fe_cfg) = WAN_TDMV_MULAW; + FE_NETWORK_SYNC(sdla_fe_cfg) = 0; - FE_LBO(sdla_fe_cfg) = WAN_E1_120; - FE_REFCLK(sdla_fe_cfg) = 0; - FE_HIMPEDANCE_MODE(sdla_fe_cfg) = 0; - FE_SIG_MODE(sdla_fe_cfg) = WAN_TE1_SIG_CCS; - FE_RX_SLEVEL(sdla_fe_cfg) = WAN_TE1_RX_SLEVEL_12_DB; + FE_LBO(sdla_fe_cfg) = WAN_E1_120; + FE_REFCLK(sdla_fe_cfg) = 0; + FE_HIMPEDANCE_MODE(sdla_fe_cfg) = 0; + FE_SIG_MODE(sdla_fe_cfg) = WAN_TE1_SIG_CCS; + FE_RX_SLEVEL(sdla_fe_cfg) = WAN_TE1_RX_SLEVEL_12_DB; #if 1 FE_CLK(sdla_fe_cfg) = WAN_NORMAL_CLK; @@ -469,7 +469,7 @@ int sangoma_port_configurator::initialize_e1_tdm_span_voice_api_configration_str wandev_conf->card_type = WANOPT_AFT; //m_DeviceInfoData.card_model; wanif_cfg->magic = ROUTER_MAGIC; - wanif_cfg->active_ch = 0xFFFFFFFE;// channels 1-31 + wanif_cfg->active_ch = 0x7FFFFFFF;// channels 1-31 (starting from bit zero) sprintf(wanif_cfg->usedby,"TDM_SPAN_VOICE_API"); wanif_cfg->u.aft.idle_flag=0xFF; wanif_cfg->mtu = 160; @@ -490,7 +490,7 @@ int sangoma_port_configurator::initialize_e1_tdm_span_voice_api_configration_str switch(FE_MEDIA(sdla_fe_cfg)) { case WAN_MEDIA_E1: - tdmv_cfg->dchan = (1<<15);/* channel 16 */ + tdmv_cfg->dchan = (1<<15);/* Channel 16. This is a bitmap, not a channel number. */ break; default: printf("%s(): Error: invalid media type!\n", __FUNCTION__); @@ -507,3 +507,34 @@ int sangoma_port_configurator::write_configration_on_persistent_storage(port_cfg return sangoma_write_port_config_on_persistent_storage(hardware_info, port_cfg, (unsigned short)span); } +int sangoma_port_configurator::control_analog_rm_lcm(port_cfg_t *port_cfg, int control_val) +{ + wandev_conf_t *wandev_conf = &port_cfg->wandev_conf; + sdla_fe_cfg_t *sdla_fe_cfg = &wandev_conf->fe_cfg; + if(wandev_conf->card_type == WANOPT_AFT_ANALOG){ //Only valid for Analog cards + if(control_val == 1){ + sdla_fe_cfg->cfg.remora.rm_lcm = 1; + }else if(control_val == 0){ + sdla_fe_cfg->cfg.remora.rm_lcm = 0; + }else{ + printf("%s(): Error: invalid parameter!\n", __FUNCTION__); + return -EINVAL; + } + } else{ + return -EINVAL; + } + return 0; +} +int sangoma_port_configurator::set_analog_opermode(port_cfg_t *port_cfg, char *opermode) +{ + wandev_conf_t *wandev_conf = &port_cfg->wandev_conf; + sdla_fe_cfg_t *sdla_fe_cfg = &wandev_conf->fe_cfg; + if(wandev_conf->card_type == WANOPT_AFT_ANALOG){ //Only valid for Analog cards + if(sizeof(sdla_fe_cfg->cfg.remora.opermode_name) < strlen(opermode)) + return -EINVAL; + strcpy(sdla_fe_cfg->cfg.remora.opermode_name,opermode); + } else{ + return -EINVAL; + } + return 0; +} diff --git a/api/libsangoma/sample_cpp/sangoma_port_configurator.h b/api/libsangoma/sample_cpp/sangoma_port_configurator.h index 542a7c2..f327ba6 100644 --- a/api/libsangoma/sample_cpp/sangoma_port_configurator.h +++ b/api/libsangoma/sample_cpp/sangoma_port_configurator.h @@ -44,12 +44,18 @@ public: sangoma_port_configurator(); virtual ~sangoma_port_configurator(); - int open_port_registry_key(hardware_info_t *hardware_info); - int get_configration(port_cfg_t *port_cfg); int set_default_configuration(port_cfg_t *port_cfg); + //Function to set Loop Current Measure (LCM) for analog FXO + //control_val 1: enable,0: disable + int control_analog_rm_lcm(port_cfg_t *port_cfg, int control_val); + + //Function to set Operation mode for analog FXO + //opermode valid country name supported by Analog FXO + int set_analog_opermode(port_cfg_t *port_cfg, char *opermode); + //Function to check correctness of 'port_cfg_t' structure. int check_port_cfg_structure(port_cfg_t *port_cfg); diff --git a/api/libsangoma/sample_cpp/sources b/api/libsangoma/sample_cpp/sources index 384521e..1e48e55 100644 --- a/api/libsangoma/sample_cpp/sources +++ b/api/libsangoma/sample_cpp/sources @@ -17,6 +17,7 @@ INCLUDES=$(DDK_INC_PATH);\ $(SANG_WP_DEVEL)\wanpipe_common\include;\ $(SANG_WP_DEVEL)\wanpipe_common\include\aft_core;\ $(SANG_WP_DEVEL)\wanpipe_windows\include;\ +$(SANG_WP_DEVEL)\wanpipe_common\wantools\wanec_apilib;\ $(SANG_WP_DEVEL)\libsangoma;\ $(SANG_WP_DEVEL)\stelephony;\ $(SANG_WP_DEVEL)\stelephony\stel_tone @@ -34,8 +35,9 @@ $(SDK_LIB_PATH)\oleaut32.lib \ $(SDK_LIB_PATH)\uuid.lib \ $(SDK_LIB_PATH)\comctl32.lib \ $(SDK_LIB_PATH)\Setupapi.lib \ -$(SANG_WP_DEVEL)\libsangoma\$(OBJ_DIR)\libsangoma.lib \ -$(SANG_WP_DEVEL)\stelephony\$(OBJ_DIR)\stelephony.lib +$(SANG_WP_DEVEL)\libsangoma\$(O)\libsangoma.lib \ +$(SANG_WP_DEVEL)\stelephony\$(O)\stelephony.lib \ +$(SANG_WP_DEVEL)\wanpipe_common\wantools\wanec_apilib\$(O)\waneclib.lib SOURCES=sample.cpp \ diff --git a/api/libsangoma/sources b/api/libsangoma/sources index 6aa4111..d89d9b4 100644 --- a/api/libsangoma/sources +++ b/api/libsangoma/sources @@ -18,13 +18,15 @@ INCLUDES=$(SDK_INC_PATH);\ $(SANG_WP_DEVEL)\wanpipe_common\include;\ $(SANG_WP_DEVEL)\wanpipe_common\include\aft_core;\ $(SANG_WP_DEVEL)\wanpipe_windows\include;\ -$(SANG_WP_DEVEL)\libsangoma +$(SANG_WP_DEVEL)\libsangoma;\ +$(SANG_WP_DEVEL)\wanpipe_common\wantools\wanec_apilib TARGETLIBS=$(SDK_LIB_PATH)\kernel32.lib \ $(SDK_LIB_PATH)\user32.lib \ $(SDK_LIB_PATH)\SetupApi.lib \ -$(SDK_LIB_PATH)\Advapi32.lib +$(SDK_LIB_PATH)\Advapi32.lib \ +$(SANG_WP_DEVEL)\wanpipe_common\wantools\wanec_apilib\$(O)\waneclib.lib -SOURCES=libsangoma.c libsangoma.rc +SOURCES=libsangoma.c libsangoma.rc libsangoma_hwec.c diff --git a/api/libstelephony/.svn/all-wcprops b/api/libstelephony/.svn/all-wcprops index 0fe7804..7c701ba 100644 --- a/api/libstelephony/.svn/all-wcprops +++ b/api/libstelephony/.svn/all-wcprops @@ -1,13 +1,13 @@ K 25 svn:wc:ra_dav:version-url V 33 -/svn/stelephony/!svn/ver/52/trunk +/svn/stelephony/!svn/ver/59/trunk END stelephony.h K 25 svn:wc:ra_dav:version-url V 46 -/svn/stelephony/!svn/ver/43/trunk/stelephony.h +/svn/stelephony/!svn/ver/59/trunk/stelephony.h END init-automake.sh K 25 @@ -73,7 +73,7 @@ PToneDecoder.h K 25 svn:wc:ra_dav:version-url V 48 -/svn/stelephony/!svn/ver/51/trunk/PToneDecoder.h +/svn/stelephony/!svn/ver/56/trunk/PToneDecoder.h END PToneEncoder.cpp K 25 @@ -91,7 +91,7 @@ libstelephony.h K 25 svn:wc:ra_dav:version-url V 49 -/svn/stelephony/!svn/ver/43/trunk/libstelephony.h +/svn/stelephony/!svn/ver/59/trunk/libstelephony.h END build.sh K 25 @@ -115,7 +115,7 @@ stelephony.cpp K 25 svn:wc:ra_dav:version-url V 48 -/svn/stelephony/!svn/ver/51/trunk/stelephony.cpp +/svn/stelephony/!svn/ver/55/trunk/stelephony.cpp END stelephony.aps K 25 @@ -138,8 +138,8 @@ END stelephony.sln K 25 svn:wc:ra_dav:version-url -V 47 -/svn/stelephony/!svn/ver/1/trunk/stelephony.sln +V 48 +/svn/stelephony/!svn/ver/59/trunk/stelephony.sln END version K 25 @@ -163,7 +163,7 @@ stelephony.def K 25 svn:wc:ra_dav:version-url V 48 -/svn/stelephony/!svn/ver/43/trunk/stelephony.def +/svn/stelephony/!svn/ver/59/trunk/stelephony.def END libstelephony_linux_compat.h K 25 @@ -175,13 +175,13 @@ PToneDecoder.cpp K 25 svn:wc:ra_dav:version-url V 50 -/svn/stelephony/!svn/ver/51/trunk/PToneDecoder.cpp +/svn/stelephony/!svn/ver/54/trunk/PToneDecoder.cpp END stelephony.vcproj K 25 svn:wc:ra_dav:version-url V 51 -/svn/stelephony/!svn/ver/43/trunk/stelephony.vcproj +/svn/stelephony/!svn/ver/59/trunk/stelephony.vcproj END README K 25 @@ -207,18 +207,6 @@ svn:wc:ra_dav:version-url V 51 /svn/stelephony/!svn/ver/43/trunk/libstelephony.cpp END -Makefile.Windows -K 25 -svn:wc:ra_dav:version-url -V 49 -/svn/stelephony/!svn/ver/1/trunk/Makefile.Windows -END -stelephony.vcproj.DAVIDRNEW.User.user -K 25 -svn:wc:ra_dav:version-url -V 70 -/svn/stelephony/!svn/ver/1/trunk/stelephony.vcproj.DAVIDRNEW.User.user -END missing K 25 svn:wc:ra_dav:version-url @@ -229,13 +217,13 @@ Makefile.am K 25 svn:wc:ra_dav:version-url V 45 -/svn/stelephony/!svn/ver/47/trunk/Makefile.am +/svn/stelephony/!svn/ver/56/trunk/Makefile.am END PToneEncoder.h K 25 svn:wc:ra_dav:version-url V 48 -/svn/stelephony/!svn/ver/43/trunk/PToneEncoder.h +/svn/stelephony/!svn/ver/56/trunk/PToneEncoder.h END ReadMe.txt K 25 diff --git a/api/libstelephony/.svn/dir-prop-base b/api/libstelephony/.svn/dir-prop-base new file mode 100644 index 0000000..3917f59 --- /dev/null +++ b/api/libstelephony/.svn/dir-prop-base @@ -0,0 +1,7 @@ +K 10 +svn:ignore +V 55 +Makefile.Windows +stelephony.vcproj.DAVIDRNEW.User.user + +END diff --git a/api/libstelephony/.svn/entries b/api/libstelephony/.svn/entries index c723a4f..f6bf3fb 100644 --- a/api/libstelephony/.svn/entries +++ b/api/libstelephony/.svn/entries @@ -1,16 +1,16 @@ 8 dir -52 +59 http://www.sangomapbx.com/svn/stelephony/trunk http://www.sangomapbx.com/svn/stelephony -2009-07-23T18:22:49.763903Z -52 +2010-03-15T22:33:02.724320Z +59 davidr - +has-props svn:special svn:externals svn:needs-lock @@ -32,10 +32,10 @@ file -2009-06-26T20:54:03.000000Z -91d1d684d4b562f449ff6226ea1c2f95 -2009-06-09T20:43:33.158857Z -43 +2010-03-23T16:15:02.000000Z +c43ebb613031858fa669fc4aca84ae43 +2010-03-15T22:33:02.724320Z +59 davidr init-automake.sh @@ -171,11 +171,11 @@ file -2009-07-21T20:55:25.000000Z -d76fa5941bf65fc8d3064f8dacc548a6 -2009-07-16T21:36:49.003747Z -51 -davidr +2010-02-10T23:38:32.000000Z +84d583985ea686ae7a2e3992245b0d2a +2010-01-18T18:07:02.752786Z +56 +davidy PToneEncoder.cpp file @@ -207,10 +207,10 @@ file -2009-06-26T20:54:03.000000Z -0abb089cf0abe6ac85d6dc7aa4edcb09 -2009-06-09T20:43:33.158857Z -43 +2010-03-23T16:15:02.000000Z +4aa0037bc2dd7fd39d7e766d9850a586 +2010-03-15T22:33:02.724320Z +59 davidr build.sh @@ -256,11 +256,11 @@ file -2009-07-21T20:55:25.000000Z -2407e5f74fcd8442360c77413f9c4969 -2009-07-16T21:36:49.003747Z -51 -davidr +2009-12-21T23:17:41.000000Z +d39810d5fd7763003fbd609890934180 +2009-12-18T19:03:36.960538Z +55 +davidy stelephony.aps file @@ -305,11 +305,11 @@ file -2009-06-26T20:54:03.000000Z -eb00665e7cd041b1c20722382a5c95de -2009-02-12T18:45:27.267919Z -1 -root +2010-03-23T16:15:02.000000Z +8796ce903cb4dd88f075b27fede7d489 +2010-03-15T22:33:02.724320Z +59 +davidr version file @@ -353,10 +353,10 @@ file -2009-06-26T20:54:03.000000Z -67c394b6dd35b19d97b5e115f62a40f6 -2009-06-09T20:43:33.158857Z -43 +2010-03-23T16:15:02.000000Z +6b2ae7a559be5bf37c05bfae9988e900 +2010-03-15T22:33:02.724320Z +59 davidr libstelephony_linux_compat.h @@ -377,11 +377,11 @@ file -2009-07-21T20:55:25.000000Z -3b55910e113ddcc12d6aa00f9884d4a5 -2009-07-16T21:36:49.003747Z -51 -davidr +2009-12-21T23:17:41.000000Z +afc68f8adeeec8bacd42fc2c6ac7d380 +2009-12-18T18:47:54.309491Z +54 +davidy stelephony.vcproj file @@ -389,10 +389,10 @@ file -2009-06-26T20:54:03.000000Z -648392d49cf6e7a38fd782c5877cf726 -2009-06-09T20:43:33.158857Z -43 +2010-03-23T16:15:02.000000Z +4a6dbf857cbeaa06e00069bcc011da43 +2010-03-15T22:33:02.724320Z +59 davidr README @@ -443,30 +443,6 @@ file 43 davidr -Makefile.Windows -file - - - - -2009-06-26T20:54:03.000000Z -a17ac70bbce5e7bd36232bdaf1691fd3 -2009-02-12T18:45:27.267919Z -1 -root - -stelephony.vcproj.DAVIDRNEW.User.user -file - - - - -2009-06-26T20:54:03.000000Z -eca17b45f5a1950d7bb704be371f2fd7 -2009-02-12T18:45:27.267919Z -1 -root - missing file @@ -486,11 +462,11 @@ file -2009-06-26T20:54:03.000000Z -18ae4124b46e9c80bb1801f3654778c5 -2009-06-26T21:23:15.165605Z -47 -ncorbic +2010-02-10T23:38:32.000000Z +0734dc12dbb49509b2d1780fd64c054f +2010-01-18T18:07:02.752786Z +56 +davidy PToneEncoder.h file @@ -498,11 +474,11 @@ file -2009-06-26T20:54:03.000000Z -7d527f032695c21ce1fb50c8ea09b07a -2009-06-09T20:43:33.158857Z -43 -davidr +2010-02-10T23:38:32.000000Z +f79dd7f4f64571da090e33b6ef5753c7 +2010-01-18T18:07:02.752786Z +56 +davidy ReadMe.txt file diff --git a/api/libstelephony/.svn/text-base/Makefile.Windows.svn-base b/api/libstelephony/.svn/text-base/Makefile.Windows.svn-base deleted file mode 100644 index 30fbb51..0000000 --- a/api/libstelephony/.svn/text-base/Makefile.Windows.svn-base +++ /dev/null @@ -1,9 +0,0 @@ -# -# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source -# file to this component. This file merely indirects to the real make file -# that is shared by all the driver components of the Windows NT DDK -# - -!INCLUDE $(NTMAKEENV)\makefile.def - - diff --git a/api/libstelephony/.svn/text-base/Makefile.am.svn-base b/api/libstelephony/.svn/text-base/Makefile.am.svn-base index dbcdf28..aa10329 100644 --- a/api/libstelephony/.svn/text-base/Makefile.am.svn-base +++ b/api/libstelephony/.svn/text-base/Makefile.am.svn-base @@ -8,7 +8,7 @@ AM_CFLAGS = -fPIC -Wall -Wstrict-prototypes -Wmissing-prototypes -g -I$(WLINC) AM_CXXFLAGS = -fPIC -Wall -g -I$(WLINC) -I$(WINC) -I. \ -D__LINUX__ -D_REENTRANT -D_GNU_SOURCE -O2 -D_DEBUG_=2 -D_GNUC_ -I../lib -Istel_tone -LIB_SOUCES = libstelephony.cpp PToneDecoder.cpp PToneEncoder.cpp Q931EventsDecoder.cpp stelephony.cpp stel_tone/stel_tone.c stel_tone/uart.c stel_tone/libteletone_generate.c stel_tone/libteletone_detect.c stel_tone/fsk.c +LIB_SOUCES = libstelephony.cpp PToneDecoder.cpp PToneEncoder.cpp Q931EventsDecoder.cpp stelephony.cpp stel_tone/stel_tone.c stel_tone/wp_uart.c stel_tone/wp_libteletone_generate.c stel_tone/wp_libteletone_detect.c stel_tone/wp_fsk.c library_includedir = $(includedir) diff --git a/api/libstelephony/.svn/text-base/PToneDecoder.cpp.svn-base b/api/libstelephony/.svn/text-base/PToneDecoder.cpp.svn-base index fe0b904..a0f2b84 100644 --- a/api/libstelephony/.svn/text-base/PToneDecoder.cpp.svn-base +++ b/api/libstelephony/.svn/text-base/PToneDecoder.cpp.svn-base @@ -86,7 +86,7 @@ int PhoneToneDecoder::Init() int PhoneToneDecoder::WaveStreamInputExFSK(int16_t* slinData, int dataLength, int *retvalue) { - char CallerNumber[15], CallerName[15], DateTime[15]; + char CallerNumber[22], CallerName[52], DateTime[16]; memset(CallerNumber,0,sizeof(CallerNumber)); memset(CallerName,0,sizeof(CallerName)); memset(DateTime,0,sizeof(DateTime)); @@ -178,7 +178,7 @@ int PhoneToneDecoder::WaveStreamInputEx(char* data, int dataLength, int *retValu STEL_ERR("Failed to alloc mem (%s:%d)\n", __FUNCTION__,__LINE__); return -1; } - memset(slinData, 0, sizeof(slinData)); + memset( slinData, 0, dataLength*2 ); if (variant.intVal==WFI_CCITT_uLaw_8kHzMono) { for(i=0; i #include "Sink.h" -#include "g711.h" -#include "libteletone.h" +#include "wp_g711.h" +#include "wp_libteletone.h" class PhoneToneDecoder { diff --git a/api/libstelephony/.svn/text-base/PToneEncoder.h.svn-base b/api/libstelephony/.svn/text-base/PToneEncoder.h.svn-base index 32e75c3..25f40b2 100644 --- a/api/libstelephony/.svn/text-base/PToneEncoder.h.svn-base +++ b/api/libstelephony/.svn/text-base/PToneEncoder.h.svn-base @@ -7,8 +7,8 @@ #include #include "Sink.h" -#include "g711.h" -#include "libteletone.h" +#include "wp_g711.h" +#include "wp_libteletone.h" class PhoneToneEncoder { diff --git a/api/libstelephony/.svn/text-base/libstelephony.h.svn-base b/api/libstelephony/.svn/text-base/libstelephony.h.svn-base index 736daea..79836d0 100644 --- a/api/libstelephony/.svn/text-base/libstelephony.h.svn-base +++ b/api/libstelephony/.svn/text-base/libstelephony.h.svn-base @@ -33,7 +33,12 @@ #pragma once #if defined (__WINDOWS__) -# define STELAPI_CALL __cdecl +#ifdef STELEPHONY_EXPORTS +# define STELAPI_CALL __declspec(dllexport) __cdecl +#else +# define STELAPI_CALL __declspec(dllimport) __cdecl +#endif + # include #elif defined (__LINUX__) @@ -138,9 +143,9 @@ typedef struct { typedef struct { unsigned char auto_datetime; /* if set to non-zero library will adjust date to system time */ - char datetime[16]; - char calling_number[16]; - char calling_name[16]; + char datetime[16]; + char calling_number[22]; + char calling_name[52]; }stelephony_caller_id_t; diff --git a/api/libstelephony/.svn/text-base/stelephony.cpp.svn-base b/api/libstelephony/.svn/text-base/stelephony.cpp.svn-base index 1d227db..beae1e3 100644 --- a/api/libstelephony/.svn/text-base/stelephony.cpp.svn-base +++ b/api/libstelephony/.svn/text-base/stelephony.cpp.svn-base @@ -158,8 +158,8 @@ void OnCallerID(void *callback_obj, char *Name, char *CallerNumber, char *Called CStelephony* stelObj = (CStelephony*)callback_obj; stelephony_callback_functions_t cbf; - DBG_STEL("%s(): Name: %s, CallerNumber: %s, CalledNumber: %s, DateTime: %s\r\n", - __FUNCTION__, Name, CallerNumber, CalledNumber, DateTime); + DBG_STEL("%s(): Name: %s(%d), CallerNumber: %s(%d), CalledNumber: %s, DateTime: %s\r\n", + __FUNCTION__, Name, strlen(Name), CallerNumber, strlen(CallerNumber), CalledNumber, DateTime); stelObj->GetCallbackFunctions(&cbf); diff --git a/api/libstelephony/.svn/text-base/stelephony.def.svn-base b/api/libstelephony/.svn/text-base/stelephony.def.svn-base index 9e0bddf..5c78995 100644 --- a/api/libstelephony/.svn/text-base/stelephony.def.svn-base +++ b/api/libstelephony/.svn/text-base/stelephony.def.svn-base @@ -1,13 +1,3 @@ LIBRARY STELEPHONY EXPORTS - StelSetup - StelCleanup - StelStreamInput - StelEventControl - StelGenerateSwDTMF - StelGenerateFSKCallerID - StelBufferInuse - StelBufferRead - StelBufferReadUlaw - StelBufferReadAlaw diff --git a/api/libstelephony/.svn/text-base/stelephony.h.svn-base b/api/libstelephony/.svn/text-base/stelephony.h.svn-base index 6327bd9..a4e117b 100644 --- a/api/libstelephony/.svn/text-base/stelephony.h.svn-base +++ b/api/libstelephony/.svn/text-base/stelephony.h.svn-base @@ -4,7 +4,7 @@ #include -#include +#include "libstelephony.h" #include "Q931EventsDecoder.h" #include "Sink.h" diff --git a/api/libstelephony/.svn/text-base/stelephony.sln.svn-base b/api/libstelephony/.svn/text-base/stelephony.sln.svn-base index bfd9c3b..630ed9a 100644 --- a/api/libstelephony/.svn/text-base/stelephony.sln.svn-base +++ b/api/libstelephony/.svn/text-base/stelephony.sln.svn-base @@ -1,6 +1,6 @@  -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stelephony", "stelephony.vcproj", "{973E1E5F-663B-40A3-90C9-8DD0772FDB01}" EndProject Global diff --git a/api/libstelephony/.svn/text-base/stelephony.vcproj.DAVIDRNEW.User.user.svn-base b/api/libstelephony/.svn/text-base/stelephony.vcproj.DAVIDRNEW.User.user.svn-base deleted file mode 100644 index a9bd0c4..0000000 --- a/api/libstelephony/.svn/text-base/stelephony.vcproj.DAVIDRNEW.User.user.svn-base +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - diff --git a/api/libstelephony/.svn/text-base/stelephony.vcproj.svn-base b/api/libstelephony/.svn/text-base/stelephony.vcproj.svn-base index 94b3a44..0494240 100644 --- a/api/libstelephony/.svn/text-base/stelephony.vcproj.svn-base +++ b/api/libstelephony/.svn/text-base/stelephony.vcproj.svn-base @@ -1,10 +1,11 @@ - @@ -192,9 +194,6 @@ - @@ -223,6 +222,10 @@ RelativePath=".\Q931EventsDecoder.cpp" > + + @@ -247,6 +250,22 @@ RelativePath=".\stelephony.rc" > + + + + + + + + + + @@ -284,6 +307,30 @@ RelativePath=".\StelephonyApi.h" > + + + + + + + + + + + + #include "Sink.h" -#include "g711.h" -#include "libteletone.h" +#include "wp_g711.h" +#include "wp_libteletone.h" class PhoneToneDecoder { diff --git a/api/libstelephony/PToneEncoder.h b/api/libstelephony/PToneEncoder.h index 32e75c3..25f40b2 100644 --- a/api/libstelephony/PToneEncoder.h +++ b/api/libstelephony/PToneEncoder.h @@ -7,8 +7,8 @@ #include #include "Sink.h" -#include "g711.h" -#include "libteletone.h" +#include "wp_g711.h" +#include "wp_libteletone.h" class PhoneToneEncoder { diff --git a/api/libstelephony/libstelephony.h b/api/libstelephony/libstelephony.h index 736daea..79836d0 100644 --- a/api/libstelephony/libstelephony.h +++ b/api/libstelephony/libstelephony.h @@ -33,7 +33,12 @@ #pragma once #if defined (__WINDOWS__) -# define STELAPI_CALL __cdecl +#ifdef STELEPHONY_EXPORTS +# define STELAPI_CALL __declspec(dllexport) __cdecl +#else +# define STELAPI_CALL __declspec(dllimport) __cdecl +#endif + # include #elif defined (__LINUX__) @@ -138,9 +143,9 @@ typedef struct { typedef struct { unsigned char auto_datetime; /* if set to non-zero library will adjust date to system time */ - char datetime[16]; - char calling_number[16]; - char calling_name[16]; + char datetime[16]; + char calling_number[22]; + char calling_name[52]; }stelephony_caller_id_t; diff --git a/api/libstelephony/stel_tone/.svn/all-wcprops b/api/libstelephony/stel_tone/.svn/all-wcprops index 8af5cc0..d375a97 100644 --- a/api/libstelephony/stel_tone/.svn/all-wcprops +++ b/api/libstelephony/stel_tone/.svn/all-wcprops @@ -1,89 +1,83 @@ K 25 svn:wc:ra_dav:version-url V 43 -/svn/stelephony/!svn/ver/43/trunk/stel_tone +/svn/stelephony/!svn/ver/59/trunk/stel_tone END -libteletone_detect.c +wp_fsk.h K 25 svn:wc:ra_dav:version-url -V 63 -/svn/stelephony/!svn/ver/6/trunk/stel_tone/libteletone_detect.c +V 52 +/svn/stelephony/!svn/ver/56/trunk/stel_tone/wp_fsk.h END -libteletone_generate.c +wp_libteletone.h K 25 svn:wc:ra_dav:version-url -V 65 -/svn/stelephony/!svn/ver/6/trunk/stel_tone/libteletone_generate.c +V 60 +/svn/stelephony/!svn/ver/56/trunk/stel_tone/wp_libteletone.h END -uart.h +wp_uart.c K 25 svn:wc:ra_dav:version-url -V 49 -/svn/stelephony/!svn/ver/6/trunk/stel_tone/uart.h +V 53 +/svn/stelephony/!svn/ver/56/trunk/stel_tone/wp_uart.c END sources K 25 svn:wc:ra_dav:version-url V 51 -/svn/stelephony/!svn/ver/43/trunk/stel_tone/sources +/svn/stelephony/!svn/ver/56/trunk/stel_tone/sources END -fsk.c +wp_libteletone_detect.c K 25 svn:wc:ra_dav:version-url -V 48 -/svn/stelephony/!svn/ver/6/trunk/stel_tone/fsk.c +V 67 +/svn/stelephony/!svn/ver/57/trunk/stel_tone/wp_libteletone_detect.c END -libteletone_detect.h +wp_libteletone_generate.c K 25 svn:wc:ra_dav:version-url -V 63 -/svn/stelephony/!svn/ver/6/trunk/stel_tone/libteletone_detect.h +V 69 +/svn/stelephony/!svn/ver/56/trunk/stel_tone/wp_libteletone_generate.c END -g711.h +wp_uart.h K 25 svn:wc:ra_dav:version-url -V 49 -/svn/stelephony/!svn/ver/6/trunk/stel_tone/g711.h +V 53 +/svn/stelephony/!svn/ver/56/trunk/stel_tone/wp_uart.h END -Makefile.Windows +wp_libteletone_detect.h K 25 svn:wc:ra_dav:version-url -V 60 -/svn/stelephony/!svn/ver/43/trunk/stel_tone/Makefile.Windows +V 67 +/svn/stelephony/!svn/ver/57/trunk/stel_tone/wp_libteletone_detect.h END -libteletone_generate.h +wp_fsk.c K 25 svn:wc:ra_dav:version-url -V 66 -/svn/stelephony/!svn/ver/43/trunk/stel_tone/libteletone_generate.h +V 52 +/svn/stelephony/!svn/ver/56/trunk/stel_tone/wp_fsk.c END -fsk.h +wp_g711.h K 25 svn:wc:ra_dav:version-url -V 48 -/svn/stelephony/!svn/ver/6/trunk/stel_tone/fsk.h -END -libteletone.h -K 25 -svn:wc:ra_dav:version-url -V 57 -/svn/stelephony/!svn/ver/12/trunk/stel_tone/libteletone.h -END -uart.c -K 25 -svn:wc:ra_dav:version-url -V 49 -/svn/stelephony/!svn/ver/6/trunk/stel_tone/uart.c +V 53 +/svn/stelephony/!svn/ver/56/trunk/stel_tone/wp_g711.h END libstelephony_tone.h K 25 svn:wc:ra_dav:version-url V 64 -/svn/stelephony/!svn/ver/43/trunk/stel_tone/libstelephony_tone.h +/svn/stelephony/!svn/ver/59/trunk/stel_tone/libstelephony_tone.h +END +wp_libteletone_generate.h +K 25 +svn:wc:ra_dav:version-url +V 69 +/svn/stelephony/!svn/ver/57/trunk/stel_tone/wp_libteletone_generate.h END stel_tone.c K 25 svn:wc:ra_dav:version-url V 55 -/svn/stelephony/!svn/ver/43/trunk/stel_tone/stel_tone.c +/svn/stelephony/!svn/ver/57/trunk/stel_tone/stel_tone.c END diff --git a/api/libstelephony/stel_tone/.svn/dir-prop-base b/api/libstelephony/stel_tone/.svn/dir-prop-base new file mode 100644 index 0000000..93d1e81 --- /dev/null +++ b/api/libstelephony/stel_tone/.svn/dir-prop-base @@ -0,0 +1,6 @@ +K 10 +svn:ignore +V 17 +Makefile.Windows + +END diff --git a/api/libstelephony/stel_tone/.svn/entries b/api/libstelephony/stel_tone/.svn/entries index e0bad07..e5a14e2 100644 --- a/api/libstelephony/stel_tone/.svn/entries +++ b/api/libstelephony/stel_tone/.svn/entries @@ -1,16 +1,16 @@ 8 dir -52 +59 http://www.sangomapbx.com/svn/stelephony/trunk/stel_tone http://www.sangomapbx.com/svn/stelephony -2009-06-09T20:43:33.158857Z -43 +2010-03-15T22:33:02.724320Z +59 davidr - +has-props svn:special svn:externals svn:needs-lock @@ -26,40 +26,40 @@ svn:special svn:externals svn:needs-lock b104fb09-bd62-0410-b2b2-f61c77a7d4c8 -libteletone_detect.c +wp_fsk.h file -2009-06-26T20:54:03.000000Z -d01d28a1232e2c040d77587c86728a78 -2009-02-25T17:10:18.278910Z -6 +2010-02-10T23:38:32.000000Z +25f0e7b6c4da30f7d830b8c44145f4cc +2010-01-18T18:07:02.752786Z +56 davidy -libteletone_generate.c +wp_libteletone.h file -2009-06-26T20:54:03.000000Z -cb27d0af61bd6afa051880fb29565478 -2009-02-25T17:10:18.278910Z -6 +2010-02-10T23:38:32.000000Z +b4c3d82fd1cb32dc2056aa0d48ff49bc +2010-01-18T18:07:02.752786Z +56 davidy -uart.h +wp_uart.c file -2009-06-26T20:54:03.000000Z -6f65a9a0b42611122f818c563eff363a -2009-02-25T17:10:18.278910Z -6 +2010-02-10T23:38:32.000000Z +d5dd6baf45bffe7586e7c203cee484df +2010-01-18T18:07:02.752786Z +56 davidy sources @@ -68,106 +68,82 @@ file -2009-06-26T20:54:03.000000Z -c2c4eaf73ce42db5532d42f1d21b462f -2009-06-09T20:43:33.158857Z -43 +2010-02-10T23:38:32.000000Z +146523eaf55c04782b46dc85613546db +2010-01-18T18:07:02.752786Z +56 +davidy + +wp_libteletone_detect.c +file + + + + +2010-02-10T23:38:32.000000Z +7c92b2cd869752c02b03b470c3e5d22a +2010-01-19T21:37:58.382301Z +57 davidr -fsk.c +wp_libteletone_generate.c file -2009-06-26T20:54:03.000000Z -37a60dd1f48bf394d7d97a784bec6f8b -2009-02-25T17:10:18.278910Z -6 +2010-02-10T23:38:32.000000Z +50d43179e19ae8a04633917e591c84d8 +2010-01-18T18:07:02.752786Z +56 davidy -libteletone_detect.h +wp_uart.h file -2009-06-26T20:54:03.000000Z -b282cf61fecc1945f17aae2deb3b0aa8 -2009-02-25T17:10:18.278910Z -6 +2010-02-10T23:38:32.000000Z +6f65a9a0b42611122f818c563eff363a +2010-01-18T18:07:02.752786Z +56 davidy -g711.h +wp_libteletone_detect.h file -2009-06-26T20:54:03.000000Z +2010-02-10T23:38:32.000000Z +5c1c8c0f4b85280627b015a4369915ca +2010-01-19T21:37:58.382301Z +57 +davidr + +wp_fsk.c +file + + + + +2010-02-10T23:38:32.000000Z +61a51df19aba69e7653e67facf8b2392 +2010-01-18T18:07:02.752786Z +56 +davidy + +wp_g711.h +file + + + + +2010-02-10T23:38:32.000000Z 0f725f95ced42af15dcaef21f3a1722b -2009-02-25T17:10:18.278910Z -6 -davidy - -Makefile.Windows -file - - - - -2009-06-26T20:54:03.000000Z -a17ac70bbce5e7bd36232bdaf1691fd3 -2009-06-09T20:43:33.158857Z -43 -davidr - -libteletone_generate.h -file - - - - -2009-06-26T20:54:03.000000Z -6a5d050715a6b6aee595b1c28254df4e -2009-06-09T20:43:33.158857Z -43 -davidr - -fsk.h -file - - - - -2009-06-26T20:54:03.000000Z -2021b0a9c9ca1e68c1cdcda1f08e32e6 -2009-02-25T17:10:18.278910Z -6 -davidy - -libteletone.h -file - - - - -2009-06-26T20:54:03.000000Z -64ea78a421b845fc49e4a1b0dcb9872e -2009-02-27T21:10:48.783827Z -12 -davidy - -uart.c -file - - - - -2009-06-26T20:54:03.000000Z -7799aa7141155aa218db05b4447f7bec -2009-02-25T17:10:18.278910Z -6 +2010-01-18T18:07:02.752786Z +56 davidy libstelephony_tone.h @@ -176,10 +152,22 @@ file -2009-06-26T20:54:03.000000Z -8a31d53e6f3c5a7add9d96ef53de462c -2009-06-09T20:43:33.158857Z -43 +2010-03-23T16:15:02.000000Z +18f1ff64f9b1dc63fcd2a12209e428a2 +2010-03-15T22:33:02.724320Z +59 +davidr + +wp_libteletone_generate.h +file + + + + +2010-02-10T23:38:32.000000Z +4ad246db91bae233ee9c42400eab0bc3 +2010-01-19T21:37:58.382301Z +57 davidr stel_tone.c @@ -188,9 +176,9 @@ file -2009-06-26T20:54:03.000000Z -02e739f9be1b73aaaf4f5c809b0aafda -2009-06-09T20:43:33.158857Z -43 +2010-02-10T23:38:32.000000Z +e9960f526d500e349876c0a4b2dfee35 +2010-01-19T21:37:58.382301Z +57 davidr diff --git a/api/libstelephony/stel_tone/.svn/text-base/Makefile.Windows.svn-base b/api/libstelephony/stel_tone/.svn/text-base/Makefile.Windows.svn-base deleted file mode 100644 index 30fbb51..0000000 --- a/api/libstelephony/stel_tone/.svn/text-base/Makefile.Windows.svn-base +++ /dev/null @@ -1,9 +0,0 @@ -# -# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source -# file to this component. This file merely indirects to the real make file -# that is shared by all the driver components of the Windows NT DDK -# - -!INCLUDE $(NTMAKEENV)\makefile.def - - diff --git a/api/libstelephony/stel_tone/.svn/text-base/libstelephony_tone.h.svn-base b/api/libstelephony/stel_tone/.svn/text-base/libstelephony_tone.h.svn-base index 5604963..9691019 100644 --- a/api/libstelephony/stel_tone/.svn/text-base/libstelephony_tone.h.svn-base +++ b/api/libstelephony/stel_tone/.svn/text-base/libstelephony_tone.h.svn-base @@ -1,8 +1,8 @@ #ifndef __FSK_CALLER_ID_H_ #define __FSK_CALLER_ID_H_ -#include "fsk.h" -#include "libteletone_generate.h" +#include "wp_fsk.h" +#include "wp_libteletone_generate.h" #define my_copy_string(x,y,z) strncpy(x, y, z - 1) @@ -12,18 +12,11 @@ #define u16 unsigned short #define s16 signed short -#ifdef __WINDOWS__ -# define STELAPI_CALL __cdecl -#else -# define STELAPI_CALL -#endif #ifdef __cplusplus extern "C" { /* for C++ users */ #endif - - typedef enum { CID_TYPE_SDMF = 0x04, CID_TYPE_MDMF = 0x80 diff --git a/api/libstelephony/stel_tone/.svn/text-base/sources.svn-base b/api/libstelephony/stel_tone/.svn/text-base/sources.svn-base index d5a7fc2..058cfee 100644 --- a/api/libstelephony/stel_tone/.svn/text-base/sources.svn-base +++ b/api/libstelephony/stel_tone/.svn/text-base/sources.svn-base @@ -15,8 +15,8 @@ INCLUDES=$(SDK_INC_PATH); TARGETLIBS=$(SDK_LIB_PATH)\user32.lib -SOURCES=fsk.c \ -libteletone_detect.c \ -libteletone_generate.c \ +SOURCES=wp_fsk.c \ +wp_libteletone_detect.c \ +wp_libteletone_generate.c \ stel_tone.c \ -uart.c +wp_uart.c diff --git a/api/libstelephony/stel_tone/.svn/text-base/stel_tone.c.svn-base b/api/libstelephony/stel_tone/.svn/text-base/stel_tone.c.svn-base index f3e42f7..185eb75 100644 --- a/api/libstelephony/stel_tone/.svn/text-base/stel_tone.c.svn-base +++ b/api/libstelephony/stel_tone/.svn/text-base/stel_tone.c.svn-base @@ -1,7 +1,7 @@ #include "libstelephony_tone.h" -#include "libteletone_generate.h" /* TODO: May not need this one */ -#include "g711.h" - +#include "wp_libteletone_generate.h" /* TODO: May not need this one */ +#include "wp_g711.h" + extern int16_t TELETONE_SINES[SINE_TABLE_MAX]; @@ -18,6 +18,16 @@ int slin2ulaw(void* data, size_t max, size_t *datalen); int buffer_id = 0; +size_t stelephony_buffer_inuse(void *pbuffer); +void bitstream_init(bitstream_t *bsp, uint8_t *data, uint32_t datalen, endian_t endian, uint8_t ss); +int fsk_modulator_init(fsk_modulator_t *, fsk_modem_types_t, uint32_t ,fsk_data_state_t*,float , uint32_t, uint32_t, uint32_t, fsk_write_sample_t ,void*); +int8_t bitstream_get_bit(bitstream_t *bsp); +void fsk_modulator_send_data(fsk_modulator_t *fsk_trans); +size_t fsk_modulator_generate_bit(fsk_modulator_t *fsk_trans, int8_t bit, int16_t *buf, size_t buflen); +size_t stelephony_buffer_read(void *pbuffer, void *data, size_t datalen); +size_t stelephony_buffer_read_ulaw(void *pbuffer, unsigned char *data, int* dlen, int max); +size_t stelephony_buffer_read_alaw(void *pbuffer, unsigned char *data, int* dlen, int max); +void fsk_modulator_generate_chan_sieze(fsk_modulator_t *fsk_trans); int fsk_write_sample(short *buf, unsigned int buflen, void *user_data) { @@ -669,68 +679,3 @@ int slin2ulaw(void* data, size_t max, size_t *datalen) return 0; } -#if 0 -#warning "REMOVE THESE FUNCTIONS LATER" -void convert_ulaw_to_linear(u16 *linear_buffer, u8 *ulaw_buffer, int size) -{ - int i; - memset(linear_buffer, 0, size); - for(i = 0; i < size; i++) { - linear_buffer[i] = ulaw_to_linear (ulaw_buffer[i]); - } -} - - -void convert_linear_to_ulaw(u8 *ulaw_buffer, u16 *linear_buffer, int size) -{ - int i; - memset(ulaw_buffer, 0, size); - for(i = 0; i < size; i++) { - ulaw_buffer[i] = linear_to_ulaw (ulaw_buffer[i]); - } -} - -int io_slin2ulaw (void *data, size_t max, size_t *datalen) -{ - int16_t sln_buf[512] = {0}, *sln = sln_buf; - uint8_t *lp = (unsigned char*) data; - uint32_t i; - 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 0; -} - -int slin2ulaw(void* data, size_t max, size_t *datalen) -{ - int16_t sln_buf[512] = {0}, *sln = sln_buf; - uint8_t *lp = (unsigned char*) data; - uint32_t i; - 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 0; -} -#endif diff --git a/api/libstelephony/stel_tone/.svn/text-base/fsk.c.svn-base b/api/libstelephony/stel_tone/.svn/text-base/wp_fsk.c.svn-base similarity index 99% rename from api/libstelephony/stel_tone/.svn/text-base/fsk.c.svn-base rename to api/libstelephony/stel_tone/.svn/text-base/wp_fsk.c.svn-base index 4d6fa54..5b33682 100644 --- a/api/libstelephony/stel_tone/.svn/text-base/fsk.c.svn-base +++ b/api/libstelephony/stel_tone/.svn/text-base/wp_fsk.c.svn-base @@ -39,8 +39,8 @@ #include #include -#include "fsk.h" -#include "uart.h" +#include "wp_fsk.h" +#include "wp_uart.h" #ifndef M_PI #define M_PI 3.14159265358979323846 diff --git a/api/libstelephony/stel_tone/.svn/text-base/fsk.h.svn-base b/api/libstelephony/stel_tone/.svn/text-base/wp_fsk.h.svn-base similarity index 99% rename from api/libstelephony/stel_tone/.svn/text-base/fsk.h.svn-base rename to api/libstelephony/stel_tone/.svn/text-base/wp_fsk.h.svn-base index b098444..536ed74 100644 --- a/api/libstelephony/stel_tone/.svn/text-base/fsk.h.svn-base +++ b/api/libstelephony/stel_tone/.svn/text-base/wp_fsk.h.svn-base @@ -33,7 +33,7 @@ #ifndef __FSK_H__ #define __FSK_H__ -#include "uart.h" +#include "wp_uart.h" typedef struct { int freq_space; /* Frequency of the 0 bit */ diff --git a/api/libstelephony/stel_tone/.svn/text-base/g711.h.svn-base b/api/libstelephony/stel_tone/.svn/text-base/wp_g711.h.svn-base similarity index 100% rename from api/libstelephony/stel_tone/.svn/text-base/g711.h.svn-base rename to api/libstelephony/stel_tone/.svn/text-base/wp_g711.h.svn-base diff --git a/api/libstelephony/stel_tone/libteletone.h b/api/libstelephony/stel_tone/.svn/text-base/wp_libteletone.h.svn-base similarity index 98% rename from api/libstelephony/stel_tone/libteletone.h rename to api/libstelephony/stel_tone/.svn/text-base/wp_libteletone.h.svn-base index aaf7b0e..9a6ce83 100644 --- a/api/libstelephony/stel_tone/libteletone.h +++ b/api/libstelephony/stel_tone/.svn/text-base/wp_libteletone.h.svn-base @@ -101,8 +101,8 @@ typedef struct { typedef __int16 int16_t; #endif -#include -#include +#include +#include #ifdef HAVE_STRING_H #include diff --git a/api/libstelephony/stel_tone/libteletone_detect.c b/api/libstelephony/stel_tone/.svn/text-base/wp_libteletone_detect.c.svn-base similarity index 99% rename from api/libstelephony/stel_tone/libteletone_detect.c rename to api/libstelephony/stel_tone/.svn/text-base/wp_libteletone_detect.c.svn-base index a089f8f..f0eca86 100644 --- a/api/libstelephony/stel_tone/libteletone_detect.c +++ b/api/libstelephony/stel_tone/.svn/text-base/wp_libteletone_detect.c.svn-base @@ -90,7 +90,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include +#include #ifndef _MSC_VER #include diff --git a/api/libstelephony/stel_tone/libteletone_detect.h b/api/libstelephony/stel_tone/.svn/text-base/wp_libteletone_detect.h.svn-base similarity index 99% rename from api/libstelephony/stel_tone/libteletone_detect.h rename to api/libstelephony/stel_tone/.svn/text-base/wp_libteletone_detect.h.svn-base index 15d5e9d..6185cc5 100644 --- a/api/libstelephony/stel_tone/libteletone_detect.h +++ b/api/libstelephony/stel_tone/.svn/text-base/wp_libteletone_detect.h.svn-base @@ -96,9 +96,9 @@ #ifdef __cplusplus extern "C" { #endif -#include +#include - /*! \file libteletone_detect.h + /*! \file wp_libteletone_detect.h \brief Tone Detection Routines This module is responsible for tone detection specifics diff --git a/api/libstelephony/stel_tone/libteletone_generate.c b/api/libstelephony/stel_tone/.svn/text-base/wp_libteletone_generate.c.svn-base similarity index 99% rename from api/libstelephony/stel_tone/libteletone_generate.c rename to api/libstelephony/stel_tone/.svn/text-base/wp_libteletone_generate.c.svn-base index a54ca0e..acb2569 100644 --- a/api/libstelephony/stel_tone/libteletone_generate.c +++ b/api/libstelephony/stel_tone/.svn/text-base/wp_libteletone_generate.c.svn-base @@ -69,7 +69,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include +#include #define SMAX 32767 #define SMIN -32768 diff --git a/api/libstelephony/stel_tone/.svn/text-base/libteletone_generate.h.svn-base b/api/libstelephony/stel_tone/.svn/text-base/wp_libteletone_generate.h.svn-base similarity index 99% rename from api/libstelephony/stel_tone/.svn/text-base/libteletone_generate.h.svn-base rename to api/libstelephony/stel_tone/.svn/text-base/wp_libteletone_generate.h.svn-base index b2725a2..b0b20b2 100644 --- a/api/libstelephony/stel_tone/.svn/text-base/libteletone_generate.h.svn-base +++ b/api/libstelephony/stel_tone/.svn/text-base/wp_libteletone_generate.h.svn-base @@ -118,7 +118,7 @@ extern float powf (float, float); #endif #include #include -#include +#include #define TELETONE_VOL_DB_MAX 0 #define TELETONE_VOL_DB_MIN -63 @@ -197,7 +197,7 @@ static __inline__ int teletone_dds_state_set_tone(teletone_dds_state_t *dds, tel -/*! \file libteletone_generate.h +/*! \file wp_libteletone_generate.h \brief Tone Generation Routines This module is responsible for tone generation specifics diff --git a/api/libstelephony/stel_tone/uart.c b/api/libstelephony/stel_tone/.svn/text-base/wp_uart.c.svn-base similarity index 99% rename from api/libstelephony/stel_tone/uart.c rename to api/libstelephony/stel_tone/.svn/text-base/wp_uart.c.svn-base index 5d88675..05eb55f 100644 --- a/api/libstelephony/stel_tone/uart.c +++ b/api/libstelephony/stel_tone/.svn/text-base/wp_uart.c.svn-base @@ -37,7 +37,7 @@ #include #include -#include "uart.h" +#include "wp_uart.h" /* * dsp_uart_attr_init diff --git a/api/libstelephony/stel_tone/.svn/text-base/uart.h.svn-base b/api/libstelephony/stel_tone/.svn/text-base/wp_uart.h.svn-base similarity index 100% rename from api/libstelephony/stel_tone/.svn/text-base/uart.h.svn-base rename to api/libstelephony/stel_tone/.svn/text-base/wp_uart.h.svn-base diff --git a/api/libstelephony/stel_tone/Makefile.Windows b/api/libstelephony/stel_tone/Makefile.Windows deleted file mode 100644 index 30fbb51..0000000 --- a/api/libstelephony/stel_tone/Makefile.Windows +++ /dev/null @@ -1,9 +0,0 @@ -# -# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source -# file to this component. This file merely indirects to the real make file -# that is shared by all the driver components of the Windows NT DDK -# - -!INCLUDE $(NTMAKEENV)\makefile.def - - diff --git a/api/libstelephony/stel_tone/libstelephony_tone.h b/api/libstelephony/stel_tone/libstelephony_tone.h index 5604963..9691019 100644 --- a/api/libstelephony/stel_tone/libstelephony_tone.h +++ b/api/libstelephony/stel_tone/libstelephony_tone.h @@ -1,8 +1,8 @@ #ifndef __FSK_CALLER_ID_H_ #define __FSK_CALLER_ID_H_ -#include "fsk.h" -#include "libteletone_generate.h" +#include "wp_fsk.h" +#include "wp_libteletone_generate.h" #define my_copy_string(x,y,z) strncpy(x, y, z - 1) @@ -12,18 +12,11 @@ #define u16 unsigned short #define s16 signed short -#ifdef __WINDOWS__ -# define STELAPI_CALL __cdecl -#else -# define STELAPI_CALL -#endif #ifdef __cplusplus extern "C" { /* for C++ users */ #endif - - typedef enum { CID_TYPE_SDMF = 0x04, CID_TYPE_MDMF = 0x80 diff --git a/api/libstelephony/stel_tone/sources b/api/libstelephony/stel_tone/sources index d5a7fc2..058cfee 100644 --- a/api/libstelephony/stel_tone/sources +++ b/api/libstelephony/stel_tone/sources @@ -15,8 +15,8 @@ INCLUDES=$(SDK_INC_PATH); TARGETLIBS=$(SDK_LIB_PATH)\user32.lib -SOURCES=fsk.c \ -libteletone_detect.c \ -libteletone_generate.c \ +SOURCES=wp_fsk.c \ +wp_libteletone_detect.c \ +wp_libteletone_generate.c \ stel_tone.c \ -uart.c +wp_uart.c diff --git a/api/libstelephony/stel_tone/stel_tone.c b/api/libstelephony/stel_tone/stel_tone.c index f3e42f7..185eb75 100644 --- a/api/libstelephony/stel_tone/stel_tone.c +++ b/api/libstelephony/stel_tone/stel_tone.c @@ -1,7 +1,7 @@ #include "libstelephony_tone.h" -#include "libteletone_generate.h" /* TODO: May not need this one */ -#include "g711.h" - +#include "wp_libteletone_generate.h" /* TODO: May not need this one */ +#include "wp_g711.h" + extern int16_t TELETONE_SINES[SINE_TABLE_MAX]; @@ -18,6 +18,16 @@ int slin2ulaw(void* data, size_t max, size_t *datalen); int buffer_id = 0; +size_t stelephony_buffer_inuse(void *pbuffer); +void bitstream_init(bitstream_t *bsp, uint8_t *data, uint32_t datalen, endian_t endian, uint8_t ss); +int fsk_modulator_init(fsk_modulator_t *, fsk_modem_types_t, uint32_t ,fsk_data_state_t*,float , uint32_t, uint32_t, uint32_t, fsk_write_sample_t ,void*); +int8_t bitstream_get_bit(bitstream_t *bsp); +void fsk_modulator_send_data(fsk_modulator_t *fsk_trans); +size_t fsk_modulator_generate_bit(fsk_modulator_t *fsk_trans, int8_t bit, int16_t *buf, size_t buflen); +size_t stelephony_buffer_read(void *pbuffer, void *data, size_t datalen); +size_t stelephony_buffer_read_ulaw(void *pbuffer, unsigned char *data, int* dlen, int max); +size_t stelephony_buffer_read_alaw(void *pbuffer, unsigned char *data, int* dlen, int max); +void fsk_modulator_generate_chan_sieze(fsk_modulator_t *fsk_trans); int fsk_write_sample(short *buf, unsigned int buflen, void *user_data) { @@ -669,68 +679,3 @@ int slin2ulaw(void* data, size_t max, size_t *datalen) return 0; } -#if 0 -#warning "REMOVE THESE FUNCTIONS LATER" -void convert_ulaw_to_linear(u16 *linear_buffer, u8 *ulaw_buffer, int size) -{ - int i; - memset(linear_buffer, 0, size); - for(i = 0; i < size; i++) { - linear_buffer[i] = ulaw_to_linear (ulaw_buffer[i]); - } -} - - -void convert_linear_to_ulaw(u8 *ulaw_buffer, u16 *linear_buffer, int size) -{ - int i; - memset(ulaw_buffer, 0, size); - for(i = 0; i < size; i++) { - ulaw_buffer[i] = linear_to_ulaw (ulaw_buffer[i]); - } -} - -int io_slin2ulaw (void *data, size_t max, size_t *datalen) -{ - int16_t sln_buf[512] = {0}, *sln = sln_buf; - uint8_t *lp = (unsigned char*) data; - uint32_t i; - 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 0; -} - -int slin2ulaw(void* data, size_t max, size_t *datalen) -{ - int16_t sln_buf[512] = {0}, *sln = sln_buf; - uint8_t *lp = (unsigned char*) data; - uint32_t i; - 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 0; -} -#endif diff --git a/api/libstelephony/stel_tone/fsk.c b/api/libstelephony/stel_tone/wp_fsk.c similarity index 99% rename from api/libstelephony/stel_tone/fsk.c rename to api/libstelephony/stel_tone/wp_fsk.c index 4d6fa54..5b33682 100644 --- a/api/libstelephony/stel_tone/fsk.c +++ b/api/libstelephony/stel_tone/wp_fsk.c @@ -39,8 +39,8 @@ #include #include -#include "fsk.h" -#include "uart.h" +#include "wp_fsk.h" +#include "wp_uart.h" #ifndef M_PI #define M_PI 3.14159265358979323846 diff --git a/api/libstelephony/stel_tone/fsk.h b/api/libstelephony/stel_tone/wp_fsk.h similarity index 99% rename from api/libstelephony/stel_tone/fsk.h rename to api/libstelephony/stel_tone/wp_fsk.h index b098444..536ed74 100644 --- a/api/libstelephony/stel_tone/fsk.h +++ b/api/libstelephony/stel_tone/wp_fsk.h @@ -33,7 +33,7 @@ #ifndef __FSK_H__ #define __FSK_H__ -#include "uart.h" +#include "wp_uart.h" typedef struct { int freq_space; /* Frequency of the 0 bit */ diff --git a/api/libstelephony/stel_tone/g711.h b/api/libstelephony/stel_tone/wp_g711.h similarity index 100% rename from api/libstelephony/stel_tone/g711.h rename to api/libstelephony/stel_tone/wp_g711.h diff --git a/api/libstelephony/stel_tone/.svn/text-base/libteletone.h.svn-base b/api/libstelephony/stel_tone/wp_libteletone.h similarity index 98% rename from api/libstelephony/stel_tone/.svn/text-base/libteletone.h.svn-base rename to api/libstelephony/stel_tone/wp_libteletone.h index aaf7b0e..9a6ce83 100644 --- a/api/libstelephony/stel_tone/.svn/text-base/libteletone.h.svn-base +++ b/api/libstelephony/stel_tone/wp_libteletone.h @@ -101,8 +101,8 @@ typedef struct { typedef __int16 int16_t; #endif -#include -#include +#include +#include #ifdef HAVE_STRING_H #include diff --git a/api/libstelephony/stel_tone/.svn/text-base/libteletone_detect.c.svn-base b/api/libstelephony/stel_tone/wp_libteletone_detect.c similarity index 99% rename from api/libstelephony/stel_tone/.svn/text-base/libteletone_detect.c.svn-base rename to api/libstelephony/stel_tone/wp_libteletone_detect.c index a089f8f..f0eca86 100644 --- a/api/libstelephony/stel_tone/.svn/text-base/libteletone_detect.c.svn-base +++ b/api/libstelephony/stel_tone/wp_libteletone_detect.c @@ -90,7 +90,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include +#include #ifndef _MSC_VER #include diff --git a/api/libstelephony/stel_tone/.svn/text-base/libteletone_detect.h.svn-base b/api/libstelephony/stel_tone/wp_libteletone_detect.h similarity index 99% rename from api/libstelephony/stel_tone/.svn/text-base/libteletone_detect.h.svn-base rename to api/libstelephony/stel_tone/wp_libteletone_detect.h index 15d5e9d..6185cc5 100644 --- a/api/libstelephony/stel_tone/.svn/text-base/libteletone_detect.h.svn-base +++ b/api/libstelephony/stel_tone/wp_libteletone_detect.h @@ -96,9 +96,9 @@ #ifdef __cplusplus extern "C" { #endif -#include +#include - /*! \file libteletone_detect.h + /*! \file wp_libteletone_detect.h \brief Tone Detection Routines This module is responsible for tone detection specifics diff --git a/api/libstelephony/stel_tone/.svn/text-base/libteletone_generate.c.svn-base b/api/libstelephony/stel_tone/wp_libteletone_generate.c similarity index 99% rename from api/libstelephony/stel_tone/.svn/text-base/libteletone_generate.c.svn-base rename to api/libstelephony/stel_tone/wp_libteletone_generate.c index a54ca0e..acb2569 100644 --- a/api/libstelephony/stel_tone/.svn/text-base/libteletone_generate.c.svn-base +++ b/api/libstelephony/stel_tone/wp_libteletone_generate.c @@ -69,7 +69,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include +#include #define SMAX 32767 #define SMIN -32768 diff --git a/api/libstelephony/stel_tone/libteletone_generate.h b/api/libstelephony/stel_tone/wp_libteletone_generate.h similarity index 99% rename from api/libstelephony/stel_tone/libteletone_generate.h rename to api/libstelephony/stel_tone/wp_libteletone_generate.h index b2725a2..b0b20b2 100644 --- a/api/libstelephony/stel_tone/libteletone_generate.h +++ b/api/libstelephony/stel_tone/wp_libteletone_generate.h @@ -118,7 +118,7 @@ extern float powf (float, float); #endif #include #include -#include +#include #define TELETONE_VOL_DB_MAX 0 #define TELETONE_VOL_DB_MIN -63 @@ -197,7 +197,7 @@ static __inline__ int teletone_dds_state_set_tone(teletone_dds_state_t *dds, tel -/*! \file libteletone_generate.h +/*! \file wp_libteletone_generate.h \brief Tone Generation Routines This module is responsible for tone generation specifics diff --git a/api/libstelephony/stel_tone/.svn/text-base/uart.c.svn-base b/api/libstelephony/stel_tone/wp_uart.c similarity index 99% rename from api/libstelephony/stel_tone/.svn/text-base/uart.c.svn-base rename to api/libstelephony/stel_tone/wp_uart.c index 5d88675..05eb55f 100644 --- a/api/libstelephony/stel_tone/.svn/text-base/uart.c.svn-base +++ b/api/libstelephony/stel_tone/wp_uart.c @@ -37,7 +37,7 @@ #include #include -#include "uart.h" +#include "wp_uart.h" /* * dsp_uart_attr_init diff --git a/api/libstelephony/stel_tone/uart.h b/api/libstelephony/stel_tone/wp_uart.h similarity index 100% rename from api/libstelephony/stel_tone/uart.h rename to api/libstelephony/stel_tone/wp_uart.h diff --git a/api/libstelephony/stelephony.cpp b/api/libstelephony/stelephony.cpp index 1d227db..beae1e3 100644 --- a/api/libstelephony/stelephony.cpp +++ b/api/libstelephony/stelephony.cpp @@ -158,8 +158,8 @@ void OnCallerID(void *callback_obj, char *Name, char *CallerNumber, char *Called CStelephony* stelObj = (CStelephony*)callback_obj; stelephony_callback_functions_t cbf; - DBG_STEL("%s(): Name: %s, CallerNumber: %s, CalledNumber: %s, DateTime: %s\r\n", - __FUNCTION__, Name, CallerNumber, CalledNumber, DateTime); + DBG_STEL("%s(): Name: %s(%d), CallerNumber: %s(%d), CalledNumber: %s, DateTime: %s\r\n", + __FUNCTION__, Name, strlen(Name), CallerNumber, strlen(CallerNumber), CalledNumber, DateTime); stelObj->GetCallbackFunctions(&cbf); diff --git a/api/libstelephony/stelephony.def b/api/libstelephony/stelephony.def index 9e0bddf..5c78995 100644 --- a/api/libstelephony/stelephony.def +++ b/api/libstelephony/stelephony.def @@ -1,13 +1,3 @@ LIBRARY STELEPHONY EXPORTS - StelSetup - StelCleanup - StelStreamInput - StelEventControl - StelGenerateSwDTMF - StelGenerateFSKCallerID - StelBufferInuse - StelBufferRead - StelBufferReadUlaw - StelBufferReadAlaw diff --git a/api/libstelephony/stelephony.h b/api/libstelephony/stelephony.h index 6327bd9..a4e117b 100644 --- a/api/libstelephony/stelephony.h +++ b/api/libstelephony/stelephony.h @@ -4,7 +4,7 @@ #include -#include +#include "libstelephony.h" #include "Q931EventsDecoder.h" #include "Sink.h" diff --git a/api/libstelephony/stelephony.sln b/api/libstelephony/stelephony.sln index bfd9c3b..630ed9a 100644 --- a/api/libstelephony/stelephony.sln +++ b/api/libstelephony/stelephony.sln @@ -1,6 +1,6 @@  -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stelephony", "stelephony.vcproj", "{973E1E5F-663B-40A3-90C9-8DD0772FDB01}" EndProject Global diff --git a/api/libstelephony/stelephony.vcproj b/api/libstelephony/stelephony.vcproj index 94b3a44..0494240 100644 --- a/api/libstelephony/stelephony.vcproj +++ b/api/libstelephony/stelephony.vcproj @@ -1,10 +1,11 @@ - @@ -192,9 +194,6 @@ - @@ -223,6 +222,10 @@ RelativePath=".\Q931EventsDecoder.cpp" > + + @@ -247,6 +250,22 @@ RelativePath=".\stelephony.rc" > + + + + + + + + + + @@ -284,6 +307,30 @@ RelativePath=".\StelephonyApi.h" > + + + + + + + + + + + + - - - - - - - - - - diff --git a/api/tdm_api/aft_tdm_hdlc_test.c b/api/tdm_api/aft_tdm_hdlc_test.c index 9211e2d..4cc4a83 100644 --- a/api/tdm_api/aft_tdm_hdlc_test.c +++ b/api/tdm_api/aft_tdm_hdlc_test.c @@ -95,7 +95,7 @@ void print_packet(unsigned char *buf, int len) void sig_end(int sigid) { - printf("%d: Got Signal %i\n",getpid(),sigid); + //printf("%d: Got Signal %i\n",getpid(),sigid); end=1; } @@ -182,7 +182,7 @@ void process_con_rx(void) unsigned char Rx_data[15000]; int error_bit=0, error_crc=0, error_abort=0, error_frm=0; struct timeval tv; - int frame=0; + int frame=0,print_status=0; int max_fd=0, packets=0; timeslot_t *slot=NULL; wanpipe_tdm_api_t tdm_api; @@ -300,6 +300,7 @@ void process_con_rx(void) continue; #endif frame++; + print_status++; slot->frames++; wanpipe_hdlc_decode(slot->hdlc_eng,rx_frame,len); @@ -320,7 +321,8 @@ void process_con_rx(void) slot->hdlc_eng->decoder.stats.crc, slot->hdlc_eng->decoder.stats.abort, slot->hdlc_eng->decoder.stats.frame_overflow); - sangoma_get_full_cfg(slot->sock, &tdm_api); + //sangoma_get_full_cfg(slot->sock, &tdm_api); + wanpipe_hdlc_dump_ring(slot->hdlc_eng); } } @@ -332,7 +334,6 @@ void process_con_rx(void) slot->hdlc_eng->decoder.stats.crc, slot->hdlc_eng->decoder.stats.abort, slot->hdlc_eng->decoder.stats.frame_overflow); - sangoma_get_full_cfg(slot->sock, &tdm_api); } #endif #if 0 @@ -345,14 +346,15 @@ void process_con_rx(void) #endif } else { - //printf("\n%s: Error receiving data\n",slot->if_name); + printf("\n%s: Error receiving data\n",slot->if_name); } } /* If rx */ } /* for all slots */ - if (frame%500==0){ + if (print_status>100*slots){ + print_status=0; putchar('\r'); printf("Slots=%04i frame=%04i packets=%04i errors=%04i (crc=%04i abort=%04i frm=%04i)", slots, @@ -385,7 +387,7 @@ void process_con_rx(void) } } - printf("\nRx Unloading HDLC\n"); + //printf("\nRx Unloading HDLC\n"); } @@ -400,6 +402,7 @@ void process_con_tx(timeslot_t *slot) wanpipe_hdlc_engine_t *hdlc_eng; unsigned char next_idle; wanpipe_tdm_api_t tdm_api; + int txdata=0; memset(&tdm_api,0,sizeof(tdm_api)); @@ -431,7 +434,7 @@ void process_con_tx(timeslot_t *slot) */ memset(&Tx_data[0],0,MAX_TX_DATA + sizeof(wp_api_hdr_t)); - slot->data=1; + slot->data=0x0; for (i=0;idata){ Tx_data[i+sizeof(wp_api_hdr_t)] = slot->data; @@ -558,6 +561,16 @@ void process_con_tx(timeslot_t *slot) mysrand(myrand(255)); } #endif + + +#if 0 + if (Tx_count%100 == 0) { + txdata = (txdata + 1) % 2; + for (i=0;i 1 && err < 1000) { + if (elapsed > elapsed_max) { + elapsed_max = elapsed; + } + if (elapsed_min > elapsed) { + elapsed_min = elapsed; + } + printf("wanpipe: Elapsed %i diff=%i max=%i min=%i\n", + elapsed,abs(20-elapsed),elapsed_max,elapsed_min); + } + + return 0; +} + + /*************************************************** * HANDLE SOCKET * @@ -221,6 +249,7 @@ void handle_span_chan(void) memset(Rx_data, 0, sizeof(Rx_data)); + err = sangoma_readmsg_tdm(dev_fd, Rx_data, sizeof(wp_tdm_api_rx_hdr_t), @@ -231,6 +260,8 @@ void handle_span_chan(void) goto bitstrm_skip_read; } + sample_time_test(); + /* err indicates bytes received */ if(err <= 0) { printf("\nError receiving data\n"); diff --git a/deb_control/wanpipe.deb b/deb_control/wanpipe.deb index 5c15e92..fcbb9a2 100644 --- a/deb_control/wanpipe.deb +++ b/deb_control/wanpipe.deb @@ -1,5 +1,5 @@ Package: wanpipe -Version: 3.5.6-0 +Version: 3.5.11-0 Section: networking Priority: optional Architecture: all diff --git a/doc/.README.xdlc_api.swp b/doc/.README.xdlc_api.swp deleted file mode 100644 index 6517929e45f58d145883732785eeaa0f9e3a2cf3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2J!~9B6vxK_p&Sq(p+X??2oeDa@A_~OC+0((E*Epeu}}7$feP*I&iO{(-C1U4 z{gD721ri7Z4G4)8fItEgXrKm>A|(`n#76-I3Sa5yApB?VE^%x}PLn2@k$&=eclOPD z|M%vtl}vkNu6~5Zyb*?R3uF1|HzpRhemV5^ZHy@?jlI3uFkHvMW|Q#zd27&*_#%&G zmhja0qu!{$6r`ER_%N=$ecnDllA%9Ss~kL1^OmDHd^*U)W!QF8oB~dP%@o+iMjt4T z+`a2Ax^+iuv&rtcQ@|lk}%D+0p*|J#29ID0c=KZBpZkKj}A z2{;K(faBm*@CpdQgJ2Alz^^wk_6s-#j)P<1D3}Dhz`1RV{SE#Ce}X^2@8C4}0K5;5 zf);3kGPnoq0NcTDH!}7)cn`b_-T^NH2~yAi9=HSC4z2~)fGyzc4TuN)06qh6f#<-p z;21axIG6;7!A@`|VBp;KjQt1x1!ur1@D(@-PJoZWo8Seo0NS7m#=t(X9sE;b?0fJX z_!fKtJ^~+t*THL`131_Z2?5{idLsuhnlVjgHtiU$#yE~nmkKI({=lSs+uCLEmm^Krh~FWKz6Xx$q(+DOI~XT%e9r6r?M} zmBJOyD5gex_Nh!eLM25QlOkOpZ34sbZC?GLxJR->hm=eW)zXOWZI=C7*`FKTdmpSE z2^Ki%TyaWee~E-^9n5o00q#aZXK}E?BP=o>AYO zLJk{MEeS+CPGor4jksQTR0rw2Oad>I2`e>u>{02!$X?nrwuj0G4vav;a(O?oQa^Vg z0trGxv%S}-2|F~KXPHzu6j~|`-12Z;H?$<;cn}3GaFz177bm0MD6vuzSZRaEO6vn- zrFu%CEh4B|mo=)`S!O1FRN*Wo;81@F0nq#4UX(8~Am@jIT zv8m~1ZJeH{OjQp}(81~2?9}ciRa-MNwW;RhQ#9LZG^S^oh1pBH!HV3>=SrmWJr7X6 zcJV8nq_rvxuUB;->Z-JKmmpA^GiD2ONvZ|wc1NUqoF(VW*mMI^?1_G^6B*?(*ET`6 zcI}B?>IN!WvPKGfWaOnK8fNtr$&BOPcAn!(bozT6I!(0d)JsdQ0(2OU4SwUaBJ-Z6 zAy%VYTW9@TD_^%oYMV^hnI~51*sY}VFvPCwdHl;rX-NZmRw) zCbOc;pmnhpD#f<6YHPMReS{(<7GW8iDieL@8H;+UM%gs#joQFUFSKGUjn>kHk)j-J zFVnq^MZ)&5!ZWzjUZIG0f;_&A5+CSi!yl}>Buw}G61Lu0iP9k9ln9*!ChTJG+AgYO zrUaTAS{u)_RY7qr+HnfWb?1ipF zB{D;y?YA1vq?? tw7@BQo5D(Nx>AkP6i@MStxDAvAw86a#l`9-{|F|p{S>l6X$%UF{SS$?$6){f diff --git a/patches/Fix_below_2211.gz b/patches/Fix_below_2211.gz deleted file mode 100644 index ba1204ae38088b31b0b25665f6843f578ad4d143..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 231 zcmVb z%L%hBt%>4>G?5!YMY{=Q%_2?v_LbnN>BdQB=J4m6aa|X+YP#=&DB%9VsISVjep6k1 zp_`*Rd8+ZWQK!-SM~8K3Fy){i3WA89K%8@jF?RRFUriCGLB}y%n$9OPuzo!vjvWlO z)4IkO*e+7JlrP9V3ek4wvo@D8#3oIauf;7aatduOG3954tg6c&D^D_;mQq^PHFFs7 h@ShW^-D|u(uY{G0J{|rZm2;cd{sj*W0ko9?003n2Z0G<0 diff --git a/patches/kdrivers/include/.wanpipe_kernel.h.swp b/patches/kdrivers/include/.wanpipe_kernel.h.swp deleted file mode 100644 index 580df09e5c1a4df0022282929cf8ac1743c24205..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeI3Uu+ab9LEPi^iWVSlAwth+Gy^|^{zdj%AW#9yDbOpQLcZefiT(L&T(t6d)wW; zwgfOSJ}UYq4=A9Cf8vAk2AGhL5Ka8|CJ(}kF`A%HCi3xZy<6_~pdg74&L*GS z?EGea^V{#t&ix{%=5V{9-l`Dn zF9?TmjnD8x(VXIj6JHlw7oSxPQ#W}#(y@(kQ+2X=ZpSAzOP}GE9oLO)p`>xI7@Opl z!3(kc0@{;Bqyka_zXBn$F45W1+Omc|x^mQSkZGxaR6r^q6_5%@1*8H}0jYpg;D4Zi zV=g80IL>>32^P7`W7CkHcK83r{uI(z-2K0>e+20rs37Y6>F!^2_ZK5?6?hhog=kP1izqyka_sen{KDj*e*3jB8psCdY~56xc_59wI{7w-V(A0p%j@CEoB zoCoK?8{i};f&E}R*a#?C4E|U_$nW4Y@F92)ybESPKiCeoffeA}<%FCCAAm!k1v~*B z2dlyL2MKux><5p5YY!0eG8hJfU>Uf)jF1<>Ua$&W3K4PvoCoK?^I$L70NTM%_ai27 z6wH8bumpSu0j`0o;2ZD>cptn4&VbV(1A4%(O9}Y}Tn1;sN8luIfDI0UouD7A0^i?@ za^NMf543>W&{SyEdBNmXSdC6Zv*>Wz)EMpXH&Jan6cPvE22$4$K3-yz zIg4{^}u{-_&H#p31qYZT2U6=3knQ=dwG8AW0HGN}=T zcGUcob2Cb^m-@R7$BIMMvz$2?Cgl;}9zODFl?PJk(Q%ewU6{k;ZFIMiNht`V)= z(DWSfPJ~ouZyy`pnan7?bU29=GK2yKW<2UM`f5^lG889@Zs_F!5H{2)j*~-%X`jq4dOLQgHV|2Gtw=?QYw=+z;qKWkpdi1E3VHylian5m-0Tn_mW8Wwwz_N}4-?XK=>s-x1i)GdVrb%#vNYrRL| z3g;@kF{dq3a~e<;n)zd?1pz})Q(~HLJOorjE7i!$X%TjF4+yV3k>|OpRb=pd82Ol` z&Qb`l05?-=mNmLSZrR6;E-J&iLA(Y9&@8LZ3{WmehiX>}A2%xy;IFY0g_DNdfSS$v zMAUC$Yw9JaUuu2=tW7=Dca7*yH=H`tZJvDz{~6|;>$9e~Fyki1VR|v^6v8Y!rLicp zOvhrFOK)QIO+F^mO@^y*kc;LPN*m_Pn@7{Gq|#%_fdw{PExqtdX;-v!161-ZPo7A& zVOp4Wa+TY!s1}RGA|bn)myNczijzLLoh^-5?Df%3ahF!#%;28h(E)`Gq{|b=kP1izpn&_F zU;XtGA@AX~@+LIF*25y6ziF@~`29lAlE6B#B+%fbtJ(X6AQ#@M0(Dn;#G(XkqqCeE z94nSi(R;x#n^zr*Kd{?cLDj4oCRVkai30x5PTgmFQAxG9SmqeX>o(0{&6DESC^~e4 pV+9H8XyyOIYcyvSr-U<6+cs4!m>}kgG4O?fkc+}^?UjLu^B+!eYjywt diff --git a/patches/kdrivers/include/aft_core.h b/patches/kdrivers/include/aft_core.h index 50d2397..9ee0207 100644 --- a/patches/kdrivers/include/aft_core.h +++ b/patches/kdrivers/include/aft_core.h @@ -136,8 +136,9 @@ # define AFT_CHIPCFG_A500_EC_INTR_ENABLE_BIT 14 /* A500 - BRI not used for now */ -# define AFT_CHIPCFG_EC_INTR_STAT_BIT 13 - +# define AFT_CHIPCFG_EC_INTR_STAT_BIT 13 +# define AFT_CHIPCFG_B600_EC_RESET_BIT 25 +# define AFT_CHIPCFG_B600_EC_CHIP_PRESENT_BIT 20 /* B600 and B601 only */ /* A104 A200 A108 Differ Here * By default any register without device name is @@ -198,6 +199,62 @@ # define AFT_CHIPCFG_WDT_TX_INTR_STAT 1 # define AFT_CHIPCFG_WDT_RX_INTR_STAT 2 + +#define AFT_CLKCFG_A600_REG 0x1090 +#define AFT_CLKCFG_A600_CLK_OUTPUT_BIT 0 +#define AFT_CLKCFG_A600_CLK_EXT_CLK_SRC_BIT 4 +#define AFT_CLKCFG_A600_CLK_SRC_BIT_MASK 0x6 +#define AFT_CLKCFG_A600_CLK_SRC_BIT_SHIFT 1 +#define AFT_CLKCFG_A600_CLK_OUT_BIT_MASK 0x7 +#define AFT_CLKCFG_A600_CLK_OUT_BIT_SHIFT 5 + + +/* Use the onboard oscillator clk 8.192 Mhz */ +# define AFT_CLKCFG_A600_CLK_SRC_OSC 0x00 + +/* Use the clock from front end no pll */ +# define AFT_CLKCFG_A600_CLK_SRC_EXT_NO_PLL 0x01 + +/* Use the clock from front end with pll */ +# define AFT_CLKCFG_A600_CLK_SRC_EXT_PLL 0x02 + +/* use the board system clock */ +# define AFT_CLKCFG_A600_CLK_OUT_BOARD 0x04 + + +#define AFT_CLKCFG_B601_EC_SRC_MUX_MASK 0x03 +#define AFT_CLKCFG_B601_EC_SRC_MUX_SHIFT 1 + +#define AFT_CLKCFG_B601_EC_SRC_MUX_OSC_CLK 0x0 +#define AFT_CLKCFG_B601_EC_SRC_MUX_EXT_CLK 0x1 +#define AFT_CLKCFG_B601_EC_SRC_MUX_TRISTATE 0x2 + +#define AFT_CLKCFG_B601_EXT_CLK_VAL_MASK 0x1 +#define AFT_CLKCFG_B601_EXT_CLK_VAL_SHIFT 4 + +#define AFT_CLKCFG_B601_EXT_CLK_MUX_MASK 0x7 +#define AFT_CLKCFG_B601_EXT_CLK_MUX_SHIFT 5 + +#define AFT_CLKCFG_B601_EXT_CLK_MUX_LINE_0 0x00 +#define AFT_CLKCFG_B601_EXT_CLK_MUX_LINE_1 0x01 +#define AFT_CLKCFG_B601_EXT_CLK_MUX_OSC_2000HZ 0x02 +#define AFT_CLKCFG_B601_EXT_CLK_MUX_OSC_1500HZ 0x03 +#define AFT_CLKCFG_B601_EXT_CLK_MUX_H100_CLK 0x04 + +#define AFT_CLKCFG_B601_PLL_CLK_SRC_MUX_MASK 0x7 +#define AFT_CLKCFG_B601_PLL_CLK_SRC_MUX_SHIFT 8 + +#define AFT_CLKCFG_B601_PLL_CLK_SRC_MUX_LINE_0 0x00 +#define AFT_CLKCFG_B601_PLL_CLK_SRC_MUX_LINE_1 0x01 +#define AFT_CLKCFG_B601_PLL_CLK_SRC_MUX_OSC_2000HZ 0x02 +#define AFT_CLKCFG_B601_PLL_CLK_SRC_MUX_OSC_1500HZ 0x03 +#define AFT_CLKCFG_B601_PLL_CLK_SRC_MUX_EXT_8000HZ 0x04 +#define AFT_CLKCFG_B601_PLL_CLK_SRC_MUX_EXT_1500HZ 0x05 + + + + + static __inline u32 aft_chipcfg_get_fifo_reset_bit(sdla_t* card) { @@ -257,30 +314,6 @@ aft_chipcfg_get_tx_intr_ack_bit_bymap(u32 comm_port) return AFT_CHIPCFG_A108_TDM_GLOBAL_TX_INTR_ACK; } - - -# define AFT_CLKCFG_A600_CLK_OUTPUT_BIT 0 -# define AFT_CLKCFG_A600_CLK_EXT_CLK_SRC_BIT 4 - -# define AFT_CLKCFG_A600_CLK_SRC_BIT_MASK 0x6 -# define AFT_CLKCFG_A600_CLK_SRC_BIT_SHIFT 1 -# define AFT_CLKCFG_A600_CLK_OUT_BIT_MASK 0x7 -# define AFT_CLKCFG_A600_CLK_OUT_BIT_SHIFT 5 - - -/* Use the onboard oscillator clk 8.192 Mhz */ -# define AFT_CLKCFG_A600_CLK_SRC_OSC 0x00 - -/* Use the clock from front end no pll */ -# define AFT_CLKCFG_A600_CLK_SRC_EXT_NO_PLL 0x01 - -/* Use the clock from front end with pll */ -# define AFT_CLKCFG_A600_CLK_SRC_EXT_PLL 0x02 - -/* use the board system clock */ -# define AFT_CLKCFG_A600_CLK_OUT_BOARD 0x04 - - /* A104 & A104D Interrupt Status Funcitons */ static __inline u32 @@ -495,20 +528,43 @@ aft_chipcfg_get_a200_ec_channels(u32 reg) } static __inline u32 -aft_chipcfg_get_a600_ec_channels(u32 reg) +aft_chipcfg_get_a600_ec_channels(u32 reg, u8 core_rev) { - switch ((reg>>AFT_CHIPCFG_A600_EC_SEC_KEY_SHIFT)&AFT_CHIPCFG_A600_EC_SEC_KEY_MASK){ - case 0x00: - return 0; - case 0x01: + if (core_rev >= 3) { + if (wan_test_bit(AFT_CHIPCFG_B600_EC_CHIP_PRESENT_BIT, ®)) { return 5; - default: - return 0; + } + return 0; + } else { + switch ((reg>>AFT_CHIPCFG_A600_EC_SEC_KEY_SHIFT) & AFT_CHIPCFG_A600_EC_SEC_KEY_MASK){ + case 0x00: + return 0; + case 0x01: + return 5; + default: + return 0; + } } - + return 0; } +static __inline u32 +aft_chipcfg_get_b601_ec_channels(u32 reg) +{ + if (wan_test_bit(AFT_CHIPCFG_B600_EC_CHIP_PRESENT_BIT, ®)) { + return 64; + } + return 0; +} + + +static __inline u32 +aft_chipcfg_get_b601_security(u32 reg) +{ + return((reg >> AFT_CHIPCFG_A600_EC_SEC_KEY_SHIFT) & AFT_CHIPCFG_A600_EC_SEC_KEY_MASK); +} + static __inline u32 aft_chipcfg_get_a700_ec_channels(u32 reg) { @@ -623,19 +679,47 @@ aft_fifo_mark_gset(u32 *reg, u8 mark) # define AFT_LCFG_A108_CLK_ROUTE_MASK 0x0F # define AFT_LCFG_A108_CLK_ROUTE_SHIFT 16 -# define AFT_LCFG_CLR_CHNL_EN 26 +# define AFT_LCFG_FE_SYNC_CNT_MASK 0xFF +# define AFT_LCFG_FE_SYNC_CNT_SHIFT 20 + +/* Not Digital */ +# define AFT_LCFG_CLR_CHNL_EN 26 # define AFT_LCFG_FE_CLK_ROUTE_BIT 27 # define AFT_LCFG_FE_CLK_SOURCE_MASK 0x03 # define AFT_LCFG_FE_CLK_SOURCE_SHIFT 28 + # define AFT_LCFG_GREEN_LED_BIT 30 # define AFT_LCFG_A108_FE_CLOCK_MODE_BIT 31 /* A108 */ # define AFT_LCFG_RED_LED_BIT 31 # define AFT_LCFG_A108_FE_TE1_MODE_BIT 30 /* A108 */ +#define AFT_LCFG_B601_CLK_ROUTE_MASK 0x07 +#define AFT_LCFG_B601_CLK_ROUTE_SHIFT 16 + +#define AFT_LCFG_B601_CLK_SRC_LINE_0 0x00 /* Receive clock from line 0 is source */ +#define AFT_LCFG_B601_CLK_SRC_LINE_1 0x01 /* Receive clock from line 1 is source , Not used for now */ +#define AFT_LCFG_B601_CLK_SRC_EXT_2000HZ 0x02 /* External clock in case 2.048 Mhz is the source */ +#define AFT_LCFG_B601_CLK_SRC_EXT_1500HZ 0x03 /* External clock in case 1.544 Mhz is the source */ +#define AFT_LCFG_B601_CLK_SRC_OSC_2000HZ 0x04 /* Master Board oscillator 2.048 Mhz is the source */ +#define AFT_LCFG_B601_CLK_SRC_OSC_1500HZ 0x05 /* Master Board oscillator 1.544 Mhz is the source */ + +#define AFT_LCFG_B601_FE_CHIP_RESET_BIT 5 /* For B601 only */ +#define AFT_LCFG_B601_GREEN_LED_BIT 6 +#define AFT_LCFG_B601_RED_LED_BIT 7 + +#define AFT_B601_ECHO_EN_CTRL_REG 0x210 +#define AFT_B601_DATA_MUX_EN_CTRL_REG 0x20C + +static __inline void +aft_lcfg_b601_fe_clk_source(u32 *reg, u32 src) +{ + *reg&=~(AFT_LCFG_B601_CLK_ROUTE_MASK<>AFT_LCFG_FE_SYNC_CNT_SHIFT)&AFT_LCFG_FE_SYNC_CNT_MASK; +} + +static __inline void +aft_lcfg_set_fe_sync_cnt(u32 *reg, int cnt) +{ + *reg&=~(AFT_LCFG_FE_SYNC_CNT_MASK<tx_pending_chain_indx; - int chain_diff=0; - - if (chan->tx_chain_indx == pending_indx){ - return chain_diff; - } - - if (chan->tx_chain_indx > pending_indx){ - chain_diff = chan->tx_chain_indx - pending_indx; - }else{ - chain_diff = MAX_AFT_DMA_CHAINS-(pending_indx - chan->tx_chain_indx); - } - - return chain_diff; -} - #define MAX_AFT_HW_DEV 20 typedef struct aft_hw_dev{ diff --git a/patches/kdrivers/include/aft_core_bert.h b/patches/kdrivers/include/aft_core_bert.h new file mode 100644 index 0000000..6ba4daa --- /dev/null +++ b/patches/kdrivers/include/aft_core_bert.h @@ -0,0 +1,175 @@ +/******************************************************************************//** + * \file aft_core_bert.h + * \brief Definitions and implementation of + * Software BERT for Sangoma AFT cards. + * + * Authors: David Rokhvarg + * + * Copyright (c) 2007 - 2010, 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 Sangoma Technologies 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 Sangoma Technologies ``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 Sangoma Technologies 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 + * =============================================================================== + */ + + +#ifndef _AFT_CORE_BERT_H +#define _AFT_CORE_BERT_H + + +#ifdef WAN_KERNEL + +# include "aft_core_options.h" +# include "if_wanpipe_common.h" /* wanpipe_common_t */ + + +typedef struct _wp_bert { + + wp_bert_sequence_type_t m_eSequenceType; + size_t m_uiNumberOfIdenticalValues; + size_t m_uiErrors; + size_t m_uiSynchronizedCount; + u8 m_bSynchronized; + size_t m_uiNumberOfIdenticalValuesRequireToSynchronize; + + u8 *m_pNextExpectedValue; + u8 *m_pNextValue; + + u8 *m_pSequenceBegin; + u8 *m_pSequenceEnd; + + u8 *m_pContext; /* an optional context */ +}wp_bert_t; + +#define WP_BERT_SEQUENCE_LENGTH 257 + +/* Sequences supported by BERT */ +static u8 const wp_bert_random_sequence[WP_BERT_SEQUENCE_LENGTH] + = { 15, 164, 118, 194, 69, 52, 47, 152, 122, 117, 44, 99, 150, + 185, 197, 226, 235, 146, 250, 135, 18, 76, 207, 115, 81, + 130, 232, 98, 153, 151, 145, 53, 253, 154, 224, 27, 14, 26, + 212, 131, 85, 95, 160, 241, 68, 203, 114, 62, 138, 83, 71, + 105, 3, 240, 156, 208, 175, 205, 107, 30, 251, 198, 40, 159, + 63, 173, 149, 169, 227, 75, 57, 46, 254, 113, 191, 24, 129, + 245, 142, 174, 100, 190, 37, 147, 28, 246, 77, 8, 230, 165, + 223, 93, 244, 111, 215, 4, 195, 242, 163, 58, 96, 219, 157, + 202, 210, 143, 13, 88, 181, 158, 243, 16, 120, 11, 214, 31, + 166, 255, 228, 91, 204, 218, 36, 19, 42, 148, 67, 29, 201, + 66, 23, 128, 87, 167, 136, 7, 193, 222, 200, 92, 144, 176, + 82, 108, 65, 172, 141, 252, 10, 234, 233, 171, 101, 54, 132, + 103, 236, 73, 61, 192, 79, 211, 221, 74, 50, 64, 180, 220, + 196, 187, 12, 102, 70, 104, 206, 0, 116, 133, 225, 170, 125, + 127, 124, 22, 209, 137, 126, 106, 186, 112, 162, 183, 237, + 238, 199, 33, 121, 155, 168, 139, 34, 216, 48, 231, 38, 51, + 5, 239, 1, 229, 84, 178, 35, 94, 43, 189, 161, 179, 90, 97, + 32, 134, 217, 6, 49, 2, 17, 56, 248, 123, 213, 89, 188, 86, + 72, 184, 39, 21, 25, 247, 55, 182, 119, 249, 140, 109, 41, + 177, 80, 45, 59, 60, 78, 20, 110, 111 }; + +static u8 const wp_bert_ascendant_sequence[WP_BERT_SEQUENCE_LENGTH] + = { 'a', 's', 'c', 'e', 'n', 'd', 'a', 'n', 't', 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 142, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, + 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, + 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248 }; + +static u8 const wp_bert_descendant_sequence[WP_BERT_SEQUENCE_LENGTH] + = { 'd', 'e', 's', 'c', 'd', 'a', 'n', 't', 249, + 248, 247, 246, 245, 244, 243, 242, 241, 240, 239, + 238, 237, 236, 235, 234, 233, 232, 231, 230, 229, + 228, 227, 226, 225, 224, 223, 222, 221, 220, 219, + 218, 217, 216, 215, 214, 213, 212, 211, 210, 209, + 208, 207, 206, 205, 204, 203, 202, 201, 200, 199, + 198, 197, 196, 195, 194, 193, 192, 191, 190, 189, + 188, 187, 186, 185, 184, 183, 182, 181, 180, 179, + 178, 177, 176, 175, 174, 173, 172, 171, 170, 169, + 168, 167, 166, 165, 164, 163, 162, 161, 160, 159, + 158, 157, 156, 155, 154, 153, 152, 151, 150, 149, + 148, 147, 146, 145, 144, 143, 142, 141, 140, 139, + 138, 137, 136, 135, 134, 133, 132, 131, 130, 129, + 128, 127, 126, 125, 124, 123, 122, 121, 120, 119, + 118, 117, 116, 115, 114, 113, 112, 111, 110, 109, + 108, 107, 106, 105, 104, 103, 102, 101, 100, 99, + 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, + 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, + 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, + 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, + 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, + 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, + 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, + 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, + 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, + 8, 7, 6, 5, 4, 3, 2, 1 }; + + +/******** BERT functions *********/ +/** @brief reset */ +int wp_bert_reset(wp_bert_t *bert); + +/** @brief change the type sequence used by this bert */ +int wp_bert_set_sequence_type(wp_bert_t *bert, wp_bert_sequence_type_t sequence_type); + +/** @bried Get the number of times when the BERT entered in the + synchronized state */ +u32 wp_bert_get_synchonized_count(wp_bert_t *bert); + +/** @brief Get the number errors */ +u32 wp_bert_get_errors(wp_bert_t *bert); + +/** @brief Returns 1 when the BERT is synchronized */ +u8 wp_bert_is_synchronized(wp_bert_t *bert); + +/** @brief Return the next value to stream toward the remote BERT + entity */ +u8 wp_bert_pop_value(wp_bert_t *bert, u8 *value); + +/** @brief Push value in the BERT. This method return 1 if the + BERT is synchronized and 0 otherwise. The expected value is + also set when the BERT is not synchronized. */ +u8 wp_bert_push_value(wp_bert_t *bert, u8 current_value, u8 *expected_value); + +/** @brief print current state of BERT */ +void wp_bert_print_state(wp_bert_t *bert); + +/****** end of BERT functions *******/ + + +#endif /* WAN_KERNEL */ + + +#endif /* _AFT_CORE_BERT_H */ + diff --git a/patches/kdrivers/include/aft_core_options.h b/patches/kdrivers/include/aft_core_options.h index 9c2437a..71a21e6 100644 --- a/patches/kdrivers/include/aft_core_options.h +++ b/patches/kdrivers/include/aft_core_options.h @@ -57,50 +57,53 @@ #if defined(__WINDOWS__) - /********** compilation flags ************/ - /* compile protocols in the LIP layer */ - #define CONFIG_PRODUCT_WANPIPE_FR - #define CONFIG_PRODUCT_WANPIPE_CHDLC - #define CONFIG_PRODUCT_WANPIPE_PPP - #define CONFIG_PRODUCT_WANPIPE_LAPB +/********** compilation flags ************/ +/* compile protocols in the LIP layer */ +# define CONFIG_PRODUCT_WANPIPE_FR +# define CONFIG_PRODUCT_WANPIPE_CHDLC +# define CONFIG_PRODUCT_WANPIPE_PPP - /* compile AFT 56k code */ - #define CONFIG_PRODUCT_WANPIPE_AFT_56K - /* compile "old" AFT T1/E1 code */ - #define CONFIG_PRODUCT_WANPIPE_AFT - /* compile "new/shark" AFT T1/E1 code */ - #define CONFIG_PRODUCT_WANPIPE_AFT_TE1 - /* compile AFT A200 Analog code */ - #define CONFIG_PRODUCT_WANPIPE_AFT_RM - #define CONFIG_WANPIPE_PRODUCT_AFT_RM +# define CONFIG_PRODUCT_WANPIPE_LIP_LAPD - /* compile AFT B600 Analog - 4 FXO / 1 FXS code */ - #define CONFIG_PRODUCT_WANPIPE_AFT_A600 +/* compile AFT 56k code */ +# define CONFIG_PRODUCT_WANPIPE_AFT_56K +/* compile "old" AFT T1/E1 code */ +# define CONFIG_PRODUCT_WANPIPE_AFT +/* compile "new/shark" AFT T1/E1 code */ +# define CONFIG_PRODUCT_WANPIPE_AFT_TE1 +/* compile AFT A200 Analog code */ +# define CONFIG_PRODUCT_WANPIPE_AFT_RM +# define CONFIG_WANPIPE_PRODUCT_AFT_RM - /* compile AFT B700 4 BRI and 2 Analog - FXO or FXS code */ - #define CONFIG_PRODUCT_WANPIPE_AFT_A700 +/* compile AFT B600 Analog - 4 FXO / 1 FXS code */ +# define CONFIG_PRODUCT_WANPIPE_AFT_A600 - /* compile HWEC code */ - #define CONFIG_WANPIPE_HWEC - /* compile ADSL code */ - #define CONFIG_PRODUCT_WANPIPE_ADSL - /* compile ISDN BRI code */ - #define CONFIG_PRODUCT_WANPIPE_AFT_BRI - #define CONFIG_WANPIPE_PRODUCT_AFT_BRI +/* compile AFT B700 4 BRI and 2 Analog - FXO or FXS code */ +# define CONFIG_PRODUCT_WANPIPE_AFT_A700 - /* compile AFT Serial code */ - #define CONFIG_PRODUCT_WANPIPE_AFT_SERIAL +/* compile HWEC code */ +# define CONFIG_WANPIPE_HWEC +/* compile ADSL code */ +# define CONFIG_PRODUCT_WANPIPE_ADSL +/* compile ISDN BRI code */ +# define CONFIG_PRODUCT_WANPIPE_AFT_BRI +# define CONFIG_WANPIPE_PRODUCT_AFT_BRI - #define WAN_IS_TASKQ_SCHEDULE 1 +/* compile AFT Serial code */ +# define CONFIG_PRODUCT_WANPIPE_AFT_SERIAL - /* compile TDM Voice API in wanpipe_tdm_api.c */ - #define BUILD_TDMV_API +# define WAN_IS_TASKQ_SCHEDULE 1 - #define SDLADRV_HW_IFACE +/* compile TDM Voice API in wanpipe_tdm_api.c */ +# define BUILD_TDMV_API - /********** end of compilation flags ************/ +# define SDLADRV_HW_IFACE + +// #define WANPIPE_PERFORMANCE_DEBUG + +/********** end of compilation flags ************/ #endif/* __WINDOWS__ */ -#endif -#endif +#endif/* WAN_KERNEL */ +#endif/* _AFT_CORE_OPTIONS_H */ diff --git a/patches/kdrivers/include/aft_core_private.h b/patches/kdrivers/include/aft_core_private.h index aa16580..1b02db0 100644 --- a/patches/kdrivers/include/aft_core_private.h +++ b/patches/kdrivers/include/aft_core_private.h @@ -1,4 +1,6 @@ +/* aft_core_private.h */ + #ifndef _AFT_CORE_PRIVATE_H #define _AFT_CORE_PRIVATE_H @@ -9,6 +11,7 @@ # include "if_wanpipe_common.h" /* wanpipe_common_t */ # include "aft_core_user.h" /* aft_op_stats_t */ # include "wanpipe_tdm_api.h" /* wanpipe_tdm_api_dev_t */ +# include "aft_core_bert.h" /* wp_bert_t */ #if defined(__WINDOWS__) # include "sdladrv_private.h" @@ -29,6 +32,7 @@ #define AFT_MIN_ANALOG_FRMW_VER 0x05 #define AFT_MIN_A600_FRMW_VER 0x01 +#define AFT_MIN_B601_FRMW_VER 0x03 #define A500_MAX_EC_CHANS 64 @@ -145,6 +149,13 @@ enum { WAN_AFT_DMA_CHAIN_SINGLE }; +enum { + AFT_BG_TIMER_RUNNING, + AFT_BG_TIMER_KILL, + + AFT_BG_TIMER_CMD_NONE +}; + /*================================================================= * Private structures *================================================================*/ @@ -196,12 +207,13 @@ typedef struct aft_config static __inline u32 AFT_PORT_REG(sdla_t *card, u32 reg) { - if (card->adptr_type == AFT_ADPTR_A600) { + if (card->adptr_type == AFT_ADPTR_A600 || + card->adptr_type == AFT_ADPTR_B601) { //A600 CASE if (reg < 0x100) { return (reg+0x1000); } else { - return (reg+0x2000); + return (reg+0x2000)+(0x8000*card->wandev.comm_port); } } else { if (reg < 0x100) { @@ -241,9 +253,18 @@ typedef struct aft_dma_swring { }aft_dma_swring_t; +/* List of Maintenance modes for 'maintenance_mode_bitmap' + * in 'private_area_t'. + * Currently only BERT is implemented. */ +enum wp_maintenance_modes { + WP_MAINTENANCE_MODE_BERT=0 +}; + + + typedef struct private_area { - wanpipe_common_t common; + wanpipe_common_t common;/* MUST be at the top */ sdla_t *card; u32 busy; @@ -258,10 +279,10 @@ typedef struct private_area u32 dma_status; unsigned char hdlc_eng; - unsigned char tx_chain_indx,tx_pending_chain_indx; + unsigned char tx_chain_indx,tx_pending_chain_indx,tx_chain_data_sz,tx_chain_sz; wan_dma_descr_t tx_dma_chain_table[MAX_AFT_DMA_CHAINS]; - unsigned char rx_chain_indx,rx_pending_chain_indx; + unsigned char rx_chain_indx,rx_pending_chain_indx,rx_chain_sz; wan_dma_descr_t rx_dma_chain_table[MAX_AFT_DMA_CHAINS]; wan_skb_queue_t wp_tx_pending_list; @@ -426,6 +447,11 @@ typedef struct private_area dma_history_t dma_history[MAX_DMA_HIST_SIZE]; #endif + u32 maintenance_mode_bitmap; + wp_bert_t wp_bert; + netskb_t *tx_bert_skb; + u32 bert_data_length; + }private_area_t; @@ -466,6 +492,10 @@ int aft_tdm_chan_ring_rsyinc(sdla_t * card, private_area_t *chan, int log ); static __inline void wan_aft_skb_defered_dealloc(private_area_t *chan, netskb_t *skb) { + if(chan->tx_bert_skb == skb){ + return; + } + wan_skb_queue_tail(&chan->wp_dealloc_list,skb); WAN_TASKLET_SCHEDULE((&chan->common.bh_task)); } @@ -483,9 +513,21 @@ static __inline int wan_chan_dev_stopped(private_area_t *chan) return wan_test_bit(0,&chan->busy); } +int aft_background_timer_kill(sdla_t* card); +int aft_background_timer_add(sdla_t* card, unsigned long delay); +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) +void aft_background_timer_expire(void* pcard); +#elif defined(__WINDOWS__) +void aft_background_timer_expire(IN PKDPC Dpc, void* pcard, void* arg2, void* arg3); +#else +void aft_background_timer_expire(unsigned long pcard); #endif +int aft_fe_loop_back_status(sdla_t *card); + +#endif /* WAN_KERNEL */ + #endif diff --git a/patches/kdrivers/include/aft_core_user.h b/patches/kdrivers/include/aft_core_user.h index 0616081..eb583dd 100644 --- a/patches/kdrivers/include/aft_core_user.h +++ b/patches/kdrivers/include/aft_core_user.h @@ -1,3 +1,4 @@ +/* aft_core_user.h */ #ifndef __AFT_CORE_USER__ #define __AFT_CORE_USER__ @@ -316,6 +317,78 @@ typedef struct pipe_mgmt_stat{ unsigned long UDP_PIPE_mgmt_passed_to_adptr; } pipe_mgmt_stat_t; +typedef struct aft_driver_isr_stats { + unsigned long all; + unsigned long aft; + unsigned long non_aft; + unsigned long fe; + unsigned long fe_run; + unsigned long tdm; + unsigned long tdm_run; + unsigned long dma; + unsigned long dma_rx; + unsigned long dma_tx; + unsigned long fifo; + unsigned long fifo_rx; + unsigned long fifo_tx; + unsigned long wdt; + unsigned long wdt_software; + unsigned long free_run; + unsigned long serial; +}aft_driver_isr_stats_t; + +typedef struct aft_driver_bh_stats { + unsigned long all; + unsigned long rx; + unsigned long rx_stack; + unsigned long rx_bri_dchan; + unsigned long tx_post; +}aft_driver_bh_stats_t; + +typedef struct aft_driver_port_task_stats { + unsigned long all; + unsigned long fe_isr; + unsigned long fe_poll; + unsigned long ec; + unsigned long ec_poll; + unsigned long led; + unsigned long serial_status; + unsigned long tap_q; + unsigned long restart; + unsigned long rbs; +}aft_driver_port_task_stats_t; + +#define MAX_SMA_IDX 255 +typedef struct aft_driver_timing { + unsigned long max_latency; + unsigned long min_latency; + unsigned long latency; + unsigned long above_avg; + unsigned long below_avg; + unsigned long limit; + int sma_idx; + unsigned long sma[MAX_SMA_IDX]; + unsigned long latency_avg; + wan_ticks_t timeout; + + wan_timeval_t timing_tv; + +}aft_driver_timing_t; + +typedef struct aft_driver_performance_stats { + aft_driver_isr_stats_t isr; + aft_driver_bh_stats_t bh; + aft_driver_port_task_stats_t port_task; + + aft_driver_timing_t aft_isr_latency; + aft_driver_timing_t kernel_isr_latency; +} aft_driver_performance_stats_t; + +#if defined(WANPIPE_PERFORMANCE_DEBUG) +#define AFT_PERF_STAT_INC(card,type,var) card->aft_perf_stats.type.var++ +#else +#define AFT_PERF_STAT_INC(card,type,var) +#endif #if defined(__WINDOWS__) @@ -396,17 +469,20 @@ typedef struct pipe_mgmt_stat{ //Definitions for 'poll_events_bitmap' and 'user_flags_bitmap' in API_POLL_STRUCT: -#define POLL_EVENT_RX_DATA 1 -#define POLL_EVENT_TX_READY (1 << 2) -#define POLL_EVENT_OOB (1 << 3) /* Out-Of-Band events such as Line Connect/Disconnect, +#define POLL_EVENT_RX_DATA (1) +#define POLL_EVENT_TX_READY (1 << 1) +#define POLL_EVENT_OOB (1 << 2) /* Out-Of-Band events such as Line Connect/Disconnect, RBS change, Ring, On/Off Hook, DTMF... */ -#define POLLIN (POLL_EVENT_RX_DATA) -#define POLLOUT (POLL_EVENT_TX_READY) -#define POLLPRI (POLL_EVENT_OOB) +#define POLLIN POLL_EVENT_RX_DATA +#define POLLOUT POLL_EVENT_TX_READY +#define POLLPRI POLL_EVENT_OOB #define POLLHUP POLLPRI #define POLLERR POLLPRI +#define POLLWRNORM 0 +#define POLLRDNORM 0 + /////////////////////////////////////////////////////////////////////////////////////// // Command to Set data in Idle Transmit buffer of the driver: // Input : ptr to TX_DATA_STRUCT structure @@ -493,6 +569,15 @@ static void print_poll_event_bitmap(u_int32_t bitmap) } } +/////////////////////////////////////////////////////////////////////////////////////// +// Command to control Logger API: +// Input : ptr to wp_logger_cmd_t structure. +// Output: ptr to wp_logger_cmd_t structure (same as input) +// Uses Buffered I/O, Synchronous call. +// This command is thread safe. +#define IoctlLoggerApiCommand \ + CTL_CODE(FILE_DEVICE_UNKNOWN, WANPIPE_IOCTL_LOGGER_CMD, METHOD_BUFFERED, FILE_ANY_ACCESS) + #endif /* __WINDOWS__ */ /* @@ -558,12 +643,24 @@ typedef struct { int pci_bus_number; int pci_slot_number; /* Number of HW Echo Canceller channels. - Zero means HW Echo Canceller not installed the card. */ + Zero means HW Echo Canceller not installed the card. */ int max_hw_ec_chans; - /* Port's number on a card (zero based). */ + /* Port's number on a card + For T1/E1: Indicates FE_LINE number starting from 1 + For Analog: Always set to 1. (Not used) (use fxo/fxs map instead) + For BRI: Indicats module number starting from 1 + For S514: Indicates Primary or Secondary Port + */ int port_number; char serial_number[CARD_SERIAL_NUMBER_LENGTH]; /* Not implemented, for future use. */ + + unsigned int chans_map; /* bitmap of available TDM slots */ + unsigned int fxo_map; /* bitmap of available fxo TDM slots, if analog port */ + unsigned int fxs_map; /* bitmap of available fxs TDM slots, if analog port */ + int max_chans_num; /* max number of TDM slots */ + int bri_modtype; /* which BRI type (MOD_TYPE_NT, MOD_TYPE_TE), if BRI port */ + }hardware_info_t; typedef struct{ diff --git a/patches/kdrivers/include/aft_core_utils.h b/patches/kdrivers/include/aft_core_utils.h index b16ee14..a53f2b2 100644 --- a/patches/kdrivers/include/aft_core_utils.h +++ b/patches/kdrivers/include/aft_core_utils.h @@ -44,6 +44,8 @@ int aft_tdmapi_mtu_check(sdla_t *card_ptr, unsigned int *mtu); int aft_devel_ioctl(sdla_t *card, struct ifreq *ifr); +void aft_tx_fifo_under_recover (sdla_t *card, private_area_t *chan); + unsigned char aft_write_ec (void *pcard, unsigned short off, unsigned char value); unsigned char aft_read_cpld(sdla_t *card, unsigned short cpld_off); unsigned char aft_read_ec (void *pcard, unsigned short off); @@ -78,51 +80,100 @@ int wan_user_process_udp_mgmt_pkt(void* card_ptr, void* chan_ptr, void *udata); *================================================================*/ #if defined WANPIPE_PERFORMANCE_DEBUG -#warning "WANPIPE_PERFORMANCE_DEBUG Enabled" + static __inline int aft_calc_elapsed(struct timeval *started, struct timeval *ended) { -#if 0 - return (((ended->tv_sec * 1000) + ended->tv_usec / 1000) - - ((started->tv_sec * 1000) + started->tv_usec / 1000)); -#else - return ended->tv_usec - started->tv_usec; -#endif + if (started->tv_usec == 0) { + return 0; + } -} + if (ended->tv_usec > started->tv_usec) { + return ended->tv_usec - started->tv_usec; + } else { + return 0; + } +} -static int __inline aft_timing_start(sdla_t * card) +#define AFT_PERFT_TIMING_START(card,var) aft_timing_start(&card->aft_perf_stats.var) + +static int __inline aft_timing_start(aft_driver_timing_t *drv_timing) { -#if 1 -#if defined(__LINUX__) - do_gettimeofday(&card->timing_tv); -#endif -#endif + do_gettimeofday(&drv_timing->timing_tv); return 0; } -static int __inline aft_timing_stop_calculate_elapsed(sdla_t * card) +#define AFT_PERFT_TIMING_STOP_AND_CALC(card,var) aft_timing_stop_calculate_elapsed(&card->aft_perf_stats.var) + +static int __inline aft_timing_stop_calculate_elapsed(aft_driver_timing_t *drv_timing) { -#if 1 -#if defined(__LINUX__) - int elapsed=0; + + unsigned long elapsed=0; + unsigned long cum=0; + int i; + int div=0; + int limit=0; + struct timeval current_tv; do_gettimeofday(¤t_tv); - elapsed=aft_calc_elapsed(&card->timing_tv,¤t_tv); - if (elapsed > card->wandev.stats.rx_errors) { - card->wandev.stats.rx_errors=elapsed; - } - if (elapsed > 1000) { - DEBUG_EVENT("%s: Error: Timeout is huge %i\n",card->devname, elapsed); - } - if (card->wandev.stats.rx_errors > 2500) { - card->wandev.stats.rx_errors=0; + elapsed=aft_calc_elapsed(&drv_timing->timing_tv,¤t_tv); + + if (elapsed == 0) { + return 0; + } + + if (elapsed > drv_timing->max_latency) { + drv_timing->max_latency=elapsed; + } + if (drv_timing->min_latency == 0 || drv_timing->min_latency > elapsed) { + drv_timing->min_latency=elapsed; + } + drv_timing->latency=elapsed; + + drv_timing->sma[drv_timing->sma_idx] = elapsed; + drv_timing->sma_idx++; + if (drv_timing->sma_idx >= MAX_SMA_IDX) { + drv_timing->sma_idx=0; + } + + for (i=0;isma[i] == 0) { + continue; + } + cum+=drv_timing->sma[i]; + div++; + } + + if (div) { + cum=cum/div; + } + + drv_timing->latency_avg = cum; + + if (drv_timing->latency_avg > 19000) { + limit=2000; + } else if (drv_timing->latency_avg > 9000) { + limit=1000; + } else if (drv_timing->latency_avg < 1000) { + limit=8; + } + + drv_timing->limit=limit; + + if (elapsed > drv_timing->latency_avg + limit) { + drv_timing->above_avg++; + } + if (drv_timing->latency_avg > limit) { + if (drv_timing->latency_avg - limit > elapsed) { + drv_timing->below_avg++; + } } - card->wandev.stats.tx_errors=elapsed; -#endif -#endif return 0; } + +#else +#define AFT_PERFT_TIMING_START(card,var) +#define AFT_PERFT_TIMING_STOP_AND_CALC(card,var) #endif diff --git a/patches/kdrivers/include/if_wanpipe_common.h b/patches/kdrivers/include/if_wanpipe_common.h index 7f1cd18..e7f4fca 100644 --- a/patches/kdrivers/include/if_wanpipe_common.h +++ b/patches/kdrivers/include/if_wanpipe_common.h @@ -51,7 +51,7 @@ typedef struct { #endif } wanpipe_common_iface_t; -typedef struct { +typedef struct wanpipe_common { #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) /* !!! IMPORTANT !!! <- Do not move this parameter (GENERIC-PPP) */ void *prot_ptr; @@ -470,6 +470,14 @@ void wp_debug_func_init(void) #endif +#if defined(__WINDOWS__) +extern int wanpipe_lip_rx(void *chan, void *sk_id); +extern int wanpipe_lip_connect(void *chan, int ); +extern int wanpipe_lip_disconnect(void *chan, int); +extern int wanpipe_lip_kick(void *chan,int); +extern int wanpipe_lip_get_if_status(void *chan, void *m); +#endif + #endif /* WAN_KERNEL */ diff --git a/patches/kdrivers/include/sdla_a600_remora.h b/patches/kdrivers/include/sdla_a600_remora.h index bfcccc3..3531c92 100644 --- a/patches/kdrivers/include/sdla_a600_remora.h +++ b/patches/kdrivers/include/sdla_a600_remora.h @@ -10,7 +10,7 @@ * ** as published by the Free Software Foundation; either version * ** 2 of the License, or (at your option) any later version. * ** ============================================================================ - * ** Oct 6, 2005 Alex Feldman Initial version. + * ** Nov , 2008 David Yat Sin Initial version * *******************************************************************************/ @@ -51,5 +51,16 @@ extern int wp_a600_iface_init(void*, void*); #define IS_A600(fe) (((sdla_t*)(fe->card))->adptr_type == AFT_ADPTR_A600) #define IS_A600_CARD(card) (card->adptr_type == AFT_ADPTR_A600) +#define IS_B601(fe) (((sdla_t*)(fe->card))->adptr_type == AFT_ADPTR_B601) +#define IS_B601_CARD(card) (card->adptr_type == AFT_ADPTR_B601) +#define IS_B601_TE1_CARD(card) (card->adptr_type == AFT_ADPTR_B601 && card->wandev.comm_port == 1) + +#define A600_MAXIM_INTERFACE_REG_ADD_LO 0x1044 +#define A600_MAXIM_INTERFACE_REG_ADD_HI 0x1046 + +#define A600_FIRST_LINE_CFG_ADD 0x2100 + +#define A600_FIRST_LINE_CFG_RESET_BIT 0x01 + #endif /* SDLA_A600_H */ - + diff --git a/patches/kdrivers/include/sdla_adsl.h b/patches/kdrivers/include/sdla_adsl.h index ae84791..5d327d1 100644 --- a/patches/kdrivers/include/sdla_adsl.h +++ b/patches/kdrivers/include/sdla_adsl.h @@ -68,7 +68,7 @@ typedef struct adsl_cfg { typedef struct adsl_private_area { - wanpipe_common_t common; + wanpipe_common_t common;/* MUST be at the top */ void* pAdapter; char if_name[WAN_IFNAME_SZ]; u_char macAddr[6]; diff --git a/patches/kdrivers/include/sdla_remora.h b/patches/kdrivers/include/sdla_remora.h index 9bb94b8..c47bb68 100644 --- a/patches/kdrivers/include/sdla_remora.h +++ b/patches/kdrivers/include/sdla_remora.h @@ -17,9 +17,9 @@ # define __SDLA_REMORA_H #ifdef __SDLA_REMORA_SRC -# define EXTERN +# define WP_EXTERN #else -# define EXTERN extern +# define WP_EXTERN extern #endif # include "aft_core_options.h" @@ -165,6 +165,7 @@ typedef struct sdla_remora_cfg_ { int fxs_ringampl; u_int8_t rm_mode; /*Analog Operation mode: default or tapping */ u_int8_t fake_polarity; + u_int8_t rm_lcm; /*Analog Loop Current Measure (LCM) : Yes Or NO */ } sdla_remora_cfg_t; typedef struct { @@ -333,6 +334,7 @@ typedef struct { int offhook; /* Xswitch */ int battery; /* Xswitch */ int battdebounce; /* Xswitch */ + int i_debounce; int ringdebounce; int wasringing; int nobatttimer; @@ -417,6 +419,7 @@ typedef struct { int polarity; /* SETPOLARITY */ unsigned short reg; /* fe register */ unsigned char value; /* fe register value */ + int rm_gain; /* Tx/Rx Gain */ } sdla_rm_event_t; typedef struct sdla_remora_param { @@ -443,7 +446,7 @@ typedef struct sdla_remora_param { unsigned char reg0shadow[MAX_REMORA_MODULES]; /* read> fxs: 68 fxo: 5 */ unsigned char reg1shadow[MAX_REMORA_MODULES]; /* read> fxs: 64 fxo: 29 */ unsigned char reg2shadow[MAX_REMORA_MODULES]; /* read> fxs: 64 fxo: 29 */ - unsigned char reg3shadow[MAX_REMORA_MODULES]; /* read > fxs : 19 for Ring/Trip Evnet , FXO no use yet */ + unsigned char reg3shadow[MAX_REMORA_MODULES]; /* read > fxs : 19 for Ring/Trip Evnet , FXO -used LCS2 (read 28) */ unsigned char reg4shadow[MAX_REMORA_MODULES]; /* read > fxs : 20 for DTMF Evnet , FXO no use yet */ unsigned char reg0shadow_write[MAX_REMORA_MODULES]; /* write> fxs: 68 fxo: 5 */ @@ -463,6 +466,6 @@ typedef struct sdla_remora_param { extern int wp_remora_iface_init(void*, void*); extern int wp_a700_remora_iface_init(void*, void*); -#undef EXTERN +#undef WP_EXTERN #endif /* __SDLA_REMORA_H */ diff --git a/patches/kdrivers/include/sdla_tdmv.h b/patches/kdrivers/include/sdla_tdmv.h index 60efca3..a5b162b 100644 --- a/patches/kdrivers/include/sdla_tdmv.h +++ b/patches/kdrivers/include/sdla_tdmv.h @@ -17,9 +17,9 @@ # define __SDLA_TDMV_H #ifdef __SDLA_TDMV_SRC -# define EXTERN +# define WP_EXTERN #else -# define EXTERN extern +# define WP_EXTERN extern #endif @@ -102,26 +102,26 @@ typedef struct wan_tdmv_iface_ /****************************************************************************** ** FUNCTION PROTOTYPES ******************************************************************************/ -EXTERN int wp_tdmv_te1_init(wan_tdmv_iface_t *iface); -EXTERN int wp_tdmv_remora_init(wan_tdmv_iface_t *iface); -EXTERN int wp_tdmv_bri_init(wan_tdmv_iface_t *iface); +WP_EXTERN int wp_tdmv_te1_init(wan_tdmv_iface_t *iface); +WP_EXTERN int wp_tdmv_remora_init(wan_tdmv_iface_t *iface); +WP_EXTERN int wp_tdmv_bri_init(wan_tdmv_iface_t *iface); #if defined(CONFIG_PRODUCT_WANPIPE_USB) -EXTERN int wp_usb_tdmv_remora_init(wan_tdmv_iface_t *iface); +WP_EXTERN int wp_usb_tdmv_remora_init(wan_tdmv_iface_t *iface); #endif #ifdef CONFIG_PRODUCT_WANPIPE_TDM_VOICE_ECHOMASTER -EXTERN int wp_tdmv_echo_check(wan_tdmv_t *wan_tdmv, void *current_ztchan, int channo); +WP_EXTERN int wp_tdmv_echo_check(wan_tdmv_t *wan_tdmv, void *current_ztchan, int channo); #endif -EXTERN int wanpipe_codec_convert_2s(u8 *data, int len, netskb_t *nskb, +WP_EXTERN int wanpipe_codec_convert_2s(u8 *data, int len, netskb_t *nskb, u16 *power_ptr, int is_alaw); -EXTERN int wanpipe_codec_convert_s2ulaw(netskb_t *skb, netskb_t *nskb, int is_alaw); +WP_EXTERN int wanpipe_codec_convert_s2ulaw(netskb_t *skb, netskb_t *nskb, int is_alaw); #endif /* WAN_KERNEL */ -#undef EXTERN +#undef WP_EXTERN #endif /* __SDLA_VOIP_H */ diff --git a/patches/kdrivers/include/sdla_te1.h b/patches/kdrivers/include/sdla_te1.h index 23a19a8..991ad47 100644 --- a/patches/kdrivers/include/sdla_te1.h +++ b/patches/kdrivers/include/sdla_te1.h @@ -45,9 +45,9 @@ # define _SDLA_TE1_H #ifdef SDLA_TE1 -# define EXTERN +# define WP_EXTERN #else -# define EXTERN extern +# define WP_EXTERN extern #endif /************************************************************************ @@ -111,6 +111,37 @@ #define IS_TE_ALARM_RAI(alarm) IS_TE_ALARM(alarm, WAN_TE_BIT_ALARM_RAI) #define IS_TE_ALARM_YEL(alarm) IS_TE_ALARM(alarm, WAN_TE_BIT_ALARM_YEL) +/* Needed for backward compatibility */ +#ifndef IS_TE_ALOS_ALARM +#define IS_TE_ALOS_ALARM IS_TE_ALARM_ALOS +#endif + +#ifndef IS_TE_LOS_ALARM +#define IS_TE_LOS_ALARM IS_TE_ALARM_LOS +#endif + +#ifndef IS_TE_RED_ALARM +#define IS_TE_RED_ALARM IS_TE_ALARM_RED +#endif + +#ifndef IS_TE_AIS_ALARM +#define IS_TE_AIS_ALARM IS_TE_ALARM_AIS +#endif + + +#ifndef IS_TE_RAI_ALARM +#define IS_TE_RAI_ALARM IS_TE_ALARM_RAI +#endif + + +#ifndef IS_TE_YEL_ALARM +#define IS_TE_YEL_ALARM IS_TE_ALARM_YEL +#endif + +#ifndef IS_TE_OOF_ALARM +#define IS_TE_OOF_ALARM IS_TE_ALARM_OOF +#endif + /* Performance monitor counters bit mask */ #define WAN_TE_BIT_PMON_LCV 0x01 /* line code violation counter */ #define WAN_TE_BIT_PMON_BEE 0x02 /* bit errror event (T1) */ @@ -169,17 +200,18 @@ #define WAN_T1_SHORT_HAUL 0x02 /* Line loopback modes */ -#define WAN_TE1_LB_NONE 0x00 -#define WAN_TE1_LINELB_MODE 0x01 -#define WAN_TE1_PAYLB_MODE 0x02 -#define WAN_TE1_DDLB_MODE 0x03 +#define WAN_TE1_LB_NONE 0x00 +#define WAN_TE1_LINELB_MODE 0x01 +#define WAN_TE1_PAYLB_MODE 0x02 +#define WAN_TE1_DDLB_MODE 0x03 #define WAN_TE1_TX_LINELB_MODE 0x04 #define WAN_TE1_LIU_ALB_MODE 0x05 #define WAN_TE1_LIU_LLB_MODE 0x06 #define WAN_TE1_LIU_RLB_MODE 0x07 #define WAN_TE1_LIU_DLB_MODE 0x08 #define WAN_TE1_TX_PAYLB_MODE 0x09 -#define WAN_TE1_PCLB_MODE 0x0A +#define WAN_TE1_PCLB_MODE 0x0A + #define WAN_TE1_LB_MODE_DECODE(mode) \ ((mode) == WAN_TE1_LINELB_MODE) ? "Line/Remote Loopback" : \ ((mode) == WAN_TE1_PAYLB_MODE) ? "Payload Loopback" : \ @@ -593,7 +625,10 @@ typedef struct /* Connection status threshold */ /* Original 5 ** Note (May 7 2009: We are waiting at least 10 sec anyway for other alarms */ -#define WAN_TE1_STATUS_THRESHOLD 1 +#define WAN_TE1_STATUS_THRESHOLD 3 /* Feb 18, 2010. + * DavidR: changed to 3 from 1 to + * avoid line bouncing during port + * startup. */ #define WAN_TE1_LBO(fe) FE_LBO(&((fe)->fe_cfg)) #define WAN_TE1_CLK(fe) FE_CLK(&((fe)->fe_cfg)) @@ -759,15 +794,51 @@ typedef struct { ****************************************************************************** */ -EXTERN int sdla_te_default_cfg(void* pfe, void* fe_cfg, int media); -EXTERN int sdla_te_copycfg(void* pfe, void* fe_cfg); +WP_EXTERN int sdla_te_default_cfg(void* pfe, void* fe_cfg, int media); +WP_EXTERN int sdla_te_copycfg(void* pfe, void* fe_cfg); -EXTERN int sdla_te_iface_init(void *p_fe, void *p_fe_iface); -EXTERN int sdla_ds_te1_iface_init(void *p_fe, void *p_fe_iface); +WP_EXTERN int sdla_te_iface_init(void *p_fe, void *p_fe_iface); +WP_EXTERN int sdla_ds_te1_iface_init(void *p_fe, void *p_fe_iface); #endif /* WAN_KERNEL */ -#undef EXTERN +#undef WP_EXTERN + + +/* Deprecated defines */ +#define WAN_TE_ALARM_MASK_FRAMER WAN_TE_ALARM_FRAMER_MASK +#define WAN_TE_ALARM_MASK_LIU WAN_TE_ALARM_LIU_MASK +#define WAN_TE_BIT_ALOS_ALARM WAN_TE_BIT_ALARM_ALOS +#define WAN_TE_BIT_LOS_ALARM WAN_TE_BIT_ALARM_LOS +#define WAN_TE_BIT_ALTLOS_ALARM WAN_TE_BIT_ALARM_ALTLOS +#define WAN_TE_BIT_OOF_ALARM WAN_TE_BIT_ALARM_OOF +#define WAN_TE_BIT_RED_ALARM WAN_TE_BIT_ALARM_RED +#define WAN_TE_BIT_AIS_ALARM WAN_TE_BIT_ALARM_AIS +#define WAN_TE_BIT_OOSMF_ALARM WAN_TE_BIT_ALARM_OOSMF +#define WAN_TE_BIT_OOCMF_ALARM WAN_TE_BIT_ALARM_OOCMF +#define WAN_TE_BIT_OOOF_ALARM WAN_TE_BIT_ALARM_OOOF +#define WAN_TE_BIT_RAI_ALARM WAN_TE_BIT_ALARM_RAI +#define WAN_TE_BIT_YEL_ALARM WAN_TE_BIT_ALARM_YEL +#define WAN_TE_BIT_LIU_ALARM WAN_TE_BIT_ALARM_LIU +#define WAN_TE_BIT_LIU_ALARM_SC WAN_TE_BIT_ALARM_LIU_SC +#define WAN_TE_BIT_LIU_ALARM_OC WAN_TE_BIT_ALARM_LIU_OC +#define WAN_TE_BIT_LIU_ALARM_LOS WAN_TE_BIT_ALARM_LIU_LOS + +#define WAN_TE_ALARM(alarm, bit) ((alarm) & (bit)) ? "ON" : "OFF" + +#define WAN_TE_ALOS_ALARM(alarm) WAN_TE_ALARM(alarm, WAN_TE_BIT_ALOS_ALARM) +#define WAN_TE_LOS_ALARM(alarm) WAN_TE_ALARM(alarm, WAN_TE_BIT_LOS_ALARM) +#define WAN_TE_OOF_ALARM(alarm) WAN_TE_ALARM(alarm, WAN_TE_BIT_OOF_ALARM) +#define WAN_TE_RED_ALARM(alarm) WAN_TE_ALARM(alarm, WAN_TE_BIT_RED_ALARM) +#define WAN_TE_AIS_ALARM(alarm) WAN_TE_ALARM(alarm, WAN_TE_BIT_AIS_ALARM) +#define WAN_TE_OOSMF_ALARM(alarm) WAN_TE_ALARM(alarm, WAN_TE_BIT_OOSMF_ALARM) +#define WAN_TE_OOCMF_ALARM(alarm) WAN_TE_ALARM(alarm, WAN_TE_BIT_OOCMF_ALARM) +#define WAN_TE_OOOF_ALARM(alarm) WAN_TE_ALARM(alarm, WAN_TE_BIT_OOOF_ALARM) +#define WAN_TE_RAI_ALARM(alarm) WAN_TE_ALARM(alarm, WAN_TE_BIT_RAI_ALARM) +#define WAN_TE_YEL_ALARM(alarm) WAN_TE_ALARM(alarm, WAN_TE_BIT_YEL_ALARM) +#define WAN_TE_LIU_ALARM_SC(alarm) WAN_TE_ALARM(alarm, WAN_TE_BIT_LIU_ALARM_SC) +#define WAN_TE_LIU_ALARM_OC(alarm) WAN_TE_ALARM(alarm, WAN_TE_BIT_LIU_ALARM_OC) +#define WAN_TE_LIU_ALARM_LOS(alarm) WAN_TE_ALARM(alarm, WAN_TE_BIT_LIU_ALARM_LOS) #endif /* _SDLA_TE1_H */ diff --git a/patches/kdrivers/include/sdla_te1_ds.h b/patches/kdrivers/include/sdla_te1_ds.h index d90d4be..5fabff8 100644 --- a/patches/kdrivers/include/sdla_te1_ds.h +++ b/patches/kdrivers/include/sdla_te1_ds.h @@ -621,6 +621,7 @@ #define BIT_TCR2_T1_TSLC96 0x40 #define BIT_TCR2_T1_TD4RM 0x04 #define BIT_TCR2_E1_AEBE 0x80 /* EBIT */ +#define BIT_TCR2_E1_ARA 0x20 #define REG_TCR3 0x183 #define BIT_TCR3_ODF 0x80 diff --git a/patches/kdrivers/include/sdla_te3.h b/patches/kdrivers/include/sdla_te3.h index 3e478ab..7a2e0cc 100644 --- a/patches/kdrivers/include/sdla_te3.h +++ b/patches/kdrivers/include/sdla_te3.h @@ -105,6 +105,7 @@ typedef struct { #if defined(WAN_KERNEL) typedef struct { + unsigned long critical; int dummy; u_int8_t cpld_cntrl; u_int8_t cpld_status; diff --git a/patches/kdrivers/include/sdla_x25.h b/patches/kdrivers/include/sdla_x25.h index e1f5a8e..30920fb 100644 --- a/patches/kdrivers/include/sdla_x25.h +++ b/patches/kdrivers/include/sdla_x25.h @@ -759,14 +759,14 @@ enum {UDP_XPIPE_TYPE}; #define XPIPE_ENABLE_TRACING 0x14 #define XPIPE_DISABLE_TRACING 0x14 #define XPIPE_GET_TRACE_INFO 0x16 -#define XPIPE_FT1_READ_STATUS 0x74 -#define XPIPE_DRIVER_STAT_IFSEND 0x75 -#define XPIPE_DRIVER_STAT_INTR 0x76 -#define XPIPE_DRIVER_STAT_GEN 0x77 -#define XPIPE_FLUSH_DRIVER_STATS 0x78 -#define XPIPE_ROUTER_UP_TIME 0x79 -#define XPIPE_SET_FT1_MODE 0x81 -#define XPIPE_FT1_STATUS_CTRL 0x80 +#define XPIPE_FT1_READ_STATUS 0x90 +#define XPIPE_DRIVER_STAT_IFSEND 0x91 +#define XPIPE_DRIVER_STAT_INTR 0x92 +#define XPIPE_DRIVER_STAT_GEN 0x93 +#define XPIPE_FLUSH_DRIVER_STATS 0x94 +#define XPIPE_ROUTER_UP_TIME 0x95 +#define XPIPE_SET_FT1_MODE 0x96 +#define XPIPE_FT1_STATUS_CTRL 0x97 /* error messages */ diff --git a/patches/kdrivers/include/sdladrv.h b/patches/kdrivers/include/sdladrv.h index 149e2a1..625b2b3 100644 --- a/patches/kdrivers/include/sdladrv.h +++ b/patches/kdrivers/include/sdladrv.h @@ -50,9 +50,9 @@ #ifndef _SDLADRV_H # define _SDLADRV_H #ifdef __SDLADRV__ -# define EXTERN +# define WP_EXTERN #else -# define EXTERN extern +# define WP_EXTERN extern #endif #if defined(__LINUX__) @@ -444,14 +444,13 @@ typedef struct sdlahw_usb_ struct usb_interface *usb_intf; # endif -//////////////////////////////// - int urbcount_read; + int urbcount_read; int urb_read_ind; struct wan_urb dataread[MAX_READ_URB_COUNT]; - int urbcount_write; + int urbcount_write; int urb_write_ind; - struct wan_urb datawrite[MAX_WRITE_URB_COUNT]; + struct wan_urb datawrite[MAX_WRITE_URB_COUNT]; char readchunk[2][WP_USB_MAX_CHUNKSIZE * 2+1]; char writechunk[2][WP_USB_MAX_CHUNKSIZE * 2+1]; @@ -471,13 +470,13 @@ typedef struct sdlahw_usb_ int rx_sync; int next_rx_ind; int next_read_ind; - char readbuf[MAX_READ_BUF_LEN+MAX_USB_RX_LEN+10]; // 10 for safety + char readbuf[MAX_READ_BUF_LEN+MAX_USB_RX_LEN+10]; // 10 for safety int next_tx_ind; int next_write_ind; - char writebuf[MAX_WRITE_BUF_LEN+MAX_USB_TX_LEN+10]; // 10 for safety + char writebuf[MAX_WRITE_BUF_LEN+MAX_USB_TX_LEN+10]; // 10 for safety - char idlebuf[MAX_USB_TX_LEN+1]; + char idlebuf[MAX_USB_TX_LEN+1]; wan_tasklet_t bh_task; @@ -493,10 +492,10 @@ typedef struct sdlahw_usb_ void (*isr_func)(void*); void *isr_arg; - // Statistics + /* Statistics */ sdla_usb_comm_err_stats_t stats; -/////////////////////////////// + } sdlahw_usb_t; #else #define WP_USB_BUSID(hwcard) "usb not supported" @@ -622,6 +621,9 @@ typedef struct sdlahw_dev int max_chans_num; /* maximum number of channels (timeslots) */ u_int32_t chans_map; /* channels map per hw device (A200/A400/ISDN) */ + u_int32_t fxo_map; /* FXO channels map per hw device (A200/A400) */ + u_int32_t fxs_map; /* FXS channels map per hw device (A200/A400) */ + int bri_modtype; /* BRI type for hw device (ISDN) */ int max_port_no; sdlahw_port_t hwport[SDLA_MAX_HWPORTS]; @@ -742,6 +744,7 @@ typedef struct sdla_hw_type_cnt unsigned char aft_56k_adapters; unsigned char aft_serial_adapters; unsigned char aft_a600_adapters; + unsigned char aft_b601_adapters; unsigned char aft_a700_adapters; unsigned char aft_x_adapters; @@ -763,17 +766,17 @@ typedef struct sdladrv_hw_probe_iface { /****** Function Prototypes *************************************************/ -EXTERN int sdladrv_hw_mode(int); -EXTERN unsigned int sdla_hw_probe(void); -EXTERN unsigned int sdla_hw_bridge_probe(void); -EXTERN void *sdla_get_hw_probe (void); -EXTERN void *sdla_get_hw_adptr_cnt(void); -EXTERN void* sdla_register (sdlahw_iface_t* hw_iface, wandev_conf_t*,char*); -EXTERN int sdla_unregister (void**, char*); +WP_EXTERN int sdladrv_hw_mode(int); +WP_EXTERN unsigned int sdla_hw_probe(void); +WP_EXTERN unsigned int sdla_hw_bridge_probe(void); +WP_EXTERN void *sdla_get_hw_probe (void); +WP_EXTERN void *sdla_get_hw_adptr_cnt(void); +WP_EXTERN void* sdla_register (sdlahw_iface_t* hw_iface, wandev_conf_t*,char*); +WP_EXTERN int sdla_unregister (void**, char*); #if defined(CONFIG_PRODUCT_WANPIPE_USB) -EXTERN int sdla_get_hw_usb_adptr_cnt(void); +WP_EXTERN int sdla_get_hw_usb_adptr_cnt(void); #endif -EXTERN int sdla_get_hwinfo(hardware_info_t *hwinfo, int card_no); +WP_EXTERN int sdla_get_hwinfo(hardware_info_t *hwinfo, int card_no); #ifdef __SDLADRV__ @@ -1226,7 +1229,8 @@ static __inline u32 SDLA_REG_OFF(sdlahw_card_t *hwcard, u32 reg) return reg; } - if (hwcard->adptr_type == AFT_ADPTR_A600) { + if (hwcard->adptr_type == AFT_ADPTR_A600 || + hwcard->adptr_type == AFT_ADPTR_B601) { if (reg < 0x100) { return (reg+0x1000); } else { @@ -1238,5 +1242,5 @@ static __inline u32 SDLA_REG_OFF(sdlahw_card_t *hwcard, u32 reg) } -#undef EXTERN +#undef WP_EXTERN #endif /* _SDLADRV_H */ diff --git a/patches/kdrivers/include/sdlapci.h b/patches/kdrivers/include/sdlapci.h index 28d1eac..103f7e2 100644 --- a/patches/kdrivers/include/sdlapci.h +++ b/patches/kdrivers/include/sdlapci.h @@ -86,6 +86,7 @@ #define AFT_56K_SHARK_SUBSYS_VENDOR 0xA056 /* AFT-56K SHARK board */ #define AFT_A600_SUBSYS_VENDOR 0xA600 /* AFT-600 Series board */ +#define AFT_B601_SUBSYS_VENDOR 0xA601 /* AFT-600 Series board */ #define AFT_2SERIAL_V35X21_SUBSYS_VENDOR 0xA031 /* AFT-A142 2 Port V.35/X.21 board */ #define AFT_4SERIAL_V35X21_SUBSYS_VENDOR 0xA032 /* AFT-A144 4 Port V.35/X.21 board */ @@ -147,6 +148,8 @@ #define PCI_MAP1_DWORD 0x44 /* PCI to local bus address 1 */ #define PCI_INT_STATUS 0x48 /* interrupt status */ #define PCI_INT_CONFIG 0x4C /* interrupt configuration */ +#define PCI_CORE_REV_REG 0xFC /* firmware version */ +#define WAN_INVALID_FIRMWARE 0x42 /* Implies the card is using new style firmware version */ /* Local PCI register usage */ #define PCI_MEMORY_ENABLE 0x00000003 /* enable PCI memory */ diff --git a/patches/kdrivers/include/sdlasfm.h b/patches/kdrivers/include/sdlasfm.h index d880984..0695078 100644 --- a/patches/kdrivers/include/sdlasfm.h +++ b/patches/kdrivers/include/sdlasfm.h @@ -144,7 +144,8 @@ enum { AFT_ADPTR_2SERIAL_RS232, /* AFT-A142 2 Port RS232 board */ AFT_ADPTR_4SERIAL_RS232, /* AFT-A144 4 Port RS232 board */ U100_ADPTR, /* USB board */ - AFT_ADPTR_A600, /* AFT A600 board */ + AFT_ADPTR_A600, /* AFT-A600 board */ + AFT_ADPTR_B601, /* AFT-B601 board */ AFT_ADPTR_FLEXBRI, /* AFT-A700 FlexBRI board */ AFT_ADPTR_LAST /* NOTE: Keep it as a last line */ @@ -266,9 +267,18 @@ enum { ((val) == AFT_RM_SECURITY_16_ECCHAN) ? 16 : \ ((val) == AFT_RM_SECURITY_32_ECCHAN) ? 32 : 0 -#define AFT_600_SECURITY_05_ECCHAN 0x01 +#define AFT_A600_SECURITY_00_ECCHAN 0x00 +#define AFT_A600_SECURITY_05_ECCHAN 0x01 #define A600_ECCHAN(val) \ - ((val) == AFT_600_SECURITY_05_ECCHAN) ? 05 : 0 + ((val) == AFT_A600_SECURITY_00_ECCHAN) ? 0 : \ + ((val) == AFT_A600_SECURITY_05_ECCHAN) ? 5 : 0 + +#define AFT_B601_SECURITY_00_ECCHAN 0x00 +#define AFT_B601_SECURITY_64_ECCHAN 0x01 +#define B601_ECCHAN(val) \ + ((val) == AFT_B601_SECURITY_00_ECCHAN) ? 0 : \ + ((val) == AFT_B601_SECURITY_64_ECCHAN) ? 64 : 0 + #define SDLA_ADPTR_NAME(adapter_type) \ (adapter_type == S5141_ADPTR_1_CPU_SERIAL) ? "S514-1-PCI" : \ @@ -293,6 +303,7 @@ enum { (adapter_type == AFT_ADPTR_4SERIAL_RS232) ? "AFT-A144" : \ (adapter_type == U100_ADPTR) ? "U100" : \ (adapter_type == AFT_ADPTR_A600) ? "AFT-B600" : \ + (adapter_type == AFT_ADPTR_B601) ? "AFT-B601" : \ (adapter_type == AFT_ADPTR_FLEXBRI) ? "AFT-B700" : \ "UNKNOWN" diff --git a/patches/kdrivers/include/wan_mem_debug.h b/patches/kdrivers/include/wan_mem_debug.h index 5ecd5a7..a798f10 100644 --- a/patches/kdrivers/include/wan_mem_debug.h +++ b/patches/kdrivers/include/wan_mem_debug.h @@ -1,4 +1,4 @@ - +/* wan_mem_debug.h */ #ifndef __WAN_MEMDEBUG_H_ #define __WAN_MEMDEBUG_H_ diff --git a/patches/kdrivers/include/wanpipe.h b/patches/kdrivers/include/wanpipe.h index cfd8a75..9f2914e 100644 --- a/patches/kdrivers/include/wanpipe.h +++ b/patches/kdrivers/include/wanpipe.h @@ -620,7 +620,8 @@ typedef struct unsigned long active_ch_map; unsigned long fifo_addr_map; unsigned long fifo_addr_map_l2; - wan_timer_t led_timer; + wan_timer_t bg_timer; + unsigned int bg_timer_cmd; unsigned char tdmv_sync; unsigned int chip_cfg_status; wan_taskq_t port_task; @@ -684,7 +685,8 @@ enum { AFT_TDM_FAST_ISR, AFT_TDM_SW_RING_BUF, AFT_TDM_RING_SYNC_RESET, - AFT_TDM_FREE_RUN_ISR + AFT_TDM_FREE_RUN_ISR, + AFT_TDM_FE_SYNC_CNT }; typedef struct @@ -907,22 +909,27 @@ typedef struct sdla PDMA_ADAPTER DmaAdapterObject; /* Object for allocating memory for DMA. * Can NOT be called from spin-locked code!! * (not from regular lock too) */ +#if 0 wan_debug_t wan_debug_rx_interrupt_timing; wan_debug_t wan_debug_tx_interrupt_timing; +#endif #endif void *tdm_api_dev; void *tdm_api_span; - unsigned char wp_debug_gen_fifo_err; + unsigned int wp_debug_gen_fifo_err_tx; + unsigned int wp_debug_gen_fifo_err_rx; unsigned char wp_debug_chan_seq; - + unsigned int wp_rx_fifo_sanity; + unsigned int wp_tx_fifo_sanity; + #if defined(WANPIPE_PERFORMANCE_DEBUG) - wan_ticks_t debug_timeout; - struct timeval timing_tv; + aft_driver_performance_stats_t aft_perf_stats; #endif } sdla_t; + /****** Public Functions ****************************************************/ void wanpipe_open (sdla_t* card); /* wpmain.c */ diff --git a/patches/kdrivers/include/wanpipe_abstr_types.h b/patches/kdrivers/include/wanpipe_abstr_types.h index ce2eb92..b34ac00 100644 --- a/patches/kdrivers/include/wanpipe_abstr_types.h +++ b/patches/kdrivers/include/wanpipe_abstr_types.h @@ -1,12 +1,21 @@ -/************************************************************************* -* wanpipe_abstr_types.h WANPIPE(tm) * -* * -* Wanpipe Kernel Abstraction type definitions * -* * -* * -* Author: Alex Feldman * -*========================================================================* -* Jan 24, 2008 Alex Feldman Initial version * +/************************************************************************ +* wanpipe_abstr_types.h WANPIPE(tm) * +* * +* Wanpipe Kernel Abstraction type definitions * +* * +* * +* Authors: Alex Feldman * + David Rokhvarg * +*=======================================================================* +* * +* Nov 02, 2009 David Rokhvarg * +* Moved definitions for Sangoma MS Windows driver from * +* wanpipe_ctypes.h to this file. The wanpipe_ctypes.h is * +* not removed, but it will include this file for backward * +* compatibility with existing source code. * +* * +* Jan 24, 2008 Alex Feldman * +* Initial version * *************************************************************************/ #ifndef __WANPIPE_ABSTR_TYPES_H @@ -17,29 +26,140 @@ #if defined(__FreeBSD__) /******************* F R E E B S D ******************************/ typedef int wan_ticks_t; -typedef unsigned long ulong_ptr_t +typedef unsigned long ulong_ptr_t; +typedef long long_t; +typedef unsigned long ulong_t; +#define wan_timeval timeval +#define wan_timeval_t struct timeval #elif defined(__OpenBSD__) /******************* O P E N B S D ******************************/ typedef int wan_ticks_t; -typedef unsigned long ulong_ptr_t +typedef unsigned long ulong_ptr_t; +typedef long long_t; +typedef unsigned long ulong_t; +#define wan_timeval timeval +#define wan_timeval_t struct timeval #elif defined(__NetBSD__) /******************* N E T B S D ******************************/ typedef int wan_ticks_t; -typedef unsigned long ulong_ptr_t +typedef unsigned long ulong_ptr_t; +typedef long long_t ; +typedef unsigned long ulong_t; +#define wan_timeval timeval +#define wan_timeval_t struct timeval #elif defined(__LINUX__) /*********************** L I N U X ******************************/ typedef unsigned long wan_ticks_t; -typedef unsigned long ulong_ptr_t; +typedef unsigned long ulong_ptr_t; typedef unsigned long wan_time_t; typedef unsigned long wan_suseconds_t; +typedef long long_t; +typedef unsigned long ulong_t; +#define wan_timeval timeval +#define wan_timeval_t struct timeval #elif defined(__WINDOWS__) /******************* W I N D O W S ******************************/ -typedef unsigned long wan_ticks_t; -typedef unsigned long wan_time_t; -typedef unsigned long wan_suseconds_t; + +/************* Basic data types **********/ + +/* signed types */ +#define int8_t INT8 +#define int16_t INT16 +#define int32_t INT32 +#define int64_t INT64 + +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; + +/* unsigned types */ +#define u_int8_t UINT8 +#define u_int16_t UINT16 +#define u_int32_t UINT32 +#define u_int64_t UINT64 + +typedef unsigned int wan_ticks_t; +typedef __time64_t wan_time_t; +typedef unsigned int wan_suseconds_t; + +struct wan_timeval +{ + wan_time_t tv_sec; + wan_suseconds_t tv_usec; +}; + +typedef struct wan_timeval wan_timeval_t; + +#if defined(WAN_KERNEL) + typedef LONG long_t; + typedef ULONG ulong_t; + +# if _AMD64bit==true /* _AMD64bit=true is set in "Server 2003 x64 Checked Build Environment" */ +# define __x86_64__ 1 /* needed for Octasic code */ +# endif + + +# if defined(_WIN64) + typedef unsigned __int64 ulong_ptr_t; +# else + typedef unsigned __int32 ulong_ptr_t; +# endif + +# define timeval wan_timeval/* kernel-mode only - will NOT conflict with winsock.h */ + +# else/* (WAN_KERNEL) */ + typedef long long_t; + typedef unsigned long ulong_t; +#endif + +typedef ulong_t u_long; + +typedef u_int8_t uint8_t; +typedef u_int16_t uint16_t; +typedef u_int32_t uint32_t; +typedef u_int64_t uint64_t; + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef uint8_t __u8; +typedef uint16_t __u16; +typedef uint32_t __u32; +typedef uint64_t __u64; + +typedef unsigned int uint; +typedef unsigned short ushort; + +#if defined(WAN_KERNEL) +typedef u_int8_t u_char; +typedef unsigned int UINT, *PUINT; + +/* AFT DMA addresses are MAXIMUM 32-bit wide!!! + * This is why this declaration is safe: */ +typedef u32 dma_addr_t; +/*typedef PHYSICAL_ADDRESS dma_addr_t;*/ +/*typedef LONGLONG dma_addr_t;*/ #endif +typedef char* caddr_t; +typedef struct { long_t counter; } atomic_t; /* Interlocked functions require LONG parameter */ +/******** End of Basic data types ********/ +typedef u32 gfp_t; +#if !defined(WAN_KERNEL) +#if defined(_WIN64) + #define DWL_MSGRESULT DWLP_MSGRESULT + #define DWL_USER DWLP_USER + #define DLGPROC_RETURN_TYPE INT_PTR +#else + #define DLGPROC_RETURN_TYPE BOOL + #endif +#endif/* !defined(WAN_KERNEL) */ + +#endif #endif /* __WANPIPE_ABSTR_TYPES_H */ diff --git a/patches/kdrivers/include/wanpipe_api_hdr.h b/patches/kdrivers/include/wanpipe_api_hdr.h index 7ee6d59..4951443 100644 --- a/patches/kdrivers/include/wanpipe_api_hdr.h +++ b/patches/kdrivers/include/wanpipe_api_hdr.h @@ -92,6 +92,9 @@ typedef struct wp_api_event u_int16_t ohttimer; /*!< */ u_int16_t polarity_reverse; /*!< */ } rm_common; + struct { + int32_t gain; + }rm_gain; struct{ u_int16_t status; /*!< Link Status (connected/disconnnected) */ } linkstatus; @@ -126,6 +129,7 @@ typedef struct wp_api_event #define wp_api_event_serial_status serial.status #define wp_api_event_time_stamp_sec time_stamp_sec #define wp_api_event_time_stamp_usec time_stamp_usec +#define wp_api_event_gain_value rm_gain.gain } wp_api_event_t; @@ -170,7 +174,6 @@ typedef struct wp_api_hdr The rx_h and tx_h are to be used with all AFT Hardware ****************************************************/ struct { - u_int16_t crc; /*!< number of crc/abort/data errors */ u_int8_t max_rx_queue_length; /*!< max data queue configured */ u_int8_t current_number_of_frames_in_rx_queue; /*!< current buffers in device rx queue */ @@ -179,6 +182,7 @@ typedef struct wp_api_hdr struct { u_int8_t max_tx_queue_length; /*!< max data queue configured */ u_int8_t current_number_of_frames_in_tx_queue; /*!< current buffers in device tx queue */ + u_int32_t tx_idle_packets; /*!< number of tx idle packes transmitted */ }tx_h; /***********************************************//** @@ -215,7 +219,7 @@ typedef struct wp_api_hdr u_int8_t repeat; u_int8_t len; u_int8_t data[8]; - }rtp; + }rpt; struct { u_int8_t type; u_int8_t force_tx; @@ -244,6 +248,7 @@ typedef struct wp_api_hdr #define wp_api_tx_hdr_max_queue_length tx_h.max_tx_queue_length #define wp_api_tx_hdr_number_of_frames_in_queue tx_h.current_number_of_frames_in_tx_queue +#define wp_api_tx_hdr_tx_idle_packets tx_h.tx_idle_packets #define wp_api_tx_hdr_time_stamp_sec time_stamp_sec #define wp_api_tx_hdr_time_stamp_use time_stamp_usec @@ -258,9 +263,9 @@ typedef struct wp_api_hdr #define wp_api_rx_hdr_time_stamp data_length #endif -#define wp_api_tx_hdr_hdlc_rpt_len rtp.len -#define wp_api_tx_hdr_hdlc_rpt_data rtp.data -#define wp_api_tx_hdr_hdlc_rpt_repeat rtp.repeat +#define wp_api_tx_hdr_hdlc_rpt_len rpt.len +#define wp_api_tx_hdr_hdlc_rpt_data rpt.data +#define wp_api_tx_hdr_hdlc_rpt_repeat rpt.repeat #define wp_api_tx_hdr_aft_ss7_type ss7_hw.type #define wp_api_tx_hdr_aft_ss7_force_tx ss7_hw.force_tx @@ -465,7 +470,7 @@ typedef enum SANG_STATUS SANG_STATUS_UNSUPPORTED_PROTOCOL, /*!< Unsupported protocol selected */ SANG_STATUS_DEVICE_ALREADY_EXIST, /*!< Device already exists */ SANG_STATUS_DEV_INIT_INCOMPLETE, /*!< Device initialization failed */ - SANG_STATUS_TRACE_QUEUE_EMPTY, /*!< Trace queueu empty */ + SANG_STATUS_TRACE_QUEUE_EMPTY, /*!< Trace queue empty */ SANG_STATUS_OPTION_NOT_SUPPORTED, /*!< Unsupported command or option */ /*************************************//** @@ -508,43 +513,44 @@ typedef enum SANG_STATUS */ #define SDLA_DECODE_SANG_STATUS(status) \ -(abs(status) == SANG_STATUS_SUCCESS) ? "SANG_STATUS_SUCCESS" :\ -(abs(status) == SANG_STATUS_COMMAND_ALREADY_RUNNING) ? "SANG_STATUS_COMMAND_ALREADY_RUNNING":\ -(abs(status) == SANG_STATUS_BUFFER_TOO_SMALL) ? "SANG_STATUS_BUFFER_TOO_SMALL":\ -(abs(status) == SANG_STATUS_FAILED_TO_LOCK_USER_MEMORY) ? "SANG_STATUS_FAILED_TO_LOCK_USER_MEMORY":\ -(abs(status) == SANG_STATUS_FAILED_ALLOCATE_MEMORY) ? "SANG_STATUS_FAILED_ALLOCATE_MEMORY":\ -(abs(status) == SANG_STATUS_INVALID_DEVICE_REQUEST) ? "SANG_STATUS_INVALID_DEVICE_REQUEST":\ -(abs(status) == SANG_STATUS_INVALID_PARAMETER) ? "SANG_STATUS_INVALID_PARAMETER":\ -(abs(status) == SANG_STATUS_DATA_QUEUE_EMPTY) ? "SANG_STATUS_DATA_QUEUE_EMPTY":\ -(abs(status) == SANG_STATUS_DATA_QUEUE_FULL) ? "SANG_STATUS_DATA_QUEUE_FULL":\ -(abs(status) == SANG_STATUS_RX_DATA_TIMEOUT) ? "SANG_STATUS_RX_DATA_TIMEOUT":\ -(abs(status) == SANG_STATUS_RX_DATA_AVAILABLE) ? "SANG_STATUS_RX_DATA_AVAILABLE":\ -(abs(status) == SANG_STATUS_TX_TIMEOUT) ? "SANG_STATUS_TX_TIMEOUT":\ -(abs(status) == SANG_STATUS_TX_DATA_TOO_LONG) ? "SANG_STATUS_TX_DATA_TOO_LONG":\ -(abs(status) == SANG_STATUS_TX_DATA_TOO_SHORT) ? "SANG_STATUS_TX_DATA_TOO_SHORT":\ -(abs(status) == SANG_STATUS_LINE_DISCONNECTED) ? "SANG_STATUS_LINE_DISCONNECTED":\ -(abs(status) == SANG_STATUS_LINE_CONNECTED) ? "SANG_STATUS_LINE_CONNECTED":\ -(abs(status) == SANG_STATUS_PROTOCOL_DISCONNECTED) ? "SANG_STATUS_PROTOCOL_DISCONNECTED":\ -(abs(status) == SANG_STATUS_PROTOCOL_CONNECTED) ? "SANG_STATUS_PROTOCOL_CONNECTED":\ -(abs(status) == SANG_STATUS_GENERAL_ERROR) ? "SANG_STATUS_GENERAL_ERROR":\ -(abs(status) == SANG_STATUS_DEVICE_BUSY) ? "SANG_STATUS_DEVICE_BUSY":\ -(abs(status) == SANG_STATUS_INVALID_DEVICE) ? "SANG_STATUS_INVALID_DEVICE":\ -(abs(status) == SANG_STATUS_IO_ERROR) ? "SANG_STATUS_IO_ERROR":\ -(abs(status) == SANG_STATUS_UNSUPPORTED_FUNCTION) ? "SANG_STATUS_UNSUPPORTED_FUNCTION":\ -(abs(status) == SANG_STATUS_UNSUPPORTED_PROTOCOL) ? "SANG_STATUS_UNSUPPORTED_PROTOCOL":\ -(abs(status) == SANG_STATUS_DEVICE_ALREADY_EXIST) ? "SANG_STATUS_DEVICE_ALREADY_EXIST":\ -(abs(status) == SANG_STATUS_DEV_INIT_INCOMPLETE) ? "SANG_STATUS_DEV_INIT_INCOMPLETE":\ -(abs(status) == SANG_STATUS_API_EVENT_AVAILABLE) ? "SANG_STATUS_API_EVENT_AVAILABLE":\ -(abs(status) == SANG_STATUS_REGISTRY_ERROR) ? "SANG_STATUS_REGISTRY_ERROR":\ -(abs(status) == SANG_STATUS_CAN_NOT_STOP_DEVICE_WHEN_ALREADY_STOPPED) ? "SANG_STATUS_CAN_NOT_STOP_DEVICE_WHEN_ALREADY_STOPPED":\ -(abs(status) == SANG_STATUS_CAN_NOT_RUN_TWO_PORT_CMDS_AT_THE_SAME_TIME) ? "SANG_STATUS_CAN_NOT_RUN_TWO_PORT_CMDS_AT_THE_SAME_TIME":\ -(abs(status) == SANG_STATUS_ASSOCIATED_IRP_SYSTEM_BUFFER_NULL_ERROR) ? "SANG_STATUS_ASSOCIATED_IRP_SYSTEM_BUFFER_NULL_ERROR":\ -(abs(status) == SANG_STATUS_STRUCTURE_SIZE_MISMATCH_ERROR) ? "SANG_STATUS_STRUCTURE_SIZE_MISMATCH_ERROR":\ -(abs(status) == SANG_STATUS_INVALID_IRQL) ? "SANG_STATUS_INVALID_IRQL":\ -(abs(status) == SANG_STATUS_NO_DATA_AVAILABLE) ? "SANG_STATUS_NO_DATA_AVAILABLE":\ -(abs(status) == SANG_STATUS_IO_PENDING) ? "SANG_STATUS_IO_PENDING":\ -(abs(status) == SANG_STATUS_APIPOLL_TIMEOUT) ? "SANG_STATUS_APIPOLL_TIMEOUT":\ -(abs(status) == SANG_STATUS_NO_FREE_BUFFERS) ? "SANG_STATUS_NO_FREE_BUFFERS":\ +(abs((int)status) == SANG_STATUS_SUCCESS) ? "SANG_STATUS_SUCCESS" :\ +(abs((int)status) == SANG_STATUS_COMMAND_ALREADY_RUNNING) ? "SANG_STATUS_COMMAND_ALREADY_RUNNING":\ +(abs((int)status) == SANG_STATUS_BUFFER_TOO_SMALL) ? "SANG_STATUS_BUFFER_TOO_SMALL":\ +(abs((int)status) == SANG_STATUS_FAILED_TO_LOCK_USER_MEMORY) ? "SANG_STATUS_FAILED_TO_LOCK_USER_MEMORY":\ +(abs((int)status) == SANG_STATUS_FAILED_ALLOCATE_MEMORY) ? "SANG_STATUS_FAILED_ALLOCATE_MEMORY":\ +(abs((int)status) == SANG_STATUS_INVALID_DEVICE_REQUEST) ? "SANG_STATUS_INVALID_DEVICE_REQUEST":\ +(abs((int)status) == SANG_STATUS_INVALID_PARAMETER) ? "SANG_STATUS_INVALID_PARAMETER":\ +(abs((int)status) == SANG_STATUS_DATA_QUEUE_EMPTY) ? "SANG_STATUS_DATA_QUEUE_EMPTY":\ +(abs((int)status) == SANG_STATUS_DATA_QUEUE_FULL) ? "SANG_STATUS_DATA_QUEUE_FULL":\ +(abs((int)status) == SANG_STATUS_RX_DATA_TIMEOUT) ? "SANG_STATUS_RX_DATA_TIMEOUT":\ +(abs((int)status) == SANG_STATUS_RX_DATA_AVAILABLE) ? "SANG_STATUS_RX_DATA_AVAILABLE":\ +(abs((int)status) == SANG_STATUS_TX_TIMEOUT) ? "SANG_STATUS_TX_TIMEOUT":\ +(abs((int)status) == SANG_STATUS_TX_DATA_TOO_LONG) ? "SANG_STATUS_TX_DATA_TOO_LONG":\ +(abs((int)status) == SANG_STATUS_TX_DATA_TOO_SHORT) ? "SANG_STATUS_TX_DATA_TOO_SHORT":\ +(abs((int)status) == SANG_STATUS_LINE_DISCONNECTED) ? "SANG_STATUS_LINE_DISCONNECTED":\ +(abs((int)status) == SANG_STATUS_LINE_CONNECTED) ? "SANG_STATUS_LINE_CONNECTED":\ +(abs((int)status) == SANG_STATUS_PROTOCOL_DISCONNECTED) ? "SANG_STATUS_PROTOCOL_DISCONNECTED":\ +(abs((int)status) == SANG_STATUS_PROTOCOL_CONNECTED) ? "SANG_STATUS_PROTOCOL_CONNECTED":\ +(abs((int)status) == SANG_STATUS_GENERAL_ERROR) ? "SANG_STATUS_GENERAL_ERROR":\ +(abs((int)status) == SANG_STATUS_DEVICE_BUSY) ? "SANG_STATUS_DEVICE_BUSY":\ +(abs((int)status) == SANG_STATUS_INVALID_DEVICE) ? "SANG_STATUS_INVALID_DEVICE":\ +(abs((int)status) == SANG_STATUS_IO_ERROR) ? "SANG_STATUS_IO_ERROR":\ +(abs((int)status) == SANG_STATUS_UNSUPPORTED_FUNCTION) ? "SANG_STATUS_UNSUPPORTED_FUNCTION":\ +(abs((int)status) == SANG_STATUS_UNSUPPORTED_PROTOCOL) ? "SANG_STATUS_UNSUPPORTED_PROTOCOL":\ +(abs((int)status) == SANG_STATUS_DEVICE_ALREADY_EXIST) ? "SANG_STATUS_DEVICE_ALREADY_EXIST":\ +(abs((int)status) == SANG_STATUS_DEV_INIT_INCOMPLETE) ? "SANG_STATUS_DEV_INIT_INCOMPLETE":\ +(abs((int)status) == SANG_STATUS_API_EVENT_AVAILABLE) ? "SANG_STATUS_API_EVENT_AVAILABLE":\ +(abs((int)status) == SANG_STATUS_REGISTRY_ERROR) ? "SANG_STATUS_REGISTRY_ERROR":\ +(abs((int)status) == SANG_STATUS_CAN_NOT_STOP_DEVICE_WHEN_ALREADY_STOPPED) ? "SANG_STATUS_CAN_NOT_STOP_DEVICE_WHEN_ALREADY_STOPPED":\ +(abs((int)status) == SANG_STATUS_CAN_NOT_RUN_TWO_PORT_CMDS_AT_THE_SAME_TIME) ? "SANG_STATUS_CAN_NOT_RUN_TWO_PORT_CMDS_AT_THE_SAME_TIME":\ +(abs((int)status) == SANG_STATUS_ASSOCIATED_IRP_SYSTEM_BUFFER_NULL_ERROR) ? "SANG_STATUS_ASSOCIATED_IRP_SYSTEM_BUFFER_NULL_ERROR":\ +(abs((int)status) == SANG_STATUS_STRUCTURE_SIZE_MISMATCH_ERROR) ? "SANG_STATUS_STRUCTURE_SIZE_MISMATCH_ERROR":\ +(abs((int)status) == SANG_STATUS_INVALID_IRQL) ? "SANG_STATUS_INVALID_IRQL":\ +(abs((int)status) == SANG_STATUS_NO_DATA_AVAILABLE) ? "SANG_STATUS_NO_DATA_AVAILABLE":\ +(abs((int)status) == SANG_STATUS_IO_PENDING) ? "SANG_STATUS_IO_PENDING":\ +(abs((int)status) == SANG_STATUS_APIPOLL_TIMEOUT) ? "SANG_STATUS_APIPOLL_TIMEOUT":\ +(abs((int)status) == SANG_STATUS_NO_FREE_BUFFERS) ? "SANG_STATUS_NO_FREE_BUFFERS":\ +(abs((int)status) == SANG_STATUS_OPTION_NOT_SUPPORTED) ? "SANG_STATUS_OPTION_NOT_SUPPORTED":\ "Status Unknown" #define SANG_SUCCESS(status) (status == SANG_STATUS_SUCCESS) @@ -580,14 +586,14 @@ typedef enum SANG_STATUS # define EPROTONOSUPPORT SANG_STATUS_UNSUPPORTED_PROTOCOL # define ENOMEM SANG_STATUS_FAILED_ALLOCATE_MEMORY # define EEXIST SANG_STATUS_DEVICE_ALREADY_EXIST -# define ENOBUFS SANG_STATUS_NO_FREE_BUFFERS /* no free tx buffers */ +# define ENOBUFS SANG_STATUS_NO_FREE_BUFFERS /* no free tx or rx buffers */ # define EOPNOTSUPP SANG_STATUS_OPTION_NOT_SUPPORTED # define ENXIO EFAULT # define ENETDOWN SANG_STATUS_LINE_DISCONNECTED # define EAGAIN SANG_STATUS_DEVICE_BUSY # define EFBIG SANG_STATUS_TX_DATA_TOO_LONG +# define EAFNOSUPPORT SANG_STATUS_UNSUPPORTED_FUNCTION #endif #endif /* __WINDOWS */ - -#endif +#endif/* __WANPIPE_API_HDR__ */ diff --git a/patches/kdrivers/include/wanpipe_api_iface.h b/patches/kdrivers/include/wanpipe_api_iface.h index 3dfe892..22c5f30 100644 --- a/patches/kdrivers/include/wanpipe_api_iface.h +++ b/patches/kdrivers/include/wanpipe_api_iface.h @@ -1,7 +1,7 @@ /******************************************************************************//** * \file wanpipe_api_iface.h * \brief WANPIPE(tm) Driver API Interface - - * \brief Provies IO/Event API Only + * \brief Provides IO/Event API Only * * Authors: Nenad Corbic * David Rokhvarg @@ -58,15 +58,28 @@ typedef int sng_fd_t; \def WP_API_FEATURE_LINK_STATUS \brief Indicates to developer that link status feature is available \def WP_API_FEATURE_POL_REV - \brief Indicates to developer that polarity reversal feature is avaliable + \brief Indicates to developer that polarity reversal feature is available + \def WP_API_FEATURE_LOGGER + \brief Indicates to developer that Wanpipe Logger API feature is available + \def WP_API_FEATURE_RM_GAIN + \brief Indicates to developer that analog hardware gain feature is available + \def WP_API_FEATURE_LOOP + \brief Indicates to developer that loop feature is available + \def WP_API_FEATURE_HWEC + \brief Indicates to developer that Hardware Echo canceller feature is available */ #define WP_API_FEATURE_DTMF_EVENTS 1 #define WP_API_FEATURE_FE_ALARM 1 #define WP_API_FEATURE_EVENTS 1 #define WP_API_FEATURE_LINK_STATUS 1 -#define WP_API_FEATURE_POL_REV 1 - - +#define WP_API_FEATURE_POL_REV 1 +#define WP_API_FEATURE_LOGGER 1 +#define WP_API_FEATURE_FAX_EVENTS 1 +#define WP_API_FEATURE_RM_GAIN 1 +#define WP_API_FEATURE_LOOP 1 +#if defined(__WINDOWS__) + #define WP_API_FEATURE_LIBSNG_HWEC 1 +#endif /*! \enum WANPIPE_IOCTL_CODE @@ -91,7 +104,8 @@ enum WANPIPE_IOCTL_CODE { WANPIPE_IOCTL_DEVEL, /*!< Development Cmds, use only for hw debugging */ WANPIPE_IOCTL_WRITE_NON_BLOCKING, /*!< Non-Blocking Write cmd, Windows Only */ WANPIPE_IOCTL_READ_NON_BLOCKING, /*!< Non-Blocking Read cmd, Windows Only */ - WANPIPE_IOCTL_CDEV_CTRL /*!< Non-Blocking Cdev control cmd, Windows Only */ + WANPIPE_IOCTL_CDEV_CTRL, /*!< Non-Blocking Cdev control cmd, Windows Only */ + WANPIPE_IOCTL_LOGGER_CMD /*!< Wanpipe Logger command */ }; @@ -135,14 +149,56 @@ enum WANPIPE_IOCTL_PIPEMON_CMDS { WANPIPEMON_GET_IBA_DATA, /*!< Get IBA Data - Deprecated not used */ WANPIPEMON_TDM_API, /*!< Windows Legacy- TDM API commands */ + WANPIPEMON_READ_PEFORMANCE_STATS, + WANPIPEMON_FLUSH_PEFORMANCE_STATS, + WANPIPEMON_GET_BIOS_ENCLOSURE3_SERIAL_NUMBER, /*!< Get Enclosure3 Serial Number from Motherboard BIOS. */ + + WANPIPEMON_ENABLE_BERT, /*!< Start BERT for the interface. */ + WANPIPEMON_DISABLE_BERT, /*!< Stop BERT for the interface. */ + WANPIPEMON_GET_BERT_STATUS, /*!< Get BERT status/statistics (Locked/Not Locked) */ /* Do not add any non-debugging commands below */ - WANPIPEMON_CHAN_SEQ_DEBUGGING, /*!< Debugging only - enable/disalbe span level sequence debugging */ + WANPIPEMON_CHAN_SEQ_DEBUGGING, /*!< Debugging only - enable/disable span level sequence debugging */ + WANPIPEMON_GEN_FIFO_ERR_TX, /*!< Debugging only - generate tx fifo error */ + WANPIPEMON_GEN_FIFO_ERR_RX, /*!< Debugging only - generate rx fifo error */ + WANPIPEMON_GEN_FE_SYNC_ERR, /*!< Debugging only - generate fe sync error */ - WANPIPEMON_PROTOCOL_PRIVATE /*!< Private Wanpipemon commands used by lower layers */ + /* All Public commands must be between WANPIPEMON_ROUTER_UP_TIME and WANPIPEMON_PROTOCOL_PRIVATE. */ + + WANPIPEMON_PROTOCOL_PRIVATE /*!< Private Wanpipemon commands used by lower layers */ }; +typedef enum wp_bert_sequence_type{ + WP_BERT_RANDOM_SEQUENCE = 1, + WP_BERT_ASCENDANT_SEQUENCE, + WP_BERT_DESCENDANT_SEQUENCE +}wp_bert_sequence_type_t; + +#define WP_BERT_DECODE_SEQUENCE_TYPE(sequence) \ + ((sequence) == WP_BERT_RANDOM_SEQUENCE) ? "WP_BERT_RANDOM_SEQUENCE" : \ + ((sequence) == WP_BERT_ASCENDANT_SEQUENCE) ? "WP_BERT_ASCENDANT_SEQUENCE" : \ + ((sequence) == WP_BERT_DESCENDANT_SEQUENCE) ? "WP_BERT_DESCENDANT_SEQUENCE" : \ + "(Unknown BERT sequence)" + +#define WP_BERT_STATUS_OUT_OF_SYNCH 0 +#define WP_BERT_STATUS_IN_SYNCH 1 + +/*! + \struct _wp_bert_status + \brief BERT status and statistics + + \typedef wp_bert_status_t +*/ +typedef struct _wp_bert_status{ + + unsigned char state; /*!< Current state of BERT */ + unsigned int errors; /*!< Nuber of errors during BERT */ + unsigned int synchonized_count; /*!< Number of times BERT got into synch */ + +}wp_bert_status_t; + + /*! \enum wanpipe_api_cmds \brief Commands used with WANPIPE_IOCTL_API_CMD IOCTL @@ -186,7 +242,7 @@ enum wanpipe_api_cmds WP_API_CMD_DISABLE_HWEC, /*!< */ WP_API_CMD_SET_FE_STATUS, /*!< */ WP_API_CMD_GET_FE_STATUS, /*!< */ - WP_API_CMD_GET_HW_DTMF, /*!< */ + WP_API_CMD_GET_HW_DTMF, /*!< Get Status of the HW DTMF. Enabled(1) or Disabled(0) */ WP_API_CMD_DRV_MGMNT, /*!< */ WP_API_CMD_RESET_STATS, /*!< Reset device statistics */ WP_API_CMD_DRIVER_VERSION, /*!< Driver Version */ @@ -201,10 +257,14 @@ enum wanpipe_api_cmds WP_API_CMD_NOTSUPP, /*!< */ WP_API_CMD_SET_RM_RXFLASHTIME, /*!< Set rxflashtime for FXS */ WP_API_CMD_SET_IDLE_FLAG, /*!< Set Idle Flag (char) for a BitStream (Voice) channel */ - WP_API_CMD_GET_HW_EC, /*!< Check to see if hwec is available */ + WP_API_CMD_GET_HW_EC, /*!< Check Status of HW Echo Cancelation. Enabled(1) or Disabled(0) */ + WP_API_CMD_GET_HW_FAX_DETECT, /*!< Check Status of HW Fax Detect. Enabled(1) or Disabled(0) */ + WP_API_CMD_ENABLE_LOOP, /*!< Remote Loop the channel */ + WP_API_CMD_DISABLE_LOOP, /*!< Disable remote loop */ /* Add only debugging commands here */ - WP_API_CMD_GEN_FIFO_ERR=500, + WP_API_CMD_GEN_FIFO_ERR_TX=500, + WP_API_CMD_GEN_FIFO_ERR_RX, WP_API_CMD_START_CHAN_SEQ_DEBUG, WP_API_CMD_STOP_CHAN_SEQ_DEBUG }; @@ -237,25 +297,28 @@ enum wanpipe_cdev_ctrl_cmds enum wanpipe_api_events { WP_API_EVENT_NONE, /*!< */ - WP_API_EVENT_RBS, /*!< */ + WP_API_EVENT_RBS, /*!< Enable Disable RBS Opertaion Mode (T1: RBS E1: CAS) */ WP_API_EVENT_ALARM, /*!< */ - WP_API_EVENT_DTMF, /*!< */ + WP_API_EVENT_DTMF, /*!< Enable Disable HW DTMF Detection */ WP_API_EVENT_RM_DTMF, /*!< */ - WP_API_EVENT_RXHOOK, /*!< */ + WP_API_EVENT_RXHOOK, /*!< */ WP_API_EVENT_RING, /*!< */ WP_API_EVENT_RING_DETECT, /*!< */ WP_API_EVENT_RING_TRIP_DETECT, /*!< */ WP_API_EVENT_TONE, /*!< */ - WP_API_EVENT_TXSIG_KEWL, /*!< */ + WP_API_EVENT_TXSIG_KEWL, /*!< */ WP_API_EVENT_TXSIG_START, /*!< */ WP_API_EVENT_TXSIG_OFFHOOK, /*!< */ WP_API_EVENT_TXSIG_ONHOOK, /*!< */ - WP_API_EVENT_ONHOOKTRANSFER, /*!< */ + WP_API_EVENT_ONHOOKTRANSFER, /*!< */ WP_API_EVENT_SETPOLARITY, /*!< */ WP_API_EVENT_BRI_CHAN_LOOPBACK, /*!< */ WP_API_EVENT_LINK_STATUS, /*!< */ - WP_API_EVENT_MODEM_STATUS, - WP_API_EVENT_POLARITY_REVERSE /*!< */ + WP_API_EVENT_MODEM_STATUS, /*!< */ + WP_API_EVENT_POLARITY_REVERSE, /*!< */ + WP_API_EVENT_FAX_DETECT, /*!< Enable Disable HW Fax Detection */ + WP_API_EVENT_SET_RM_TX_GAIN, /*!< Set Tx Gain for FXO/FXS */ + WP_API_EVENT_SET_RM_RX_GAIN /*!< Set Rx Gain for FXO/FXS */ }; @@ -428,6 +491,7 @@ enum wanpipe_api_events #define WP_CTRL_DEV_NAME "wanpipe_ctrl" #define WP_CONFIG_DEV_NAME "wanpipe" #define WP_TIMER_DEV_NAME_FORM "wanpipe_timer%d" +#define WP_LOGGER_DEV_NAME "wanpipe_logger" #pragma pack(1) @@ -497,13 +561,23 @@ typedef struct _DRIVER_VERSION { }wan_driver_version_t, DRIVER_VERSION, *PDRIVER_VERSION; #define WANPIPE_API_CMD_SZ 512 +#define WANPIPE_API_DEV_CFG_MAX_SZ 333 /* The the union size is max-cmd-result-span-chan-data_len */ #define WANPIPE_API_CMD_SZ_UNION WANPIPE_API_CMD_SZ - (sizeof(unsigned int)*3) - (sizeof(unsigned char)*2) -#define WANPIPE_API_CMD_RESERVED_SZ 128 - sizeof(int)*1 - sizeof(char)*1 -#define WANPIPE_API_DEV_CFG_SZ sizeof(int)*20 + sizeof(char)*2 + WANPIPE_API_CMD_RESERVED_SZ + sizeof(wanpipe_chan_stats_t) +/* Each time you add a parameter to the wanpipe_api_dev_cfg_t you must update + WANPIPE_API_CMD_RESERVED_SZ as well as WANPIPE_API_DEV_CFG_SZ */ + +/* rxflashtime hw_ec,hw_fax,loop */ +#define WANPIPE_API_CMD_RESERVED_SZ 128 - sizeof(int)*1 - sizeof(char)*3 + +/* The sizeof WANPIPE_API_DEV_CFG_SZ must account for every variable in + wanpipe_api_dev_cfg_t strcture */ + +/* 20 int 4 chars */ +#define WANPIPE_API_DEV_CFG_SZ sizeof(int)*20 + sizeof(char)*4 + WANPIPE_API_CMD_RESERVED_SZ + sizeof(wanpipe_chan_stats_t) /*! @@ -540,6 +614,8 @@ typedef struct wanpipe_api_dev_cfg /* Any new paramets should decrement the reserved size */ unsigned int rxflashtime; /* Set Rxflash time for Wink-Flash */ unsigned char hw_ec; + unsigned char hw_fax; + unsigned char loop; unsigned char reserved[WANPIPE_API_CMD_RESERVED_SZ]; /* Duplicate the structure below */ @@ -549,7 +625,7 @@ typedef struct wanpipe_api_dev_cfg /*! \struct wanpipe_api_cmd - \brief Wanpipe API Device Configuration Structure used with WANPIPE_IOCTL_API_CMD + \brief Wanpipe API Device Command Structure used with WANPIPE_IOCTL_API_CMD Wanpipe API Commands structure used to execute WANPIPE_IOCTL_API_CMD iocl commands All commands are defined in: @@ -590,6 +666,8 @@ typedef struct wanpipe_api_cmd /* Any new paramets should decrement the reserved size */ unsigned int rxflashtime; /*!< Set Rxflash time for Wink-Flash */ unsigned char hw_ec; + unsigned char hw_fax; + unsigned char loop; unsigned char reserved[WANPIPE_API_CMD_RESERVED_SZ]; wanpipe_chan_stats_t stats; /*!< Wanpipe API Statistics */ @@ -603,6 +681,12 @@ typedef struct wanpipe_api_cmd }; }wanpipe_api_cmd_t; +/* \brief Initialize 'span' in wanpipe_api_cmd_t */ +#define WANPIPE_API_CMD_INIT_SPAN(wp_cmd_ptr, span_no) ((wp_cmd_ptr)->span = (unsigned char)span_no) + +/* \brief Initialize 'channel' in wanpipe_api_cmd_t */ +#define WANPIPE_API_CMD_INIT_CHAN(wp_cmd_ptr, chan_no) ((wp_cmd_ptr)->chan = (unsigned char)chan_no) + /*! \struct wanpipe_api_callbacks \brief Wanpipe API Callback Structure @@ -636,6 +720,12 @@ typedef struct wanpipe_api wanpipe_api_callbacks_t wp_callback; /*!< Deprecated: Callbacks used by libsangoma */ }wanpipe_api_t; +/* \brief Initialize 'span' in wanpipe_api_t->wp_cmd */ +#define WANPIPE_API_INIT_SPAN(wanpipe_api_ptr, span_no) WANPIPE_API_CMD_INIT_SPAN(&wanpipe_api_ptr->wp_cmd, span_no) + +/* \brief Initialize 'channel' in wanpipe_api_t->wp_cmd */ +#define WANPIPE_API_INIT_CHAN(wanpipe_api_ptr, chan_no) WANPIPE_API_CMD_INIT_CHAN(&wanpipe_api_ptr->wp_cmd, chan_no) + #pragma pack() diff --git a/patches/kdrivers/include/wanpipe_cdev_iface.h b/patches/kdrivers/include/wanpipe_cdev_iface.h index 37ba9e7..3b69d91 100644 --- a/patches/kdrivers/include/wanpipe_cdev_iface.h +++ b/patches/kdrivers/include/wanpipe_cdev_iface.h @@ -1,3 +1,4 @@ +/* wanpipe_cdev_iface.h */ #ifndef __WANPIPE_CDEV_IFACE__ #define __WANPIPE_CDEV_IFACE__ @@ -28,6 +29,9 @@ typedef struct wanpipe_cdev_ops int (*write)(void* dev_ptr, netskb_t *skb, wp_api_hdr_t *hdr); int (*read)(void* dev_ptr, netskb_t **skb, wp_api_hdr_t *hdr, int count); + /* handle transmission time out */ + int (*tx_timeout)(void* dev_ptr); + }wanpipe_cdev_ops_t; @@ -50,6 +54,7 @@ typedef struct file_operations enum WP_TDM_OPMODE{ WP_TDM_OPMODE_CHAN = 0, WP_TDM_OPMODE_SPAN, + WP_TDM_OPMODE_MTP1 }; enum { @@ -64,8 +69,8 @@ typedef struct wanpipe_cdev u_int8_t init; u_int8_t used; - char span; - char chan; + int32_t span; + int32_t chan; u_int8_t name[WAN_IFNAME_SZ+1]; void *dev_ptr; @@ -73,8 +78,15 @@ typedef struct wanpipe_cdev u8 operation_mode;/* WP_TDM_OPMODE */ + wan_timer_t tx_timeout_timer; + /* Private to cdev code - Do not use*/ void *priv; + +#if defined(__WINDOWS__) + void *PnpFdoPdx; +#endif + } wanpipe_cdev_t; @@ -147,13 +159,13 @@ int wanpipe_global_cdev_free(void); int wanpipe_cdev_tdm_create(wanpipe_cdev_t *cdev); int wanpipe_cdev_free(wanpipe_cdev_t *cdev); +int wanpipe_cdev_lip_api_create(wanpipe_cdev_t *cdev); - -int wanpipe_cdev_tdm_create(wanpipe_cdev_t *cdev); int wanpipe_cdev_tdm_ctrl_create(wanpipe_cdev_t *cdev); int wanpipe_cdev_cfg_ctrl_create(wanpipe_cdev_t *cdev); int wanpipe_cdev_timer_create(wanpipe_cdev_t *cdev); +int wanpipe_cdev_logger_create(wanpipe_cdev_t *cdev); #endif diff --git a/patches/kdrivers/include/wanpipe_cfg.h b/patches/kdrivers/include/wanpipe_cfg.h index c1971ad..e747082 100644 --- a/patches/kdrivers/include/wanpipe_cfg.h +++ b/patches/kdrivers/include/wanpipe_cfg.h @@ -46,12 +46,12 @@ For example: a number of DLCIs. */ #define MIN_N393 1 #define MAX_N393 10 -#define HIGHEST_VALID_DLCI MAX_NUMBER_OF_PROTOCOL_INTERFACES +#define HIGHEST_VALID_DLCI 991 #define LOWEST_VALID_DLCI 16 /********** end of sprotocol.dll definitions ************/ #pragma warning( disable : 4200 ) /* zero-sized array in struct */ -#endif +#endif/* __WINDOWS__ */ #define USED_BY_FIELD 128 /* max length of the used by field */ @@ -68,7 +68,7 @@ enum { API, /* Data OR Voice. Requires API Poll. */ BRIDGE, BRIDGE_NODE, - SWITCH, + WP_SWITCH, STACK, ANNEXG, TTY, @@ -81,7 +81,8 @@ enum { XMTP2_API, TDM_SPAN_VOICE_API, /* Voice API support. Delivers Voice stream from a Span of channels. */ TDM_CHAN_VOICE_API, /* Voice API support. Delivers Voice stream from a single channel. */ - API_LEGACY /* Data OR Voice. Windows: Does NOT require API Poll. Linux: runs on Sangoma Socket interface. */ + API_LEGACY, /* Data OR Voice. Windows: Does NOT require API Poll. Linux: runs on Sangoma Socket interface. */ + MTP1_API }; @@ -155,6 +156,7 @@ enum { (card_type == WANOPT_AFT_SERIAL) ? "A14x" : \ (card_type == WANOPT_USB_ANALOG) ? "U100" : \ (card_type == WANOPT_AFT600) ? "A600" : \ + (card_type == WANOPT_AFT601) ? "B601" : \ "Unknown" #define COMPORT_DECODE(port) (port == WANOPT_PRI) ? "PRI" : "SEC" @@ -200,6 +202,58 @@ enum { typedef char devname_t[WAN_DRVNAME_SZ+1]; +/* Return: + * sub-media as string or + * 'N/A' if sub-media does not exist or + * 'InvalidSubMedia' if error. */ +static __inline const char* wp_decode_submedia(int media, int sub_media) +{ + switch(media) + { + case WAN_MEDIA_FXOFXS: + switch(sub_media) + { + case MOD_TYPE_FXS: + return "FXS"; + case MOD_TYPE_FXO: + return "FXO"; + default: + return "InvalidSubMedia"; + } + break;/* WAN_MEDIA_FXOFXS */ + + case WAN_MEDIA_BRI: + switch(sub_media) + { + case MOD_TYPE_NT: + return "ISDN BRI NT"; + case MOD_TYPE_TE: + return "ISDN BRI TE"; + default: + return "InvalidSubMedia"; + } + break;/* WAN_MEDIA_BRI */ + + case WAN_MEDIA_SERIAL: + return INT_DECODE(sub_media); + + default: + return "N/A";/* this media does not have any sub-media (e.g. T1) */ + } +} + +#define SDLA_DECODE_WAN_MEDIA(wan_media) \ +(wan_media == WAN_MEDIA_NONE) ? "MEDIA_NONE": \ +(wan_media == WAN_MEDIA_T1) ? "MEDIA_T1": \ +(wan_media == WAN_MEDIA_E1) ? "MEDIA_E1": \ +(wan_media == WAN_MEDIA_56K) ? "MEDIA_56K": \ +(wan_media == WAN_MEDIA_DS3) ? "MEDIA_DS3": \ +(wan_media == WAN_MEDIA_E3) ? "MEDIA_E3": \ +(wan_media == WAN_MEDIA_STS1) ? "MEDIA_STS1": \ +(wan_media == WAN_MEDIA_J1) ? "MEDIA_J1": \ +(wan_media == WAN_MEDIA_FXOFXS) ? "MEDIA_FXOFXS": \ +(wan_media == WAN_MEDIA_BRI) ? "MEDIA_BRI": \ +(wan_media == WAN_MEDIA_SERIAL) ? "MEDIA_SERIAL": "Media Unknown" typedef struct wan_atm_conf { @@ -603,6 +657,15 @@ typedef struct wan_hwec_conf_ unsigned int tone_disabler_delay; /* delay in a fax mode */ } wan_hwec_conf_t; +typedef struct wan_hwec_dev_state +{ + u_int32_t ec_state; + u_int32_t ec_mode_map; + u_int32_t dtmf_map; + u_int32_t fax_called_map; + u_int32_t fax_calling_map; +}wan_hwec_dev_state_t; + #define MAX_PARAM_LEN 50 #define MAX_VALUE_LEN 50 typedef struct wan_custom_param_ @@ -816,33 +879,6 @@ typedef struct wan_hwec_if_conf } wan_hwec_if_conf_t; - -enum DEVICE_CONFIGURATION_RETURN_CODE{ - - DEVICE_CFG_OK = 1, - DEVICE_CFG_FAILED_COMMS_ENABLED, - DEVICE_CFG_FAILED_INVALID_LENGTH_OF_DATA_QUEUE, //must be between MINIMUM_LENGTH_OF_DATA_QUEUE - //and MAXIMUM_LENGTH_OF_DATA_QUEUE - - DEVICE_CFG_FAILED_INVALID_DATA_LENGTH, //must be at least MINIMUM_LENGTH_OF_DATA - DEVICE_CFG_FAILED_MEMORY_ALLOCATION_ERR -}; - -typedef struct _DEVICE_CONFIGURATION -{ - u_int8_t return_code; - - u_int16_t maximum_length_of_rx_queue; - u_int16_t maximum_rx_data_length; //including CRC bytes - - u_int16_t maximum_length_of_tx_queue; - u_int16_t maximum_tx_data_length; //not including CRC bytes - - /* Number of received blocks of data before Receive event is indicated to API caller. */ - u_int16_t buffer_multiplier_factor; -}DEVICE_CONFIGURATION, *PDEVICE_CONFIGURATION; - - /*---------------------------------------------------------------------------- * WAN interface (logical channel) configuration (for ROUTER_IFNEW IOCTL). */ @@ -952,7 +988,6 @@ typedef struct wanif_conf unsigned char lip_prot; - DEVICE_CONFIGURATION device_cfg; unsigned char line_mode[USED_BY_FIELD]; unsigned char interface_number;/* from 0 to 31 */ @@ -965,12 +1000,10 @@ typedef struct wanif_conf wan_bitstrm_conf_if_t bitstrm; wan_xilinx_conf_if_t aft; wan_xdlc_conf_t xdlc; + /* In LIP layer the same config structure 'wan_sppp_if_conf_t' used + * for both PPP and CHDLC. */ wan_sppp_if_conf_t ppp; -#if defined(__WINDOWS__) - wan_sppp_if_conf_t chdlc; -#else wan_chdlc_conf_t chdlc; -#endif wan_lip_hdlc_if_conf_t lip_hdlc; }u; @@ -1003,7 +1036,7 @@ typedef struct wan_debug { typedef struct wanpipe_debug_msg_hdr_t { int len; - unsigned long time; + wan_time_t time; } wanpipe_kernel_msg_hdr_t; @@ -1032,7 +1065,7 @@ typedef struct { (usedby == API) ? "API" : \ (usedby == BRIDGE) ? "BRIDGE" : \ (usedby == BRIDGE_NODE) ? "BRIDGE_NODE" : \ - (usedby == SWITCH) ? "SWITCH" : \ + (usedby == WP_SWITCH) ? "SWITCH" : \ (usedby == STACK) ? "STACK" : \ (usedby == TDM_VOICE_API) ? "TDM_VOICE_API" : \ (usedby == TDM_SPAN_VOICE_API) ? "TDM_SPAN_VOICE_API" : \ diff --git a/patches/kdrivers/include/wanpipe_cfg_def.h b/patches/kdrivers/include/wanpipe_cfg_def.h index 0533bff..cc41bfc 100644 --- a/patches/kdrivers/include/wanpipe_cfg_def.h +++ b/patches/kdrivers/include/wanpipe_cfg_def.h @@ -103,6 +103,7 @@ enum { #define WANOPT_AFT_SERIAL 14 #define WANOPT_USB_ANALOG 15 #define WANOPT_AFT600 16 +#define WANOPT_AFT601 17 /* * Configuration options defines. @@ -221,4 +222,8 @@ enum { #define WAN_CLK_OUT_OSC 0x03 #define WAN_CLK_OUT_LINE 0x04 +#define WAN_CLK_IN_8000HZ 0x05 +#define WAN_CLK_IN_2000HZ 0x06 +#define WAN_CLK_IN_1500HZ 0x07 + #endif /* __WANPIPE_CFG_DEF_H__ */ diff --git a/patches/kdrivers/include/wanpipe_cfg_sppp.h b/patches/kdrivers/include/wanpipe_cfg_sppp.h index 8a0e703..5d7cad5 100644 --- a/patches/kdrivers/include/wanpipe_cfg_sppp.h +++ b/patches/kdrivers/include/wanpipe_cfg_sppp.h @@ -41,12 +41,7 @@ typedef struct sppp_parms_struct { unsigned char ppp_prot; /* CHDLC */ -#if defined(__WINDOWS__) - unsigned int sppp_max_keepalive_count; -#else unsigned int keepalive_err_margin; -#endif - unsigned char disable_magic; }wan_sppp_if_conf_t; diff --git a/patches/kdrivers/include/wanpipe_common.h b/patches/kdrivers/include/wanpipe_common.h index 17128e8..da9eb4c 100644 --- a/patches/kdrivers/include/wanpipe_common.h +++ b/patches/kdrivers/include/wanpipe_common.h @@ -8,9 +8,13 @@ /**************************************************************************** * wanpipe_common.h WANPIPE(tm) Multiprotocol WAN Link Driver. * - * Author: Alex Feldman + * Authors: Alex Feldman + * David Rokhvarg * * ========================================================================== + * + * Nov 09, 2009 David Rokhvarg Added wan_get_timestamp() + * * Nov 27, 2007 David Rokhvarg Implemented functions/definitions for * Sangoma MS Windows Driver and API. * @@ -26,6 +30,8 @@ #endif # include "wanpipe_cfg_def.h" +# include "wanpipe_logger.h" + #if defined(__WINDOWS__) # if defined(WAN_KERNEL) # include "wanpipe_skb.h" @@ -43,24 +49,12 @@ allocate_dma_buffer( OUT PVOID *virtual_addr, OUT u32 *physical_addr ); + # endif/* WAN_KERNEL */ -# include - -# if defined(WAN_KERNEL) && defined(NTSTRSAFE_USE_SECURE_CRT) -# define wan_snwprintf RtlStringCbPrintfW -# define wan_strcpy RtlStringCchCopy -# else -# define wan_snwprintf _snwprintf -# endif - -# define strlcpy strncpy -# define strncasecmp _strnicmp -# define strcasecmp _stricmp -# define snprintf _snprintf -# define vsnprintf _vsnprintf -# define unlink _unlink +# include "sang_status_defines.h" #endif/* __WINDOWS__ */ + /**************************************************************************** ** D E F I N E S ****************************************************************************/ @@ -217,6 +211,13 @@ static __inline void WP_MDELAY (u32 ms) { #define WAN_STIMEOUT(start, timeout) \ ((SYSTEM_TICKS - (start)) > ((timeout) * HZ)) + +#define WP_MICROSECONDS_IN_A_TICK() (1000000 / HZ) +#define WP_TICKS_IN_LAST_SECOND() (SYSTEM_TICKS % HZ) + +#define WAN_TICKS_TO_MICROSECONDS() \ + WP_TICKS_IN_LAST_SECOND() * WP_MICROSECONDS_IN_A_TICK() + #if defined(__LINUX__) # define WAN_COPY_FROM_USER(k,u,l) copy_from_user(k,u,l) # define WAN_COPY_TO_USER(u,k,l) copy_to_user(u,k,l) @@ -298,7 +299,16 @@ static __inline void WP_MDELAY (u32 ms) { #elif defined(__WINDOWS__) # define WAN_NET_RATELIMIT() 1 -# define WAN_NETIF_QUEUE_STOPPED(dev) 1 /*test_bit(0,&dev->tbusy)*/ /* must be always stopped so aft_core.c would call wanpipe_wake_stack() */ +# if defined(SPROTOCOL) +# define WAN_NETIF_QUEUE_STOPPED(dev) 0 /* for LIP layer to work, it must be zero */ +# define WAN_NETIF_CARRIER_OFF(dev) netif_carrier_off(dev) +# define WAN_NETIF_CARRIER_ON(dev) netif_carrier_on(dev) +# else +# define WAN_NETIF_QUEUE_STOPPED(dev) 1 /*test_bit(0,&dev->tbusy)*/ /* must be always stopped so aft_core.c would call wanpipe_wake_stack() */ +# define WAN_NETIF_CARRIER_OFF(dev) ((dev)->current_line_state = SANG_STATUS_LINE_DISCONNECTED) +# define WAN_NETIF_CARRIER_ON(dev) ((dev)->current_line_state = SANG_STATUS_LINE_CONNECTED) +# endif + # define WAN_NETIF_STOP_QUEUE(dev) # define WAN_NETIF_START_QUEUE(dev) @@ -308,13 +318,14 @@ static __inline void WP_MDELAY (u32 ms) { # define WAN_NETIF_WAKE_QUEUE(dev) netif_wake_queue(dev) -# define WAN_NETIF_UP(dev) ((dev)->current_line_state == SANG_STATUS_LINE_CONNECTED) -# define WAN_NETIF_CARRIER_OFF(dev) ((dev)->current_line_state = SANG_STATUS_LINE_DISCONNECTED) -# define WAN_NETIF_CARRIER_ON(dev) ((dev)->current_line_state = SANG_STATUS_LINE_CONNECTED) +/*# define WAN_NETIF_UP(dev) ((dev)->current_line_state == SANG_STATUS_LINE_CONNECTED)*/ +# define WAN_NETIF_UP(dev) (1) # define WAN_IFQ_LEN(ifqueue) skb_queue_len(ifqueue) # define NET_ADMIN_CHECK() # define WARN_ON(a) +# define MOD_INC_USE_COUNT +# define MOD_DEC_USE_COUNT #else # error "Undefined WAN_NETIF_x macros!" @@ -452,7 +463,7 @@ static __inline void WP_MDELAY (u32 ms) { # define WAN_HOLD(str) str->refcnt++ #elif defined(__WINDOWS__) -# define WAN_DEV_PUT(dev) wan_atomic_dec(&(dev)->refcnt) +# define WAN_DEV_PUT(dev) wan_atomic_dec(&dev->refcnt) # define WAN_DEV_HOLD(dev) wan_atomic_inc(&(dev)->refcnt) # define __WAN_PUT(str) wan_atomic_dec(&(str)->refcnt) @@ -508,11 +519,6 @@ typedef char *va_list; #define WAN_VA_END(ap) (void) 0 #endif -/* String library definitions */ -#if defined(__LINUX__) -# define strncasecmp strnicmp -# define _snprintf snprintf -#endif /**************************************************************************** ** T Y P E D E F S @@ -589,10 +595,21 @@ void wanpipe_debugging (ulong_ptr_t data); ****************************************************************************/ #ifdef WAN_DEBUG_MEM +#if defined(__WINDOWS__) +int __sdla_memdbg_init(void); +int __sdla_memdbg_free(void); +int __sdla_memdbg_push(void *mem, const char *func_name, const int line, int len); +int __sdla_memdbg_pull(void *mem, const char *func_name, const int line); +int sdla_memdbg_init(void); +int sdla_memdbg_free(void); int sdla_memdbg_push(void *mem, const char *func_name, const int line, int len); int sdla_memdbg_pull(void *mem, const char *func_name, const int line); +#else +int sdla_memdbg_push(void *mem, const char *func_name, const int line, int len); +int sdla_memdbg_pull(void *mem, const char *func_name, const int line); +#endif #endif @@ -983,7 +1000,7 @@ static __inline unsigned long wan_dma_get_paddr(void* card, wan_dma_descr_org_t* /********************** WANPIPE TIMER FUNCTION **************************/ -//static __inline int wan_getcurrenttime(unsigned long *sec, unsigned long *usec) + static __inline int wan_getcurrenttime(wan_time_t *sec, wan_suseconds_t *usec) { #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) @@ -1009,6 +1026,34 @@ static __inline int wan_getcurrenttime(wan_time_t *sec, wan_suseconds_t *usec) #endif } + +static __inline int wan_get_timestamp(wan_time_t *sec, wan_suseconds_t *usec) +{ +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) + struct timeval tv; + microtime(&tv); + if (sec) *sec = tv.tv_sec; + if (usec) *usec = tv.tv_usec; + return 0; +#elif defined(__LINUX__) + struct timeval tv; + jiffies_to_timeval(jiffies,&tv); + if (sec) *sec = tv.tv_sec; + if (usec) *usec = tv.tv_usec; + return 0; +#elif defined(__WINDOWS__) + struct timeval tv; + wp_time(&tv.tv_sec);/* number of seconds elapsed since midnight (00:00:00), January 1, 1970 */ + tv.tv_usec = WAN_TICKS_TO_MICROSECONDS(); + if (sec) *sec = tv.tv_sec; + if (usec) *usec = tv.tv_usec; + return 0; +#else +# error "wan_get_timestamp() function is not supported yet!" +#endif +} + + /* ** wan_init_timer */ @@ -1109,27 +1154,10 @@ wan_add_timer(wan_timer_t* wan_timer, unsigned long delay) wan_timer->timer_func, wan_timer->timer_arg); #elif defined(__WINDOWS__) -#if 0 - LARGE_INTEGER large_int_delay; - - if(0)DEBUG_TIMER("%s(): delay: %u\n", __FUNCTION__, delay); - - /* The 'delay' is in milliseconds - RtlConvertLongToLargeInteger(-10000000L * 1) - this is 1 second. - Converted to milliseconds: - */ - large_int_delay = RtlConvertLongToLargeInteger(-10000L * delay); - - KeSetTimer(&wan_timer->timer_info.Timer, large_int_delay, - &wan_timer->timer_info.TimerDpcObject); -#endif LARGE_INTEGER CurrentTime; - if(0)DBG_ADSL_SYNCH("%s(): delay: %u\n", __FUNCTION__, delay); - /* The 'delay' is in SYSTEM TICKS! */ - /* 10 000 000 * System time is a count of 100-nanosecond intervals * since January 1, 1601. System time is typically @@ -2314,7 +2342,9 @@ static __inline int wan_netif_init(netdevice_t* dev, char* ifname) strcpy(dev->name, ifname); # endif #elif defined(__WINDOWS__) - strncpy(dev->name, ifname, WAN_IFNAME_SZ); + if(ifname){ + strncpy(dev->name, ifname, WAN_IFNAME_SZ); + } #else # error "wan_netif_init() function is not supported yet!" #endif @@ -2533,7 +2563,6 @@ static __inline void* wan_netif_priv(netdevice_t* dev) #endif } - static __inline void wan_netif_set_priv(netdevice_t* dev, void* priv) { WAN_ASSERT1(dev == NULL); @@ -2767,6 +2796,17 @@ static __inline void wan_spin_unlock_irq(void *lock, wan_smp_flag_t *flag) #endif } +/* special locking functions for the Wanpipe Protocol Layer (LIP) */ +#if defined(__WINDOWS__) +# define wplip_spin_lock_irq_init spin_lock_init +# define wplip_spin_lock_irq wp_spin_lock +# define wplip_spin_unlock_irq wp_spin_unlock +#else +# define wplip_spin_lock_irq_init wan_spin_lock_irq_init +# define wplip_spin_lock_irq wan_spin_lock_irq +# define wplip_spin_unlock_irq wan_spin_unlock_irq +#endif + static __inline int wan_spin_trylock(void *lock, wan_smp_flag_t *flag) { #if defined(__LINUX__) diff --git a/patches/kdrivers/include/wanpipe_debug.h b/patches/kdrivers/include/wanpipe_debug.h index bb52905..ad2ebf5 100644 --- a/patches/kdrivers/include/wanpipe_debug.h +++ b/patches/kdrivers/include/wanpipe_debug.h @@ -3,9 +3,16 @@ * wanpipe_debug.h WANPIPE(tm) Global definition for Sangoma * * Debugging messages * * * - * Author: Alex Feldman * + * Authors: Alex Feldman * + * David Rokhvarg * *======================================================================* - * May 10 2002 Alex Feldman Initial version * + * * + * September 21 2009 David Rokhvarg * + * Added Wanpipe Logger definitions. * + * Improved cross-platform macro definitions for driver debugging. * + * * + * May 10 2002 Alex Feldman * + * Initial version * * * ************************************************************************ */ @@ -13,9 +20,16 @@ #ifndef __WANPIPE_DEBUG_H # define __WANPIPE_DEBUG_H + #if defined(WAN_KERNEL) -#define WAN_DEBUG_EVENT +#include "wanpipe_logger.h" + +#undef WAN_DEBUG_TE1 +#undef WAN_DEBUG_HWEC +#undef WAN_DEBUG_TDMAPI +#undef WAN_DEBUG_BRI + #undef WAN_DEBUG_KERNEL #undef WAN_DEBUG_MOD #undef WAN_DEBUG_CFG @@ -30,7 +44,6 @@ #undef WAN_DEBUG_TX_ERROR #undef WAN_DEBUG_TIMER #undef WAN_DEBUG_UDP -#undef WAN_DEBUG_TE1 #undef WAN_DEBUG_56K #undef WAN_DEBUG_A600 #undef WAN_DEBUG_PROCFS @@ -41,546 +54,239 @@ #undef WAN_DEBUG_SNMP #undef WAN_DEBUG_TE3 #undef WAN_DEBUG_RM -#undef WAN_DEBUG_HWEC -#undef WAN_DEBUG_TDMAPI #undef WAN_DEBUG_FE #undef WAN_DEBUG_NG #undef WAN_DEBUG_MEM -#undef WAN_DEBUG_BRI #undef WAN_DEBUG_BRI_INIT #undef WAN_DEBUG_USB #undef WAN_DEBUG_FUNC -#if defined (__WINDOWS__) -void OutputLogString(PUCHAR pvFormat, ...); - -# define DEBUG_NONE -#if 1 -# define PRINT OutputLogString -#else -# define PRINT if(1)DbgPrint -#endif - - -# define DEBUG_PRINT DbgPrint -# define _DEBUG_PRINT DbgPrint - -#define _DEBUG_EVENT DbgPrint - -# define DEBUG_KERNEL DEBUG_NONE -# define DEBUG_EVENT DEBUG_NONE -# define DEBUG_MOD DEBUG_NONE -# define DEBUG_CFG DEBUG_NONE -# define DEBUG_REG DEBUG_NONE -# define DEBUG_INIT DEBUG_NONE -# define DEBUG_IOCTL DEBUG_NONE -# define DEBUG_CMD DEBUG_NONE -# define DEBUG_ISR DEBUG_NONE -# define DEBUG_RX DEBUG_NONE -# define DEBUG_RX_ERR DEBUG_NONE -# define DEBUG_TX DEBUG_NONE -# define _DEBUG_TX DEBUG_NONE -# define DEBUG_TX_ERR DEBUG_NONE -# define DEBUG_TIMER DEBUG_NONE -# define DEBUG_UDP DEBUG_NONE -# define DEBUG_TE1 DEBUG_NONE -# define DEBUG_TE3 DEBUG_NONE -# define DEBUG_56K DEBUG_NONE -# define DEBUG_A600 DEBUG_NONE -# define DEBUG_BRI DEBUG_NONE -# define DEBUG_PROCFS DEBUG_NONE -# define DEBUG_TDMV DEBUG_NONE -# define DEBUG_TEST DEBUG_NONE -# define DEBUG_DBG DEBUG_NONE -# define DEBUG_DMA DEBUG_NONE -# define DEBUG_SNMP DEBUG_NONE -# define DEBUG_RM DEBUG_NONE -# define DEBUG_HWEC DEBUG_NONE -# define DEBUG_TDMAPI DEBUG_NONE -# define DEBUG_FE DEBUG_NONE -# define DEBUG_NG DEBUG_NONE -# define WAN_DEBUG_FUNC_START DEBUG_NONE -# define WAN_DEBUG_FUNC_END DEBUG_NONE -# define WAN_DEBUG_FUNC_LINE DEBUG_NONE -# define DEBUG_BRI DEBUG_NONE -# define DEBUG_BRI_INIT DEBUG_NONE - -# ifdef WAN_DEBUG_KERNEL -# undef DEBUG_KERNEL -# define DEBUG_KERNEL DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_EVENT -# undef DEBUG_EVENT -# define DEBUG_EVENT PRINT -# endif -# ifdef WAN_DEBUG_MOD -# undef DEBUG_MOD -# define DEBUG_MOD DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_CFG -# undef DEBUG_CFG -# define DEBUG_CFG DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_REG -# undef DEBUG_REG -# define DEBUG_REG DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_INIT_VAR -# undef DEBUG_INIT -# define DEBUG_INIT DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_IOCTL -# undef DEBUG_IOCTL -# define DEBUG_IOCTL DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_CMD -# undef DEBUG_CMD -# define DEBUG_CMD DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_ISR -# undef DEBUG_ISR -# define DEBUG_ISR DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_RX -# undef DEBUG_RX -# define DEBUG_RX DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_RX_ERROR -# undef DEBUG_RX_ERR -# define DEBUG_RX_ERR DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_TX -# undef DEBUG_TX -# define DEBUG_TX DEBUG_PRINT -# undef _DEBUG_TX -# define _DEBUG_TX _DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_TX_ERROR -# undef DEBUG_TX_ERR -# define DEBUG_TX_ERR DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_TIMER -# undef DEBUG_TIMER -# define DEBUG_TIMER DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_UDP -# undef DEBUG_UDP -# define DEBUG_UDP DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_TE1 -# undef DEBUG_TE1 -# define DEBUG_TE1 DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_TE3 -# undef DEBUG_TE3 -# define DEBUG_TE3 DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_56K -# undef DEBUG_56K -# define DEBUG_56K DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_A600 -# undef DEBUG_A600 -# define DEBUG_A600 DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_BRI -# undef DEBUG_BRI -# define DEBUG_BRI DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_PROCFS -# undef DEBUG_PROCFS -# define DEBUG_PROCFS DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_TDM_VOICE -# undef DEBUG_TDMV -# define DEBUG_TDMV DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_TEST -# undef DEBUG_TEST -# define DEBUG_TEST DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_DBG -# undef DEBUG_DBG -# define DEBUG_DBG DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_DMA -# undef DEBUG_DMA -# define DEBUG_DMA DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_SNMP -# undef DEBUG_SNMP -# define DEBUG_SNMP DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_RM -# undef DEBUG_RM -# define DEBUG_RM DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_FE -# undef DEBUG_FE -# define DEBUG_FE DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_HWEC -# undef DEBUG_HWEC -# define DEBUG_HWEC DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_TDMAPI -# undef DEBUG_TDMAPI -# define DEBUG_TDMAPI DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_NG -# undef DEBUG_NG -# define DEBUG_NG DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_BRI -# undef DEBUG_BRI -# define DEBUG_BRI DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_BRI_INIT -# undef DEBUG_BRI_INIT -# define DEBUG_BRI_INIT DEBUG_PRINT -# endif -# ifdef WAN_DEBUG_USB -# undef DEBUG_USB -# define DEBUG_USB DEBUG_PRINT -# endif - -//uncomment -DSANG_DBG in C_DEFINES in Sources to get debug output -#if defined(SANG_DBG) - //print driver name and a line - #define DbgOut(_f_, _x_) if(_f_) { DbgPrint("%s: ", Globals.DriverName); DbgPrint _x_;} - //print a line. used when driver name is not needed - #define DbgOutL(_f_, _x_) if(_f_) { DbgPrint _x_;} - #define BUS_DEFAULT_DEBUG_OUTPUT_LEVEL 0x000FFFFF -#else //#if SANG_DBG - #define BUS_DEFAULT_DEBUG_OUTPUT_LEVEL 0x0 - #define DbgOut(_f_, _x_) - #define DbgOutL(_f_, _x_) -#endif//#if SANG_DBG - -extern ULONG BusEnumDebugLevel; - -# define DEBUG_ADD_MEM -# define DEBUG_SUB_MEM - -# define splimp() 0 -# define splx(l) - -#define ERR_DBG_OUT if(0)DbgPrint -#define DBG_NOT_IMPL if(0)DbgPrint -#define FUNC_NOT_IMPL() if(0)DbgPrint("%s()-Not Implemented(File:%s, Line:%i)\n", __FUNCTION__, __FILE__, __LINE__) -#define DBG_DSL_NOT_IMPLD FUNC_NOT_IMPL() - -/* debugging of SngBus.sys */ -#define DEBUG_SNGBUS if(0)DbgPrint - -#define DBG_SINGLE_IRQLOCK if(0)DbgPrint - -/* debugging of wanpipe_kernel.h */ -#define DBG_KRN if(0)DbgPrint -#define DBG_COPY_FROM_TO_USER if(0)DbgPrint -#define DBG_SHUTDOWN if(0)DbgPrint - -#define DBG_FAST_TX if(0)DbgPrint - -#define DEBUG_SHARED_EVENT if(0)DbgPrint -#define DBG_ADSL_TX if(0)DbgPrint -#define DBG_IRQLOCK if(0)DbgPrint -#define DBG_ADSL_FAST_TX if(0)DbgPrint - -#define DBG_SET_CFG if(0)DbgPrint -#define DBG_ADSL_INIT if(0)DbgPrint - -#define DBG_A200_INIT if(0)DbgPrint - -#define DBG_LIP_OOB if(0)DbgPrint -#define DEBUG_XLNX_AFT if(0)DbgPrint -#define DEBUG_AFT if(0)DbgPrint - -#define DBG_BSTRM if(0)DbgPrint -#define DEBUG_FIRMWARE_UPDATE if(0)DbgPrint -#define DBG_ADSL_RX if(0)DbgPrint - -#define DBG_FE_LOCK if(0)DbgPrint -#define DBG_DRVSTOP if(0)DbgPrint -#define DBG_GET_REGISTRY if(0)DbgPrint -#define DBG_FE_LOCK if(0)DbgPrint - -#define DEBUG_RX_FIFO if(0)DbgPrint - -/* sprotocol.sys */ -#define DEBUG_LIP if(0)DbgPrint -#define DBG_LIP_SKB if(0)DbgPrint -/* wanpipe.sys */ -#define DEBUG_REQUEST if(0)DbgPrint -#define DEBUG_IF_TX if(0)DbgPrint -#define DEBUG_COMMON if(0)DbgPrint -#define DEBUG_IF_RX if(0)DbgPrint -#define DEBUG_NET_IF if(0)DbgPrint - -#define DBG_RBS_EVENT if(0)DbgPrint - -#define DBG_ADSL_MACADDR if(0)DbgPrint -#define DBG_ADSL_SYNCH if(0)DbgPrint - -#define DBG_MEM if(0)DbgPrint - -#define DBG_PNP if(0)DbgPrint(DRIVER_NAME);if(0)DbgPrint - -#define DBG_SYMB_LINK if(0)DbgPrint - -#define DBG_BUFLEN if(0)DbgPrint - -#define ADSL_TEST 0 - -/* These are defined in "sources" file of each driver */ -#if defined( VIRTUAL_IF_DRV ) - #define DRIVER_NAME "SDLADRV" -#elif defined( BUSENUM_DRV ) - #define DRIVER_NAME "SngBus" -#elif defined( NDIS_MINIPORT_DRIVER ) - #define DRIVER_NAME "WANPIPE" -#elif defined( SPROTOCOL ) - #define DRIVER_NAME "SPROTOCOL" -#endif - -#define DBG_BUFFER_LEN 512 - -static void my_func_dbg(char *drv_name, char *func, char *file, int line) -{ - DbgPrint("%s:%s(): File: %s, Line: %d.\n", drv_name, func, file, line); -} - -//#define AFT_FUNC_DEBUG() if(0)my_func_dbg(DRIVER_NAME, __FUNCTION__, __FILE__, __LINE__) -#define TDM_FUNC_DBG() if(0)my_func_dbg(DRIVER_NAME, __FUNCTION__, __FILE__, __LINE__) -#define EC_FUNC_DEBUG() if(0)my_func_dbg(DRIVER_NAME, __FUNCTION__, __FILE__, __LINE__) -#define DBG_SET_CFG_FUNC() if(0)my_func_dbg(DRIVER_NAME, __FUNCTION__, __FILE__, __LINE__) -#define DBG_ACUAPI_FUNC() if(0)my_func_dbg(DRIVER_NAME, __FUNCTION__, __FILE__, __LINE__) -#define FUNC_DEBUG() if(0)my_func_dbg(DRIVER_NAME, __FUNCTION__, __FILE__, __LINE__) -#define TDM_FUNC_DBG() if(0)my_func_dbg(DRIVER_NAME, __FUNCTION__, __FILE__, __LINE__) -#define SKB_FUNC() if(0)my_func_dbg(DRIVER_NAME, __FUNCTION__, __FILE__, __LINE__) -#define PROT_FUNC_DEBUG() if(0)my_func_dbg(DRIVER_NAME, __FUNCTION__, __FILE__, __LINE__) -#define TDMAPI_FUNC_DEBUG() if(0)my_func_dbg(DRIVER_NAME, __FUNCTION__, __FILE__, __LINE__) - -#else /* !__WINDOWS__*/ - -/* L I N U X */ - -# define DEBUG_KERNEL(format,msg...) -# define DEBUG_EVENT(format,msg...) -# define DEBUG_MOD(format,msg...) -# define DEBUG_CFG(format,msg...) -# define DEBUG_REG(format,msg...) -# define DEBUG_INIT(format,msg...) -# define DEBUG_IOCTL(format,msg...) -# define DEBUG_CMD(format,msg...) -# define DEBUG_ISR(format,msg...) -# define DEBUG_RX(format,msg...) -# define DEBUG_RX_ERR(format,msg...) -# define DEBUG_TX(format,msg...) -# define _DEBUG_TX(format,msg...) -# define DEBUG_TX_ERR(format,msg...) -# define DEBUG_TIMER(format,msg...) -# define DEBUG_UDP(format,msg...) -# define DEBUG_TE1(format,msg...) -# define DEBUG_TE3(format,msg...) -# define DEBUG_56K(format,msg...) -# define DEBUG_BRI(format,msg...) -# define DEBUG_PROCFS(format,msg...) -# define DEBUG_TDMV(format,msg...) -# define DEBUG_A600(format,msg...) -# define DEBUG_TEST(format,msg...) -# define DEBUG_DBG(format,msg...) -# define DEBUG_DMA(format,msg...) -# define DEBUG_SNMP(format,msg...) -# define DEBUG_ADD_MEM(a) -# define DEBUG_SUB_MEM(a) -# define DEBUG_RM(format,msg...) -# define DEBUG_HWEC(format,msg...) -# define DEBUG_TDMAPI(format,msg...) -# define DEBUG_FE(format,msg...) -# define DEBUG_NG(format,msg...) -# define WAN_DEBUG_FUNC_START -# define WAN_DEBUG_FUNC_END -# define WAN_DEBUG_FUNC_LINE -# define DEBUG_BRI(format,msg...) -# define DEBUG_BRI_INIT(format,msg...) -# define DEBUG_USB(format,msg...) +#define WAN_DEBUG_EVENT /* must be defined for wpabs_debug_event() */ #define AFT_FUNC_DEBUG() - -# if (defined __FreeBSD__) || (defined __OpenBSD__) || defined(__NetBSD__) - -# define DEBUG_PRINT(format,msg...) log(LOG_INFO, format, ##msg) -# define _DEBUG_PRINT(format,msg...) log(LOG_INFO, format, ##msg) - -# else /* !__FreeBSD__ && !__OpenBSD__ */ - -# define DEBUG_PRINT(format,msg...) printk(KERN_INFO format, ##msg) -# define _DEBUG_PRINT(format,msg...) printk(format,##msg) - -# endif /* __FreeBSD__ || __OpenBSD__ */ - -# ifdef WAN_DEBUG_KERNEL -# undef DEBUG_KERNEL -# define DEBUG_KERNEL(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_EVENT -# undef DEBUG_EVENT -# define DEBUG_EVENT(format,msg...) DEBUG_PRINT(format,##msg) -# undef _DEBUG_EVENT -# define _DEBUG_EVENT(format,msg...) _DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_MOD -# undef DEBUG_MOD -# define DEBUG_MOD(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_CFG -# undef DEBUG_CFG -# define DEBUG_CFG(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_REG -# undef DEBUG_REG -# define DEBUG_REG(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_INIT_VAR -# undef DEBUG_INIT -# define DEBUG_INIT(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_IOCTL -# undef DEBUG_IOCTL -# define DEBUG_IOCTL(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_CMD -# undef DEBUG_CMD -# define DEBUG_CMD(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_ISR -# undef DEBUG_ISR -# define DEBUG_ISR(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_RX -# undef DEBUG_RX -# define DEBUG_RX(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_RX_ERROR -# undef DEBUG_RX_ERR -# define DEBUG_RX_ERR(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_TX -# undef DEBUG_TX -# define DEBUG_TX(format,msg...) DEBUG_PRINT(format,##msg) -# undef _DEBUG_TX -# define _DEBUG_TX(format,msg...) _DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_TX_ERROR -# undef DEBUG_TX_ERR -# define DEBUG_TX_ERR(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_TIMER -# undef DEBUG_TIMER -# define DEBUG_TIMER(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_UDP -# undef DEBUG_UDP -# define DEBUG_UDP(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_TE1 -# undef DEBUG_TE1 -# define DEBUG_TE1(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_TE3 -# undef DEBUG_TE3 -# define DEBUG_TE3(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_56K -# undef DEBUG_56K -# define DEBUG_56K(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_BRI -# undef DEBUG_BRI -# define DEBUG_BRI(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_A600 -# undef DEBUG_A600 -# define DEBUG_A600(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_PROCFS -# undef DEBUG_PROCFS -# define DEBUG_PROCFS(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_TDM_VOICE -# undef DEBUG_TDMV -# define DEBUG_TDMV(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_TEST -# undef DEBUG_TEST -# define DEBUG_TEST(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_DBG -# undef DEBUG_DBG -# define DEBUG_DBG(format,msg...) DEBUG_PRINT(format,##msg) -# endif - -#if 0 -# ifdef WAN_DEBUG_MEM -/* This is not used any more */ -# undef DEBUG_ADD_MEM -# define DEBUG_ADD_MEM(a) -# undef DEBUG_SUB_MEM -# define DEBUG_SUB_MEM(a) -#endif -#endif - -# ifdef WAN_DEBUG_DMA -# undef DEBUG_DMA -# define DEBUG_DMA(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_SNMP -# undef DEBUG_SNMP -# define DEBUG_SNMP(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_RM -# undef DEBUG_RM -# define DEBUG_RM(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_HWEC -# undef DEBUG_HWEC -# define DEBUG_HWEC(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_TDMAPI -# undef DEBUG_TDMAPI -# define DEBUG_TDMAPI(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_FE -# undef DEBUG_FE -# define DEBUG_FE(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_BRI -# undef DEBUG_BRI -# define DEBUG_BRI(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_BRI_INIT -# undef DEBUG_BRI_INIT -# define DEBUG_BRI_INIT(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_NG -# undef DEBUG_NG -# define DEBUG_NG(format,msg...) DEBUG_PRINT(format,##msg) -# endif -# ifdef WAN_DEBUG_USB -# undef DEBUG_USB -# define DEBUG_USB(format,msg...) DEBUG_PRINT(format,##msg) -# endif - -#endif /* __WINDOWS__ */ +#define WAN_KRN_BREAK_POINT() #if defined (__WINDOWS__) -#define WAN_KRN_BREAK_POINT() if(0)DbgBreakPoint() -#else -#define WAN_KRN_BREAK_POINT() +extern void OutputLogString(const char *fmt, ...); /* Print to wanpipelog.txt (NOT to Debugger). */ +# define DEBUG_PRINT(...) OutputLogString(## __VA_ARGS__) +# define _DEBUG_PRINT(...) OutputLogString(## __VA_ARGS__) + +# undef WAN_KRN_BREAK_POINT +# define WAN_KRN_BREAK_POINT() if(0)DbgBreakPoint() #endif +#if (defined __FreeBSD__) || (defined __OpenBSD__) || defined(__NetBSD__) +# define DEBUG_PRINT(format,msg...) log(LOG_INFO, format, ##msg) +# define _DEBUG_PRINT(format,msg...) log(LOG_INFO, format, ##msg) +#endif + +#if defined (__LINUX__) +# define DEBUG_PRINT(...) printk(KERN_INFO ## __VA_ARGS__) +# define _DEBUG_PRINT(...) printk(## __VA_ARGS__) +#endif + + +/*======================================================== + COMMON CODE + *========================================================*/ + +#define DEBUG_KERNEL(...) +#define DEBUG_MOD(...) +#define DEBUG_CFG(...) +#define DEBUG_REG(...) +#define DEBUG_INIT(...) +#define DEBUG_IOCTL(...) +#define DEBUG_CMD(...) +#define DEBUG_ISR(...) +#define DEBUG_RX(...) +#define DEBUG_RX_ERR(...) +#define DEBUG_TX(...) +#define _DEBUG_TX(...) +#define DEBUG_TX_ERR(...) +#define DEBUG_TIMER(...) +#define DEBUG_UDP(...) +#define DEBUG_TE3(...) +#define DEBUG_56K(...) +#define DEBUG_A600(...) +#define DEBUG_PROCFS(...) +#define DEBUG_TDMV(...) +#define DEBUG_TEST(...) +#define DEBUG_DBG(...) +#define DEBUG_DMA(...) +#define DEBUG_SNMP(...) +#define DEBUG_RM(...) +#define DEBUG_NG(...) +#define DEBUG_BRI_INIT(...) +#define DEBUG_USB(...) +#define _DEBUG_EVENT(...) + +#define DEBUG_ADD_MEM(a) +#define DEBUG_SUB_MEM(a) +#define WAN_DEBUG_FUNC_START +#define WAN_DEBUG_FUNC_END +#define WAN_DEBUG_FUNC_LINE + + +#if 0 +# undef _DEBUG_EVENT +# if 1 +# define _DEBUG_EVENT(...) _DEBUG_PRINT(...) +# else +# define _DEBUG_EVENT(format,msg...) _DEBUG_PRINT(format,##msg) +# endif +#endif + +#ifdef WAN_DEBUG_KERNEL +# undef DEBUG_KERNEL +# define DEBUG_KERNEL(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_MOD +# undef DEBUG_MOD +# define DEBUG_MOD(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_CFG +# undef DEBUG_CFG +# define DEBUG_CFG(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_REG +# undef DEBUG_REG +# define DEBUG_REG(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_INIT_VAR +# undef DEBUG_INIT +# define DEBUG_INIT(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_IOCTL +# undef DEBUG_IOCTL +# define DEBUG_IOCTL(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_CMD +# undef DEBUG_CMD +# define DEBUG_CMD(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_ISR +# undef DEBUG_ISR +# define DEBUG_ISR(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_RX +# undef DEBUG_RX +# define DEBUG_RX(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_RX_ERROR +# undef DEBUG_RX_ERR +# define DEBUG_RX_ERR DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_TX +# undef DEBUG_TX +# define DEBUG_TX(...) DEBUG_PRINT(## __VA_ARGS__) +# undef _DEBUG_TX +# define _DEBUG_TX(...) _DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_TX_ERROR +# undef DEBUG_TX_ERR +# define DEBUG_TX_ERR(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_TIMER +# undef DEBUG_TIMER +# define DEBUG_TIMER(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_UDP +# undef DEBUG_UDP +# define DEBUG_UDP(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_TE3 +# undef DEBUG_TE3 +# define DEBUG_TE3(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_56K +# undef DEBUG_56K +# define DEBUG_56K(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_A600 +# undef DEBUG_A600 +# define DEBUG_A600(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_PROCFS +# undef DEBUG_PROCFS +# define DEBUG_PROCFS(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_TDM_VOICE +# undef DEBUG_TDMV +# define DEBUG_TDMV(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_TEST +# undef DEBUG_TEST +# define DEBUG_TEST(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_DBG +# undef DEBUG_DBG +# define DEBUG_DBG(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_DMA +# undef DEBUG_DMA +# define DEBUG_DMA(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_SNMP +# undef DEBUG_SNMP +# define DEBUG_SNMP(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_RM +# undef DEBUG_RM +# define DEBUG_RM(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_NG +# undef DEBUG_NG +# define DEBUG_NG(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_BRI_INIT +# undef DEBUG_BRI_INIT +# define DEBUG_BRI_INIT(...) DEBUG_PRINT(## __VA_ARGS__) +#endif +#ifdef WAN_DEBUG_USB +# undef DEBUG_USB +# define DEBUG_USB(...) DEBUG_PRINT(## __VA_ARGS__) +#endif + + +/*=================================================*/ +/* general Wanpipe Logger macros */ +#define DEBUG_EVENT(...) \ + WP_DEBUG(WAN_LOGGER_DEFAULT, SANG_LOGGER_INFORMATION, ## __VA_ARGS__) +#define DEBUG_WARNING(...) \ + WP_DEBUG(WAN_LOGGER_DEFAULT, SANG_LOGGER_WARNING, ## __VA_ARGS__) +#define DEBUG_ERROR(...) \ + WP_DEBUG(WAN_LOGGER_DEFAULT, SANG_LOGGER_ERROR, ## __VA_ARGS__) + +/***************************************/ +/* task-specific Wanpipe Logger macros */ + +/* T1/E1 */ +#define DEBUG_TE1(...) \ + WP_DEBUG(WAN_LOGGER_TE1, SANG_LOGGER_TE1_DEFAULT, ## __VA_ARGS__) + +/* Hardware Echo Canceller */ +#define DEBUG_HWEC(...) \ + WP_DEBUG(WAN_LOGGER_HWEC, SANG_LOGGER_HWEC_DEFAULT, ## __VA_ARGS__) + +/* TDM API */ +#define DEBUG_TDMAPI(...) \ + WP_DEBUG(WAN_LOGGER_TDMAPI, SANG_LOGGER_TDMAPI_DEFAULT, ## __VA_ARGS__) + +/* General Front End code */ +#define DEBUG_FE(...) \ + WP_DEBUG(WAN_LOGGER_FE, SANG_LOGGER_FE_DEFAULT, ## __VA_ARGS__) + +/* BRI */ +#define DEBUG_BRI(...) \ + WP_DEBUG(WAN_LOGGER_BRI, SANG_LOGGER_BRI_DEFAULT, ## __VA_ARGS__) +#define DEBUG_HFC_S0_STATES(...) \ + WP_DEBUG(WAN_LOGGER_BRI, SANG_LOGGER_BRI_HFC_S0_STATES, ## __VA_ARGS__) + +/*==== End of Wanpipe Logger macro definitions ====*/ +/*=================================================*/ + + #define WAN_DEBUG_FLINE DEBUG_EVENT("[%s]: %s:%d\n", \ __FILE__,__FUNCTION__,__LINE__); @@ -597,8 +303,8 @@ static void my_func_dbg(char *drv_name, char *func, char *file, int line) #define BRI_FUNC() if(0)DEBUG_EVENT("%s(): line:%d\n", __FUNCTION__, __LINE__) #else -#define BRI_FUNC() -#endif +# define BRI_FUNC() +#endif /* WAN_DEBUG_FUNC */ #define WAN_ASSERT(val) if (val){ \ DEBUG_EVENT("************** ASSERT FAILED **************\n"); \ @@ -824,14 +530,6 @@ static void my_func_dbg(char *drv_name, char *func, char *file, int line) #endif -#if defined(__WINDOWS__) -#define DBG_NEWDRV if(0)DbgPrint -#define FUNC_NEWDRV() if(0)DbgPrint("%s():Line:%i\n", __FUNCTION__, __LINE__) -#else -#define DBG_NEWDRV if(1)DEBUG_TEST -#define FUNC_NEWDRV() if(1)DEBUG_TEST("%s():Line:%i\n", __FUNCTION__, __LINE__) -#endif - static __inline void debug_print_skb_pkt(unsigned char *name, unsigned char *data, int len, int direction) { #if defined(__LINUX__) && defined(__KERNEL__) diff --git a/patches/kdrivers/include/wanpipe_defines.h b/patches/kdrivers/include/wanpipe_defines.h index 207b358..eb597c8 100644 --- a/patches/kdrivers/include/wanpipe_defines.h +++ b/patches/kdrivers/include/wanpipe_defines.h @@ -29,13 +29,38 @@ #if defined(WAN_KERNEL) #include "wanpipe_kernel.h" #endif -#include "wanpipe_abstr_types.h" +#include "wanpipe_abstr_types.h" /* Basic data types */ + #if defined(__WINDOWS__) -# include "wanpipe_ctypes.h" /* Basic data types */ -# if defined(WAN_KERNEL) +# if defined(WAN_KERNEL) # include "wanpipe_skb.h" -# endif +# define inline __inline +# if defined(NTSTRSAFE_USE_SECURE_CRT) +# define wp_snwprintf RtlStringCbPrintfW +# define wp_strcpy RtlStringCchCopy +# else +# define wp_snwprintf _snwprintf +# endif/* NTSTRSAFE_USE_SECURE_CRT */ +# endif/* WAN_KERNEL */ + +# define wp_strlcpy strncpy +# define wp_strncasecmp _strnicmp +# define wp_strcasecmp _stricmp +# define wp_snprintf _snprintf +# define wp_vsnprintf _vsnprintf +# define wp_unlink _unlink +#else/* ! __WINDOWS__ */ +# define wp_strlcpy strlcpy +# define wp_strncasecmp strnicmp +# define wp_strcasecmp strcasecmp +# define wp_snprintf snprintf +# define wp_vsnprintf vsnprintf +# define wp_unlink unlink +# define wp_sleep sleep +# define wp_gettimeofday gettimeofday +# define wp_localtime_r localtime_r +# define wp_usleep usleep #endif @@ -141,9 +166,6 @@ typedef struct tcphdr tcphdr_t; # define w_tcp_seq seq # define w_tcp_ack_seq ack_seq -# define wan_time_t unsigned long -# define wan_suseconds_t unsigned long - #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) typedef struct ip iphdr_t; typedef struct udphdr udphdr_t; @@ -237,9 +259,6 @@ typedef struct udphdr udphdr_t; # define w_tcp_seq seq # define w_tcp_ack_seq ack_seq -# define wan_time_t unsigned long -# define wan_suseconds_t unsigned long - #if !defined snprintf # define snprintf _snprintf #endif @@ -346,7 +365,25 @@ typedef u_long wan_ioctl_cmd_t; #elif defined(__LINUX__) /*********************** L I N U X ******************************/ # define ETHER_ADDR_LEN ETH_ALEN -# define WP_DELAY(usecs) udelay(usecs) + +static __inline void WP_DELAY(int usecs) +{ + if ((usecs) <= 1000) { + udelay(usecs) ; + } else { + int delay=usecs/1000; + int i; + if (delay < 1) { + delay=1; + } + for (i=0;inetdev_ops = &_ops_name + +#define WAN_NETDEV_OPS_INIT(dev,ops,wan_init) ops.ndo_init = wan_init +#define WAN_NETDEV_OPS_OPEN(dev,ops,wan_open) ops.ndo_open = wan_open +#define WAN_NETDEV_OPS_STOP(dev,ops,wan_stop) ops.ndo_stop = wan_stop +#define WAN_NETDEV_OPS_XMIT(dev,ops,wan_send) ops.ndo_start_xmit = wan_send +#define WAN_NETDEV_OPS_STATS(dev,ops,wan_stats) ops.ndo_get_stats = wan_stats +#define WAN_NETDEV_OPS_TIMEOUT(dev,ops,wan_timeout) ops.ndo_tx_timeout = wan_timeout +#define WAN_NETDEV_OPS_IOCTL(dev,ops,wan_ioctl) ops.ndo_do_ioctl = wan_ioctl +#define WAN_NETDEV_OPS_MTU(dev,ops,wan_mtu) ops.ndo_change_mtu = wan_mtu +#define WAN_NETDEV_OPS_CONFIG(dev,ops,wan_set_config) ops.ndo_set_config = wan_set_config +#define WAN_NETDEV_OPS_SET_MULTICAST_LIST(dev,ops,wan_multicast_list) ops.ndo_set_multicast_list = wan_multicast_list +//#define WAN_CHANGE_MTU(dev) dev->netdev_ops->ndo_change_mtu +//#define WAN_XMIT(dev) dev->netdev_ops->ndo_start_xmit +//#define WAN_IOCTL(dev) dev->netdev_ops->ndo_do_ioctl +#define WAN_NETDEV_TEST_XMIT(dev) dev->netdev_ops->ndo_start_xmit +#define WAN_NETDEV_XMIT(skb,dev) dev->netdev_ops->ndo_start_xmit(skb,dev) +#define WAN_NETDEV_TEST_IOCTL(dev) dev->netdev_ops->ndo_do_ioctl +#define WAN_NETDEV_IOCTL(dev,ifr,cmd) dev->netdev_ops->ndo_do_ioctl(dev,ifr,cmd) +#define WAN_NETDEV_TEST_MTU(dev) dev->netdev_ops->ndo_change_mtu +#define WAN_NETDEV_CHANGE_MTU(dev,skb) dev->netdev_ops->ndo_change_mtu(dev,skb) + +#else + +#define WAN_DECLARE_NETDEV_OPS(_ops_name) +#define WAN_NETDEV_OPS_BIND(dev,_ops_name) +#define WAN_NETDEV_OPS_INIT(dev,ops,wan_init) dev->init = wan_init +#define WAN_NETDEV_OPS_OPEN(dev,ops,wan_open) dev->open = wan_open +#define WAN_NETDEV_OPS_STOP(dev,ops,wan_stop) dev->stop = wan_stop +#define WAN_NETDEV_OPS_XMIT(dev,ops,wan_send) dev->hard_start_xmit = wan_send +#define WAN_NETDEV_OPS_STATS(dev,ops,wan_stats) dev->get_stats = wan_stats +#define WAN_NETDEV_OPS_TIMEOUT(dev,ops,wan_timeout) dev->tx_timeout = wan_timeout +#define WAN_NETDEV_OPS_IOCTL(dev,ops,wan_ioctl) dev->do_ioctl = wan_ioctl +#define WAN_NETDEV_OPS_MTU(dev,ops,wan_mtu) dev->change_mtu = wan_mtu +#define WAN_NETDEV_OPS_CONFIG(dev,ops,wan_set_config) dev->set_config = wan_set_config +#define WAN_NETDEV_OPS_SET_MULTICAST_LIST(dev,ops,wan_multicast_list) dev->set_multicast_list = wan_multicast_list +//#define WAN_CHANGE_MTU(dev) dev->change_mtu +//#define WAN_XMIT(dev) dev->hard_start_xmit +//#define WAN_IOCTL(dev) dev->do_ioctl +#define WAN_NETDEV_TEST_XMIT(dev) dev->hard_start_xmit +#define WAN_NETDEV_XMIT(skb,dev) dev->hard_start_xmit(skb,dev) +#define WAN_NETDEV_TEST_IOCTL(dev) dev->do_ioctl +#define WAN_NETDEV_IOCTL(dev,ifr,cmd) dev->do_ioctl(dev,ifr,cmd) +#define WAN_NETDEV_TEST_MTU(dev) dev->change_mtu +#define WAN_NETDEV_CHANGE_MTU(dev,skb) dev->change_mtu(dev,skb) + +#endif /* HAVE_NET_DEVICE_OPS */ + #endif /* KERNEL */ #endif /* __WANPIPE_DEFINES_H */ diff --git a/patches/kdrivers/include/wanpipe_edac_iface.h b/patches/kdrivers/include/wanpipe_edac_iface.h index 5b08ada..dee8f64 100644 --- a/patches/kdrivers/include/wanpipe_edac_iface.h +++ b/patches/kdrivers/include/wanpipe_edac_iface.h @@ -1,3 +1,4 @@ +/* wanpipe_edac_iface.h */ #ifndef __WANPIPE_EDAC_IFACE_H #define __WANPIPE_EDAC_IFACE_H diff --git a/patches/kdrivers/include/wanpipe_events.h b/patches/kdrivers/include/wanpipe_events.h index f0b7d4d..344eac7 100644 --- a/patches/kdrivers/include/wanpipe_events.h +++ b/patches/kdrivers/include/wanpipe_events.h @@ -97,7 +97,7 @@ #define WAN_EVENT_EC_DTMF 0x0001 /* WAN_EVENT_EC_TONE_DTMF */ #define WAN_EVENT_RM_POWER 0x0002 #define WAN_EVENT_RM_LC 0x0003 -#define WAN_EVENT_RM_RING_TRIP 0x0004 +#define WAN_EVENT_RM_RING_TRIP 0x0004 #define WAN_EVENT_RM_DTMF 0x0005 #define WAN_EVENT_TE_RBS 0x0006 #define WAN_EVENT_RM_RING 0x0007 @@ -115,6 +115,9 @@ #define WAN_EVENT_BRI_CHAN_LOOPBACK 0x0013 #define WAN_EVENT_LINK_STATUS 0x0014 #define WAN_EVENT_RM_POLARITY_REVERSE 0x0016 +#define WAN_EVENT_EC_FAX_DETECT 0x0017 +#define WAN_EVENT_RM_SET_TX_GAIN 0x0018 +#define WAN_EVENT_RM_SET_RX_GAIN 0x0019 #define WAN_EVENT_TYPE_DECODE(type) \ @@ -138,6 +141,9 @@ ((type) == WAN_EVENT_BRI_CHAN_LOOPBACK) ? "BRI B-Chan Loopback" : \ ((type) == WAN_EVENT_LINK_STATUS) ? "Link Status" : \ ((type) == WAN_EVENT_RM_POLARITY_REVERSE) ? "RM Polarity Reverse" : \ + ((type) == WAN_EVENT_EC_FAX_DETECT) ? "EC FAX Detect" : \ + ((type) == WAN_EVENT_RM_SET_TX_GAIN) ? "RM Set Tx Gain" : \ + ((type) == WAN_EVENT_RM_SET_TX_GAIN) ? "RM Set Rx Gain" : \ "(Unknown type)" /* tone type list */ @@ -178,11 +184,12 @@ typedef struct wan_event_ctrl_ u_int8_t mode; int mod_no; /* A200-Remora */ int channel; - unsigned char ec_tone_port; /* EC DTMF: SOUT or ROUT */ + unsigned char ec_tone_port; /* EC Tone: SOUT or ROUT */ unsigned long ts_map; u_int8_t tone; int ohttimer; /* On-hook transfer */ int polarity; /* SETPOLARITY */ + signed int rm_gain; /* RM GAIN VALUE */ #if !defined(__WINDOWS__) WAN_LIST_ENTRY(wan_event_ctrl_) next; #endif diff --git a/patches/kdrivers/include/wanpipe_fr_iface.h b/patches/kdrivers/include/wanpipe_fr_iface.h index cdee472..bf75590 100644 --- a/patches/kdrivers/include/wanpipe_fr_iface.h +++ b/patches/kdrivers/include/wanpipe_fr_iface.h @@ -66,11 +66,7 @@ extern int wp_fr_close_chan(void *chan_ptr); extern int wp_fr_ioctl (void *chan_ptr, int cmd, void *arg); extern int wp_fr_rx (void * prot_ptr, void *rx_pkt); -#if defined(__WINDOWS__) -extern int wp_fr_timer (void *prot_ptr, unsigned int *period); -#else extern int wp_fr_timer (void *prot_ptr, unsigned int *period, unsigned int carrier_reliable); -#endif extern int wp_fr_tx (void * chan_ptr, void *skb, int type); extern int wp_fr_pipemon(void *chan, int cmd, int dlci, unsigned char* data, unsigned int *len); extern int wp_fr_snmp(void* chan_ptr, void* data); diff --git a/patches/kdrivers/include/wanpipe_iface.h b/patches/kdrivers/include/wanpipe_iface.h index 3368e13..7a0c75f 100644 --- a/patches/kdrivers/include/wanpipe_iface.h +++ b/patches/kdrivers/include/wanpipe_iface.h @@ -96,11 +96,7 @@ struct if_settings typedef struct { -#if defined(__WINDOWS__) - netdevice_t*(*alloc)(int); -#else netdevice_t*(*alloc)(int, int ifType); -#endif void(*free)(netdevice_t*); int(*attach)(netdevice_t*, char*, int); void(*detach)(netdevice_t*, int); diff --git a/patches/kdrivers/include/wanpipe_includes.h b/patches/kdrivers/include/wanpipe_includes.h index 9afdf95..d1bbdfb 100644 --- a/patches/kdrivers/include/wanpipe_includes.h +++ b/patches/kdrivers/include/wanpipe_includes.h @@ -1,16 +1,16 @@ /* ************************************************************************ - * wanpipe_includes.h * - * WANPIPE(tm) Global includes for Sangoma drivers * - * * - * Author: Alex Feldman * + * wanpipe_includes.h * + * WANPIPE(tm) Global includes for Sangoma drivers * + * * + * Author: Alex Feldman * *======================================================================* - * * + * * * Nov 27, 2007 David Rokhvarg Added header files needed to compile * - * Sangoma MS Windows Driver and API. * - * * - * Aug 10, 2002 Alex Feldman Initial version * - * * + * Sangoma MS Windows Driver and API. * + * * + * Aug 10, 2002 Alex Feldman Initial version * + * * ************************************************************************ */ @@ -275,7 +275,12 @@ # include # include # if defined(CONFIG_PRODUCT_WANPIPE_USB) -# include +# if defined(CONFIG_USB) || defined (CONFIG_USB_SUPPORT) +# include +# else +# warning "USB Kernel support not found... disabling usb support" +# undef CONFIG_PRODUCT_WANPIPE_USB +# endif # endif # include # include @@ -331,44 +336,44 @@ ** *** W I N D O W S *** */ - #if defined(WAN_KERNEL) || defined(__KERNEL__) # include # include /* clock_t */ -# if defined(VIRTUAL_IF_DRV) || defined(SPROTOCOL) || defined(BUSENUM_DRV) -# include -# include "wanpipe_ctypes.h" -# include "wanpipe_kernel_types.h" -# include "aft_core_options.h" -# include "wanpipe_debug.h" -# include "wanpipe_kernel.h" -# include "wanpipe_skb.h" -# include "wanpipe_defines.h" -# include "wanpipe_common.h" -# include "wanpipe_cfg.h" -# include "sdladrv.h" /* API definitions */ -# include "wanpipe_abstr.h" +# if !defined(NDIS_MINIPORT_DRIVER) +# include +# include "wanpipe_abstr_types.h" +# include "wanpipe_kernel_types.h" +# include "aft_core_options.h" +# include "wanpipe_debug.h" +# include "wanpipe_kernel.h" +# include "wanpipe_skb.h" +# include "wanpipe_defines.h" +# include "wanpipe_common.h" +# include "wanpipe_cfg.h" +# include "sdladrv.h" /* API definitions */ +# include "wanpipe_abstr.h" # endif -#if defined( NDIS_MINIPORT_DRIVER ) -# undef BINARY_COMPATIBLE -# define BINARY_COMPATIBLE 0 /* compile for Win2000 and later */ -# define NDIS50_MINIPORT 1 -# include -# include -# include "wanpipe_ctypes.h" -# include "wanpipe_kernel_types.h" -# include "wanpipe_debug.h" -#endif +# if defined( NDIS_MINIPORT_DRIVER ) +# undef BINARY_COMPATIBLE +# define BINARY_COMPATIBLE 0 /* Windows 2000 and later (no Win98 support) */ +# define NDIS50_MINIPORT 1 +# include +# include +# include "wanpipe_abstr_types.h" +# include "wanpipe_kernel_types.h" +# include "wanpipe_debug.h" +# endif -#else -# include -#endif +# else /* WAN_KERNEL */ +# include +# include "wanpipe_abstr_types.h" +# endif -#include -#include -#include /* offsetof, etc. */ +# include +# include +# include /* offsetof, etc. */ #elif defined (__SOLARIS__) # include diff --git a/patches/kdrivers/include/wanpipe_kernel.h b/patches/kdrivers/include/wanpipe_kernel.h index 1a6ce9a..5d86121 100644 --- a/patches/kdrivers/include/wanpipe_kernel.h +++ b/patches/kdrivers/include/wanpipe_kernel.h @@ -67,6 +67,58 @@ #define WAN_DEV_NAME(device) device->dev.bus_id #endif +////////////////////// +#ifdef HAVE_NET_DEVICE_OPS +#define WAN_DECLARE_NETDEV_OPS(_ops_name) static struct net_device_ops _ops_name = {0}; + +#define WAN_NETDEV_OPS_BIND(dev,_ops_name) dev->netdev_ops = &_ops_name + +#define WAN_NETDEV_OPS_INIT(dev,ops,wan_init) ops.ndo_init = wan_init +#define WAN_NETDEV_OPS_OPEN(dev,ops,wan_open) ops.ndo_open = wan_open +#define WAN_NETDEV_OPS_STOP(dev,ops,wan_stop) ops.ndo_stop = wan_stop +#define WAN_NETDEV_OPS_XMIT(dev,ops,wan_send) ops.ndo_start_xmit = wan_send +#define WAN_NETDEV_OPS_STATS(dev,ops,wan_stats) ops.ndo_get_stats = wan_stats +#define WAN_NETDEV_OPS_TIMEOUT(dev,ops,wan_timeout) ops.ndo_tx_timeout = wan_timeout +#define WAN_NETDEV_OPS_IOCTL(dev,ops,wan_ioctl) ops.ndo_do_ioctl = wan_ioctl +#define WAN_NETDEV_OPS_MTU(dev,ops,wan_mtu) ops.ndo_change_mtu = wan_mtu +#define WAN_NETDEV_OPS_CONFIG(dev,ops,wan_set_config) ops.ndo_set_config = wan_set_config +#define WAN_NETDEV_OPS_SET_MULTICAST_LIST(dev,ops,wan_multicast_list) ops.ndo_set_multicast_list = wan_multicast_list +//#define WAN_CHANGE_MTU(dev) dev->netdev_ops->ndo_change_mtu +//#define WAN_XMIT(dev) dev->netdev_ops->ndo_start_xmit +//#define WAN_IOCTL(dev) dev->netdev_ops->ndo_do_ioctl +#define WAN_NETDEV_TEST_XMIT(dev) dev->netdev_ops->ndo_start_xmit +#define WAN_NETDEV_XMIT(skb,dev) dev->netdev_ops->ndo_start_xmit(skb,dev) +#define WAN_NETDEV_TEST_IOCTL(dev) dev->netdev_ops->ndo_do_ioctl +#define WAN_NETDEV_IOCTL(dev,ifr,cmd) dev->netdev_ops->ndo_do_ioctl(dev,ifr,cmd) +#define WAN_NETDEV_TEST_MTU(dev) dev->netdev_ops->ndo_change_mtu +#define WAN_NETDEV_CHANGE_MTU(dev,skb) dev->netdev_ops->ndo_change_mtu(dev,skb) + +#else +#define WAN_DECLARE_NETDEV_OPS(_ops_name) +#define WAN_NETDEV_OPS_BIND(dev,_ops_name) +#define WAN_NETDEV_OPS_INIT(dev,ops,wan_init) dev->init = wan_init +#define WAN_NETDEV_OPS_OPEN(dev,ops,wan_open) dev->open = wan_open +#define WAN_NETDEV_OPS_STOP(dev,ops,wan_stop) dev->stop = wan_stop +#define WAN_NETDEV_OPS_XMIT(dev,ops,wan_send) dev->hard_start_xmit = wan_send +#define WAN_NETDEV_OPS_STATS(dev,ops,wan_stats) dev->get_stats = wan_stats +#define WAN_NETDEV_OPS_TIMEOUT(dev,ops,wan_timeout) dev->tx_timeout = wan_timeout +#define WAN_NETDEV_OPS_IOCTL(dev,ops,wan_ioctl) dev->do_ioctl = wan_ioctl +#define WAN_NETDEV_OPS_MTU(dev,ops,wan_mtu) dev->change_mtu = wan_mtu +#define WAN_NETDEV_OPS_CONFIG(dev,ops,wan_set_config) dev->set_config = wan_set_config +#define WAN_NETDEV_OPS_SET_MULTICAST_LIST(dev,ops,wan_multicast_list) dev->set_multicast_list = wan_multicast_list +//#define WAN_CHANGE_MTU(dev) dev->change_mtu +//#define WAN_XMIT(dev) dev->hard_start_xmit +//#define WAN_IOCTL(dev) dev->do_ioctl +#define WAN_NETDEV_TEST_XMIT(dev) dev->hard_start_xmit +#define WAN_NETDEV_XMIT(skb,dev) dev->hard_start_xmit(skb,dev) +#define WAN_NETDEV_TEST_IOCTL(dev) dev->do_ioctl +#define WAN_NETDEV_IOCTL(dev,ifr,cmd) dev->do_ioctl(dev,ifr,cmd) +#define WAN_NETDEV_TEST_MTU(dev) dev->change_mtu +#define WAN_NETDEV_CHANGE_MTU(dev,skb) dev->change_mtu(dev,skb) + +#endif +////////////////////////// + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) || defined(LINUX_FEAT_2624) # ifndef LINUX_FEAT_2624 # define LINUX_FEAT_2624 1 @@ -97,6 +149,23 @@ typedef int (wan_get_info_t)(char *, char **, off_t, int); #define IRQF_SHARED SA_SHIRQ #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) +static inline int strncasecmp(const char *s1, const char *s2, size_t n) +{ + if (n == 0) + return 0; + + while (n-- != 0 && tolower(*s1) == tolower(*s2)) + { + if (n == 0 || *s1 == '\0' || *s2 == '\0') + break; + s1++; + s2++; + } + + return tolower(*(unsigned char *) s1) - tolower(*(unsigned char *) s2); +} +#endif /*========================================================================== KERNEL 2.6. diff --git a/patches/kdrivers/include/wanpipe_lapb_iface.h b/patches/kdrivers/include/wanpipe_lapb_iface.h index afc62e0..b115336 100644 --- a/patches/kdrivers/include/wanpipe_lapb_iface.h +++ b/patches/kdrivers/include/wanpipe_lapb_iface.h @@ -46,10 +46,6 @@ extern int wp_lapb_close(void *lapb_ptr); extern int wp_lapb_rx(void *lapb_ptr, void *skb); extern int wp_lapb_bh(void *lapb_ptr); extern int wp_lapb_tx(void *lapb_ptr, void *skb, int type); -#if defined(__WINDOWS__) -extern int wp_lapb_timer(void *lapb_ptr, unsigned int *period); -#else extern int wp_lapb_timer(void *lapb_ptr, unsigned int *period, unsigned int); -#endif #endif diff --git a/patches/kdrivers/include/wanpipe_lip.h b/patches/kdrivers/include/wanpipe_lip.h index 986f662..8667ce4 100644 --- a/patches/kdrivers/include/wanpipe_lip.h +++ b/patches/kdrivers/include/wanpipe_lip.h @@ -17,6 +17,10 @@ # include "if_wanpipe.h" #endif +#if defined(__WINDOWS__) +# include "linux_if_ether.h" +#endif + #include "if_wanpipe_common.h" #include "wanpipe_fr_iface.h" #include "wanpipe_lip_atm_iface.h" @@ -49,8 +53,10 @@ #if defined(__WINDOWS__) /* Prototypes for interface between LIP and 'sprotocol' code in virt_adap_enum.c: */ -int sdla_tx_down(void* dev, void *tx_skb); -int sdla_data_rx_up(void* sdla_net_dev, void *rx_skb); +extern int sdla_tx_down(void* dev, void *tx_skb); +extern int sdla_data_rx_up(void* sdla_net_dev, void *rx_skb); +extern void netif_carrier_off(void* dev); +extern void netif_carrier_on(void* dev); #endif @@ -214,11 +220,8 @@ typedef struct wplip_link unsigned char carrier_state; unsigned char prot_state; -#if defined(__WINDOWS__) - void *prot_obj; /* this is the pointer to protocol object */ -#else - void *prot; -#endif + void *prot;/* this is the pointer to protocol object */ + wan_timer_t prot_timer; unsigned char protocol; @@ -369,11 +372,9 @@ typedef struct wplip_prot_iface unsigned int *len); int (*rx) (void *prot_ptr, void *rx_pkt); -#if defined(__WINDOWS__) - int (*timer) (void *prot_ptr, unsigned int *period); -#else + int (*timer) (void *prot_ptr, unsigned int *period, unsigned int); -#endif + int (*bh) (void *); int (*snmp) (void *, void *); int (*task) (void *prot_ptr); @@ -407,8 +408,17 @@ typedef struct wplip_prot_iface #define wplip_hold(_dev) wan_atomic_inc(&(_dev)->refcnt) #define wplip_put(_dev) wan_atomic_dec(&(_dev)->refcnt) + #define wplip_get_link(_reg) (_reg)->wplip_link -#define wplip_get_lipdev(_dev) (wplip_dev_t*)wan_netif_priv((_dev)) + +/*--------------------------------------------------------------*/ +/* Macros to access lipdev pointer inside of netdevice_t pointer. + * Use of those macros encouraged for code readability reasons and + * allows to adapt easily to future modifications in netdevice_t + * structure. */ +#define wplip_netdev_get_lipdev(_dev) (wplip_dev_t*)wan_netif_priv((_dev)) +#define wplip_netdev_set_lipdev(_dev, _lipdev) wan_netif_set_priv(_dev, _lipdev) +/*--------------------------------------------------------------*/ #define wplip_liplink_magic(_link) ((_link)->magic == WPLIP_MAGIC_LINK) #define wplip_lipdev_magic(_lipdev) ((_lipdev)->magic == WPLIP_MAGIC_DEV) @@ -429,8 +439,8 @@ extern int wplip_set_hw_idle_frame (void *liplink_ptr, unsigned char *data, in #if defined(__WINDOWS__) extern wplip_reg_t wplip_protocol; -extern int wanpipe_lip_init(void); -extern int wanpipe_lip_exit(void); +extern int wanpipe_lip_init(void*); +extern int wanpipe_lip_exit(void*); #endif /* wanpipe_lip_sub.c */ @@ -441,12 +451,12 @@ extern int wplip_link_exists(wplip_link_t *lip_link); extern void wplip_free_link(wplip_link_t *lip_link); extern int wplip_lipdev_latency_change(wplip_link_t *lip_link); - -#if 1 -extern wplip_dev_t* wplip_create_lipdev(char *dev_name,int); +#if defined(__WINDOWS__) + extern wplip_dev_t* wplip_create_lipdev(netdevice_t *dev, int usedby); #else -extern wplip_dev_t* wplip_create_lipdev(char *dev_name); + extern wplip_dev_t* wplip_create_lipdev(char *dev_name, int usedby); #endif + extern void wplip_free_lipdev(wplip_dev_t *lip_dev); extern int wplip_lipdev_exists(wplip_link_t *lip_link, char *dev_name); extern void wplip_remove_lipdev(wplip_link_t *lip_link, @@ -676,133 +686,14 @@ static __inline void wp_lip_config_bridge_mode(wplip_dev_t *lip_dev) #define PACKET_TYPE_DECODE(type) \ ((type == WPLIP_RAW) ? "WPLIP_RAW" : \ (type == WPLIP_IP) ? "WPLIP_IP" : \ - (type == WPLIP_IPV6) ? "WPLIP_IPV6" : \ + (type == WPLIP_IPV6) ? "WPLIP_IPV6" : \ (type == WPLIP_IPX) ? "WPLIP_IPX": \ (type == WPLIP_FR_ARP) ? "WPLIP_FR_ARP": "Unknown Packet type") -//convert integer definition of a protocol to string +/*convert integer definition of a protocol to string*/ static char * get_protocol_string(int protocol) { -#define MAX_PROT_NAME_LENGTH 256 -#define snprintf _snprintf - static char protocol_name[MAX_PROT_NAME_LENGTH]; - - switch(protocol) - { - case WANCONFIG_X25: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "X25"); - break; - - case WANCONFIG_FR: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "Frame Relay"); - break; - - case WANCONFIG_PPP: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "PPP"); - break; - - case WANCONFIG_CHDLC: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "CHDLC"); - break; - - case WANCONFIG_BSC: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "BiSync Streaming"); - break; - - case WANCONFIG_HDLC: - //used with CHDLC firmware - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "HDLC Streaming"); - break; - - case WANCONFIG_MPPP://and WANCONFIG_MPROT too - //snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "Multi Port PPP"); - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "PPP"); - break; - - case WANCONFIG_LAPB: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "LAPB"); - break; - - case WANCONFIG_BITSTRM: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "Bit Stream"); - break; - - case WANCONFIG_EDUKIT: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "WAN EduKit"); - break; - - case WANCONFIG_SS7: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "SS7"); - break; - - case WANCONFIG_BSCSTRM: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "Bisync Streaming Nasdaq"); - break; - - case WANCONFIG_MFR: - //snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "Multi-Port Frame Relay"); - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "Frame Relay"); - break; - - case WANCONFIG_ADSL: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "LLC Ethernet (ADSL)"); - break; - - case WANCONFIG_SDLC: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "SDLC"); - break; - - case WANCONFIG_ATM: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "ATM"); - break; - - case WANCONFIG_POS: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "Point-of-Sale"); - break; - - case WANCONFIG_AFT: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "AFT"); - break; - - case WANCONFIG_AFT_TE3: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "AFT"); - break; - - case WANCONFIG_DEBUG: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "Real Time Debugging"); - break; - - case WANCONFIG_ADCCP: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "Special HDLC LAPB"); - break; - - case WANCONFIG_MLINK_PPP: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "Multi-Link PPP"); - break; - - case WANCONFIG_GENERIC: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "WANPIPE Generic driver"); - break; - - case WANCONFIG_MPCHDLC: - //snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "Multi-Port CHDLC"); - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "CHDLC"); - break; - - case WANCONFIG_TTY: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "TTY"); - break; -/* - case PROTOCOL_TDM_VOICE: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "TDM Voice"); - break; -*/ - default: - snprintf((char*)protocol_name, MAX_PROT_NAME_LENGTH, "Invalid Protocol"); - break; - } - - return (char*)protocol_name; + return SDLA_DECODE_PROTOCOL(protocol); } #endif diff --git a/patches/kdrivers/include/wanpipe_lip_kernel.h b/patches/kdrivers/include/wanpipe_lip_kernel.h index cee5b7b..644d815 100644 --- a/patches/kdrivers/include/wanpipe_lip_kernel.h +++ b/patches/kdrivers/include/wanpipe_lip_kernel.h @@ -32,8 +32,7 @@ typedef struct wplip_reg int (*wplip_unbind_link) (void*, netdevice_t *); #if defined(__WINDOWS__) - int (*wplip_if_reg) (void* lip_link_ptr, char *dev_name, - wanif_conf_t *conf, void** new_lip_dev); + int (*wplip_if_reg) (void *lip_link_ptr, netdevice_t *dev, wanif_conf_t *conf); int (*wplip_if_unreg) (void *); #else int (*wplip_if_reg) (void*, char*, wanif_conf_t *); diff --git a/patches/kdrivers/include/wanpipe_logger.h b/patches/kdrivers/include/wanpipe_logger.h new file mode 100644 index 0000000..022ef65 --- /dev/null +++ b/patches/kdrivers/include/wanpipe_logger.h @@ -0,0 +1,312 @@ +/******************************************************************************//** + * \file wanpipe_logger.h + * \brief WANPIPE(tm) Wanpipe Logger API Headers and Defines + * + * Authors: David Rokhvarg + * + * Copyright (c) 2007 - 09, 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 Sangoma Technologies 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 Sangoma Technologies ``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 Sangoma Technologies 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 + * =============================================================================== + */ + +#ifndef __WANPIPE_LOGGER_API_HDR__ +#define __WANPIPE_LOGGER_API_HDR__ + +#include "wanpipe_abstr_types.h" + +/***********************************************//** + Wanpipe Logger Event Structure +****************************************************/ + +/*! + \def WP_MAX_NO_BYTES_IN_LOGGER_EVENT + \brief Maximum length of event string + */ +#define WP_MAX_NO_BYTES_IN_LOGGER_EVENT 256 + +/*! + \def WP_MAX_NO_LOGGER_EVENTS + \brief Maximum number of logger events kept by API driver + */ +#define WP_MAX_NO_LOGGER_EVENTS 512 + +/*! + \struct wp_logger_event + \brief Wanpipe API Logger Event contains event type, + timestamp and data. + + \typedef wp_logger_event_t + \brief Wanpipe API Logger Event contains event type, + timestamp and data. +*/ +typedef struct wp_logger_event{ + + u_int32_t logger_type; /*!< Type of logger which produced the event. One of the types in wan_logger_type. */ + u_int32_t event_type; /*!< A bitmap indicating type of the event. Only a single bit will be set by API. */ + wan_time_t time_stamp_sec; /*!< Timestamp in seconds (Since January 1, 1970). */ + wan_suseconds_t time_stamp_usec;/*!< microseconds portion of the timestamp */ + unsigned char data[WP_MAX_NO_BYTES_IN_LOGGER_EVENT]; /*!< data area containing the logger event null-terminated string */ + +}wp_logger_event_t; + + +/* Definitions for 'logger_type' in wp_logger_event_t: */ +enum wan_logger_type{ + WAN_LOGGER_DEFAULT, /* controled by bitmaps in wp_default_logger_level */ + WAN_LOGGER_TE1, /* controled by bitmaps in wp_te1_logger_level */ + WAN_LOGGER_HWEC, /* controled by bitmaps in wp_hwec_logger_level */ + WAN_LOGGER_TDMAPI, /* controled by bitmaps in wp_tdmapi_logger_level */ + WAN_LOGGER_FE, /* controled by bitmaps in wp_fe_logger_level */ + WAN_LOGGER_BRI /* controled by bitmaps in wp_bri_logger_level */ +}; + +/* Definitions for 'event_type' in wp_logger_event_t: */ + +/* General purpose Wanpipe Logger event types. */ +enum wp_default_logger_level{ + SANG_LOGGER_INFORMATION = (1), + SANG_LOGGER_WARNING = (1 << 1), + SANG_LOGGER_ERROR = (1 << 2) +}; + +#define SANG_DECODE_DEFAULT_LOGGER_EVENT_TYPE(bit) \ + (bit & SANG_LOGGER_INFORMATION) ? "SANG_LOGGER_INFORMATION": \ + (bit & SANG_LOGGER_WARNING) ? "SANG_LOGGER_WARNING" : \ + (bit & SANG_LOGGER_ERROR) ? "SANG_LOGGER_ERROR" : \ + "Invalid Bit for Default Logger" + + +/* Task-specific Wanpipe Logger event types - for advanced debugging only. */ +/* T1/E1 */ +enum wp_te1_logger_level{ + SANG_LOGGER_TE1_DEFAULT = (1) +}; + +#define SANG_DECODE_TE1_LOGGER_EVENT_TYPE(bit) \ + (bit & SANG_LOGGER_TE1_DEFAULT) ? "SANG_LOGGER_TE1_DEFAULT": \ + "Invalid Bit for TE1 Logger" + +/* HWEC */ +enum wp_hwec_logger_level{ + SANG_LOGGER_HWEC_DEFAULT = (1) +}; + +#define SANG_DECODE_HWEC_LOGGER_EVENT_TYPE(bit) \ + (bit & SANG_LOGGER_HWEC_DEFAULT) ? "SANG_LOGGER_HWEC_DEFAULT": \ + "Invalid Bit for HWEC Logger" + + +/* TDMAPI */ +enum wp_tdmapi_logger_level{ + SANG_LOGGER_TDMAPI_DEFAULT = (1) +}; + +#define SANG_DECODE_TDMAPI_LOGGER_EVENT_TYPE(bit) \ + (bit & SANG_LOGGER_TDMAPI_DEFAULT) ? "SANG_LOGGER_TDMAPI_DEFAULT": \ + "Invalid Bit for TDMAPI Logger" + +/* FE */ +enum wp_fe_logger_level{ + SANG_LOGGER_FE_DEFAULT = (1) +}; + +#define SANG_DECODE_FE_LOGGER_EVENT_TYPE(bit) \ + (bit & SANG_LOGGER_FE_DEFAULT) ? "SANG_LOGGER_FE_DEFAULT": \ + "Invalid Bit for FE Logger" + +/* BRI */ +enum wp_bri_logger_level{ + SANG_LOGGER_BRI_DEFAULT = (1), + SANG_LOGGER_BRI_HFC_S0_STATES = (1 << 1) +}; + +#define SANG_DECODE_BRI_LOGGER_EVENT_TYPE(bit) \ + (bit & SANG_LOGGER_BRI_DEFAULT) ? "SANG_LOGGER_BRI_DEFAULT": \ + (bit & SANG_LOGGER_BRI_HFC_S0_STATES) ? "SANG_LOGGER_BRI_HFC_S0_STATES": \ + "Invalid Bit for TE1 Logger" + + +static __inline const char* wp_decode_logger_event_type(u_int32_t logger_type, u_int32_t evt_type) +{ + switch(logger_type) + { + case WAN_LOGGER_DEFAULT: + return SANG_DECODE_DEFAULT_LOGGER_EVENT_TYPE(evt_type); + case WAN_LOGGER_TE1: + return SANG_DECODE_TE1_LOGGER_EVENT_TYPE(evt_type); + case WAN_LOGGER_HWEC: + return SANG_DECODE_HWEC_LOGGER_EVENT_TYPE(evt_type); + case WAN_LOGGER_TDMAPI: + return SANG_DECODE_TDMAPI_LOGGER_EVENT_TYPE(evt_type); + case WAN_LOGGER_FE: + return SANG_DECODE_FE_LOGGER_EVENT_TYPE(evt_type); + case WAN_LOGGER_BRI: + return SANG_DECODE_BRI_LOGGER_EVENT_TYPE(evt_type); + default: + return "Error: unknown logger type"; + } +} + + +/*! + \struct wp_logger_stats + \brief Wanpipe Logger API Statistics Structure. Used with WP_API_LOGGER_CMD_GET_STATS command. + + \typedef wp_logger_stats_t + */ +typedef struct wp_logger_stats +{ + ulong_t rx_events; /*!< total Events received */ + ulong_t rx_events_dropped; /*!< number of Events discarded because no free buffer available */ + ulong_t max_event_queue_length;/*!< maximum number of Events which can be stored in API queue */ + ulong_t current_number_of_events_in_event_queue;/*!< number of Events currently in API queue */ + +}wp_logger_stats_t; + +/*! + \struct wp_logger_level_control + \brief Wanpipe Logger API Levle Control Structure. + Used with WP_API_LOGGER_CMD_GET_LOGGER_LEVEL and WP_API_LOGGER_CMD_SET_LOGGER_LEVEL commands. + + \typedef wp_logger_level_control_t + */ +typedef struct wp_logger_level_control +{ + u_int32_t logger_type; /*!< Type of logger which level is being controlled. One of the types in wan_logger_type. */ + u_int32_t logger_level; /*!< A bitmap indicating types of the events logged by Wanpipe Logger. */ + +}wp_logger_level_control_t; + +/*! + \struct wp_logger_cmd + \brief Wanpipe Logger API Command Structure used with WANPIPE_IOCTL_LOGGER_CMD + + Wanpipe Logger API Command structure used to execute WANPIPE_IOCTL_LOGGER_CMD commands + All commands are defined in: + enum wp_logger_cmds + + \typedef wp_logger_cmd_t + */ +typedef struct wp_logger_cmd +{ + unsigned int cmd; /*!< Command defined in enum wp_logger_cmds */ + unsigned int result; /*!< Result defined in: enum SANG_STATUS or SANG_STATUS_T */ + + union { + + wp_logger_event_t logger_event; + + wp_logger_level_control_t logger_level_ctrl; + + u_int32_t open_cnt; /*!< Number of Open file descriptors for the device. + Used with WP_API_LOGGER_CMD_OPEN_CNT command. */ + wp_logger_stats_t stats; + }; + +}wp_logger_cmd_t; + +/*! + \enum wanpipe_api_cmds + \brief Commands used with WANPIPE_IOCTL_LOGGER_CMD IOCTL + */ +enum wp_logger_cmds +{ + WP_API_LOGGER_CMD_FLUSH_BUFFERS, /*!< Flush Buffers */ + WP_API_LOGGER_CMD_READ_EVENT, /*!< Read Logger Event */ + + WP_API_LOGGER_CMD_GET_STATS, /*!< Get device statistics */ + WP_API_LOGGER_CMD_RESET_STATS, /*!< Reset device statistics */ + WP_API_LOGGER_CMD_OPEN_CNT, /*!< Get Number of Open file descriptors for the device */ + + WP_API_LOGGER_CMD_GET_LOGGER_LEVEL, /*!< Get current level (types of events) of Wanpipe Logger */ + WP_API_LOGGER_CMD_SET_LOGGER_LEVEL /*!< Set current level (types of events) of Wanpipe Logger */ +}; + + +#if defined(__KERNEL__) +int wp_logger_create(void); +void wp_logger_delete(void); +void wp_logger_input(u_int32_t logger_type, u_int32_t evt_type, const char * fmt, ...); +int wp_logger_repeating_message_filter(u_int32_t logger_type, u_int32_t evt_type, const char * fmt, ...); + +extern u_int32_t wp_logger_level_default; +extern u_int32_t wp_logger_level_te1; +extern u_int32_t wp_logger_level_hwec; +extern u_int32_t wp_logger_level_tdmapi; +extern u_int32_t wp_logger_level_fe; +extern u_int32_t wp_logger_level_bri; + +/* macros for checking if a level of debugging is enabled */ +#define WAN_LOGGER_TEST_LEVEL_DEFAULT(level) (level & wp_logger_level_default) +#define WAN_LOGGER_TEST_LEVEL_TE1(level) (level & wp_logger_level_te1) +#define WAN_LOGGER_TEST_LEVEL_HWEC(level) (level & wp_logger_level_hwec) +#define WAN_LOGGER_TEST_LEVEL_TDMAPI(level) (level & wp_logger_level_tdmapi) +#define WAN_LOGGER_TEST_LEVEL_FE(level) (level & wp_logger_level_fe) +#define WAN_LOGGER_TEST_LEVEL_BRI(level) (level & wp_logger_level_bri) + + +#define WP_DEBUG(logger_type, logger_level, ...) \ +{ \ + if(!wp_logger_repeating_message_filter(logger_type, logger_level, ## __VA_ARGS__)){ \ + \ + switch(logger_type) \ + { \ + case WAN_LOGGER_DEFAULT: \ + if(WAN_LOGGER_TEST_LEVEL_DEFAULT(logger_level)){ \ + wp_logger_input(logger_type, logger_level, ## __VA_ARGS__); \ + } \ + break; \ + case WAN_LOGGER_TE1: \ + if(WAN_LOGGER_TEST_LEVEL_TE1(logger_level)){ \ + wp_logger_input(logger_type, logger_level, ## __VA_ARGS__); \ + } \ + break; \ + case WAN_LOGGER_HWEC: \ + if(WAN_LOGGER_TEST_LEVEL_HWEC(logger_level)){ \ + wp_logger_input(logger_type, logger_level, ## __VA_ARGS__); \ + } \ + break; \ + case WAN_LOGGER_TDMAPI: \ + if(WAN_LOGGER_TEST_LEVEL_TDMAPI(logger_level)){ \ + wp_logger_input(logger_type, logger_level, ## __VA_ARGS__); \ + } \ + break; \ + case WAN_LOGGER_FE: \ + if(WAN_LOGGER_TEST_LEVEL_FE(logger_level)){ \ + wp_logger_input(logger_type, logger_level, ## __VA_ARGS__); \ + } \ + break; \ + case WAN_LOGGER_BRI: \ + if(WAN_LOGGER_TEST_LEVEL_BRI(logger_level)){ \ + wp_logger_input(logger_type, logger_level, ## __VA_ARGS__); \ + } \ + break; \ + }/* switch(type) */ \ + \ + }/* if() */ \ +} + +#endif/* __KERNEL__ */ + +#endif/* __WANPIPE_LOGGER_API_HDR__ */ diff --git a/patches/kdrivers/include/wanpipe_mtp1.h b/patches/kdrivers/include/wanpipe_mtp1.h new file mode 100644 index 0000000..1ff3aa8 --- /dev/null +++ b/patches/kdrivers/include/wanpipe_mtp1.h @@ -0,0 +1,60 @@ +/***************************************************************************** +* wanpipe_mtp1.h +* +* WANPIPE(tm) MTP1 Support +* +* Authors: Nenad Corbic +* +* Copyright: (c) 2009 Sangoma Technologies Inc. +* +* 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. +* ============================================================================*/ + + + +enum { + WP_MTP1_HDLC_8BIT, + WP_MTP1_HDLC_7BIT, + WP_MTP1_HDLC_BIT_0_FIRST, + WP_MTP1_HDLC_BIT_7_FIRST, +}; + + +typedef struct wp_mtp1_cfg{ + + u8 hdlc_op_mode; /* 8bit or 7bit hdlc */ + u8 hdlc_bit_endian; /* bit 0 first */ + u32 param_N; + +} wp_mtp1_cfg_t; + + +typedef struct wp_mtp1_stats{ + + u32 rx_octet_count; + u32 rx_suerm_count; + u32 tx_octet_count; + +} wp_mtp1_stats_t; + + +typedef struct wp_mtp1_reg { + void *priv_ptr; + int (*rx_data)(void *priv_ptr, u8 *data, int len); + int (*rx_reject)(void *priv_prt, char *reason); + int (*rx_suerm)(void *priv_ptr); + int (*tx_data)(void *priv_ptr, u8 *data, int len); + int (*wakeup)(void *priv_ptr); + wp_mtp1_cfg_t cfg; +}wp_mtp1_reg_t; + + +extern void *wp_mtp1_register(wp_mtp1_reg_t *reg); +extern int wp_mtp1_free(void *mtp1); +extern int wp_mtp1_rx_handler(void *mtp1, u8 *data, int len); +extern int wp_mtp1_tx_handler(void *mtp1, u8 *data, int len); + + diff --git a/patches/kdrivers/include/wanpipe_sppp_iface.h b/patches/kdrivers/include/wanpipe_sppp_iface.h index 5351daf..56a0fa6 100644 --- a/patches/kdrivers/include/wanpipe_sppp_iface.h +++ b/patches/kdrivers/include/wanpipe_sppp_iface.h @@ -41,10 +41,8 @@ extern int wp_sppp_tx(void *sppp_ptr, void *skb, int type); #if defined(__WINDOWS__) extern void *wp_register_sppp_prot(void *, char *, void *, wplip_prot_reg_t *); -extern int wp_sppp_timer(void *sppp_ptr, unsigned int *period); -#else -extern int wp_sppp_timer(void *sppp_ptr, unsigned int *period, unsigned int carrier_reliable); #endif +extern int wp_sppp_timer(void *sppp_ptr, unsigned int *period, unsigned int carrier_reliable); extern int wp_sppp_pipemon(void *sppp, int cmd, int addr, unsigned char* data, unsigned int *len); extern int wp_sppp_task(void *sppp_ptr); diff --git a/patches/kdrivers/include/wanpipe_tdm_api.h b/patches/kdrivers/include/wanpipe_tdm_api.h index 50e09ca..1e76cbd 100644 --- a/patches/kdrivers/include/wanpipe_tdm_api.h +++ b/patches/kdrivers/include/wanpipe_tdm_api.h @@ -182,6 +182,8 @@ typedef struct wanpipe_tdm_api_dev { int (*read_rbs_bits)(void *chan, u32 ch, u8 *rbs_bits); int (*write_rbs_bits)(void *chan, u32 ch, u8 rbs_bits); int (*write_hdlc_frame)(void *chan, netskb_t *skb, wp_api_hdr_t *hdr); + int (*write_hdlc_check)(void *chan, int lock); + int (*write_hdlc_timeout)(void *chan, int lock); int (*pipemon)(void* card, void* chan, void *udata); int (*driver_ctrl)(void *chan_ptr, int cmd, wanpipe_api_cmd_t *api_cmd); @@ -195,6 +197,8 @@ typedef struct wanpipe_tdm_api_dev { uint8_t operation_mode;/* WP_TDM_OPMODE */ uint8_t api_mode; int mtu_mru; + + void * mtp1_dev; }wanpipe_tdm_api_dev_t; diff --git a/patches/kdrivers/include/wanpipe_version.h b/patches/kdrivers/include/wanpipe_version.h index d95d806..148aff6 100644 --- a/patches/kdrivers/include/wanpipe_version.h +++ b/patches/kdrivers/include/wanpipe_version.h @@ -10,7 +10,7 @@ #define WANPIPE_COMPANY "Sangoma Technologies Inc" /********** LINUX **********/ -#define WANPIPE_VERSION "3.5.6" +#define WANPIPE_VERSION "3.5.11" #define WANPIPE_SUB_VERSION "0" #define WANPIPE_LITE_VERSION "1.1.1" @@ -40,31 +40,31 @@ #if defined(__WINDOWS__) -# define WANPIPE_VERSION_MAJOR 6 -# define WANPIPE_VERSION_MINOR 0 -# define WANPIPE_VERSION_MINOR1 9 -# define WANPIPE_VERSION_MINOR2 16 +# define WANPIPE_VERSION_MAJOR 6 /* major upgrade */ +# define WANPIPE_VERSION_MINOR 0 +# define WANPIPE_VERSION_MINOR1 19 /* frozen feature number */ +# define WANPIPE_VERSION_MINOR2 0 /* patch number for WANPIPE_VERSION_MINOR1 */ -# undef VER_PRODUCTVERSION +# undef VER_PRODUCTVERSION # undef VER_PRODUCTVERSION_STR # undef VER_PRODUCTNAME_STR # undef VER_COMPANYNAME_STR -# define VER_PRODUCTVERSION 6,0,9,16 -# define VER_PRODUCTVERSION_STR "6.0.9.16" -# define __BUILDDATE__ June 4, 2009 +# define VER_PRODUCTVERSION 6,0,19,0 +# define VER_PRODUCTVERSION_STR "6.0.19.0" +# define __BUILDDATE__ February 25, 2010 # define VER_COMPANYNAME_STR "Sangoma Technologies Corporation" -# define VER_LEGALCOPYRIGHT_YEARS "1984-2009" -# define VER_LEGALCOPYRIGHT_STR "Copyright (c) Sangoma Technologies Corporation" +# define VER_LEGALCOPYRIGHT_YEARS "1984-2011" +# define VER_LEGALCOPYRIGHT_STR "Copyright (c) Sangoma Technologies Corp." # define VER_PRODUCTNAME_STR "Sangoma WANPIPE (TM)" # undef WANPIPE_VERSION # undef WANPIPE_VERSION_BETA # undef WANPIPE_SUB_VERSION -# define WANPIPE_VERSION_Windows "6.0.9" -# define WANPIPE_SUB_VERSION_Windows "16" +# define WANPIPE_VERSION_Windows "6.0.19" +# define WANPIPE_SUB_VERSION_Windows "0" # define WANPIPE_VERSION_BETA_Windows 0 # define WANPIPE_VERSION WANPIPE_VERSION_Windows diff --git a/patches/kdrivers/include/wanrouter.h b/patches/kdrivers/include/wanrouter.h index b41a593..805324b 100644 --- a/patches/kdrivers/include/wanrouter.h +++ b/patches/kdrivers/include/wanrouter.h @@ -269,7 +269,7 @@ WAN_LIST_HEAD(wan_dev_lhead, wan_dev_le); typedef struct wan_device { - unsigned magic; /* magic number */ + unsigned int magic; /* magic number */ char* name; /* -> WAN device name (ASCIIZ) */ void* priv; /* -> driver private data */ unsigned config_id; /* Configuration ID */ @@ -277,17 +277,13 @@ typedef struct wan_device unsigned ioport; /* adapter I/O port base #1 */ char S514_cpu_no[1]; /* PCI CPU Number */ unsigned char S514_slot_no; /* PCI Slot Number */ -#if 0 - //ALEX_TODAY maddr not really used - unsigned long maddr; /* dual-port memory address */ - unsigned msize; /* dual-port memory size */ -#endif + int irq; /* interrupt request level */ int dma; /* DMA request level */ unsigned int bps; /* data transfer rate */ unsigned int mtu; /* max physical transmit unit size */ unsigned int udp_port; /* UDP port for management */ - unsigned char ttl; /* Time To Live for UDP security */ + unsigned char ttl; /* Time To Live for UDP security */ unsigned int enable_tx_int; /* Transmit Interrupt enabled or not */ char electrical_interface; /* RS-232/V.35, etc. */ char clocking; /* external/internal */ @@ -296,13 +292,10 @@ typedef struct wan_device char connection; /* permanent/switched/on-demand */ char signalling; /* Signalling RS232 or V35 */ char read_mode; /* read mode: Polling or interrupt */ - char new_if_cnt; /* Number of interfaces per wanpipe */ + char new_if_cnt; /* Number of interfaces per wanpipe */ char del_if_cnt; /* Number of times del_if() gets called */ unsigned char piggyback; /* Piggibacking a port */ -#if 0 - //ALEX_TODAY hw_opt[0] -> card->type - unsigned hw_opt[4]; /* other hardware options */ -#endif + /****** status and statistics *******/ char state; /* device state */ char api_status; /* device api status */ @@ -310,12 +303,13 @@ typedef struct wan_device unsigned reserved[16]; /* reserved for future use */ unsigned long critical; /* critical section flag */ wan_spinlock_t lock; /* Support for SMP Locking */ + /****** device management methods ***/ int (*setup) (struct wan_device *wandev, wandev_conf_t *conf); int (*shutdown) (struct wan_device *wandev, wandev_conf_t* conf); int (*update) (struct wan_device *wandev); #if defined(__LINUX__) - int (*ioctl) (struct wan_device *wandev, unsigned cmd, unsigned long arg); + int (*ioctl) (struct wan_device *wandev, unsigned int cmd, unsigned long arg); #else int (*ioctl) (struct wan_device *wandev, u_long cmd, caddr_t arg); #endif @@ -345,16 +339,19 @@ typedef struct wan_device write_proc_t* set_dev_config; write_proc_t* set_if_info; #endif + int (*get_info)(void*, struct seq_file* m, int *); void (*fe_enable_timer) (void* card_id); void (*te_report_rbsbits) (void* card_id, int channel, unsigned char rbsbits); void (*te_report_alarms) (void* card_id, unsigned long alarams); void (*te_link_state) (void* card_id); - int (*te_signaling_config) (void* card_id, unsigned long); - int (*te_disable_signaling) (void* card_id, unsigned long); - int (*te_read_signaling_config) (void* card_id); - int (*report_dtmf) (void* card_id, int, unsigned char); - void (*ec_enable_timer) (void* card_id); + void (*te_link_reset) (void* card_id); + int (*te_signaling_config) (void* card_id, unsigned long); + int (*te_disable_signaling) (void* card_id, unsigned long); + int (*te_read_signaling_config) (void* card_id); + int (*report_dtmf) (void* card_id, int, unsigned char); + void (*ec_enable_timer) (void* card_id); + struct { void (*rbsbits) (void* card_id, int, unsigned char); void (*alarms) (void* card_id, wan_event_t*); @@ -368,66 +365,53 @@ typedef struct wan_device unsigned char ignore_front_end_status; unsigned char line_idle; -#if defined(__WINDOWS__) - u32 card_type; -#else - unsigned char card_type; -#endif - atomic_t if_cnt; - atomic_t if_up_cnt; + unsigned int card_type; + atomic_t if_cnt; + atomic_t if_up_cnt; wan_sdlc_conf_t sdlc_cfg; - wan_bscstrm_conf_t bscstrm_cfg; - int (*debugging) (struct wan_device *wandev); - int (*debug_read) (void*, void*); - int comm_port; + wan_bscstrm_conf_t bscstrm_cfg; + int (*debugging) (struct wan_device *wandev); + int (*debug_read) (void*, void*); + int comm_port; #if defined(__LINUX__) spinlock_t get_map_lock; - int (*get_map)(struct wan_device*,netdevice_t*,struct seq_file* m, int *); - - int (*bind_annexg) (netdevice_t *dev, netdevice_t *adev); - netdevice_t *(*un_bind_annexg) (struct wan_device *wandev,netdevice_t *adev); - void (*get_active_inactive)(struct wan_device*,netdevice_t*,void*); + int (*get_map)(struct wan_device*,netdevice_t*,struct seq_file* m, int *); + int (*bind_annexg) (netdevice_t *dev, netdevice_t *adev); + netdevice_t *(*un_bind_annexg) (struct wan_device *wandev,netdevice_t *adev); + void (*get_active_inactive)(struct wan_device*,netdevice_t*,void*); #endif #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(CONFIG_PRODUCT_WANPIPE_GENERIC) -#if 0 -/* Moved to common structure */ - int (*protocol_open) (netdevice_t*); - int (*protocol_close) (netdevice_t*); - int (*protocol_send) (netskb_t* skb, netdevice_t*); - struct net_device_stats* (*protocol_ifstats) (netdevice_t*); - int (*protocol_ioctl) (netdevice_t*, struct ifreq*, int); - void (*protocol_tx_timeout) (netdevice_t*); - int (*hdlc_xmit) (netskb_t*, netdevice_t* dev); + int (*wanpipe_ioctl) (netdevice_t*, struct ifreq*, int); #endif - int (*wanpipe_ioctl) (netdevice_t*, struct ifreq*, int); -#endif - unsigned char macAddr[ETHER_ADDR_LEN]; + unsigned char macAddr[ETHER_ADDR_LEN]; - sdla_fe_iface_t fe_iface; + sdla_fe_iface_t fe_iface; sdla_fe_notify_iface_t fe_notify_iface; - void *ec_dev; + void *ec_dev; - unsigned long ec_enable_map; - unsigned long fe_ec_map; - wan_ticks_t ec_intmask; + unsigned long ec_enable_map; + unsigned long fe_ec_map; + wan_ticks_t ec_intmask; - int (*ec_enable)(void *pcard, int, int); + int (*ec_enable)(void *pcard, int, int); + int (*ec_state) (void* card_id, wan_hwec_dev_state_t *ec_state); - unsigned char (*write_ec)(void*, unsigned short, unsigned char); - unsigned char (*read_ec)(void*, unsigned short); - int (*hwec_reset)(void* card_id, int); - int (*hwec_enable)(void* card_id, int, int); + unsigned char (*write_ec)(void*, unsigned short, unsigned char); + unsigned char (*read_ec)(void*, unsigned short); + int (*hwec_reset)(void* card_id, int); + int (*hwec_enable)(void* card_id, int, int); - unsigned long rtp_tap_call_map; - unsigned long rtp_tap_call_status; - wan_rtp_chan_t rtp_chan[32]; - void *rtp_dev; - int rtp_len; - void (*rtp_tap)(void *card, u8 chan, u8* rx, u8* tx, u32 len); - void *port_cfg; + unsigned long rtp_tap_call_map; + unsigned long rtp_tap_call_status; + wan_rtp_chan_t rtp_chan[32]; + void *rtp_dev; + int rtp_len; + void (*rtp_tap)(void *card, u8 chan, u8* rx, u8* tx, u32 len); + void *port_cfg; + } wan_device_t; WAN_LIST_HEAD(wan_devlist_, wan_device); diff --git a/patches/kdrivers/include/zapcompat.h b/patches/kdrivers/include/zapcompat.h index 54ec696..77b29d3 100644 --- a/patches/kdrivers/include/zapcompat.h +++ b/patches/kdrivers/include/zapcompat.h @@ -12,6 +12,7 @@ * ============================================================================ * Sep 06, 2008 Moises Silva Initial Version * Nov 20, 2008 Alex Feldman Added ZT_XLAW + * Sep 22, 2009 Moises Silva Added dahdi_alarm_channel stuff ****************************************************************************** */ @@ -137,6 +138,7 @@ // functions #define zt_rbsbits dahdi_rbsbits #define zt_alarm_notify dahdi_alarm_notify +#define zt_alarm_channel dahdi_alarm_channel #define zt_receive dahdi_receive #define zt_transmit dahdi_transmit #define zt_ec_chunk dahdi_ec_chunk @@ -156,6 +158,10 @@ // to something like WP_XX instead of ZT_XX, but I don't see any benefit on it // and would make this file bigger #include +#ifndef ZT_GET_PARMS_V1 +#define zt_alarm_channel(a,b) zt_qevent_lock(a,( (b)==ZT_ALARM_NONE )? \ + ZT_EVENT_NOALARM : ZT_EVENT_ALARM) +#endif # define WP_ZT_QEVENT_LOCK(chan, event) zt_qevent_lock(&(chan),(event)) diff --git a/patches/kdrivers/src/lip/wanpipe_lip_bh.c b/patches/kdrivers/src/lip/wanpipe_lip_bh.c index c7177d9..4c9fe79 100644 --- a/patches/kdrivers/src/lip/wanpipe_lip_bh.c +++ b/patches/kdrivers/src/lip/wanpipe_lip_bh.c @@ -1,11 +1,15 @@ #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) # include +#elif defined(__WINDOWS__) +# include "wanpipe_lip.h" #else -#include +# include #endif #if defined(__LINUX__) void wplip_link_bh(unsigned long data); +#elif defined(__WINDOWS__) +void wplip_link_bh(IN PKDPC Dpc, IN PVOID data, IN PVOID SystemArgument1, IN PVOID SystemArgument2); #else void wplip_link_bh(void* data, int pending); #endif @@ -123,6 +127,8 @@ static int wplip_bh_transmit(wplip_link_t *lip_link) WAN_NETIF_START_QUEUE(lip_dev->common.dev); #if defined(__LINUX__) wan_wakeup_api(lip_dev); +#elif defined(__WINDOWS__) + WAN_NETIF_WAKE_QUEUE (lip_dev->common.dev); #endif }else if (lip_dev->common.lip){ /*STACK*/ WAN_NETIF_START_QUEUE(lip_dev->common.dev); @@ -203,6 +209,8 @@ static int wplip_retrigger_bh(wplip_link_t *lip_link) #if defined(__LINUX__) void wplip_link_bh(unsigned long data) +#elif defined(__WINDOWS__) +void wplip_link_bh(IN PKDPC Dpc, IN PVOID data, IN PVOID SystemArgument1, IN PVOID SystemArgument2) #else void wplip_link_bh(void* data, int pending) #endif @@ -217,7 +225,7 @@ void wplip_link_bh(void* data, int pending) return; } - wan_spin_lock_irq(&lip_link->bh_lock, &s); + wplip_spin_lock_irq(&lip_link->bh_lock, &s); wan_set_bit(WPLIP_BH_RUNNING,&lip_link->tq_working); @@ -229,7 +237,7 @@ void wplip_link_bh(void* data, int pending) WAN_TASKLET_END((&lip_link->task)); - wan_spin_unlock_irq(&lip_link->bh_lock, &s); + wplip_spin_unlock_irq(&lip_link->bh_lock, &s); wplip_retrigger_bh(lip_link); diff --git a/patches/kdrivers/src/lip/wanpipe_lip_iface.c b/patches/kdrivers/src/lip/wanpipe_lip_iface.c index 5d03859..dabee2b 100644 --- a/patches/kdrivers/src/lip/wanpipe_lip_iface.c +++ b/patches/kdrivers/src/lip/wanpipe_lip_iface.c @@ -5,8 +5,12 @@ * * =========================================================== * + * Oct 06 2009 David Rokhvarg Modifications for Sangoma + * Windows Device driver. + * * Feb 09 2007 Joel M. Pareja Added link state notification * for NETGRAPH failover support. + * * Dec 02 2003 Nenad Corbic Initial Driver */ @@ -19,8 +23,12 @@ #include #elif defined(__LINUX__) #include +#elif defined(__WINDOWS__) +#include "wanpipe_lip.h" #endif +WAN_DECLARE_NETDEV_OPS(wan_netdev_ops) + /*============================================================= * Definitions */ @@ -58,6 +66,8 @@ static int wplip_unreg(void *reg_ptr); #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) static void wplip_if_task (void *arg, int dummy); +#elif defined(__WINDOWS__) +static void wplip_if_task (IN PKDPC Dpc, IN PVOID arg, IN PVOID SystemArgument1, IN PVOID SystemArgument2); #else # if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) static void wplip_if_task (void *arg); @@ -129,10 +139,10 @@ int sdla_memdbg_push(void *mem, const char *func_name, const int line, int len) sdla_mem_el->mem=mem; strncpy(sdla_mem_el->cmd_func,func_name,sizeof(sdla_mem_el->cmd_func)-1); - wan_spin_lock_irq(&wan_debug_mem_lock,&flags); + wplip_spin_lock_irq(&wan_debug_mem_lock,&flags); wan_debug_mem+=sdla_mem_el->len; WAN_LIST_INSERT_HEAD(&sdla_memdbg_head, sdla_mem_el, next); - wan_spin_unlock_irq(&wan_debug_mem_lock,&flags); + wplip_spin_unlock_irq(&wan_debug_mem_lock,&flags); DEBUG_EVENT("%s:%d: Alloc %p Len=%i Total=%i\n", sdla_mem_el->cmd_func,sdla_mem_el->line, @@ -148,7 +158,7 @@ int sdla_memdbg_pull(void *mem, const char *func_name, const int line) wan_smp_flag_t flags; int err=-1; - wan_spin_lock_irq(&wan_debug_mem_lock,&flags); + wplip_spin_lock_irq(&wan_debug_mem_lock,&flags); WAN_LIST_FOREACH(sdla_mem_el, &sdla_memdbg_head, next){ if (sdla_mem_el->mem == mem) { @@ -160,7 +170,7 @@ int sdla_memdbg_pull(void *mem, const char *func_name, const int line) WAN_LIST_REMOVE(sdla_mem_el, next); wan_debug_mem-=sdla_mem_el->len; - wan_spin_unlock_irq(&wan_debug_mem_lock,&flags); + wplip_spin_unlock_irq(&wan_debug_mem_lock,&flags); DEBUG_EVENT("%s:%d: DeAlloc %p Len=%i Total=%i (From %s:%d)\n", func_name,line, @@ -173,7 +183,7 @@ int sdla_memdbg_pull(void *mem, const char *func_name, const int line) #endif err=0; } else { - wan_spin_unlock_irq(&wan_debug_mem_lock,&flags); + wplip_spin_unlock_irq(&wan_debug_mem_lock,&flags); } if (err) { @@ -361,13 +371,19 @@ static int wplip_unreg(void *lip_link_ptr) return 0; } - +#if defined(__WINDOWS__) +static int wplip_if_reg(void *lip_link_ptr, netdevice_t *dev, wanif_conf_t *conf) +#else static int wplip_if_reg(void *lip_link_ptr, char *dev_name, wanif_conf_t *conf) +#endif { wplip_link_t *lip_link = (wplip_link_t*)lip_link_ptr; wplip_dev_t *lip_dev; int usedby=0; int err; +#if defined(__WINDOWS__) + const char *dev_name = dev->name; +#endif if (!conf){ DEBUG_EVENT("%s: LIP DEV: If Registartion without configuration!\n",dev_name); @@ -422,7 +438,11 @@ static int wplip_if_reg(void *lip_link_ptr, char *dev_name, wanif_conf_t *conf) return -EINVAL; } +#if defined(__WINDOWS__) + if ((lip_dev = wplip_create_lipdev(dev, usedby)) == NULL){ +#else if ((lip_dev = wplip_create_lipdev(dev_name, usedby)) == NULL){ +#endif DEBUG_EVENT("%s: LIP: Failed to create lip priv device %s\n", lip_link->name,dev_name); return -ENOMEM; @@ -430,7 +450,7 @@ static int wplip_if_reg(void *lip_link_ptr, char *dev_name, wanif_conf_t *conf) #if defined(__LINUX__) if (conf->mtu){ - lip_dev->common.dev->mtu = conf->mtu; + lip_dev->common.dev->mtu = conf->mtu; } #endif @@ -439,7 +459,8 @@ static int wplip_if_reg(void *lip_link_ptr, char *dev_name, wanif_conf_t *conf) lip_dev->protocol = conf->protocol; lip_dev->common.usedby = usedby; lip_dev->common.state = WAN_DISCONNECTED; - WAN_NETIF_CARRIER_OFF(lip_dev->common.dev); + + WAN_NETIF_CARRIER_OFF(lip_dev->common.dev); #if defined(__LINUX__) if (conf->true_if_encoding){ @@ -548,6 +569,7 @@ static int wplip_if_reg(void *lip_link_ptr, char *dev_name, wanif_conf_t *conf) lip_link->name, lip_dev, lip_dev->magic); + return 0; } @@ -568,22 +590,23 @@ static int wplip_if_reg(void *lip_link_ptr, char *dev_name, wanif_conf_t *conf) static int wplip_if_unreg (netdevice_t *dev) { - wplip_dev_t *lip_dev = (wplip_dev_t *)wan_netif_priv(dev); + wplip_dev_t *lip_dev = wplip_netdev_get_lipdev(dev); wplip_link_t *lip_link = NULL; wplip_link_t *stack_lip_link = NULL; wan_smp_flag_t flags; if (!lip_dev) return -ENODEV; - + + /* under windows interface is always up, disregard this flag */ +#ifndef __WINDOWS__ if (WAN_NETIF_UP(dev)){ DEBUG_EVENT("%s: Failed to unregister: Device UP!\n", wan_netif_name(dev)); return -EBUSY; } - +#endif lip_link = lip_dev->lip_link; - if (wplip_link_exists(lip_link) != 0){ DEBUG_EVENT("%s: Failed to unregister: no link device\n", wan_netif_name(dev)); @@ -609,10 +632,10 @@ static int wplip_if_unreg (netdevice_t *dev) wplip_close_lipdev_prot(lip_dev); - wan_spin_lock_irq(&lip_link->bh_lock,&flags); + wplip_spin_lock_irq(&lip_link->bh_lock,&flags); lip_link->cur_tx=NULL; wan_skb_queue_purge(&lip_dev->tx_queue); - wan_spin_unlock_irq(&lip_link->bh_lock,&flags); + wplip_spin_unlock_irq(&lip_link->bh_lock,&flags); DEBUG_EVENT("%s: Unregistering LIP device\n", wan_netif_name(dev)); @@ -643,6 +666,7 @@ static int wplip_bind_link(void *lip_id,netdevice_t *dev) wplip_link_t *lip_link = (wplip_link_t*)lip_id; wplip_dev_list_t *lip_dev_list_el; wan_smp_flag_t flags; + wanpipe_common_t *common = (wanpipe_common_t*)wan_netif_priv(dev); if (!lip_id){ return -ENODEV; @@ -663,14 +687,22 @@ static int wplip_bind_link(void *lip_id,netdevice_t *dev) WAN_DEV_HOLD(dev); + /* Check if lower layer is connected. We must do this + in case the lower layer is already connected on startup. + Otherwise we can get into a condition where LIP layer is down + and lower layer is UP */ + if (common && common->state == WAN_CONNECTED) { + lip_link->carrier_state = WAN_CONNECTED; + } + lip_dev_list_el->dev=dev; - wan_spin_lock_irq(&lip_link->bh_lock,&flags); + wplip_spin_lock_irq(&lip_link->bh_lock,&flags); WAN_LIST_INSERT_HEAD(&lip_link->list_head_tx_ifdev,lip_dev_list_el,list_entry); lip_link->tx_dev_cnt++; - wan_spin_unlock_irq(&lip_link->bh_lock,&flags); + wplip_spin_unlock_irq(&lip_link->bh_lock,&flags); return 0; } @@ -691,7 +723,7 @@ static int wplip_unbind_link(void *lip_id,netdevice_t *dev) } - wan_spin_lock_irq(&lip_link->bh_lock,&flags); + wplip_spin_lock_irq(&lip_link->bh_lock,&flags); WAN_LIST_FOREACH(lip_dev_list_el,&lip_link->list_head_tx_ifdev,list_entry){ if (lip_dev_list_el->dev == dev){ @@ -702,7 +734,7 @@ static int wplip_unbind_link(void *lip_id,netdevice_t *dev) } } - wan_spin_unlock_irq(&lip_link->bh_lock,&flags); + wplip_spin_unlock_irq(&lip_link->bh_lock,&flags); if (err==0){ WAN_DEV_PUT(lip_dev_list_el->dev); @@ -847,6 +879,7 @@ int wplip_data_rx_up(wplip_dev_t* lip_dev, void *skb) break; #endif +#if !defined(__WINDOWS__) case STACK: if (lip_dev->common.lip){ int err=wplip_rx(lip_dev->common.lip,skb); @@ -863,7 +896,10 @@ int wplip_data_rx_up(wplip_dev_t* lip_dev, void *skb) } break; +#endif + default: + /* Windows note: rx data always handled by 'default' case! */ if (wan_iface.input && wan_iface.input(lip_dev->common.dev, skb) == 0){ WAN_NETIF_STATS_INC_RX_PACKETS(&lip_dev->common); //lip_dev->ifstats.rx_packets++; WAN_NETIF_STATS_INC_RX_BYTES(&lip_dev->common,len); //lip_dev->ifstats.rx_bytes += len; @@ -910,7 +946,7 @@ int wplip_data_tx_down(wplip_link_t *lip_link, void *skb) if (!lip_link->tx_dev_cnt){ - DEBUG_EVENT("%s: %s: Tx Dev List empty! dropping...\n", + DEBUG_EVENT("%s(): %s: (1) Tx Dev List empty! dropping...\n", __FUNCTION__,lip_link->name); return -ENODEV; } @@ -919,7 +955,7 @@ int wplip_data_tx_down(wplip_link_t *lip_link, void *skb) * For now, we can only transmit on a FIRST Tx device */ lip_dev_list_el=WAN_LIST_FIRST(&lip_link->list_head_tx_ifdev); if (!lip_dev_list_el){ - DEBUG_EVENT("%s: %s: Tx Dev List empty! dropping...\n", + DEBUG_EVENT("%s(): %s: (2) Tx Dev List empty! dropping...\n", __FUNCTION__,lip_link->name); return -ENODEV; } @@ -941,12 +977,12 @@ int wplip_data_tx_down(wplip_link_t *lip_link, void *skb) return -EBUSY; } -#if defined(__LINUX__) +#if defined(__LINUX__) || defined(__WINDOWS__) if (lip_link->latency_qlen != dev->tx_queue_len) { dev->tx_queue_len=lip_link->latency_qlen; } - return dev->hard_start_xmit(skb,dev); + return WAN_NETDEV_XMIT(skb,dev); #else if (!(WAN_NETIF_QUEUE_STOPPED(dev)) && dev->if_output){ return dev->if_output(dev, skb, NULL,NULL); @@ -960,7 +996,7 @@ int wplip_data_tx_down(wplip_link_t *lip_link, void *skb) int wplip_change_mtu(netdevice_t *dev, int new_mtu) { #if defined(__LINUX__) - wplip_dev_t *lip_dev = (wplip_dev_t *)wan_netif_priv(dev); + wplip_dev_t *lip_dev = wplip_netdev_get_lipdev(dev); wplip_link_t *lip_link = lip_dev->lip_link; netdevice_t *hw_dev; wplip_dev_list_t *lip_dev_list_el; @@ -993,13 +1029,13 @@ int wplip_change_mtu(netdevice_t *dev, int new_mtu) __FUNCTION__,lip_link->name); return -ENODEV; } + + if (WAN_NETDEV_TEST_MTU(hw_dev)) { + err = WAN_NETDEV_CHANGE_MTU(hw_dev,new_mtu); + } - if (hw_dev->change_mtu) { - err = hw_dev->change_mtu(hw_dev,new_mtu); - } - if (err == 0) { - dev->mtu = new_mtu; + WAN_NETDEV_OPS_MTU(dev,wan_netdev_ops,new_mtu); } return err; @@ -1148,10 +1184,10 @@ static void wplip_connect(void *wplip_id,int reason) DEBUG_EVENT("%s: Lip Link Carrier Connected! \n", lip_link->name); - wan_spin_lock_irq(&lip_link->bh_lock,&flags); + wplip_spin_lock_irq(&lip_link->bh_lock,&flags); wan_skb_queue_purge(&lip_link->tx_queue); wan_skb_queue_purge(&lip_link->rx_queue); - wan_spin_unlock_irq(&lip_link->bh_lock,&flags); + wplip_spin_unlock_irq(&lip_link->bh_lock,&flags); wplip_kick(lip_link,0); @@ -1394,6 +1430,8 @@ unsigned int wplip_get_ipv4_addr (void *wplip_id, int type) default: return 0; } +#elif defined(__WINDOWS__) + /* not used */ #else netdevice_t *ifp = NULL; struct ifaddr *ifa = NULL; @@ -1478,6 +1516,9 @@ int wplip_set_ipv4_addr (void *wplip_id, err = wp_devinet_ioctl(SIOCSIFDSTADDR, &if_info); set_fs(fs); + return 0; +#elif defined(__WINDOWS__) + /* not used */ return 0; #else netdevice_t *ifp = NULL; @@ -1596,6 +1637,8 @@ static int wplip_change_dev_flags (netdevice_t *dev, unsigned flags) #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) static void wplip_if_task (void *arg, int dummy) +#elif defined(__WINDOWS__) +static void wplip_if_task (IN PKDPC Dpc, IN PVOID arg, IN PVOID SystemArgument1, IN PVOID SystemArgument2) #else # if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) static void wplip_if_task (void *arg) @@ -1607,7 +1650,7 @@ static void wplip_if_task (struct work_struct *work) #if defined(__LINUX__) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) - wplip_dev_t *lip_dev=(wplip_dev_t *)container_of(work, wplip_dev_t, if_task); + wplip_dev_t *lip_dev=(wplip_dev_t *)container_of(work, wplip_dev_t, if_task); #else wplip_dev_t *lip_dev=(wplip_dev_t *)arg; #endif @@ -1627,7 +1670,7 @@ static void wplip_if_task (struct work_struct *work) return; } - DEBUG_TEST("%s:%d: Device %s\n",__FUNCTION__,__LINE__,lip_dev->name); + DEBUG_TEST("%s():line:%d: LIP Device %s\n",__FUNCTION__,__LINE__,lip_dev->name); lip_link = lip_dev->lip_link; dev=lip_dev->common.dev; diff --git a/patches/kdrivers/src/lip/wanpipe_lip_ipx.c b/patches/kdrivers/src/lip/wanpipe_lip_ipx.c index bd34461..331d770 100644 --- a/patches/kdrivers/src/lip/wanpipe_lip_ipx.c +++ b/patches/kdrivers/src/lip/wanpipe_lip_ipx.c @@ -1,5 +1,7 @@ #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) # include +#elif defined(__WINDOWS__) +#include "wanpipe_lip.h" #else # include #endif diff --git a/patches/kdrivers/src/lip/wanpipe_lip_netdev.c b/patches/kdrivers/src/lip/wanpipe_lip_netdev.c index a6122d3..f92aadb 100644 --- a/patches/kdrivers/src/lip/wanpipe_lip_netdev.c +++ b/patches/kdrivers/src/lip/wanpipe_lip_netdev.c @@ -15,6 +15,8 @@ #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) # include +#elif defined(__WINDOWS__) +#include "wanpipe_lip.h" #else # include #endif @@ -50,7 +52,7 @@ char* get_master_dev_name(wplip_link_t *lip_link); int wplip_open_dev(netdevice_t *dev) { - wplip_dev_t *lip_dev = (wplip_dev_t *)wan_netif_priv(dev); + wplip_dev_t *lip_dev = wplip_netdev_get_lipdev(dev); if (!lip_dev || !lip_dev->lip_link){ return -ENODEV; @@ -122,7 +124,7 @@ int wplip_open_dev(netdevice_t *dev) int wplip_stop_dev(netdevice_t *dev) { - wplip_dev_t *lip_dev = (wplip_dev_t *)wan_netif_priv(dev); + wplip_dev_t *lip_dev = wplip_netdev_get_lipdev(dev); if (!lip_dev || !lip_dev->lip_link){ return 0; @@ -163,7 +165,36 @@ int wplip_stop_dev(netdevice_t *dev) static struct net_device_stats gstats; struct net_device_stats* wplip_ifstats (netdevice_t *dev) { - wplip_dev_t *lip_dev = (wplip_dev_t *)wan_netif_priv(dev); + wplip_dev_t *lip_dev = wplip_netdev_get_lipdev(dev); + + DEBUG_TEST("%s: LIP %s()\n", + wan_netif_name(dev),__FUNCTION__); + + if (lip_dev){ + return &lip_dev->common.if_stats; + } + + return &gstats; +} +#endif + +#if defined(__WINDOWS__) +/*============================================================== + * wplip_ifstats + * + * Description: + * This fucntion interfaces the /proc file system + * to the svc device. The svc keeps protocol and + * packet statistcs. This function passes these + * statistics to the /proc file system. + * + * Usedby: + * Kernel /proc/net/dev file system + */ +static struct net_device_stats gstats; +struct net_device_stats* wplip_ifstats (netdevice_t *dev) +{ + wplip_dev_t *lip_dev = wplip_netdev_get_lipdev(dev); DEBUG_TEST("%s: LIP %s()\n", wan_netif_name(dev),__FUNCTION__); @@ -202,14 +233,14 @@ struct net_device_stats* wplip_ifstats (netdevice_t *dev) * Kernel TCP/IP stack or upper layers to transmit data. */ -#if defined(__LINUX__) +#if defined(__LINUX__) || defined(__WINDOWS__) int wplip_if_send (netskb_t* skb, netdevice_t* dev) #else int wplip_if_output (netdevice_t* dev,netskb_t* skb,struct sockaddr* sa, struct rtentry* rt) #endif { - wplip_dev_t *lip_dev =wplip_get_lipdev(dev); - wan_api_tx_hdr_t *api_tx_hdr =NULL; + wplip_dev_t *lip_dev = wplip_netdev_get_lipdev(dev); + wan_api_tx_hdr_t *api_tx_hdr = NULL; int err, type; int len = skb?wan_skb_len(skb):0; @@ -266,7 +297,7 @@ int wplip_if_output (netdevice_t* dev,netskb_t* skb,struct sockaddr* sa, struct type = WPLIP_RAW; }else{ -#if defined(__LINUX__) +#if defined(__LINUX__) || defined(__WINDOWS__) type = wplip_decode_protocol(lip_dev,skb); #else type = wplip_decode_protocol(lip_dev,sa); @@ -329,11 +360,9 @@ int wplip_if_output (netdevice_t* dev,netskb_t* skb,struct sockaddr* sa, struct } #if defined(__LINUX__) - - static void wplip_tx_timeout (netdevice_t *dev) { - wplip_dev_t *lip_dev = (wplip_dev_t *)wan_netif_priv(dev); + wplip_dev_t *lip_dev = wplip_netdev_get_lipdev(dev); wplip_link_t *lip_link = lip_dev->lip_link; /* If our device stays busy for at least 5 seconds then we will @@ -356,13 +385,42 @@ static void wplip_tx_timeout (netdevice_t *dev) wan_netif_set_ticks(dev, SYSTEM_TICKS); } -#endif +#endif/* __LINUX__ */ + +#if defined(__WINDOWS__) +static void wplip_tx_timeout (netdevice_t *dev) +{ + wplip_dev_t *lip_dev = wplip_netdev_get_lipdev(dev); + wplip_link_t *lip_link = lip_dev->lip_link; + + /* If our device stays busy for at least 5 seconds then we will + * kick start the device by making dev->tbusy = 0. We expect + * that our device never stays busy more than 5 seconds. So this + * is only used as a last resort. + */ + +#if 0 + gdbg_flag=1; +#endif + wan_clear_bit(WPLIP_BH_AWAITING_KICK,&lip_link->tq_working); + wplip_kick_trigger_bh(lip_link); + + WAN_NETIF_WAKE_QUEUE (dev); + + if (lip_dev->common.usedby == API){ + wan_update_api_state(lip_dev); + } + + wan_netif_set_ticks(dev, SYSTEM_TICKS); +} +#endif/* __WINDOWS__ */ + static int wplip_ioctl (netdevice_t *dev, struct ifreq *ifr, wan_ioctl_cmd_t cmd) { - wplip_dev_t *lip_dev= wplip_get_lipdev(dev); + wplip_dev_t *lip_dev = wplip_netdev_get_lipdev(dev); wplip_link_t *lip_link; wan_smp_flag_t flags; int err=0; @@ -390,14 +448,14 @@ wplip_ioctl (netdevice_t *dev, struct ifreq *ifr, wan_ioctl_cmd_t cmd) break; } - wan_spin_lock_irq(&lip_link->bh_lock,&flags); + wplip_spin_lock_irq(&lip_link->bh_lock,&flags); if (lip_dev->common.usedby == API){ dev->watchdog_timeo=HZ*60; } err=wan_bind_api_to_svc(lip_dev,ifr->ifr_data); - wan_spin_unlock_irq(&lip_link->bh_lock,&flags); + wplip_spin_unlock_irq(&lip_link->bh_lock,&flags); break; case SIOC_WANPIPE_UNBIND_SK: @@ -406,13 +464,13 @@ wplip_ioctl (netdevice_t *dev, struct ifreq *ifr, wan_ioctl_cmd_t cmd) break; } - wan_spin_lock_irq(&lip_link->bh_lock,&flags); + wplip_spin_lock_irq(&lip_link->bh_lock,&flags); err=wan_unbind_api_from_svc(lip_dev,ifr->ifr_data); if (lip_dev->common.usedby == API && lip_dev->protocol == WANCONFIG_XDLC){ wplip_close_lipdev_prot(lip_dev); } - wan_spin_unlock_irq(&lip_link->bh_lock,&flags); + wplip_spin_unlock_irq(&lip_link->bh_lock,&flags); break; case SIOC_WANPIPE_CHECK_TX: @@ -430,13 +488,13 @@ wplip_ioctl (netdevice_t *dev, struct ifreq *ifr, wan_ioctl_cmd_t cmd) case SIOC_WANPIPE_PIPEMON: - wan_spin_lock_irq(&lip_link->bh_lock,&flags); + wplip_spin_lock_irq(&lip_link->bh_lock,&flags); if (lip_dev->udp_pkt_len != 0){ - wan_spin_unlock_irq(&lip_link->bh_lock,&flags); + wplip_spin_unlock_irq(&lip_link->bh_lock,&flags); return -EBUSY; } lip_dev->udp_pkt_len = sizeof(wan_udp_hdr_t); - wan_spin_unlock_irq(&lip_link->bh_lock,&flags); + wplip_spin_unlock_irq(&lip_link->bh_lock,&flags); wan_udp_pkt=(wan_udp_pkt_t*)&lip_dev->udp_pkt_data; if (WAN_COPY_FROM_USER(&wan_udp_pkt->wan_udp_hdr,ifr->ifr_data,sizeof(wan_udp_hdr_t))){ @@ -444,7 +502,7 @@ wplip_ioctl (netdevice_t *dev, struct ifreq *ifr, wan_ioctl_cmd_t cmd) return -EFAULT; } - wan_spin_lock_irq(&lip_link->bh_lock,&flags); + wplip_spin_lock_irq(&lip_link->bh_lock,&flags); if(wan_udp_pkt->wan_udp_command == WAN_GET_MASTER_DEV_NAME){ char* master_dev_name; @@ -464,7 +522,7 @@ wplip_ioctl (netdevice_t *dev, struct ifreq *ifr, wan_ioctl_cmd_t cmd) }else{ if (wplip_prot_udp_mgmt_pkt(lip_dev,wan_udp_pkt) <= 0){ lip_dev->udp_pkt_len=0; - wan_spin_unlock_irq(&lip_link->bh_lock,&flags); + wplip_spin_unlock_irq(&lip_link->bh_lock,&flags); return -EINVAL; } } @@ -473,11 +531,11 @@ wplip_ioctl (netdevice_t *dev, struct ifreq *ifr, wan_ioctl_cmd_t cmd) DEBUG_EVENT("%s: Error: Pipemon buf too bit on the way up! %i\n", lip_dev->name,lip_dev->udp_pkt_len); lip_dev->udp_pkt_len=0; - wan_spin_unlock_irq(&lip_link->bh_lock,&flags); + wplip_spin_unlock_irq(&lip_link->bh_lock,&flags); return -EINVAL; } - wan_spin_unlock_irq(&lip_link->bh_lock,&flags); + wplip_spin_unlock_irq(&lip_link->bh_lock,&flags); /* This area will still be critical to other * PIPEMON commands due to udp_pkt_len @@ -506,14 +564,14 @@ wplip_ioctl (netdevice_t *dev, struct ifreq *ifr, wan_ioctl_cmd_t cmd) if (cmd >= SIOC_WANPIPE_DEVPRIVATE) { - wan_spin_lock_irq(&lip_link->bh_lock,&flags); + wplip_spin_lock_irq(&lip_link->bh_lock,&flags); cmd-=SIOC_WANPIPE_DEVPRIVATE; if (ifr == NULL){ err=wplip_prot_ioctl(lip_dev,cmd,NULL); }else{ err=wplip_prot_ioctl(lip_dev,cmd,ifr->ifr_data); } - wan_spin_unlock_irq(&lip_link->bh_lock,&flags); + wplip_spin_unlock_irq(&lip_link->bh_lock,&flags); return err; } @@ -583,7 +641,7 @@ char* get_master_dev_name(wplip_link_t *lip_link) */ int wplip_if_init(netdevice_t *dev) { - wplip_dev_t* lip_dev= wplip_get_lipdev(dev); + wplip_dev_t* lip_dev = wplip_netdev_get_lipdev(dev); #if defined(__LINUX__) lip_dev->common.is_netdev = 1; @@ -595,6 +653,17 @@ int wplip_if_init(netdevice_t *dev) lip_dev->common.iface.get_stats = &wplip_ifstats; lip_dev->common.iface.change_mtu = &wplip_change_mtu; + return 0; +#elif defined(__WINDOWS__) + lip_dev->common.is_netdev = 1; + lip_dev->common.iface.open = &wplip_open_dev; + lip_dev->common.iface.close = &wplip_stop_dev; + lip_dev->common.iface.send = &wplip_if_send; + lip_dev->common.iface.ioctl = &wplip_ioctl; + lip_dev->common.iface.tx_timeout= &wplip_tx_timeout; + lip_dev->common.iface.get_stats = &wplip_ifstats; +// lip_dev->common.iface.change_mtu = &wplip_change_mtu; + return 0; #else DEBUG_EVENT("%s: Initialize network interface...\n", diff --git a/patches/kdrivers/src/lip/wanpipe_lip_prot.c b/patches/kdrivers/src/lip/wanpipe_lip_prot.c index 632f853..b801d8c 100644 --- a/patches/kdrivers/src/lip/wanpipe_lip_prot.c +++ b/patches/kdrivers/src/lip/wanpipe_lip_prot.c @@ -1,6 +1,8 @@ #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) # include +#elif defined(__WINDOWS__) +# include "wanpipe_lip.h" #else # include #endif @@ -16,9 +18,18 @@ static wplip_prot_iface_t *wplip_prot_ops[MAX_LIP_PROTOCOLS]; */ static wplip_prot_reg_t wplip_prot_reg_ops; + +#if defined(__WINDOWS__) +static void wplip_prot_timer(IN PKDPC Dpc, void *arg, void *arg2, void *arg3); +extern void wplip_poll_carrier_status(wplip_link_t *lip_link); +#else static void wplip_prot_timer(wan_timer_arg_t arg); +#endif + #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) static void wplip_port_task (void *arg, int); +#elif defined(__WINDOWS__) +static void wplip_port_task (IN PKDPC Dpc, void *arg, void *arg2, void *arg3); #else # if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) static void wplip_port_task (void *arg); @@ -535,6 +546,8 @@ int wplip_prot_udp_snmp_pkt(wplip_dev_t * lip_dev, int cmd, struct ifreq* ifr) #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) static void wplip_port_task (void *arg, int dummy) +#elif defined(__WINDOWS__) +static void wplip_port_task (IN PKDPC Dpc, void *arg, void *arg2, void *arg3) #else # if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) static void wplip_port_task (void *arg) @@ -570,8 +583,12 @@ static void wplip_port_task (struct work_struct *work) } } - +#if defined(__WINDOWS__) +/* This timer runs all the time. The delay is 1 second. */ +static void wplip_prot_timer(IN PKDPC Dpc, void *arg, void *arg2, void *arg3) +#else static void wplip_prot_timer(wan_timer_arg_t arg) +#endif { wplip_link_t *lip_link=(wplip_link_t *)arg; unsigned int period=HZ; @@ -595,7 +612,15 @@ static void wplip_prot_timer(wan_timer_arg_t arg) return; } - wan_spin_lock_irq(&lip_link->bh_lock,&flags); +#if defined(__WINDOWS__) + /* Front End often gets connected before Protocol is fully + * initialized (especially on 56k), so "Connected" event is lost. + * Poll the Carrier for FE Status and, if different from what + * 'lip_link->carrier_state', call wplip_connect()/wplip_disconnect(). */ + wplip_poll_carrier_status(lip_link); +#endif + + wplip_spin_lock_irq(&lip_link->bh_lock,&flags); if (lip_link->carrier_state == WAN_CONNECTED){ prot_iface->timer(lip_link->prot,&period,1); @@ -603,7 +628,7 @@ static void wplip_prot_timer(wan_timer_arg_t arg) prot_iface->timer(lip_link->prot,&period,0); } - wan_spin_unlock_irq(&lip_link->bh_lock,&flags); + wplip_spin_unlock_irq(&lip_link->bh_lock,&flags); if (wan_skb_queue_len(&lip_link->tx_queue)){ wplip_trigger_bh(lip_link); @@ -645,8 +670,7 @@ static int wplip_prot_rx_up(void *lip_dev_ptr, void *skb, int type) case WPLIP_RAW: - if ((lip_dev->protocol == WANCONFIG_LIP_ATM || - lip_dev->protocol == WANCONFIG_LIP_ATM) && + if ((lip_dev->protocol == WANCONFIG_LIP_ATM) && lip_dev->common.usedby != API) { /* If ATM is not in API mode Dont pass RAW Cells up the stack */ @@ -881,31 +905,31 @@ int wplip_init_prot(void) prot_iface->bh = wp_lapb_bh; prot_iface->snmp = NULL; #endif - - /* LAPD initialization */ + + /* LAPD initialization */ #ifdef CONFIG_PRODUCT_WANPIPE_LIP_LAPD - offset+=sprintf(&tmp[offset],"%s ","LIP_LAPD"); - prot_iface=wan_kmalloc(sizeof(wplip_prot_iface_t)); - if (!prot_iface){ - return -ENOMEM; - } - memset(prot_iface,0,sizeof(wplip_prot_iface_t)); - wplip_prot_ops[WANCONFIG_LAPD]=prot_iface; - - prot_iface->init = 1; - prot_iface->prot_link_register = wp_register_lapd_prot; - prot_iface->prot_link_unregister = wp_unregister_lapb_prot; - prot_iface->prot_chan_register = wp_register_lapb_chan_prot; - prot_iface->prot_chan_unregister = wp_unregister_lapb_chan_prot; - prot_iface->open_chan = wp_lapb_open; - prot_iface->close_chan = wp_lapb_close; - prot_iface->tx = wp_lapb_tx; - prot_iface->ioctl = NULL; - prot_iface->pipemon = NULL; - prot_iface->rx = wp_lapb_rx; - prot_iface->timer = wp_lapb_timer; - prot_iface->bh = wp_lapb_bh; - prot_iface->snmp = NULL; + offset+=sprintf(&tmp[offset],"%s ","LIP_LAPD"); + prot_iface=wan_kmalloc(sizeof(wplip_prot_iface_t)); + if (!prot_iface){ + return -ENOMEM; + } + memset(prot_iface,0,sizeof(wplip_prot_iface_t)); + wplip_prot_ops[WANCONFIG_LAPD]=prot_iface; + + prot_iface->init = 1; + prot_iface->prot_link_register = wp_register_lapd_prot; + prot_iface->prot_link_unregister = wp_unregister_lapb_prot; + prot_iface->prot_chan_register = wp_register_lapb_chan_prot; + prot_iface->prot_chan_unregister = wp_unregister_lapb_chan_prot; + prot_iface->open_chan = wp_lapb_open; + prot_iface->close_chan = wp_lapb_close; + prot_iface->tx = wp_lapb_tx; + prot_iface->ioctl = NULL; + prot_iface->pipemon = NULL; + prot_iface->rx = wp_lapb_rx; + prot_iface->timer = wp_lapb_timer; + prot_iface->bh = wp_lapb_bh; + prot_iface->snmp = NULL; #endif diff --git a/patches/kdrivers/src/lip/wanpipe_lip_sub.c b/patches/kdrivers/src/lip/wanpipe_lip_sub.c index bd5d785..57cba0d 100644 --- a/patches/kdrivers/src/lip/wanpipe_lip_sub.c +++ b/patches/kdrivers/src/lip/wanpipe_lip_sub.c @@ -1,5 +1,7 @@ #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) # include +#elif defined(__WINDOWS__) +#include "wanpipe_lip.h" #else # include #endif @@ -17,6 +19,8 @@ static int wplip_register_netif(netdevice_t *dev, wplip_dev_t *lip_dev, int ifty static void wplip_unregister_netif(netdevice_t *dev, wplip_dev_t *lip_dev); #if defined(__LINUX__) extern void wplip_link_bh(unsigned long data); +#elif defined(__WINDOWS__) +extern void wplip_link_bh(IN PKDPC Dpc, IN PVOID data, IN PVOID SystemArgument1, IN PVOID SystemArgument2); #else extern void wplip_link_bh(void *data, int pending); #endif @@ -60,7 +64,7 @@ wplip_link_t *wplip_create_link(char *devname) wan_skb_queue_init(&lip_link->tx_queue); wan_skb_queue_init(&lip_link->rx_queue); - wan_spin_lock_irq_init(&lip_link->bh_lock, "wan_lip_bh_lock"); + wplip_spin_lock_irq_init(&lip_link->bh_lock, "wan_lip_bh_lock"); wan_atomic_set(&lip_link->refcnt,0); @@ -228,9 +232,9 @@ int wplip_lipdev_latency_change(wplip_link_t *lip_link) if (latency_qlen > 0 && latency_qlen < 0xFFFF) { wan_smp_flag_t flags; - wan_spin_lock_irq(&lip_link->bh_lock,&flags); + wplip_spin_lock_irq(&lip_link->bh_lock,&flags); lip_link->latency_qlen = latency_qlen; - wan_spin_unlock_irq(&lip_link->bh_lock,&flags); + wplip_spin_unlock_irq(&lip_link->bh_lock,&flags); } return -ENODEV; @@ -259,9 +263,20 @@ int wplip_lipdev_latency_change(wplip_link_t *lip_link) * x25_register */ +#if defined(__WINDOWS__) +/* Under Windows the order of structure creation is opposite to + Linux order. The netdev already exist when svc is created. + So the binding between the two structures is done a little + differently. */ +wplip_dev_t *wplip_create_lipdev(netdevice_t *dev, int usedby) +#else wplip_dev_t *wplip_create_lipdev(char *dev_name, int usedby) +#endif { wplip_dev_t *lip_dev; +#if defined(__WINDOWS__) + char *dev_name = dev->name; +#endif lip_dev=wan_kmalloc(sizeof(wplip_dev_t)); if (lip_dev == NULL){ @@ -273,7 +288,12 @@ wplip_dev_t *wplip_create_lipdev(char *dev_name, int usedby) lip_dev->magic=WPLIP_MAGIC_DEV; lip_dev->common.state = WAN_DISCONNECTED; lip_dev->common.usedby = usedby; + +#if defined(__WINDOWS__) + snprintf(lip_dev->name, sizeof(lip_dev->name), "%s", dev_name); +#else strncpy(lip_dev->name,dev_name,MAX_PROC_NAME); +#endif /* FIXME: No Entry Intializer */ /*WPLIP_INIT_LIST_HEAD(&lip_dev->list_entry);*/ @@ -282,14 +302,20 @@ wplip_dev_t *wplip_create_lipdev(char *dev_name, int usedby) wan_atomic_set(&lip_dev->refcnt,0); +#if defined(__WINDOWS__) + lip_dev->common.dev = dev; +#else lip_dev->common.dev=wplip_create_netif(dev_name, usedby); if (!lip_dev->common.dev){ wan_free(lip_dev); return NULL; } +#endif if (wplip_register_netif(lip_dev->common.dev, lip_dev, usedby)){ +#ifndef __WINDOWS__ wplip_free_netif(lip_dev->common.dev); +#endif lip_dev->common.dev=NULL; wan_free(lip_dev); return NULL; @@ -313,7 +339,7 @@ void wplip_free_lipdev(wplip_dev_t *lip_dev) if (lip_dev->common.dev){ /* FIXME: PRIV attached during unregister is it OK?*/ WAN_DEV_PUT(lip_dev->common.dev); - /*wan_netif_set_priv(lip_dev->common.dev, NULL);*/ + /*wplip_netdev_set_lipdev(lip_dev->common.dev, NULL);*/ wplip_unregister_netif(lip_dev->common.dev, lip_dev); lip_dev->common.dev=NULL; } @@ -405,7 +431,7 @@ void wplip_remove_lipdev(wplip_link_t *lip_link, wplip_dev_t *lip_dev) - +#ifndef __WINDOWS__ unsigned int dec_to_uint (unsigned char* str, int len) { unsigned val; @@ -418,7 +444,7 @@ unsigned int dec_to_uint (unsigned char* str, int len) return val; } - +#endif /****************************************************** @@ -488,7 +514,7 @@ static int wplip_register_netif(netdevice_t *dev, wplip_dev_t *lip_dev, int used { int err = -EINVAL; - wan_netif_set_priv(dev, lip_dev); + wplip_netdev_set_lipdev(dev, lip_dev); wplip_if_init(dev); /* From this point forward, wplip_free_if() will deallocate @@ -508,7 +534,7 @@ static int wplip_register_netif(netdevice_t *dev, wplip_dev_t *lip_dev, int used } if (err){ - wan_netif_set_priv(dev, NULL); + wplip_netdev_set_lipdev(dev, NULL); } return err; } @@ -521,7 +547,9 @@ static void wplip_unregister_netif(netdevice_t *dev, wplip_dev_t *lip_dev) } if (wan_iface.free){ - wan_iface.free(dev); +#ifndef __WINDOWS__ + wan_iface.free(dev); +#endif } return; diff --git a/patches/kdrivers/src/lip/wanpipe_lip_tty.c b/patches/kdrivers/src/lip/wanpipe_lip_tty.c index 49dee12..27dc1a4 100644 --- a/patches/kdrivers/src/lip/wanpipe_lip_tty.c +++ b/patches/kdrivers/src/lip/wanpipe_lip_tty.c @@ -74,7 +74,10 @@ static void tty_poll_task (struct work_struct *work) return; while ((skb=wan_skb_dequeue(&lip_link->tty_rx)) != NULL){ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)) + const struct tty_ldisc_ops *ops = tty->ldisc->ops; +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) const struct tty_ldisc_ops *ops = tty->ldisc.ops; #else const struct tty_ldisc *ops = &tty->ldisc; @@ -86,14 +89,17 @@ static void tty_poll_task (struct work_struct *work) wan_skb_free(skb); } - wan_spin_lock_irq(&lip_link->bh_lock,&smp_flags); + wplip_spin_lock_irq(&lip_link->bh_lock,&smp_flags); if (wan_test_and_clear_bit(WPLIP_TTY_BUSY,&lip_link->tq_working)){ err=1; } - wan_spin_unlock_irq(&lip_link->bh_lock,&smp_flags); + wplip_spin_unlock_irq(&lip_link->bh_lock,&smp_flags); if (err){ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)) + const struct tty_ldisc_ops *ops = tty->ldisc->ops; +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) const struct tty_ldisc_ops *ops = tty->ldisc.ops; #else const struct tty_ldisc *ops = &tty->ldisc; @@ -142,7 +148,7 @@ static void wanpipe_tty_close(struct tty_struct *tty, struct file * filp) wplip_link_prot_change_state(lip_link,WAN_DISCONNECTED,NULL,0); - wan_spin_lock_irq(&lip_link->bh_lock,&smp_flags); + wplip_spin_lock_irq(&lip_link->bh_lock,&smp_flags); lip_link->tty=NULL; while ((skb=wan_skb_dequeue(&lip_link->tty_rx)) != NULL){ @@ -150,7 +156,7 @@ static void wanpipe_tty_close(struct tty_struct *tty, struct file * filp) } /* BUGFIX: Moved down here */ - wan_spin_unlock_irq(&lip_link->bh_lock,&smp_flags); + wplip_spin_unlock_irq(&lip_link->bh_lock,&smp_flags); } return; @@ -182,9 +188,9 @@ static int wanpipe_tty_open(struct tty_struct *tty, struct file * filp) lip_link = (wplip_link_t*)tty->driver_data; if (!lip_link){ - wan_spin_lock_irq(&lip_link->bh_lock,&smp_flags); + wplip_spin_lock_irq(&lip_link->bh_lock,&smp_flags); lip_link->tty=NULL; - wan_spin_unlock_irq(&lip_link->bh_lock,&smp_flags); + wplip_spin_unlock_irq(&lip_link->bh_lock,&smp_flags); return -ENODEV; } @@ -193,9 +199,9 @@ static int wanpipe_tty_open(struct tty_struct *tty, struct file * filp) if (lip_link->tty_open == 0){ - wan_spin_lock_irq(&lip_link->bh_lock,&smp_flags); + wplip_spin_lock_irq(&lip_link->bh_lock,&smp_flags); lip_link->tty=tty; - wan_spin_unlock_irq(&lip_link->bh_lock,&smp_flags); + wplip_spin_unlock_irq(&lip_link->bh_lock,&smp_flags); wan_skb_queue_init(&lip_link->tty_rx); @@ -268,9 +274,9 @@ static int wanpipe_tty_write(struct tty_struct * tty, int from_user, err=wplip_data_tx_down(lip_link,skb); if (err){ - wan_spin_lock_irq(&lip_link->bh_lock,&smp_flags); + wplip_spin_lock_irq(&lip_link->bh_lock,&smp_flags); wan_set_bit(WPLIP_TTY_BUSY,&lip_link->tq_working); - wan_spin_unlock_irq(&lip_link->bh_lock,&smp_flags); + wplip_spin_unlock_irq(&lip_link->bh_lock,&smp_flags); DEBUG_TEST("%s: Failed to tx down!\n", lip_link->name); @@ -404,9 +410,9 @@ static void wanpipe_tty_put_char(struct tty_struct *tty, unsigned char ch) err=wplip_data_tx_down(lip_link,skb); if (err){ - wan_spin_lock_irq(&lip_link->bh_lock,&smp_flags); + wplip_spin_lock_irq(&lip_link->bh_lock,&smp_flags); wan_set_bit(WPLIP_TTY_BUSY,&lip_link->tq_working); - wan_spin_unlock_irq(&lip_link->bh_lock,&smp_flags); + wplip_spin_unlock_irq(&lip_link->bh_lock,&smp_flags); wan_skb_free(skb); } @@ -435,7 +441,10 @@ static void wanpipe_tty_flush_buffer(struct tty_struct *tty) wake_up_interruptible(&tty->poll_wait); #endif if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)) + const struct tty_ldisc_ops *ops = tty->ldisc->ops; +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) const struct tty_ldisc_ops *ops = tty->ldisc.ops; #else const struct tty_ldisc *ops = &tty->ldisc; @@ -506,9 +515,9 @@ static void wanpipe_tty_hangup(struct tty_struct *tty) if (!lip_link) return; - wan_spin_lock_irq(&lip_link->bh_lock,&smp_flags); + wplip_spin_lock_irq(&lip_link->bh_lock,&smp_flags); set_modem_status(lip_link,0); - wan_spin_unlock_irq(&lip_link->bh_lock,&smp_flags); + wplip_spin_unlock_irq(&lip_link->bh_lock,&smp_flags); return; } @@ -753,7 +762,7 @@ int wplip_unreg_tty(wplip_link_t *lip_link) lip_link->name,WAN_TTY_MAJOR); } - wan_spin_lock_irq(&lip_link->bh_lock,&smp_flags); + wplip_spin_lock_irq(&lip_link->bh_lock,&smp_flags); lip_link->tty=NULL; if (tty_lip_link_map[lip_link->tty_minor] == lip_link){ @@ -762,7 +771,7 @@ int wplip_unreg_tty(wplip_link_t *lip_link) state = &rs_table[lip_link->tty_minor]; memset(state,0,sizeof(*state)); - wan_spin_unlock_irq(&lip_link->bh_lock,&smp_flags); + wplip_spin_unlock_irq(&lip_link->bh_lock,&smp_flags); return 0; } diff --git a/patches/kdrivers/src/net/Makefile b/patches/kdrivers/src/net/Makefile index 989f6f9..5227c38 100644 --- a/patches/kdrivers/src/net/Makefile +++ b/patches/kdrivers/src/net/Makefile @@ -18,10 +18,11 @@ CONFIG_WANPIPE_MULTPPP=n EXTRA_CFLAGS=$(EXTRA_FLAGS) PRODUCT_DEFINES= -DCONFIG_PRODUCT_WANPIPE_BASE -DCONFIG_PRODUCT_WANPIPE_AFT -DCONFIG_PRODUCT_WANPIPE_AFT_CORE -PRODUCT_DEFINES+= -DCONFIG_PRODUCT_WANPIPE_AFT_TE1 -DCONFIG_PRODUCT_WANPIPE_AFT_TE3 +PRODUCT_DEFINES+= -DCONFIG_PRODUCT_WANPIPE_AFT_TE1 -DCONFIG_PRODUCT_WANPIPE_AFT_TE3 -DCONFIG_PRODUCT_WANPIPE_AFT_56K PRODUCT_DEFINES+= -DCONFIG_WANPIPE_HWEC -DCONFIG_PRODUCT_WANPIPE_SOCK_DATASCOPE -DCONFIG_PRODUCT_WANPIPE_AFT_BRI -DCONFIG_PRODUCT_WANPIPE_AFT_SERIAL PRODUCT_DEFINES+= -DCONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN -DCONFIG_PRODUCT_WANPIPE_CODEC_SLINEAR_LAW -DCONFIG_PRODUCT_WANPIPE_AFT_RM PRODUCT_DEFINES+= -DCONFIG_PRODUCT_WANPIPE_USB -DCONFIG_PRODUCT_WANPIPE_A700 -DCONFIG_PRODUCT_A600 -DCONFIG_PRODUCT_WANPIPE_AFT_A600 -DCONFIG_PRODUCT_WANPIPE_AFT_A700 +PRODUCT_DEFINES+= -DCONFIG_PRODUCT_WANPIPE_AFT_B601 wanpipe-y := sdlamain.o @@ -78,9 +79,9 @@ wanec-y += $(OCTAPIDIR)/oct6100_user.o wanec-objs := $(wanec-y) -sdladrv-objs := sdladrv_src.o sdladrv_fe.o sdladrv_utils.o sdladrv_usb.o wan_mem_debug.o +sdladrv-objs := sdladrv_src.o sdladrv_fe.o sdladrv_utils.o sdladrv_usb.o wan_mem_debug.o wanpipe_cdev_linux.o wanpipe_logger.o -wanrouter-objs := ../wanrouter/wanmain.o ../wanrouter/wanproc.o ../wanrouter/waniface.o ../wanrouter/wanpipe_cdev_linux.o ../wanrouter/wandev.o +wanrouter-objs := ../wanrouter/wanmain.o ../wanrouter/wanproc.o ../wanrouter/waniface.o ../wanrouter/wandev.o af_wanpipe-objs := ../wanrouter/af_wanpipe_src.o ../wanrouter/af_wanpipe_datascope.o diff --git a/util/wancfg_zaptel/templates/.svn/text-base/openzap.conf.svn-base b/patches/kdrivers/src/net/Module.markers similarity index 100% rename from util/wancfg_zaptel/templates/.svn/text-base/openzap.conf.svn-base rename to patches/kdrivers/src/net/Module.markers diff --git a/patches/kdrivers/src/net/aft_a104.c b/patches/kdrivers/src/net/aft_a104.c index be810f5..c262bef 100644 --- a/patches/kdrivers/src/net/aft_a104.c +++ b/patches/kdrivers/src/net/aft_a104.c @@ -79,6 +79,9 @@ sdla_te_timer( #if defined(CONFIG_WANPIPE_HWEC) static int aft_hwec_reset(void *pcard, int reset); static int aft_hwec_enable(void *pcard, int enable, int fe_chan); + +static int aft_hwec_reset_b601(void *pcard, int reset); +static int aft_hwec_enable_b601(void *pcard, int enable, int fe_chan); #endif int __a104_write_fe (void *pcard, ...); @@ -151,7 +154,7 @@ static int request_fifo_baddr_and_size(sdla_t *card, private_area_t *chan) fifo_size=(u8)aft_map_fifo_baddr_and_size(card,req_fifo_size,&chan->fifo_base_addr); if (fifo_size == 0 || chan->fifo_base_addr == 31){ - DEBUG_EVENT("%s:%s: Error: Failed to obtain fifo size %d or addr %d \n", + DEBUG_ERROR("%s:%s: Error: Failed to obtain fifo size %d or addr %d \n", card->devname,chan->if_name,fifo_size,chan->fifo_base_addr); return -EINVAL; } @@ -168,7 +171,7 @@ static int request_fifo_baddr_and_size(sdla_t *card, private_area_t *chan) } if (fifo_size != req_fifo_size){ - DEBUG_EVENT("%s:%s: Warning: Failed to obtain the req fifo %d got %d\n", + DEBUG_WARNING("%s:%s: Warning: Failed to obtain the req fifo %d got %d\n", card->devname,chan->if_name,req_fifo_size,fifo_size); } @@ -312,14 +315,10 @@ static int aft_request_logical_channel_num (sdla_t *card, private_area_t *chan, } if (force_lch) { - - /* NOTE This section is not used and should be take out after B601 code is done */ - DEBUG_EVENT("%s: Error, request logical channel - internal error - unsupported mode!\n",card->devname); - return -1; - - logic_ch=(char)chan->first_time_slot; - if (wan_test_and_set_bit(logic_ch,&card->u.aft.logic_ch_map)){ - DEBUG_EVENT("%s: Error, request logical ch=%d map busy (map 0x%08lX)\n", + if (!wan_test_and_set_bit(chan->first_time_slot,&card->u.aft.logic_ch_map)){ + logic_ch=(char)chan->first_time_slot; + } else { + DEBUG_ERROR("%s: Error, request logical ch=%d map busy (map 0x%08lX)\n", card->devname,chan->first_time_slot,card->u.aft.logic_ch_map); return -1; } @@ -368,7 +367,7 @@ static int aft_request_logical_channel_num (sdla_t *card, private_area_t *chan, } if (card->u.aft.dev_to_ch_map[(unsigned char)logic_ch]){ - DEBUG_EVENT("%s: Error, request logical ch=%d map busy\n", + DEBUG_ERROR("%s: Error, request logical ch=%d map busy\n", card->devname,logic_ch); return -1; } @@ -431,7 +430,7 @@ int a104_test_sync(sdla_t *card, int tx_only) card->hw_iface.bus_read_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG), ®); if (wan_test_bit(AFT_LCFG_FE_IFACE_RESET_BIT,®)){ - DEBUG_EVENT("%s: Warning: T1/E1 Reset Enabled %d! reg=0x%08X \n", + DEBUG_WARNING("%s: Warning: T1/E1 Reset Enabled %d! reg=0x%08X \n", card->devname, card->wandev.comm_port+1,reg); } @@ -470,6 +469,13 @@ int a104_led_ctrl(sdla_t *card, int color, int led_pos, int on) AFT_FUNC_DEBUG(); #if INIT_FE_ONLY #else + if (IS_B601_CARD(card)) { + card->hw_iface.bus_read_4(card->hw, AFT_PORT_REG(card,AFT_LINE_CFG_REG),®); + aft_set_led_b601(color, led_pos, on, ®); + card->hw_iface.bus_write_4(card->hw, AFT_PORT_REG(card,AFT_LINE_CFG_REG),reg); + return 0; + } + if (IS_SERIAL_CARD(card)) { return 0; } @@ -512,11 +518,104 @@ int a104_led_ctrl(sdla_t *card, int color, int led_pos, int on) return 0; } +int a104_set_octasic_clk_src(sdla_t *card) +{ + u32 reg; + u16 max_ec_chans; + card->hw_iface.getcfg(card->hw, SDLA_HWEC_NO, &max_ec_chans); + + if (max_ec_chans) { + card->hw_iface.bus_read_4(card->hw,AFT_PORT_REG(card,AFT_CHIP_CFG_REG),®); + aft_chipcfg_set_oct_clk_src(®,card->wandev.comm_port); + card->hw_iface.bus_write_4(card->hw,AFT_PORT_REG(card,AFT_CHIP_CFG_REG),reg); + } + return 0; +} + +int a104_set_octasic_clk_src_b601(sdla_t *card) +{ + wan_smp_flag_t smp_flags, flags; + u32 reg_1090; + u8 ext_clk_mux_bits = 0; + u8 pll_clk_mux_bits = 0; + u16 max_ec_chans; + + max_ec_chans = 0; + card->hw_iface.getcfg(card->hw, SDLA_HWEC_NO, &max_ec_chans); + + card->hw_iface.hw_lock(card->hw,&smp_flags); + wan_spin_lock_irq(&card->wandev.lock,&flags); + card->hw_iface.bus_read_4(card->hw, AFT_CLKCFG_A600_REG, ®_1090); + + /* Configure EC Clock source Mux */ + reg_1090 &= ~(AFT_CLKCFG_B601_EC_SRC_MUX_MASK<fe) == WAN_CLK_IN_2000HZ) { + reg_1090 |= 1 << AFT_CLKCFG_B601_EXT_CLK_VAL_SHIFT; + } + /* Configure External clock out Mux */ + reg_1090 &= ~(AFT_CLKCFG_B601_EXT_CLK_MUX_MASK<fe) == WAN_MASTER_CLK) { + if (IS_T1_CARD(card)) { + switch (WAN_FE_NETWORK_SYNC(&card->fe)) { + case WANOPT_NO: + ext_clk_mux_bits = AFT_CLKCFG_B601_EXT_CLK_MUX_OSC_1500HZ; + pll_clk_mux_bits = AFT_CLKCFG_B601_PLL_CLK_SRC_MUX_OSC_1500HZ; + break; + case WAN_CLK_IN_1500HZ: + ext_clk_mux_bits = AFT_CLKCFG_B601_EXT_CLK_MUX_H100_CLK; + pll_clk_mux_bits = AFT_CLKCFG_B601_PLL_CLK_SRC_MUX_EXT_1500HZ; + break; + case WANOPT_YES: /* Invalid */ + case WAN_CLK_IN_8000HZ: /* Invalid */ + case WAN_CLK_IN_2000HZ: /* Invalid */ + default: + DEBUG_EVENT("%s: Error:Invalid T1 Network sync option\n", card->devname); + return -EINVAL; + } + } else { + switch (WAN_FE_NETWORK_SYNC(&card->fe)) { + case WANOPT_NO: + ext_clk_mux_bits = AFT_CLKCFG_B601_EXT_CLK_MUX_H100_CLK; + pll_clk_mux_bits = AFT_CLKCFG_B601_PLL_CLK_SRC_MUX_OSC_2000HZ; + break; + case WANOPT_YES: + case WAN_CLK_IN_8000HZ: + case WAN_CLK_IN_2000HZ: /* bit 4 specifies 2Mhz or 8 Mhz source */ + ext_clk_mux_bits = AFT_CLKCFG_B601_EXT_CLK_MUX_H100_CLK; + pll_clk_mux_bits = AFT_CLKCFG_B601_PLL_CLK_SRC_MUX_EXT_8000HZ; + break; + case WAN_CLK_IN_1500HZ: /* Invalid */ + default: + DEBUG_EVENT("%s: Error:Invalid E1 Network sync option\n", card->devname); + return -EINVAL; + } + } + } + reg_1090 |= pll_clk_mux_bits << AFT_CLKCFG_B601_PLL_CLK_SRC_MUX_SHIFT; + reg_1090 |= ext_clk_mux_bits << AFT_CLKCFG_B601_EXT_CLK_MUX_SHIFT; + + card->hw_iface.bus_write_4(card->hw, AFT_CLKCFG_A600_REG, reg_1090); + wan_spin_unlock_irq(&card->wandev.lock,&flags); + card->hw_iface.hw_unlock(card->hw,&smp_flags); + return 0; +} + int a104_global_chip_config(sdla_t *card) { - u32 reg; + u32 reg, reg_line_cfg; int err=0; + int used_cnt; + + reg=0; /*============ GLOBAL CHIP CONFIGURATION ===============*/ AFT_FUNC_DEBUG(); #if INIT_FE_ONLY @@ -525,41 +624,65 @@ int a104_global_chip_config(sdla_t *card) err=card->wandev.fe_iface.global_config(&card->fe); } if (err){ - DEBUG_EVENT("%s(): %d: Error: global_config() ptr is NULL\n", + DEBUG_ERROR("%s(): %d: Error: global_config() ptr is NULL\n", __FUNCTION__, __LINE__); return err; } #else - /* Enable the chip/hdlc reset condition */ - reg=0; - wan_set_bit(AFT_CHIPCFG_SFR_EX_BIT,®); - wan_set_bit(AFT_CHIPCFG_SFR_IN_BIT,®); + card->hw_iface.getcfg(card->hw, SDLA_HWCPU_USEDCNT, &used_cnt); + card->hw_iface.bus_read_4(card->hw,AFT_PORT_REG(card,AFT_CHIP_CFG_REG),®); - DEBUG_CFG("--- AFT Chip Reset. -- \n"); + if (IS_B601_CARD(card)) { + /* This B600 card should not have a daughter card */ + if(!aft_chipcfg_get_b601_security(reg)) { + DEBUG_EVENT("%s: Error: Chip security conflict, cannot load T1/E1\n", card->devname); + return -EINVAL; + } + if (used_cnt > 1) { + DEBUG_EVENT("%s: Error: T1/E1 port has to be started first on B601\n", card->devname); + return -EINVAL; + } + } - card->hw_iface.bus_write_4(card->hw,AFT_PORT_REG(card,AFT_CHIP_CFG_REG),reg); + /*============ GLOBAL CHIP CONFIGURATION ===============*/ + if (used_cnt == 1) { + /* Enable the chip/hdlc reset condition */ + wan_set_bit(AFT_CHIPCFG_SFR_EX_BIT,®); + wan_set_bit(AFT_CHIPCFG_SFR_IN_BIT,®); - WP_DELAY(10); - - /* Disable the chip/hdlc reset condition */ - wan_clear_bit(AFT_CHIPCFG_SFR_EX_BIT,®); - wan_clear_bit(AFT_CHIPCFG_SFR_IN_BIT,®); + DEBUG_CFG("--- AFT Chip Reset. -- \n"); - wan_clear_bit(AFT_CHIPCFG_FE_INTR_CFG_BIT,®); + card->hw_iface.bus_write_4(card->hw,AFT_PORT_REG(card,AFT_CHIP_CFG_REG),reg); + + WP_DELAY(900); - if (IS_T1_CARD(card)){ - wan_clear_bit(AFT_CHIPCFG_TE1_CFG_BIT,®); - }else if (IS_E1_CARD(card)){ - wan_set_bit(AFT_CHIPCFG_TE1_CFG_BIT,®); - }else if (IS_56K_CARD(card)){ - wan_set_bit(AFT_CHIPCFG_56K_CFG_BIT,®); - }else if (IS_SERIAL_CARD(card)) { - /* No bit to set nere */ - }else{ - DEBUG_EVENT("%s: Error: Xilinx doesn't support non T1/E1 interface!\n", - card->devname); - return -EINVAL; + /* Disable the chip/hdlc reset condition */ + wan_clear_bit(AFT_CHIPCFG_SFR_EX_BIT,®); + wan_clear_bit(AFT_CHIPCFG_SFR_IN_BIT,®); + + wan_clear_bit(AFT_CHIPCFG_FE_INTR_CFG_BIT,®); + + card->hw_iface.bus_write_4(card->hw,AFT_PORT_REG(card,AFT_CHIP_CFG_REG),reg); + + WP_DELAY(900); + + if (!IS_B601_CARD(card)) { + /* On B601, these bits are specified in AFT_LINE_CFG_REG */ + if (IS_T1_CARD(card)){ + wan_clear_bit(AFT_CHIPCFG_TE1_CFG_BIT,®); + }else if (IS_E1_CARD(card)){ + wan_set_bit(AFT_CHIPCFG_TE1_CFG_BIT,®); + }else if (IS_56K_CARD(card)){ + wan_set_bit(AFT_CHIPCFG_56K_CFG_BIT,®); + }else if (IS_SERIAL_CARD(card)) { + /* No bit to set nere */ + }else{ + DEBUG_ERROR("%s: Error: Xilinx doesn't support non T1/E1 interface!\n", + card->devname); + return -EINVAL; + } + } } /* Enable FRONT END Interrupt */ @@ -572,16 +695,51 @@ int a104_global_chip_config(sdla_t *card) DEBUG_CFG("--- Chip enable/config. -- \n"); card->hw_iface.bus_write_4(card->hw,AFT_PORT_REG(card,AFT_CHIP_CFG_REG),reg); + + WP_DELAY(500); + + if (IS_B601_CARD(card)) { + wan_smp_flag_t smp_flags; + + /* Maxim external reset off */ + reg_line_cfg = 0; + card->hw_iface.hw_lock(card->hw,&smp_flags); + + card->hw_iface.bus_read_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG), ®_line_cfg); + + wan_clear_bit(AFT_LCFG_B601_FE_CHIP_RESET_BIT, ®_line_cfg); + card->hw_iface.bus_write_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG), reg_line_cfg); + WP_DELAY(1000); + wan_set_bit(AFT_LCFG_B601_FE_CHIP_RESET_BIT, ®_line_cfg); + card->hw_iface.bus_write_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG), reg_line_cfg); + WP_DELAY(1000); + WP_DELAY(1000); + WP_DELAY(1000); + + card->hw_iface.hw_unlock(card->hw,&smp_flags); + + } else if (card->u.aft.firm_id == AFT_DS_FE_CORE_ID){ - if (card->u.aft.firm_id == AFT_DS_FE_CORE_ID){ /* A104/A108 with Dallas FE */ wan_smp_flag_t smp_flags,flags; + + + card->hw_iface.hw_lock(card->hw,&smp_flags); + wan_spin_lock_irq(&card->wandev.lock,&flags); + aft_te1_write_cpld(card,0x00,0x00); + wan_spin_unlock_irq(&card->wandev.lock,&flags); + card->hw_iface.hw_unlock(card->hw,&smp_flags); + + WP_DELAY(500); + card->hw_iface.hw_lock(card->hw,&smp_flags); wan_spin_lock_irq(&card->wandev.lock,&flags); aft_te1_write_cpld(card,0x00,0x06); wan_spin_unlock_irq(&card->wandev.lock,&flags); card->hw_iface.hw_unlock(card->hw,&smp_flags); + WP_DELAY(500); + }else if (IS_SERIAL_CARD(card)) { /* There is no CPLD reset for Serial card */ @@ -604,7 +762,7 @@ int a104_global_chip_config(sdla_t *card) err=aft_test_hdlc(card); if (err != 0){ - DEBUG_EVENT("%s: Error: HDLC Core Not Ready (0x%X)!\n", + DEBUG_ERROR("%s: Error: HDLC Core Not Ready (0x%X)!\n", card->devname,reg); return -EINVAL; } else{ @@ -616,21 +774,31 @@ int a104_global_chip_config(sdla_t *card) err=card->wandev.fe_iface.global_config(&card->fe); } if (err){ - DEBUG_EVENT("%s(): %d: Error: global_config() ptr is NULL\n", + DEBUG_ERROR("%s(): %d: Error: global_config() ptr is NULL\n", __FUNCTION__, __LINE__); return err; } - if (card->adptr_subtype == AFT_SUBTYPE_SHARK){ - u16 max_ec_chans; - card->hw_iface.getcfg(card->hw, SDLA_HWEC_NO, &max_ec_chans); + if (IS_B601_CARD(card)) { + if (used_cnt == 1) { + if(a104_set_octasic_clk_src_b601(card)) { + DEBUG_EVENT("%s: Failed to configure B601 Octasic clock source\n", + card->devname); + return -EINVAL; + } + } + } else { + if (card->adptr_subtype == AFT_SUBTYPE_SHARK) { + u16 max_ec_chans; + card->hw_iface.getcfg(card->hw, SDLA_HWEC_NO, &max_ec_chans); - if (max_ec_chans) { - DEBUG_EVENT("%s: Global HWEC Clock Source : %i\n", - card->devname,card->wandev.comm_port+1); - card->hw_iface.bus_read_4(card->hw,AFT_PORT_REG(card,AFT_CHIP_CFG_REG),®); - aft_chipcfg_set_oct_clk_src(®,card->wandev.comm_port); - card->hw_iface.bus_write_4(card->hw,AFT_PORT_REG(card,AFT_CHIP_CFG_REG),reg); + if (max_ec_chans) { + if(a104_set_octasic_clk_src(card)) { + DEBUG_EVENT("%s: Failed to configure A10X Octasic clock source\n", + card->devname); + return -EINVAL; + } + } } } @@ -644,6 +812,7 @@ int a104_global_chip_config(sdla_t *card) int a104_global_chip_unconfig(sdla_t *card) { u32 reg=0; + int used_cnt; AFT_FUNC_DEBUG(); #if INIT_FE_ONLY @@ -652,6 +821,7 @@ int a104_global_chip_unconfig(sdla_t *card) card->wandev.fe_iface.global_unconfig(&card->fe); } #else + card->hw_iface.getcfg(card->hw, SDLA_HWCPU_USEDCNT, &used_cnt); /* Global T1/E1 unconfig */ if (card->wandev.fe_iface.global_unconfig){ @@ -661,11 +831,15 @@ int a104_global_chip_unconfig(sdla_t *card) /* Set Octasic/TE1 clocking to reset (A104) ** Set Octasic/Framer to reset (A108) */ - aft_te1_write_cpld(card,0x00,0x00); + if (!IS_B601_CARD(card)) { + aft_te1_write_cpld(card,0x00,0x00); + } - /* Disable the chip/hdlc reset condition */ - wan_set_bit(AFT_CHIPCFG_SFR_EX_BIT,®); - wan_set_bit(AFT_CHIPCFG_SFR_IN_BIT,®); + if (used_cnt == 1) { + /* Disable the chip/hdlc reset condition */ + wan_set_bit(AFT_CHIPCFG_SFR_EX_BIT,®); + wan_set_bit(AFT_CHIPCFG_SFR_IN_BIT,®); + } wan_clear_bit(AFT_CHIPCFG_FE_INTR_CFG_BIT,®); card->hw_iface.bus_write_4(card->hw,AFT_PORT_REG(card,AFT_CHIP_CFG_REG),reg); @@ -673,6 +847,55 @@ int a104_global_chip_unconfig(sdla_t *card) return 0; } +static int aft_ds_set_clock_ref_b601(sdla_t *card, u32 *reg, u32 unused) +{ + u8 clk_route_bits; + AFT_FUNC_DEBUG(); + + clk_route_bits = 0; + + *reg &= ~(AFT_LCFG_B601_CLK_ROUTE_MASK << AFT_LCFG_B601_CLK_ROUTE_SHIFT); + if (WAN_TE1_CLK(&card->fe) == WAN_NORMAL_CLK) { + return 0; + } + + if (IS_T1_CARD(card)) { + switch (WAN_FE_NETWORK_SYNC(&card->fe)) { + case WANOPT_NO: + clk_route_bits = AFT_LCFG_B601_CLK_SRC_OSC_1500HZ; + break; + case WAN_CLK_IN_1500HZ: + clk_route_bits = AFT_LCFG_B601_CLK_SRC_EXT_1500HZ; + break; + case WANOPT_YES: /* Invalid */ + case WAN_CLK_IN_8000HZ: /* Invalid */ + case WAN_CLK_IN_2000HZ: /* Invalid */ + default: + DEBUG_EVENT("%s: Error:Invalid T1 Network sync option\n", card->devname); + return -EINVAL; + } + } else { + switch (WAN_FE_NETWORK_SYNC(&card->fe)) { + case WANOPT_NO: + clk_route_bits = AFT_LCFG_B601_CLK_SRC_OSC_2000HZ; + break; + case WANOPT_YES: + case WAN_CLK_IN_8000HZ: + case WAN_CLK_IN_2000HZ: + clk_route_bits = AFT_LCFG_B601_CLK_SRC_EXT_2000HZ; + break; + case WAN_CLK_IN_1500HZ: /* Invalid */ + default: + DEBUG_EVENT("%s: Error:Invalid E1 Network sync option\n", card->devname); + return -EINVAL; + } + } + + *reg |= clk_route_bits << AFT_LCFG_B601_CLK_ROUTE_SHIFT; + return 0; +} + + static int aft_ds_set_clock_ref(sdla_t *card, u32 *reg, u32 master_port) { u32 master_cfg; @@ -761,7 +984,7 @@ int a104_chip_config(sdla_t *card, wandev_conf_t *conf) card->hw_iface.bus_read_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG), ®); if (!wan_test_bit(AFT_LCFG_FE_IFACE_RESET_BIT,®)){ - DEBUG_EVENT("%s: Error: Physical Port %d is busy!\n", + DEBUG_ERROR("%s: Error: Physical Port %d is busy!\n", card->devname, card->wandev.comm_port+1); return -EBUSY; } @@ -780,7 +1003,11 @@ int a104_chip_config(sdla_t *card, wandev_conf_t *conf) card->hw_iface.hw_lock(card->hw,&smp_flags); wan_spin_lock_irq(&card->wandev.lock,&flags); - aft_ds_set_clock_ref(card,®,WAN_FE_LINENO(&card->fe)); + if (IS_B601_CARD(card)) { + aft_ds_set_clock_ref_b601(card,®,WAN_FE_LINENO(&card->fe)); + } else { + aft_ds_set_clock_ref(card,®,WAN_FE_LINENO(&card->fe)); + } wan_spin_unlock_irq(&card->wandev.lock,&flags); card->hw_iface.hw_unlock(card->hw,&smp_flags); @@ -834,14 +1061,14 @@ int a104_chip_config(sdla_t *card, wandev_conf_t *conf) WAN_TE1_REFCLK(&card->fe) > 0){ int mclk_ver=AFT_TDMV_FRM_CLK_SYNC_VER; - int max_port=4; + int max_port=4; if (card->adptr_subtype == AFT_SUBTYPE_SHARK){ mclk_ver=AFT_TDMV_SHARK_FRM_CLK_SYNC_VER; } if (card->u.aft.firm_id == AFT_DS_FE_CORE_ID){ - mclk_ver=AFT_TDMV_SHARK_A108_FRM_CLK_SYNC_VER; + mclk_ver=AFT_TDMV_SHARK_A108_FRM_CLK_SYNC_VER; switch(card->adptr_type){ case A108_ADPTR_8TE1: max_port=8; @@ -852,6 +1079,7 @@ int a104_chip_config(sdla_t *card, wandev_conf_t *conf) case A101_ADPTR_2TE1: max_port=2; break; + case AFT_ADPTR_B601: case A101_ADPTR_1TE1: max_port=1; break; @@ -859,7 +1087,7 @@ int a104_chip_config(sdla_t *card, wandev_conf_t *conf) } if (card->u.aft.firm_ver < mclk_ver){ - DEBUG_EVENT("%s: Error: AFT FE Clock Sync Depends on Firmware Ver: %X (Cur=%X)\n", + DEBUG_ERROR("%s: Error: AFT FE Clock Sync Depends on Firmware Ver: %X (Cur=%X)\n", card->devname,mclk_ver,card->u.aft.firm_ver); DEBUG_EVENT("%s: Please upgrade your AFT Firmware to Ver=%X or greater!\n", card->devname,mclk_ver); @@ -867,14 +1095,14 @@ int a104_chip_config(sdla_t *card, wandev_conf_t *conf) } if (WAN_TE1_REFCLK(&card->fe) == card->wandev.comm_port+1){ - DEBUG_EVENT("%s: Error: Invalid FE Clock Source Line=%i (same as configured line=%i)\n", + DEBUG_ERROR("%s: Error: Invalid FE Clock Source Line=%i (same as configured line=%i)\n", card->devname,WAN_TE1_REFCLK(&card->fe), card->wandev.comm_port+1); return -EINVAL; } if (WAN_TE1_REFCLK(&card->fe) > max_port){ - DEBUG_EVENT("%s: Error: Invalid FE Clock Source Line=%i\n", + DEBUG_ERROR("%s: Error: Invalid FE Clock Source Line=%i\n", card->devname,WAN_TE1_REFCLK(&card->fe)); return -EINVAL; @@ -887,17 +1115,20 @@ int a104_chip_config(sdla_t *card, wandev_conf_t *conf) AFT_PORT_REG(card,AFT_LINE_CFG_REG),®); if (card->u.aft.firm_id == AFT_DS_FE_CORE_ID){ - card->hw_iface.hw_lock(card->hw,&smp_flags); - wan_spin_lock_irq(&card->wandev.lock,&flags); - - /* For A108 the refclock indicates NORMAL mode. - * For backward compatilbity we make the user - * indicate a MASTER mode */ - - WAN_TE1_CLK(&card->fe) = WAN_NORMAL_CLK; - aft_ds_set_clock_ref(card,®,WAN_TE1_REFCLK(&card->fe)-1); + wan_spin_lock_irq(&card->wandev.lock,&flags); + if (IS_B601_CARD(card)) { + aft_ds_set_clock_ref(card, ® , 0); + } else { + /* For A108 the refclock indicates NORMAL mode. + * For backward compatilbity we make the user + * indicate a MASTER mode */ + + WAN_TE1_CLK(&card->fe) = WAN_NORMAL_CLK; + aft_ds_set_clock_ref(card,®,WAN_TE1_REFCLK(&card->fe)-1); + } + wan_spin_unlock_irq(&card->wandev.lock,&flags); card->hw_iface.hw_unlock(card->hw,&smp_flags); @@ -942,7 +1173,7 @@ int a104_chip_config(sdla_t *card, wandev_conf_t *conf) card->hw_iface.bus_read_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG),®); if (err != 0){ - DEBUG_EVENT("%s: Error: Front End Interface Not Ready (0x%08X)!\n", + DEBUG_ERROR("%s: Error: Front End Interface Not Ready (0x%08X)!\n", card->devname,reg); return err; } else{ @@ -962,11 +1193,11 @@ int a104_chip_config(sdla_t *card, wandev_conf_t *conf) card->hw_iface.getcfg(card->hw, SDLA_HWEC_NO, &max_ec_chans); - card->hw_iface.bus_read_4(card->hw,AFT_PORT_REG(card,AFT_CHIP_CFG_REG), &cfg_reg); + card->hw_iface.bus_read_4(card->hw,AFT_PORT_REG(card,AFT_CHIP_CFG_REG), &cfg_reg); if (max_ec_chans > aft_chipcfg_get_ec_channels(cfg_reg)){ - DEBUG_EVENT("%s: Critical Error: Exceeded Maximum Available Echo Channels!\n", + DEBUG_ERROR("%s: Critical Error: Exceeded Maximum Available Echo Channels!\n", card->devname); - DEBUG_EVENT("%s: Critical Error: Max Allowed=%d Configured=%d\n", + DEBUG_ERROR("%s: Critical Error: Max Allowed=%d Configured=%d\n", card->devname, aft_chipcfg_get_ec_channels(cfg_reg), max_ec_chans); @@ -975,8 +1206,14 @@ int a104_chip_config(sdla_t *card, wandev_conf_t *conf) if (max_ec_chans){ #if defined(CONFIG_WANPIPE_HWEC) - card->wandev.hwec_reset = aft_hwec_reset; - card->wandev.hwec_enable = aft_hwec_enable; + if (IS_B601_CARD(card)) { + card->wandev.hwec_reset = aft_hwec_reset_b601; + card->wandev.hwec_enable = aft_hwec_enable_b601; + } else { + card->wandev.hwec_reset = aft_hwec_reset; + card->wandev.hwec_enable = aft_hwec_enable; + } + card->wandev.ec_dev = wanpipe_ec_register( card, 0, @@ -990,8 +1227,9 @@ int a104_chip_config(sdla_t *card, wandev_conf_t *conf) return -EINVAL; } - - aft_set_hwec_clock_src(card); + if (!IS_B601_CARD(card)) { + aft_set_hwec_clock_src(card); + } #else @@ -1121,6 +1359,42 @@ int a104_chip_unconfig(sdla_t *card) } +void a104_chan_config_mux_enable_b601(sdla_t *card, private_area_t *chan) +{ + u32 reg; + u32 i; + card->hw_iface.bus_read_4(card->hw,AFT_PORT_REG(card, AFT_B601_DATA_MUX_EN_CTRL_REG),®); + if (chan->cfg.data_mux) { + for (i=0;iu.aft.num_of_time_slots;i++){ + if (wan_test_bit(i,&chan->time_slot_map)){ + wan_set_bit(i, ®); + } + } + } else { + for (i=0;iu.aft.num_of_time_slots;i++){ + if (wan_test_bit(i,&chan->time_slot_map)){ + wan_clear_bit(i, ®); + } + } + } + card->hw_iface.bus_write_4(card->hw,AFT_PORT_REG(card, AFT_B601_DATA_MUX_EN_CTRL_REG),reg); +} + +void a104_chan_dev_config_tdmv(sdla_t *card, private_area_t *chan) +{ + u32 reg; + if (chan->channelized_cfg && !chan->hdlc_eng){ + + card->hw_iface.bus_read_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG),®); + aft_lcfg_tdmv_cnt_inc(®); + + card->hw_iface.bus_write_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG),reg); + card->u.aft.lcfg_reg=reg; + + wan_set_bit(chan->logic_ch_num,&card->u.aft.tdm_logic_ch_map); + } +} + int a104_chan_dev_config(sdla_t *card, void *chan_ptr) { u32 reg; @@ -1241,16 +1515,7 @@ int a104_chan_dev_config(sdla_t *card, void *chan_ptr) } } - if (chan->channelized_cfg && !chan->hdlc_eng){ - - card->hw_iface.bus_read_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG),®); - aft_lcfg_tdmv_cnt_inc(®); - - card->hw_iface.bus_write_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG),reg); - card->u.aft.lcfg_reg=reg; - - wan_set_bit(chan->logic_ch_num,&card->u.aft.tdm_logic_ch_map); - } + a104_chan_dev_config_tdmv(card, chan); #endif return 0; } @@ -1390,7 +1655,7 @@ unsigned char aft_te1_read_cpld(sdla_t *card, unsigned short cpld_off) int err = -EINVAL; if (card->hw_iface.fe_test_and_set_bit(card->hw,0)){ - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", card->devname, __FUNCTION__,__LINE__); return 0x00; } @@ -1408,7 +1673,7 @@ int aft_te1_write_cpld(sdla_t *card, unsigned short off,u_int16_t data) int err = -EINVAL; if (card->hw_iface.fe_test_and_set_bit(card->hw,0)){ - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", card->devname, __FUNCTION__,__LINE__); return 0x00; } @@ -1427,7 +1692,7 @@ unsigned char a108m_read_cpld(sdla_t *card, unsigned short cpld_off) u8 tmp; if (card->hw_iface.fe_test_and_set_bit(card->hw,0)){ - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", card->devname, __FUNCTION__,__LINE__); return 0x00; } @@ -1458,7 +1723,7 @@ int a108m_write_cpld(sdla_t *card, unsigned short off,u_int16_t data) u16 org_off; if (card->hw_iface.fe_test_and_set_bit(card->hw,0)){ - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", card->devname, __FUNCTION__,__LINE__); return 0x00; } @@ -1518,6 +1783,43 @@ void a104_fifo_adjust(sdla_t *card, u32 level) } #if defined(CONFIG_WANPIPE_HWEC) +static int aft_hwec_reset_b601(void *pcard, int reset) +{ + sdla_t *card = (sdla_t*)pcard; + wan_smp_flag_t smp_flags,flags; + int err = -EINVAL; + u32 reg; + + card->hw_iface.hw_lock(card->hw,&smp_flags); + wan_spin_lock_irq(&card->wandev.lock,&flags); + card->hw_iface.bus_read_4(card->hw, + AFT_PORT_REG(card,AFT_CHIP_CFG_REG), + ®); + if (!reset){ + DEBUG_EVENT("%s: B601 Clear Echo Canceller chip reset.\n", + card->devname); + + reg &= ~0x1000000; + card->hw_iface.bus_write_4(card->hw, AFT_PORT_REG(card,AFT_CHIP_CFG_REG), reg); + + WP_DELAY(1000); + err = 0; + } else { + DEBUG_EVENT("%s: B601 Set Echo Canceller chip reset.\n", + card->devname); + + reg |= 0x1000000; + card->hw_iface.bus_write_4(card->hw, AFT_PORT_REG(card,AFT_CHIP_CFG_REG), reg); + + err = 0; + } + + wan_spin_unlock_irq(&card->wandev.lock,&flags); + card->hw_iface.hw_unlock(card->hw,&smp_flags); + + return err; +} + static int aft_hwec_reset(void *pcard, int reset) { sdla_t *card = (sdla_t*)pcard; @@ -1572,7 +1874,43 @@ static int aft_hwec_reset(void *pcard, int reset) } #endif -#if defined(CONFIG_WANPIPE_HWEC) +#if defined(CONFIG_WANPIPE_HWEC) +static int aft_hwec_enable_b601(void *pcard, int enable, int fe_chan) +{ + sdla_t *card = (sdla_t*)pcard; + int hw_chan = fe_chan; + unsigned int value; + + WAN_ASSERT(card == NULL); + + if (IS_T1_CARD(card)){ + hw_chan = fe_chan-1; + } + + card->hw_iface.bus_read_4( + card->hw, + AFT_PORT_REG(card, AFT_B601_ECHO_EN_CTRL_REG), + &value); + + if (enable) { + wan_set_bit(fe_chan-1, &value); + } else { + wan_clear_bit(fe_chan-1, &value); + } + + DEBUG_HWEC("[HWEC]: %s: %s bypass mode for fe_chan:%d (value=%X)...!\n", + card->devname, + (enable) ? "Enable" : "Disable", + fe_chan, value); + + card->hw_iface.bus_write_4( + card->hw, + AFT_PORT_REG(card, AFT_B601_ECHO_EN_CTRL_REG), + value); + + return 0; +} + /****************************************************************************** ** aft_hwec_enable() ** diff --git a/patches/kdrivers/src/net/aft_analog.c b/patches/kdrivers/src/net/aft_analog.c index 2575bfe..f645ce8 100644 --- a/patches/kdrivers/src/net/aft_analog.c +++ b/patches/kdrivers/src/net/aft_analog.c @@ -59,8 +59,7 @@ static int aft_analog_hwec_reset(void *pcard, int reset); static int aft_analog_hwec_enable(void *pcard, int enable, int fe_chan); - -static int aft_a600_hwec_reset(void *pcard, int reset); +static int aft_analog_hwec_reset_a600(void *pcard, int reset); #endif static int aft_map_fifo_baddr_and_size(sdla_t *card, unsigned char fifo_size, unsigned char *addr); @@ -78,17 +77,17 @@ static int request_fifo_baddr_and_size(sdla_t *card, private_area_t *chan) req_fifo_size = 1; - DBG_NEWDRV("%s:%s: Optimal Fifo Size =%d Timeslots=%d \n", + DEBUG_INIT("%s:%s: Optimal Fifo Size =%d Timeslots=%d \n", card->devname,chan->if_name,req_fifo_size,chan->num_of_time_slots); fifo_size=(unsigned char)aft_map_fifo_baddr_and_size(card,req_fifo_size,&chan->fifo_base_addr); if (fifo_size == 0 || chan->fifo_base_addr == 31){ - DEBUG_EVENT("%s:%s: Error: Failed to obtain fifo size %d or addr %d \n", + DEBUG_ERROR("%s:%s: Error: Failed to obtain fifo size %d or addr %d \n", card->devname,chan->if_name,fifo_size,chan->fifo_base_addr); return -EINVAL; } - DBG_NEWDRV("%s:%s: Optimal Fifo Size =%d Timeslots=%d New Fifo Size=%d \n", + DEBUG_INIT("%s:%s: Optimal Fifo Size =%d Timeslots=%d New Fifo Size=%d \n", card->devname,chan->if_name,req_fifo_size,chan->num_of_time_slots,fifo_size); @@ -100,11 +99,11 @@ static int request_fifo_baddr_and_size(sdla_t *card, private_area_t *chan) } if (fifo_size != req_fifo_size){ - DEBUG_EVENT("%s:%s: Warning: Failed to obtain the req fifo %d got %d\n", + DEBUG_WARNING("%s:%s: Warning: Failed to obtain the req fifo %d got %d\n", card->devname,chan->if_name,req_fifo_size,fifo_size); } - DBG_NEWDRV("%s: %s:Fifo Size=%d Timeslots=%d Fifo Code=%d Addr=%d\n", + DEBUG_INIT("%s: %s:Fifo Size=%d Timeslots=%d Fifo Code=%d Addr=%d\n", card->devname,chan->if_name,fifo_size, chan->num_of_time_slots,chan->fifo_size_code, chan->fifo_base_addr); @@ -222,7 +221,7 @@ static char aft_request_logical_channel_num (sdla_t *card, private_area_t *chan) } if (timeslots > 1){ - DEBUG_EVENT("%s: Error: Analog Interface can only support a single timeslot\n", + DEBUG_ERROR("%s: Error: Analog Interface can only support a single timeslot\n", card->devname); chan->first_time_slot = -1; return -1; @@ -251,7 +250,7 @@ static char aft_request_logical_channel_num (sdla_t *card, private_area_t *chan) } if (card->u.aft.dev_to_ch_map[(unsigned char)logic_ch]){ - DEBUG_EVENT("%s: Error, request logical ch=%d map busy\n", + DEBUG_ERROR("%s: Error, request logical ch=%d map busy\n", card->devname,logic_ch); return -1; } @@ -283,7 +282,7 @@ int aft_analog_test_sync(sdla_t *card, int tx_only) card->hw_iface.bus_read_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG), ®); if (wan_test_bit(AFT_LCFG_FE_IFACE_RESET_BIT,®)){ - DEBUG_EVENT("%s: Warning: Analog Reset Enabled %d! \n", + DEBUG_WARNING("%s: Warning: Analog Reset Enabled %d! \n", card->devname, card->wandev.comm_port+1); } @@ -324,16 +323,16 @@ int aft_analog_led_ctrl(sdla_t *card, int color, int led_pos, int on) return 0; } -int aft_a600_clock_config(sdla_t *card) +int aft_analog_set_network_sync_a600(sdla_t *card) { u32 reg_1090; reg_1090 = 0x00; - card->hw_iface.bus_read_4(card->hw, 0x1090, ®_1090); + card->hw_iface.bus_read_4(card->hw, AFT_CLKCFG_A600_REG, ®_1090); switch (WAN_FE_NETWORK_SYNC(&card->fe)) { default: - DEBUG_EVENT("%s: Warning:Invalid FE_NETWORK_SYNC value\n", + DEBUG_WARNING("%s: Warning:Invalid FE_NETWORK_SYNC value\n", card->devname); case WANOPT_NO: DEBUG_EVENT("%s: Configuring for internal oscillator\n", @@ -365,7 +364,7 @@ int aft_a600_clock_config(sdla_t *card) /* daughter card is required for line clock output - not supported yet*/ #if 1 /* Configure for oscillator output for now */ - DEBUG_EVENT("%s: Warning: Clock output line not supported on A600\n", + DEBUG_WARNING("%s: Warning: Clock output line not supported on A600\n", card->devname); DEBUG_EVENT("%s: Configuring for oscillator output\n", card->devname); @@ -388,16 +387,91 @@ int aft_a600_clock_config(sdla_t *card) /* Set the external clock output mux to use board clock */ reg_1090 |= (AFT_CLKCFG_A600_CLK_OUT_BOARD << 5); - card->hw_iface.bus_write_4(card->hw, 0x1090, reg_1090); + card->hw_iface.bus_write_4(card->hw, AFT_CLKCFG_A600_REG, reg_1090); return 0; } +int aft_analog_set_network_sync_b601(sdla_t *card) +{ + wan_smp_flag_t smp_flags, flags; + u32 reg_1090; + u16 max_ec_chans; + + reg_1090 = 0x00; + max_ec_chans = 0; + card->hw_iface.getcfg(card->hw, SDLA_HWEC_NO, &max_ec_chans); + + card->hw_iface.hw_lock(card->hw,&smp_flags); + wan_spin_lock_irq(&card->wandev.lock,&flags); + card->hw_iface.bus_read_4(card->hw, AFT_CLKCFG_A600_REG, ®_1090); + + /* Configure EC Clock source Mux */ + reg_1090 &= ~(AFT_CLKCFG_B601_EC_SRC_MUX_MASK<hw_iface.bus_write_4(card->hw, AFT_CLKCFG_A600_REG, reg_1090); + wan_spin_unlock_irq(&card->wandev.lock,&flags); + card->hw_iface.hw_unlock(card->hw,&smp_flags); + return 0; +} + +int aft_analog_set_octasic_clk_src_b601(sdla_t *card) +{ + wan_smp_flag_t smp_flags, flags; + u32 reg_1090; + u8 ext_clk_mux_bits = 0; + u8 pll_clk_mux_bits = 0; + + reg_1090 = 0x00; + + card->hw_iface.hw_lock(card->hw,&smp_flags); + wan_spin_lock_irq(&card->wandev.lock,&flags); + card->hw_iface.bus_read_4(card->hw, AFT_CLKCFG_A600_REG, ®_1090); + + /* Define Ext Clock Val */ + reg_1090 &= ~(AFT_CLKCFG_B601_EXT_CLK_VAL_MASK<fe) == WAN_CLK_IN_2000HZ) { + reg_1090 |= 1 << AFT_CLKCFG_B601_EXT_CLK_VAL_SHIFT; + } + + /* Configure External clock out Mux */ + reg_1090 &= ~(AFT_CLKCFG_B601_EXT_CLK_MUX_MASK<fe)) { + case WANOPT_NO: + ext_clk_mux_bits = AFT_CLKCFG_B601_EXT_CLK_MUX_OSC_2000HZ; + pll_clk_mux_bits = AFT_CLKCFG_B601_PLL_CLK_SRC_MUX_OSC_2000HZ; + break; + case WANOPT_YES: + case WAN_CLK_IN_8000HZ: + case WAN_CLK_IN_2000HZ: + ext_clk_mux_bits = AFT_CLKCFG_B601_EXT_CLK_MUX_H100_CLK; + pll_clk_mux_bits = AFT_CLKCFG_B601_PLL_CLK_SRC_MUX_EXT_8000HZ; + break; + default: + case WAN_CLK_IN_1500HZ: + DEBUG_EVENT("%s: Error:Invalid Analog Network sync option\n", card->devname); + return -EINVAL; + } + reg_1090 |= pll_clk_mux_bits << AFT_CLKCFG_B601_PLL_CLK_SRC_MUX_SHIFT; + reg_1090 |= ext_clk_mux_bits << AFT_CLKCFG_B601_EXT_CLK_MUX_SHIFT; + + card->hw_iface.bus_write_4(card->hw, AFT_CLKCFG_A600_REG, reg_1090); + wan_spin_unlock_irq(&card->wandev.lock,&flags); + card->hw_iface.hw_unlock(card->hw,&smp_flags); + return 0; +} + int aft_analog_global_chip_config(sdla_t *card) { u32 reg; int err; int used_cnt; + if (IS_A700_CARD(card)) { wan_set_bit(0,&card->fe_ignore_intr); @@ -427,7 +501,6 @@ int aft_analog_global_chip_config(sdla_t *card) /* Do not allow front end interrupt to start */ if (!IS_A700_CARD(card)) { - DEBUG_CFG("--- Chip enable/config. -- \n"); wan_clear_bit(AFT_CHIPCFG_FE_INTR_CFG_BIT,®); } if (IS_A200_CARD(card) || IS_A700_CARD(card)) { @@ -437,31 +510,62 @@ int aft_analog_global_chip_config(sdla_t *card) wan_set_bit(AFT_CHIPCFG_ANALOG_CLOCK_SELECT_BIT,®); } } - + DEBUG_CFG("--- Chip enable/config. -- \n"); card->hw_iface.bus_write_4(card->hw,AFT_PORT_REG(card,AFT_CHIP_CFG_REG),reg); + + if (IS_B601_CARD(card)) { + err = aft_analog_set_octasic_clk_src_b601(card); + if (err) { + DEBUG_EVENT("%s: Failed to configure B601 Octasic clock source\n", + card->devname); + return -EINVAL; + } + err = aft_analog_set_network_sync_b601(card); + if (err) { + DEBUG_EVENT("%s: Failed to configure B601 Network Sync\n", + card->devname); + return -EINVAL; + } + } else if (IS_A600_CARD(card)) { + u8 core_rev; + card->hw_iface.getcfg(card->hw, SDLA_COREREV, &core_rev); + + if (core_rev >= 3 && aft_chipcfg_get_b601_security(reg)) { + /* It looks like the customer bought a B601 but removed the T1/E1 daughter card */ + DEBUG_EVENT("%s: AFT-B600 card was built as AFT-B601\n", card->devname); + + err = aft_analog_set_octasic_clk_src_b601(card); + if (err) { + DEBUG_EVENT("%s: Failed to configure B601 Octasic clock source\n", + card->devname); + return -EINVAL; + } + err = aft_analog_set_network_sync_b601(card); + if (err) { + DEBUG_EVENT("%s: Failed to configure B601 Network Sync\n", + card->devname); + return -EINVAL; + } + } else { + err = aft_analog_set_network_sync_a600(card); + if (err) { + DEBUG_EVENT("%s: Failed to configure clock source\n", + card->devname); + return -EINVAL; + } + } + } else { + /* Set Octasic reset */ + aft_analog_write_cpld(card, 0x00, 0x00); + } } else { if (WAN_FE_NETWORK_SYNC(&card->fe)){ DEBUG_EVENT("%s: Ignoring Network Sync\n", card->devname); } } - if (IS_A600_CARD(card)) { - err = aft_a600_clock_config(card); - if (err) { - DEBUG_EVENT("%s: Failed to configure clock source\n", - card->devname); - return -EINVAL; - } - } - - if (used_cnt == 1) { - if (!IS_A600_CARD(card)) { - /* Set Octasic reset */ - aft_analog_write_cpld(card, 0x00, 0x00); - } - } - + DEBUG_EVENT("%s: Global Front End Configuration!\n",card->devname); err = -EINVAL; @@ -512,7 +616,7 @@ int aft_analog_chip_config(sdla_t *card, wandev_conf_t *conf) card->hw_iface.bus_read_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG), ®); if (!wan_test_bit(AFT_LCFG_FE_IFACE_RESET_BIT,®)){ - DEBUG_EVENT("%s: Error: Physical Port %i is busy! \n", + DEBUG_ERROR("%s: Error: Physical Port %i is busy! \n", card->devname, card->wandev.comm_port+1); return -EBUSY; } @@ -542,7 +646,7 @@ int aft_analog_chip_config(sdla_t *card, wandev_conf_t *conf) card->hw_iface.bus_read_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG),®); if (err != 0){ - DEBUG_EVENT("%s: Error: Front End Interface Not Ready (0x%08X)!\n", + DEBUG_ERROR("%s: Error: Front End Interface Not Ready (0x%08X)!\n", card->devname,reg); return err; } else{ @@ -555,15 +659,19 @@ int aft_analog_chip_config(sdla_t *card, wandev_conf_t *conf) u16 max_ec_chans, max_chans_no; u16 max_chip_cfg_ec_chans; u32 cfg_reg, fe_chans_map; + u8 core_rev; card->hw_iface.getcfg(card->hw, SDLA_HWEC_NO, &max_ec_chans); card->hw_iface.getcfg(card->hw, SDLA_CHANS_NO, &max_chans_no); card->hw_iface.getcfg(card->hw, SDLA_CHANS_MAP, &fe_chans_map); + card->hw_iface.getcfg(card->hw, SDLA_COREREV, &core_rev); card->hw_iface.bus_read_4(card->hw,AFT_PORT_REG(card,AFT_CHIP_CFG_REG), &cfg_reg); - if (IS_A600_CARD(card)) { - max_chip_cfg_ec_chans = (u16)aft_chipcfg_get_a600_ec_channels(cfg_reg); + if (IS_A600_CARD(card)) { + max_chip_cfg_ec_chans = (u16)aft_chipcfg_get_a600_ec_channels(cfg_reg, core_rev); + } else if (IS_B601_CARD(card)) { + max_chip_cfg_ec_chans = (u16)aft_chipcfg_get_b601_ec_channels(cfg_reg); } else if (IS_A700_CARD(card)) { max_chip_cfg_ec_chans = (u16)aft_chipcfg_get_a700_ec_channels(cfg_reg); } else { @@ -571,9 +679,9 @@ int aft_analog_chip_config(sdla_t *card, wandev_conf_t *conf) } if (max_ec_chans > max_chip_cfg_ec_chans){ - DEBUG_EVENT("%s: Critical Error: Exceeded Maximum Available Echo Channels!\n", + DEBUG_ERROR("%s: Critical Error: Exceeded Maximum Available Echo Channels!\n", card->devname); - DEBUG_EVENT("%s: Critical Error: Max Allowed=%d Configured=%d (%X)\n", + DEBUG_ERROR("%s: Critical Error: Max Allowed=%d Configured=%d (%X)\n", card->devname, max_chip_cfg_ec_chans, max_ec_chans, @@ -590,9 +698,8 @@ int aft_analog_chip_config(sdla_t *card, wandev_conf_t *conf) max_ec_chans, (void*)&conf->oct_conf); - - if (IS_A600_CARD(card)) { - card->wandev.hwec_reset = aft_a600_hwec_reset; + if (IS_A600_CARD(card) || IS_B601_CARD(card)) { + card->wandev.hwec_reset = aft_analog_hwec_reset_a600; } else { card->wandev.hwec_reset = aft_analog_hwec_reset; } @@ -631,9 +738,6 @@ int aft_analog_chip_config(sdla_t *card, wandev_conf_t *conf) aft_dmactrl_set_max_logic_ch(®,0); wan_clear_bit(AFT_DMACTRL_GLOBAL_INTR_BIT,®); card->hw_iface.bus_write_4(card->hw,AFT_PORT_REG(card,AFT_DMA_CTRL_REG),reg); - - - /*============ ENABLE MUX ==================*/ @@ -868,7 +972,7 @@ unsigned char aft_analog_read_cpld(sdla_t *card, unsigned short cpld_off) u8 tmp; if (card->hw_iface.fe_test_and_set_bit(card->hw,0)){ - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", card->devname, __FUNCTION__,__LINE__); return 0x00; } @@ -900,7 +1004,7 @@ int aft_analog_write_cpld(sdla_t *card, unsigned short off, u_int16_t data_in) u8 data=(u8)data_in; if (card->hw_iface.fe_test_and_set_bit(card->hw,0)){ - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", card->devname, __FUNCTION__,__LINE__); return 0x00; } @@ -941,7 +1045,7 @@ void aft_analog_fifo_adjust(sdla_t *card,u32 level) } #if defined(CONFIG_WANPIPE_HWEC) -static int aft_a600_hwec_reset(void *pcard, int reset) +static int aft_analog_hwec_reset_a600(void *pcard, int reset) { sdla_t *card = (sdla_t*)pcard; wan_smp_flag_t smp_flags,flags; @@ -983,8 +1087,6 @@ static int aft_a600_hwec_reset(void *pcard, int reset) return err; } - - static int aft_analog_hwec_reset(void *pcard, int reset) { sdla_t *card = (sdla_t*)pcard; diff --git a/patches/kdrivers/src/net/aft_bri.c b/patches/kdrivers/src/net/aft_bri.c index ffb67c4..c61fc62 100644 --- a/patches/kdrivers/src/net/aft_bri.c +++ b/patches/kdrivers/src/net/aft_bri.c @@ -173,7 +173,7 @@ static int request_fifo_baddr_and_size(sdla_t *card, private_area_t *chan) fifo_size=(u8)aft_map_fifo_baddr_and_size(card,req_fifo_size,&chan->fifo_base_addr); if (fifo_size == 0 || chan->fifo_base_addr == 31){ - DEBUG_EVENT("%s:%s: Error: Failed to obtain fifo size %d or addr %d \n", + DEBUG_ERROR("%s:%s: Error: Failed to obtain fifo size %d or addr %d \n", card->devname,chan->if_name,fifo_size,chan->fifo_base_addr); return -EINVAL; } @@ -190,7 +190,7 @@ static int request_fifo_baddr_and_size(sdla_t *card, private_area_t *chan) } if (fifo_size != req_fifo_size){ - DEBUG_EVENT("%s:%s: Warning: Failed to obtain the req fifo %d got %d\n", + DEBUG_WARNING("%s:%s: Warning: Failed to obtain the req fifo %d got %d\n", card->devname,chan->if_name,req_fifo_size,fifo_size); } @@ -366,7 +366,7 @@ static int aft_request_logical_channel_num (sdla_t *card, private_area_t *chan) } if (card->u.aft.dev_to_ch_map[(unsigned char)logic_ch]){ - DEBUG_EVENT("%s: Error, request logical ch=%d map busy\n", + DEBUG_ERROR("%s: Error, request logical ch=%d map busy\n", card->devname,logic_ch); return -1; } @@ -426,7 +426,7 @@ int aft_bri_test_sync(sdla_t *card, int tx_only) card->hw_iface.bus_read_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG), ®); if (wan_test_bit(AFT_LCFG_FE_IFACE_RESET_BIT,®)){ - DEBUG_EVENT("%s: Warning: BRI Reset Enabled %d! \n", + DEBUG_WARNING("%s: Warning: BRI Reset Enabled %d! \n", card->devname, card->wandev.comm_port+1); } @@ -553,7 +553,7 @@ int aft_bri_global_chip_config(sdla_t *card) } if (!IS_BRI_CARD(card)) { - DEBUG_EVENT("%s: Error: Xilinx doesn't support non BRI interface!\n", + DEBUG_ERROR("%s: Error: Xilinx doesn't support non BRI interface!\n", card->devname); return -EINVAL; } @@ -571,7 +571,7 @@ int aft_bri_global_chip_config(sdla_t *card) err=aft_test_hdlc(card); if (err != 0){ - DEBUG_EVENT("%s: Error: HDLC Core Not Ready (0x%X)!\n", + DEBUG_ERROR("%s: Error: HDLC Core Not Ready (0x%X)!\n", card->devname,reg); return -EINVAL; } else{ @@ -804,7 +804,7 @@ int aft_bri_chip_config(sdla_t *card, wandev_conf_t *conf) card->hw_iface.bus_read_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG),®); if (err != 0){ - DEBUG_EVENT("%s: Error: Front End Interface Not Ready (0x%08X)!\n", + DEBUG_ERROR("%s: Error: Front End Interface Not Ready (0x%08X)!\n", card->devname,reg); return err; } else{ @@ -1146,7 +1146,7 @@ unsigned char aft_bri_read_cpld(sdla_t *card, unsigned short cpld_off) BRI_FUNC(); if (card->hw_iface.fe_test_and_set_bit(card->hw,0)){ - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", card->devname, __FUNCTION__,__LINE__); return 0x00; } @@ -1168,7 +1168,7 @@ int aft_bri_write_cpld(sdla_t *card, unsigned short off,unsigned short data) if (card->hw_iface.fe_test_and_set_bit(card->hw,0)){ - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", card->devname, __FUNCTION__,__LINE__); return 0x00; } @@ -1190,7 +1190,7 @@ int bri_write_cpld(sdla_t *card, unsigned short off,unsigned char data) BRI_FUNC(); if (card->hw_iface.fe_test_and_set_bit(card->hw,0)){ - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", card->devname, __FUNCTION__,__LINE__); return 0x00; } @@ -1384,10 +1384,10 @@ int aft_bri_write_fe(void* pcard, ...) if (card->hw_iface.fe_test_and_set_bit(card->hw,0)){ #if defined(WAN_DEBUG_FE) - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE (%s:%d)!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE (%s:%d)!\n", card->devname, __FUNCTION__,__LINE__, fname, fline); #else - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", card->devname, __FUNCTION__,__LINE__); #endif return -EINVAL; @@ -1432,10 +1432,10 @@ unsigned char aft_bri_read_fe (void* pcard, ...) if (card->hw_iface.fe_test_and_set_bit(card->hw,0)){ #if defined(WAN_DEBUG_FE) - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE (%s:%d)!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE (%s:%d)!\n", card->devname, __FUNCTION__,__LINE__,fname,fline); #else - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", card->devname, __FUNCTION__,__LINE__); #endif return 0x00; @@ -1766,7 +1766,7 @@ write_bri_fe_byte(sdla_t *card, unsigned char mod_no, unsigned char addr, unsign card->hw_iface.bus_read_4(card->hw, 0x40, dummy_ptr); card->hw_iface.bus_read_4(card->hw, SPI_INTERFACE_REG, dummy_ptr); if((*data_ptr & 0xFFFF) != (*dummy_ptr & 0xFFFF)){ - DEBUG_EVENT("%s:%s(): Line:%d: Error: *data_ptr: 0x%02X, *dummy_ptr: 0x%02X\n", + DEBUG_ERROR("%s:%s(): Line:%d: Error: *data_ptr: 0x%02X, *dummy_ptr: 0x%02X\n", card->devname, __FUNCTION__, __LINE__, *data_ptr, *dummy_ptr); } */ @@ -1790,7 +1790,7 @@ int aft_bri_dchan_transmit(sdla_t *card, void *chan_ptr, void *src_data_buffer, src_data_buffer, buffer_len); }else{ - DEBUG_EVENT("%s():%s: Warning: uninitialized isdn_bri_dchan_tx() pointer.\n", + DEBUG_WARNING("%s():%s: Warning: uninitialized isdn_bri_dchan_tx() pointer.\n", __FUNCTION__, card->devname); } @@ -1814,7 +1814,7 @@ int aft_bri_dchan_receive( sdla_t *card, void *chan_ptr, void *dst_data_buffer, dst_data_buffer, buffer_len); }else{ - DEBUG_EVENT("%s():%s: Warning: uninitialized isdn_bri_dchan_rx() pointer.\n", + DEBUG_WARNING("%s():%s: Warning: uninitialized isdn_bri_dchan_rx() pointer.\n", __FUNCTION__, card->devname); } diff --git a/patches/kdrivers/src/net/aft_core.c b/patches/kdrivers/src/net/aft_core.c index 6717960..ffd27b7 100644 --- a/patches/kdrivers/src/net/aft_core.c +++ b/patches/kdrivers/src/net/aft_core.c @@ -5,7 +5,7 @@ * * Authors: Nenad Corbic * -* Copyright: (c) 2003-2008 Sangoma Technologies Inc. +* Copyright: (c) 2003-2009 Sangoma Technologies Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -165,16 +165,28 @@ #if 0 -#define AFT_FIFO_GEN_DEBUGGING +#define AFT_FIFO_GEN_DEBUGGING_TX # if defined(__WINDOWS__) -# pragma message("Warning: AFT_FIFO_GEN_DEBUGGING Enabled") +# pragma message("Warning: AFT_FIFO_GEN_DEBUGGING_TX Enabled") # else -# warning "Warning: AFT_FIFO_GEN_DEBUGGING Enabled" +# warning "Warning: AFT_FIFO_GEN_DEBUGGING_TX Enabled" # endif #else -# undef AFT_FIFO_GEN_DEBUGGING +# undef AFT_FIFO_GEN_DEBUGGING_TX #endif +#if 0 +#define AFT_FIFO_GEN_DEBUGGING_RX +# if defined(__WINDOWS__) +# pragma message("Warning: AFT_FIFO_GEN_DEBUGGING_RX Enabled") +# else +# warning "Warning: AFT_FIFO_GEN_DEBUGGING_RX Enabled" +# endif +#else +# undef AFT_FIFO_GEN_DEBUGGING_RX +#endif + + #if defined(WANPIPE_64BIT_4G_DMA) #warning "Wanpipe compiled for 64bit 4G DMA" #endif @@ -199,9 +211,16 @@ #undef WANPIPE_CODEC_CONVERTER #endif + +#if 0 +#define SPAN_TIMING_DEBUGGING +#else +#undef SPAN_TIMING_DEBUGGING +#endif + #define WP_ZAPTEL_ENABLED 0 #define WP_ZAPTEL_DCHAN_OPTIMIZATION 1 - +#define WP_RX_TX_FIFO_SANITY 100 #ifdef __WINDOWS__ @@ -218,6 +237,8 @@ #endif #endif +WAN_DECLARE_NETDEV_OPS(wan_netdev_ops) + /*================================================================= * Defines & Macros *================================================================*/ @@ -298,7 +319,7 @@ static int set_chan_state(sdla_t* card, netdevice_t* dev, u8 state); static WAN_IRQ_RETVAL wp_aft_global_isr (sdla_t* card); static void wp_aft_dma_per_port_isr(sdla_t *card, int tdm); static void wp_aft_tdmv_per_port_isr(sdla_t *card); -static void wp_aft_fifo_per_port_isr(sdla_t *card); +static int wp_aft_fifo_per_port_isr(sdla_t *card); static void wp_aft_wdt_per_port_isr(sdla_t *card, int wdt_intr); static void wp_aft_serial_status_isr(sdla_t *card, u32 status); static void wp_aft_free_timer_status_isr(sdla_t *card, u32 free_run_intr_status); @@ -307,7 +328,7 @@ static void front_end_interrupt(sdla_t *card, unsigned long reg, int lock); static void enable_data_error_intr(sdla_t *card); static void disable_data_error_intr(sdla_t *card, unsigned char); -static void aft_tx_fifo_under_recover (sdla_t *card, private_area_t *chan); +void aft_tx_fifo_under_recover (sdla_t *card, private_area_t *chan); static void aft_rx_fifo_over_recover(sdla_t *card, private_area_t *chan); @@ -320,19 +341,19 @@ static void aft_port_task (void * card_ptr); static void aft_port_task (struct work_struct *work); # endif #elif defined(__WINDOWS__) - static void wp_bh (IN PKDPC Dpc, IN PVOID data, IN PVOID SystemArgument1, IN PVOID SystemArgument2); - static void aft_port_task (IN PKDPC Dpc, IN PVOID card_ptr, IN PVOID SystemArgument1, IN PVOID SystemArgument2); + KDEFERRED_ROUTINE wp_bh; + KDEFERRED_ROUTINE aft_port_task; void __aft_port_task (IN PVOID card_ptr); - #if defined(WAN_SPINLOCK_IS_FASTMUTEX) - extern int trigger_aft_port_task(sdla_t *card); - #endif + extern int trigger_aft_port_task(sdla_t *card); +#if 0 extern void wan_debug_update_timediff( wan_debug_t *wan_debug_ptr, const char *caller_name ); +#endif #else static void wp_bh (void*,int); @@ -396,7 +417,8 @@ static int aft_tdmv_free(sdla_t *card); static int aft_tdmv_if_init(sdla_t *card, private_area_t *chan, wanif_conf_t *conf); static int aft_tdmv_if_free(sdla_t *card, private_area_t *chan); -static void callback_front_end_state(void *card_id); +static void callback_front_end_state(void *card_id); +static void callback_front_end_reset(void *card_id); /* Procfs functions */ static int wan_aft_get_info(void* pcard, struct seq_file* m, int* stop_cnt); @@ -583,7 +605,7 @@ int wp_aft_analog_init (sdla_t *card, wandev_conf_t* conf) if (card->adptr_type != A200_ADPTR_ANALOG && card->adptr_type != A400_ADPTR_ANALOG && card->adptr_type != AFT_ADPTR_FLEXBRI) { - DEBUG_EVENT( "%s: Error: Attempting to configure for Analog on non A200/A400/B700 analog hw!\n", + DEBUG_ERROR( "%s: Error: Attempting to configure for Analog on non A200/A400/B700 analog hw!\n", card->devname); return -EINVAL; } @@ -637,7 +659,7 @@ int wp_aft_analog_init (sdla_t *card, wandev_conf_t* conf) DEBUG_TEST("%s(): card->fe.fe_cfg.line_no: %d\n", __FUNCTION__, card->fe.fe_cfg.line_no); card->wandev.comm_port=card->fe.fe_cfg.line_no; if (card->wandev.comm_port != 0){ - DEBUG_EVENT("%s: Error: Invalid port selected %d (Port 1)\n", + DEBUG_ERROR("%s: Error: Invalid port selected %d (Port 1)\n", card->devname,card->wandev.comm_port); return -EINVAL; } @@ -663,8 +685,8 @@ int wp_aft_a600_init (sdla_t* card, wandev_conf_t* conf) ASSERT_AFT_HWDEV(card->wandev.card_type); - if (card->adptr_type != AFT_ADPTR_A600) { - DEBUG_EVENT( "%s: Error: Attempting to configure for Analog on non B600 analog hw!\n", + if (card->adptr_type != AFT_ADPTR_A600 && card->adptr_type != AFT_ADPTR_B601 ) { + DEBUG_ERROR( "%s: Error: Attempting to configure for Analog on non B600 analog hw!\n", card->devname); return -EINVAL; } @@ -733,7 +755,7 @@ int wp_aft_bri_init (sdla_t *card, wandev_conf_t* conf) ASSERT_AFT_HWDEV(card->wandev.card_type); if (card->wandev.card_type != WANOPT_AFT_ISDN) { - DEBUG_EVENT( "%s: Error: Attempting to configure for BRI on non A500/B700 hw!\n", + DEBUG_ERROR( "%s: Error: Attempting to configure for BRI on non A500/B700 hw!\n", card->devname); return -EINVAL; } @@ -807,7 +829,7 @@ int wp_aft_serial_init (sdla_t *card, wandev_conf_t* conf) ASSERT_AFT_HWDEV(card->wandev.card_type); if (card->wandev.card_type != WANOPT_AFT_SERIAL) { - DEBUG_EVENT( "%s: Error: Attempting to configure for SERIAL on non A14X hw!\n", + DEBUG_ERROR( "%s: Error: Attempting to configure for SERIAL on non A14X hw!\n", card->devname); return -EINVAL; } @@ -865,9 +887,11 @@ int wp_aft_serial_init (sdla_t *card, wandev_conf_t* conf) #if defined(CONFIG_PRODUCT_WANPIPE_AFT_TE1) int wp_aft_te1_init (sdla_t* card, wandev_conf_t* conf) { - + int min_firm_ver; + AFT_FUNC_DEBUG(); + min_firm_ver= AFT_MIN_FRMW_VER; wan_set_bit(CARD_DOWN,&card->wandev.critical); /* Verify configuration ID */ @@ -885,9 +909,10 @@ int wp_aft_te1_init (sdla_t* card, wandev_conf_t* conf) case A104_ADPTR_4TE1: /* Quad line T1/E1 */ case A104_ADPTR_4TE1_PCIX: /* Quad line T1/E1 PCI Express */ case A108_ADPTR_8TE1: + case AFT_ADPTR_B601: break; default: - DEBUG_EVENT( "%s: Error: Attempting to configure for T1/E1 on non AFT A101/2/4/8 hw (%d)!\n", + DEBUG_ERROR( "%s: Error: Attempting to configure for T1/E1 on non AFT A101/2/4/8 hw (%d)!\n", card->devname,card->adptr_type); return -EINVAL; } @@ -895,9 +920,14 @@ int wp_aft_te1_init (sdla_t* card, wandev_conf_t* conf) card->hw_iface.getcfg(card->hw, SDLA_COREREV, &card->u.aft.firm_ver); card->hw_iface.getcfg(card->hw, SDLA_COREID, &card->u.aft.firm_id); - if (card->u.aft.firm_ver < AFT_MIN_FRMW_VER){ + if (IS_B601_CARD(card)) { + min_firm_ver = AFT_MIN_B601_FRMW_VER; + card->u.aft.firm_id = AFT_DS_FE_CORE_ID; + } + + if (card->u.aft.firm_ver < min_firm_ver){ DEBUG_EVENT( "%s: Invalid/Obselete AFT firmware version %X (not >= %X)!\n", - card->devname, card->u.aft.firm_ver,AFT_MIN_FRMW_VER); + card->devname, card->u.aft.firm_ver,min_firm_ver); DEBUG_EVENT( "%s Refer to /usr/share/doc/wanpipe/README.aft_firm_update\n", card->devname); DEBUG_EVENT( "%s: Please contact Sangoma Technologies for more info.\n", @@ -931,6 +961,7 @@ int wp_aft_te1_init (sdla_t* card, wandev_conf_t* conf) }else{ sdla_te_iface_init(&card->fe, &card->wandev.fe_iface); } + card->fe.name = card->devname; card->fe.card = card; card->fe.write_fe_reg = card->hw_iface.fe_write; /*a104_write_fe;*/ @@ -940,6 +971,8 @@ int wp_aft_te1_init (sdla_t* card, wandev_conf_t* conf) card->wandev.fe_enable_timer = enable_timer; card->wandev.ec_enable_timer = enable_ec_timer; card->wandev.te_link_state = callback_front_end_state; + card->wandev.te_link_reset = callback_front_end_reset; + conf->electrical_interface = IS_T1_CARD(card) ? WANOPT_V35 : WANOPT_RS232; @@ -984,7 +1017,7 @@ int wp_aft_56k_init (sdla_t* card, wandev_conf_t* conf) ASSERT_AFT_HWDEV(card->wandev.card_type); if (card->wandev.card_type != WANOPT_AFT_56K) { - DEBUG_EVENT( "%s: Error: Attempting to configure for 56K on non A056K hw!\n", + DEBUG_ERROR( "%s: Error: Attempting to configure for 56K on non A056K hw!\n", card->devname); return -EINVAL; } @@ -1085,12 +1118,13 @@ static int wan_aft_init (sdla_t *card, wandev_conf_t* conf) wan_skb_queue_init(&card->u.aft.rtp_tap_list); + /* Set the span_no by default to card number, unless set by user */ #if 0 if (card->tdmv_conf.span_no == 0) { card->tdmv_conf.span_no = card->card_no; if (!card->card_no) { - DEBUG_EVENT("%s: Error: Internal Driver Error: Card card_no filed is ZERO!\n", + DEBUG_ERROR("%s: Error: Internal Driver Error: Card card_no filed is ZERO!\n", card->devname); return -1; } @@ -1154,7 +1188,7 @@ static int wan_aft_init (sdla_t *card, wandev_conf_t* conf) card->wandev.mtu=conf->mtu; if (card->wandev.mtu > MAX_WP_PRI_MTU || card->wandev.mtu < MIN_WP_PRI_MTU){ - DEBUG_EVENT("%s: Error Invalid Global MTU %d (Min=%d, Max=%d)\n", + DEBUG_ERROR("%s: Error Invalid Global MTU %d (Min=%d, Max=%d)\n", card->devname,card->wandev.mtu, MIN_WP_PRI_MTU,MAX_WP_PRI_MTU); @@ -1170,7 +1204,7 @@ static int wan_aft_init (sdla_t *card, wandev_conf_t* conf) if (card->u.aft.cfg.mru > MAX_WP_PRI_MTU || card->u.aft.cfg.mru < MIN_WP_PRI_MTU){ - DEBUG_EVENT("%s: Error Invalid Global MRU %d (Min=%d, Max=%d)\n", + DEBUG_ERROR("%s: Error Invalid Global MRU %d (Min=%d, Max=%d)\n", card->devname,card->u.aft.cfg.mru, MIN_WP_PRI_MTU,MAX_WP_PRI_MTU); @@ -1195,7 +1229,11 @@ static int wan_aft_init (sdla_t *card, wandev_conf_t* conf) __sdla_push_ptr_isr_array(card->hw,card,WAN_FE_LINENO(&card->fe)); - card->isr = &wp_aft_global_isr; + card->isr = &wp_aft_global_isr; + + wan_init_timer(&card->u.aft.bg_timer, aft_background_timer_expire, (wan_timer_arg_t)card); + card->u.aft.bg_timer_cmd=0; + aft_background_timer_add(card, 5); #if 1 card->hw_iface.getcfg(card->hw, SDLA_HWTYPE_USEDCNT, &used_type_cnt); @@ -1280,6 +1318,7 @@ static int wan_aft_init (sdla_t *card, wandev_conf_t* conf) wan_clear_bit(AFT_TDM_GLOBAL_ISR,&card->u.aft.chip_cfg_status); wan_clear_bit(AFT_TDM_RING_BUF,&card->u.aft.chip_cfg_status); + wan_clear_bit(AFT_TDM_FE_SYNC_CNT,&card->u.aft.chip_cfg_status); if (card->u.aft.firm_id == AFT_DS_FE_CORE_ID) { if ((card->adptr_type == A108_ADPTR_8TE1 && @@ -1304,13 +1343,24 @@ static int wan_aft_init (sdla_t *card, wandev_conf_t* conf) wan_set_bit(AFT_TDM_FREE_RUN_ISR,&card->u.aft.chip_cfg_status); } + if ((card->adptr_type == A108_ADPTR_8TE1 && + card->u.aft.firm_ver >= 0x40) || + (card->adptr_type == A104_ADPTR_4TE1 && + card->u.aft.firm_ver >= 0x37) || + (card->adptr_type == A101_ADPTR_2TE1 && + card->u.aft.firm_ver >= 0x37) || + (card->adptr_type == A101_ADPTR_1TE1 && + card->u.aft.firm_ver >= 0x37)) { + wan_set_bit(AFT_TDM_FE_SYNC_CNT,&card->u.aft.chip_cfg_status); + + } } else { - if ((card->adptr_type == A104_ADPTR_4TE1 && - card->adptr_subtype == AFT_SUBTYPE_SHARK && - card->u.aft.firm_ver >= 0x23)) { - wan_set_bit(AFT_TDM_GLOBAL_ISR,&card->u.aft.chip_cfg_status); - wan_set_bit(AFT_TDM_RING_BUF,&card->u.aft.chip_cfg_status); + if ((card->adptr_type == A104_ADPTR_4TE1 && + card->adptr_subtype == AFT_SUBTYPE_SHARK && + card->u.aft.firm_ver >= 0x23)) { + wan_set_bit(AFT_TDM_GLOBAL_ISR,&card->u.aft.chip_cfg_status); + wan_set_bit(AFT_TDM_RING_BUF,&card->u.aft.chip_cfg_status); } } @@ -1563,7 +1613,7 @@ static int aft_transp_if_init(sdla_t *card, private_area_t *chan, wanif_conf_t * unsigned char *buf; if (chan->mtu&0x03){ - DEBUG_EVENT("%s:%s: Error, Transparent MTU must be word aligned!\n", + DEBUG_ERROR("%s:%s: Error, Transparent MTU must be word aligned!\n", card->devname,chan->if_name); return -EINVAL; } @@ -1571,11 +1621,7 @@ static int aft_transp_if_init(sdla_t *card, private_area_t *chan, wanif_conf_t * chan->max_idle_size=(u16)chan->mtu; if (!chan->channelized_cfg && chan->num_of_time_slots > 1){ - if (conf->u.aft.mtu_idle == 0) { - if (chan->tdm_api_chunk > 40) { - chan->max_idle_size=40*chan->num_of_time_slots; - } - } else { + if (conf->u.aft.mtu_idle > 0) { switch (conf->u.aft.mtu_idle) { case 40: case 80: @@ -1584,7 +1630,7 @@ static int aft_transp_if_init(sdla_t *card, private_area_t *chan, wanif_conf_t * chan->max_idle_size=conf->u.aft.mtu_idle*chan->num_of_time_slots; break; default: - DEBUG_EVENT("%s:%s: Warning: Invalid mtu idle lenght %i defaulting to 40bytes (5ms)\n", + DEBUG_WARNING("%s:%s: Warning: Invalid mtu idle lenght %i defaulting to 40bytes (5ms)\n", card->devname,chan->if_name,conf->u.aft.mtu_idle); chan->max_idle_size=40*chan->num_of_time_slots; break; @@ -1593,10 +1639,10 @@ static int aft_transp_if_init(sdla_t *card, private_area_t *chan, wanif_conf_t * } if (chan->tslot_sync && chan->mtu%chan->num_of_time_slots){ - DEBUG_EVENT("%s:%s: Error, Sync Transparent MTU must be timeslot aligned!\n", + DEBUG_ERROR("%s:%s: Error, Sync Transparent MTU must be timeslot aligned!\n", card->devname,chan->if_name); - DEBUG_EVENT("%s:%s: Error, MTU=%d not multiple of %d timeslots!\n", + DEBUG_ERROR("%s:%s: Error, MTU=%d not multiple of %d timeslots!\n", card->devname,chan->if_name, chan->mtu,chan->num_of_time_slots); @@ -1606,10 +1652,10 @@ static int aft_transp_if_init(sdla_t *card, private_area_t *chan, wanif_conf_t * if (conf->protocol != WANCONFIG_LIP_ATM && conf->protocol != WANCONFIG_LIP_KATM && chan->mru%chan->num_of_time_slots){ - DEBUG_EVENT("%s:%s: Error, Transparent MRU must be timeslot aligned!\n", + DEBUG_ERROR("%s:%s: Error, Transparent MRU must be timeslot aligned!\n", card->devname,chan->if_name); - DEBUG_EVENT("%s:%s: Error, MRU=%d not multiple of %d timeslots!\n", + DEBUG_ERROR("%s:%s: Error, MRU=%d not multiple of %d timeslots!\n", card->devname,chan->if_name, chan->mru,chan->num_of_time_slots); @@ -1691,6 +1737,11 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* silent=0; } + if (IS_B601_CARD(card) && dchan >= 0) { + DEBUG_EVENT( "%s: Error: Hardware HDLC framing not supported on B601\n", card->devname); + return -EINVAL; + } + if_cnt = wan_atomic_read(&card->wandev.if_cnt)+1; if (silent) { @@ -1710,7 +1761,7 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* if (card->adptr_subtype != AFT_SUBTYPE_SHARK){ if (card->u.aft.security_id != 0x01 && card->u.aft.security_cnt >= 2){ - DEBUG_EVENT("%s: Error: Security: Max HDLC channels(2) exceeded!\n", + DEBUG_ERROR("%s: Error: Security: Max HDLC channels(2) exceeded!\n", card->devname); DEBUG_EVENT("%s: Un-Channelised AFT supports 2 HDLC ifaces!\n", card->devname); @@ -1790,7 +1841,7 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* if (chan->mtu > MAX_WP_PRI_MTU || chan->mtu < MIN_WP_PRI_MTU){ - DEBUG_EVENT("%s: Error Invalid %s MTU %d (Min=%d, Max=%d)\n", + DEBUG_ERROR("%s: Error Invalid %s MTU %d (Min=%d, Max=%d)\n", card->devname,chan->if_name,chan->mtu, MIN_WP_PRI_MTU,MAX_WP_PRI_MTU); @@ -1812,7 +1863,7 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* chan->mru = conf->u.aft.mru; if (chan->mru > MAX_WP_PRI_MTU || chan->mru < MIN_WP_PRI_MTU){ - DEBUG_EVENT("%s: Error Invalid %s MRU %d (Min=%d, Max=%d)\n", + DEBUG_ERROR("%s: Error Invalid %s MRU %d (Min=%d, Max=%d)\n", card->devname,chan->if_name,chan->mru, MIN_WP_PRI_MTU,MAX_WP_PRI_MTU); @@ -1828,7 +1879,7 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* chan->mru = chan->mtu; if (chan->mtu > MAX_WP_PRI_MTU || chan->mtu < MIN_WP_PRI_MTU){ - DEBUG_EVENT("%s: Error Invalid %s MTU %d (Min=%d, Max=%d)\n", + DEBUG_ERROR("%s: Error Invalid %s MTU %d (Min=%d, Max=%d)\n", card->devname,chan->if_name,chan->mtu, MIN_WP_PRI_MTU,MAX_WP_PRI_MTU); @@ -1847,7 +1898,7 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* } if (chan->mtu % 8 != 0) { - DEBUG_EVENT("%s:%s: Error: MTU %d is not 8 byte aligned!\n", + DEBUG_ERROR("%s:%s: Error: MTU %d is not 8 byte aligned!\n", card->devname, chan->if_name, chan->mtu); err= -EINVAL; goto new_if_error; @@ -1867,7 +1918,7 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* case 40: break; default: - DEBUG_EVENT("%s:%s: Error: Invalid MTU %d - Supported: 8/16/40/80/160/240/320!\n", + DEBUG_ERROR("%s:%s: Error: Invalid MTU %d - Supported: 8/16/40/80/160/240/320!\n", card->devname, chan->if_name, chan->mtu); err= -EINVAL; goto new_if_error; @@ -1884,7 +1935,7 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* we must use MTU based on global configuraiton */ if (chan->mtu > MAX_WP_PRI_MTU || chan->mtu < MIN_WP_PRI_MTU){ - DEBUG_EVENT("%s: Error Invalid %s MTU %d (Min=%d, Max=%d)\n", + DEBUG_ERROR("%s: Error Invalid %s MTU %d (Min=%d, Max=%d)\n", card->devname,chan->if_name,chan->mtu, MIN_WP_PRI_MTU,MAX_WP_PRI_MTU); @@ -1898,7 +1949,7 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* } else { if (chan->mtu % 4 != 0) { - DEBUG_EVENT("%s:%s: Error: MTU %d is not 4 byte aligned!\n", + DEBUG_ERROR("%s:%s: Error: MTU %d is not 4 byte aligned!\n", card->devname, chan->if_name, chan->mtu); err= -EINVAL; goto new_if_error; @@ -1927,7 +1978,7 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* } if (chan->mtu % timeslots != 0 || chan->mtu&0x03) { - DEBUG_EVENT("%s:%s Error: MTU %d is not timeslot (%d) aligned!\n", + DEBUG_ERROR("%s:%s Error: MTU %d is not timeslot (%d) aligned!\n", card->devname, chan->if_name, chan->mtu,timeslots); err= -EINVAL; goto new_if_error; @@ -1990,7 +2041,7 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* } else if( strcmp(conf->usedby, "TDM_API") == 0) { - DEBUG_EVENT("%s:%s: Error: TDM API mode is not supported!\n", + DEBUG_ERROR("%s:%s: Error: TDM API mode is not supported!\n", card->devname,chan->if_name); err=-EINVAL; goto new_if_error; @@ -2024,7 +2075,7 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* if (card->tdmv_conf.span_no == 0) { card->tdmv_conf.span_no = card->card_no; if (!card->card_no) { - DEBUG_EVENT("%s: Error: Internal Driver Error: Card card_no filed is ZERO!\n", + DEBUG_ERROR("%s: Error: Internal Driver Error: Card card_no filed is ZERO!\n", card->devname); return -EINVAL; } @@ -2098,6 +2149,19 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* } #endif + } else if (strcmp(conf->usedby, "MTP1_API") == 0) { + + chan->common.usedby = API; + chan->usedby_cfg = MTP1_API; + + chan->wp_api_op_mode=WP_TDM_OPMODE_SPAN; + + chan->cfg.data_mux=0; + conf->hdlc_streaming=0; + chan->dma_chain_opmode = WAN_AFT_DMA_CHAIN_IRQ_ALL; + chan->max_tx_bufs=MAX_AFT_DMA_CHAINS; + conf->mtu=80; + #if defined(AFT_XMTP2_API_SUPPORT) } else if (strcmp(conf->usedby, "XMTP2_API") == 0) { chan->common.usedby = XMTP2_API; @@ -2146,7 +2210,7 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* chan->dma_chain_opmode = WAN_AFT_DMA_CHAIN; chan->mru=chan->mtu=1500; # else - DEBUG_EVENT("%s: Error: TDMV_DCHAN Option not compiled into the driver!\n", + DEBUG_ERROR("%s: Error: TDMV_DCHAN Option not compiled into the driver!\n", card->devname); err=-EINVAL; goto new_if_error; @@ -2160,7 +2224,7 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* } #else DEBUG_EVENT("\n"); - DEBUG_EVENT("%s:%s: Error: TDM VOICE prot not compiled\n", + DEBUG_ERROR("%s:%s: Error: TDM VOICE prot not compiled\n", card->devname,chan->if_name); DEBUG_EVENT("%s:%s: during installation process!\n", card->devname,chan->if_name); @@ -2274,7 +2338,7 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* card->devname,chan->if_name); }else{ - DEBUG_EVENT( "%s:%s: Error: Invalid IF operation mode %s\n", + DEBUG_ERROR( "%s:%s: Error: Invalid IF operation mode %s\n", card->devname,chan->if_name,conf->usedby); err=-EINVAL; goto new_if_error; @@ -2325,7 +2389,7 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* chan->time_slot_map); if (!chan->num_of_time_slots){ - DEBUG_EVENT("%s: Error: Invalid number of timeslots in map 0x%08X!\n", + DEBUG_ERROR("%s: Error: Invalid number of timeslots in map 0x%08X!\n", chan->if_name,chan->time_slot_map); err=-EINVAL; goto new_if_error; @@ -2406,7 +2470,7 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* chan->dma_mru = aft_valid_mtu((u16)chan->dma_mru); if (!chan->dma_mru){ - DEBUG_EVENT("%s:%s: Error invalid MTU %d MRU %d\n", + DEBUG_ERROR("%s:%s: Error invalid MTU %d MRU %d\n", card->devname, chan->if_name, chan->mtu,chan->mru); @@ -2418,6 +2482,7 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* DMA Chains so disable dma chains for them */ if (conf->single_tx_buf || !IS_TE1_CARD(card) || + IS_B601_CARD(card) || ((card->adptr_type == A101_ADPTR_2TE1 || card->adptr_type == A101_ADPTR_1TE1) && card->u.aft.firm_id == AFT_DS_FE_CORE_ID)){ @@ -2535,11 +2600,6 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* } #endif - err=aft_hwec_config(card,chan,conf,1); - if (err){ - goto new_if_error; - } - if (chan->channelized_cfg && !chan->hdlc_eng) { chan->dma_mru = 1024; dma_per_ch = 4; @@ -2663,6 +2723,11 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* goto new_if_error; } + chan->tx_realign_buf=wan_kmalloc(chan->dma_mru); + if(!chan->tx_realign_buf){ + goto new_if_error; + } + chan->dma_per_ch = (u16)dma_per_ch; /*======================================================= @@ -2722,7 +2787,16 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* * finished successfully. DO NOT place any code below that * can return an error */ #if defined(__LINUX__) || defined(__WINDOWS__) - dev->init = &if_init; + WAN_NETDEV_OPS_BIND(dev,wan_netdev_ops); + WAN_NETDEV_OPS_INIT(dev,wan_netdev_ops,&if_init); + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&if_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&if_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&if_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&if_stats); + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&if_tx_timeout); + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,&if_do_ioctl); + WAN_NETDEV_OPS_MTU(dev,wan_netdev_ops,if_change_mtu); + # if defined(CONFIG_PRODUCT_WANPIPE_GENERIC) if_init(dev); # endif @@ -2775,7 +2849,7 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* return 0; new_if_error: - FUNC_NEWDRV(); + return err; @@ -2856,21 +2930,21 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) break; } if (err){ - DEBUG_EVENT("%s: Error: Failed to initialize tdmv functions!\n", + DEBUG_ERROR("%s: Error: Failed to initialize tdmv functions!\n", card->devname); return -EINVAL; } WAN_TDMV_CALL(create, (card, &card->tdmv_conf), err); if (err){ - DEBUG_EVENT("%s: Error: Failed to create tdmv span!\n", + DEBUG_ERROR("%s: Error: Failed to create tdmv span!\n", card->devname); return err; } } #else DEBUG_EVENT("\n"); - DEBUG_EVENT("%s: Error: TDM VOICE prot not compiled\n", + DEBUG_ERROR("%s: Error: TDM VOICE prot not compiled\n", card->devname); DEBUG_EVENT("%s: during installation process!\n", card->devname); @@ -2902,7 +2976,7 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) if (!card->tdmv_conf.span_no){ DEBUG_EVENT("\n"); - DEBUG_EVENT("%s: Error: TDM SPAN not configured! Failed to start channel!\n", + DEBUG_ERROR("%s: Error: TDM SPAN not configured! Failed to start channel!\n", card->devname); return -EINVAL; } @@ -2932,7 +3006,11 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) card->tdmv_conf.dchan,master_if); if (strcmp(conf->usedby, "TDM_SPAN_VOICE_API") ==0) { - + +#ifdef SPAN_TIMING_DEBUGGING + aft_free_running_timer_set_enable(card,10); +#endif + conf->active_ch=active_ch&~(card->tdmv_conf.dchan); conf->u.aft.tdmv_master_if=1; err=new_if_private(wandev,dev,conf,0,-1,if_cnt); @@ -2957,6 +3035,8 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) } } + + } else { for (i=0;iu.aft.num_of_time_slots;i++){ @@ -2995,7 +3075,7 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) if(IS_BRI_CARD(card)) { if (conf->active_ch & ~(0x07)) { - DEBUG_EVENT("%s: Error: BRI Active Channels range 1 to 3: Range 0x%08X invalid\n", + DEBUG_ERROR("%s: Error: BRI Active Channels range 1 to 3: Range 0x%08X invalid\n", card->devname,conf->active_ch); err=-EINVAL; goto new_if_cfg_skip; @@ -3089,6 +3169,8 @@ new_if_cfg_skip: } } + + AFT_FUNC_DEBUG(); return err; @@ -3120,14 +3202,14 @@ static int del_if_private (wan_device_t* wandev, netdevice_t* dev) int i; if (!chan){ - DEBUG_EVENT("%s: Critical Error del_if_private() chan=NULL!\n", + DEBUG_ERROR("%s: Critical Error del_if_private() chan=NULL!\n", wan_netif_name(dev)); return 0; } card = chan->card; if (!card){ - DEBUG_EVENT("%s: Critical Error del_if_private() chan=NULL!\n", + DEBUG_ERROR("%s: Critical Error del_if_private() chan=NULL!\n", wan_netif_name(dev)); return 0; } @@ -3161,8 +3243,6 @@ static int del_if_private (wan_device_t* wandev, netdevice_t* dev) } #endif - aft_hwec_config(card,chan,NULL,0); - wan_spin_lock_irq(&card->wandev.lock,&flags); aft_dev_unconfigure(card,chan); wan_spin_unlock_irq(&card->wandev.lock,&flags); @@ -3179,7 +3259,7 @@ static int del_if_private (wan_device_t* wandev, netdevice_t* dev) } if (aft_tdm_api_free(card,chan)) { - DEBUG_EVENT("%s: Error: Failed to del iface: TDM API Device in use!\n", + DEBUG_ERROR("%s: Error: Failed to del iface: TDM API Device in use!\n", chan->if_name); return -EBUSY; } @@ -3255,6 +3335,11 @@ static int del_if_private (wan_device_t* wandev, netdevice_t* dev) chan->tx_idle_skb=NULL; } + if (chan->tx_bert_skb){ + wan_skb_free(chan->tx_bert_skb); + chan->tx_bert_skb=NULL; + } + if (chan->tx_realign_buf){ wan_free(chan->tx_realign_buf); chan->tx_realign_buf=NULL; @@ -3301,13 +3386,13 @@ static int del_if (wan_device_t* wandev, netdevice_t* dev) int err=0; if (!chan){ - DEBUG_EVENT("%s: Critical Error del_if() chan=NULL!\n", + DEBUG_ERROR("%s: Critical Error del_if() chan=NULL!\n", wan_netif_name(dev)); return 0; } if (!(card=chan->card)){ - DEBUG_EVENT("%s: Critical Error del_if() chan=NULL!\n", + DEBUG_ERROR("%s: Critical Error del_if() chan=NULL!\n", wan_netif_name(dev)); return 0; } @@ -3336,7 +3421,7 @@ static int del_if (wan_device_t* wandev, netdevice_t* dev) /* Disable the TDMV Interrupt first, before * shutting down all TDMV channels */ - wan_spin_lock_irq(&card->wandev.lock,&flags); + wan_spin_lock_irq(&card->wandev.lock,&flags); if (chan->channelized_cfg) { if (IS_BRI_CARD(card)) { @@ -3357,15 +3442,17 @@ static int del_if (wan_device_t* wandev, netdevice_t* dev) This code re-triggers it just in case */ card->hw_iface.bus_read_4(card->hw, AFT_PORT_REG(card,AFT_DMA_CTRL_REG),&dmareg); - wan_set_bit(AFT_DMACTRL_TDMV_RX_TOGGLE,&dmareg); - wan_set_bit(AFT_DMACTRL_TDMV_TX_TOGGLE,&dmareg); - card->hw_iface.bus_write_4(card->hw, + wan_set_bit(AFT_DMACTRL_TDMV_RX_TOGGLE,&dmareg); + wan_set_bit(AFT_DMACTRL_TDMV_TX_TOGGLE,&dmareg); + card->hw_iface.bus_write_4(card->hw, AFT_PORT_REG(card,AFT_DMA_CTRL_REG),dmareg); } } else { aft_tdm_intr_ctrl(card,0); aft_fifo_intr_ctrl(card, 0); } + } else { + disable_data_error_intr(card,DEVICE_DOWN); } /* Disable RTP Tap */ @@ -3379,9 +3466,10 @@ static int del_if (wan_device_t* wandev, netdevice_t* dev) #endif wan_spin_unlock_irq(&card->wandev.lock,&flags); - while(chan){ + while (chan){ int err=del_if_private(wandev,dev); if (err) { + aft_critical_shutdown(card); return err; } wan_netif_set_priv(dev, chan->next); @@ -3402,6 +3490,7 @@ static int del_if (wan_device_t* wandev, netdevice_t* dev) err=0; } else { + err=del_if_private(wandev,dev); wan_netif_set_priv(dev, NULL); wan_free(chan); @@ -3464,7 +3553,7 @@ static void disable_comm (sdla_t *card) disable_comm_delay(); /* 100 ms */ port_task_timeout++; if (port_task_timeout > 20) { - DEBUG_EVENT("%s: Warning: Port Task stuck! timing out!\n",card->devname); + DEBUG_WARNING("%s: Warning: Port Task stuck! timing out!\n",card->devname); break; } } @@ -3474,6 +3563,7 @@ static void disable_comm (sdla_t *card) DEBUG_EVENT("%s: %s\n",card->devname, err?"TASKQ Successfully Stopped":"TASKQ Not Running"); #endif + aft_background_timer_kill(card); /* Unconfiging, only on shutdown */ if (card->wandev.fe_iface.pre_release){ @@ -3586,31 +3676,42 @@ static int if_init (netdevice_t* dev) #endif /* Initialize device driver entry points */ - dev->open = &if_open; - dev->stop = &if_close; + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&if_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&if_close); + #if defined(CONFIG_PRODUCT_WANPIPE_GENERIC) hdlc = dev_to_hdlc(dev); hdlc->xmit = if_send; #else - dev->hard_start_xmit = &if_send; + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&if_send); #endif - dev->get_stats = &if_stats; - + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&if_stats); #if 0 dev->tx_timeout = &if_tx_timeout; dev->watchdog_timeo = 2*HZ; #else if (chan->common.usedby == TDM_VOICE || chan->common.usedby == TDM_VOICE_API){ - dev->tx_timeout = NULL; + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,NULL); }else{ - dev->tx_timeout = &if_tx_timeout; + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&if_tx_timeout); } + dev->watchdog_timeo = 2*HZ; + { + u32 secs = ((chan->max_tx_bufs/2)*chan->dma_mru*8) / (64000 * chan->num_of_time_slots); + secs+=2; + if (secs < 2) { + secs=2; + } + dev->watchdog_timeo = secs*HZ; + + DEBUG_TEST("%s: Dev watch dog = %i mtu=%i\n", card->devname, secs,chan->dma_mru); + } #endif - dev->do_ioctl = if_do_ioctl; - dev->change_mtu = if_change_mtu; + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,&if_do_ioctl); + WAN_NETDEV_OPS_MTU(dev,wan_netdev_ops,if_change_mtu); if (chan->common.usedby == BRIDGE || chan->common.usedby == BRIDGE_NODE){ @@ -3698,8 +3799,6 @@ static int if_open (netdevice_t* dev) sdla_t* card = chan->card; - FUNC_NEWDRV(); - #if defined(__LINUX__) /* Only one open per interface is allowed */ if (open_dev_check(dev)){ @@ -3739,8 +3838,8 @@ static int if_open (netdevice_t* dev) wanpipe_open(card); - if (card->wandev.config_id == WANCONFIG_AFT_56K) { - FUNC_NEWDRV(); + if (card->wandev.config_id == WANCONFIG_AFT_56K || + IS_TE1_CARD(card)) { if (!wan_test_bit(CARD_PORT_TASK_DOWN,&card->wandev.critical)){ wan_set_bit(AFT_FE_POLL,&card->u.aft.port_task_cmd); WAN_TASKQ_SCHEDULE((&card->u.aft.port_task)); @@ -3837,11 +3936,12 @@ static void if_tx_timeout (netdevice_t *dev) card->hw_iface.bus_read_4(card->hw,dma_ram_desc,®); cur_dma_ptr=aft_dmachain_get_tx_dma_addr(reg); - DEBUG_EVENT("%s: Chain TxPend=%d, TxCur=%d, TxPend=%d HwCur=%d TxA=%d TxC=%ld\n", + DEBUG_EVENT("%s: Chain TxPend=%d, TxCur=%d, TxPend=%d TxSize=%i HwCur=%d TxA=%d TxC=%ld\n", chan->if_name, wan_test_bit(TX_INTR_PENDING,&chan->dma_chain_status), chan->tx_chain_indx, chan->tx_pending_chain_indx, + chan->tx_chain_sz, cur_dma_ptr, chan->tx_attempts, WAN_NETIF_STATS_TX_PACKETS(&chan->common)); @@ -3859,10 +3959,14 @@ static void if_tx_timeout (netdevice_t *dev) wan_spin_lock_irq(&card->wandev.lock, &smp_flags); if (chan->channelized_cfg || chan->wp_api_op_mode){ for (ch_ptr=chan;ch_ptr;ch_ptr=ch_ptr->next){ - aft_tx_fifo_under_recover(card,chan); + if (ch_ptr->hdlc_eng) { + aft_tx_fifo_under_recover(card,ch_ptr); + wanpipe_wake_stack(ch_ptr); + } } - }else{ + } else { aft_tx_fifo_under_recover(card,chan); + wanpipe_wake_stack(chan); } wan_spin_unlock_irq(&card->wandev.lock, &smp_flags); @@ -3870,8 +3974,6 @@ static void if_tx_timeout (netdevice_t *dev) aft_list_tx_descriptors(chan); #endif - wanpipe_wake_stack(chan); - } @@ -4008,7 +4110,7 @@ static int if_send(netdevice_t *dev, netskb_t *skb, struct sockaddr *dst,struct } if (!chan){ - DEBUG_EVENT("%s: Warning: DCHAN TX: No DCHAN Configured (not preset)! Discarding data.\n", + DEBUG_WARNING("%s: Warning: DCHAN TX: No DCHAN Configured (not preset)! Discarding data.\n", card->devname); wan_skb_free(skb); WAN_NETIF_START_QUEUE(dev); @@ -4035,7 +4137,7 @@ static int if_send(netdevice_t *dev, netskb_t *skb, struct sockaddr *dst,struct /* For TDM_VOICE_API no tx is supported in if_send */ if (chan->common.usedby == TDM_VOICE_API || chan->wp_api_op_mode){ - DEBUG_EVENT("%s: Warning: IF SEND TX: Chan in VOICE API Mode\n", + DEBUG_WARNING("%s: Warning: IF SEND TX: Chan in VOICE API Mode\n", card->devname); WAN_NETIF_STATS_INC_TX_ERRORS(&chan->common); WP_AFT_CHAN_ERROR_STATS(chan->chan_stats,tx_errors); @@ -4102,7 +4204,7 @@ static int if_send(netdevice_t *dev, netskb_t *skb, struct sockaddr *dst,struct if (!chan->hdlc_eng && chan->tslot_sync){ if (wan_skb_len(skb)%chan->num_of_time_slots){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s:%s: Tx Error pkt len(%d) not multiple of timeslots(%d)\n", + DEBUG_ERROR("%s:%s: Tx Error pkt len(%d) not multiple of timeslots(%d)\n", card->devname, chan->if_name, wan_skb_len(skb), @@ -4120,7 +4222,7 @@ static int if_send(netdevice_t *dev, netskb_t *skb, struct sockaddr *dst,struct if (!chan->hdlc_eng && !chan->lip_atm && (wan_skb_len(skb)%4)){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: Tx Error: Tx Length %d is not 32bit divisible\n", + DEBUG_ERROR("%s: Tx Error: Tx Length %d is not 32bit divisible\n", chan->if_name,wan_skb_len(skb)); } wan_skb_free(skb); @@ -4217,10 +4319,11 @@ if_send_exit_crit: */ #if defined(__LINUX__) || defined(__WINDOWS__) static struct net_device_stats gstats; + static struct net_device_stats* if_stats (netdevice_t* dev) { private_area_t* chan; - sdla_t *card; + sdla_t *card; if ((chan=wan_netif_priv(dev)) == NULL) return &gstats; @@ -4240,7 +4343,8 @@ static struct net_device_stats* if_stats (netdevice_t* dev) } else #endif if (card->tdm_api_span && - card->wandev.config_id != WANCONFIG_AFT_ANALOG) { + card->wandev.config_id != WANCONFIG_AFT_ANALOG && + !IS_B601_TE1_CARD(card)) { chan->common.if_stats.rx_packets = card->wandev.stats.rx_packets; chan->common.if_stats.tx_packets = card->wandev.stats.tx_packets; } @@ -4361,6 +4465,9 @@ if_do_ioctl(netdevice_t *dev, struct ifreq *ifr, wan_ioctl_cmd_t cmd) #if !defined(__WINDOWS__) case SIOC_AFT_CUSTOMER_ID: + if (IS_B601_CARD(card) || IS_A600_CARD(card)) { + return -EINVAL; + } if (!ifr){ return -EINVAL; } else { @@ -4394,7 +4501,7 @@ if_do_ioctl(netdevice_t *dev, struct ifreq *ifr, wan_ioctl_cmd_t cmd) #if 0 case SIOC_WAN_EC_IOCTL: if (wan_test_and_set_bit(CARD_HW_EC,&card->wandev.critical)){ - DEBUG_EVENT("%s: Error: EC IOCTL Reentrant!\n", + DEBUG_ERROR("%s: Error: EC IOCTL Reentrant!\n", card->devname); return -EBUSY; } @@ -4437,8 +4544,8 @@ if_do_ioctl(netdevice_t *dev, struct ifreq *ifr, wan_ioctl_cmd_t cmd) *******************************************************************/ -#define FIFO_RESET_TIMEOUT_CNT 1000 -#define FIFO_RESET_TIMEOUT_US 10 +#define FIFO_RESET_TIMEOUT_CNT 5000 +#define FIFO_RESET_TIMEOUT_US 1 static int aft_init_rx_dev_fifo(sdla_t *card, private_area_t *chan, unsigned char wait) { @@ -4482,6 +4589,7 @@ static int aft_init_rx_dev_fifo(sdla_t *card, private_area_t *chan, unsigned cha WP_DELAY(FIFO_RESET_TIMEOUT_US); continue; } + DEBUG_TEST("%s: RX RESET in %i us (cnt=%i)\n",card->devname,i*FIFO_RESET_TIMEOUT_US,i); timeout=0; break; } @@ -4492,7 +4600,7 @@ static int aft_init_rx_dev_fifo(sdla_t *card, private_area_t *chan, unsigned cha card->hw_iface.bus_read_4(card->hw,AFT_PORT_REG(card,AFT_DMA_CTRL_REG),&dmareg); max_logic_ch = aft_dmactrl_get_max_logic_ch(dmareg); - DEBUG_EVENT("%s:%s: Error: Rx fifo reset timedout %u us (ch=%d) DMA CTRL=0x%08X DMA MAX=%d\n", + DEBUG_ERROR("%s:%s: Error: Rx fifo reset timedout %u us (ch=%d) DMA CTRL=0x%08X DMA MAX=%d\n", card->devname, chan->if_name, i*FIFO_RESET_TIMEOUT_US, @@ -4554,11 +4662,12 @@ static int aft_init_tx_dev_fifo(sdla_t *card, private_area_t *chan, unsigned cha continue; } timeout=0; + DEBUG_TEST("%s: TX RESET in %i us (cnt=%i)\n",card->devname,i*FIFO_RESET_TIMEOUT_US,i); break; } if (timeout){ - DEBUG_EVENT("%s:%s: Error: Tx fifo reset timedout %u us (ch=%d)\n", + DEBUG_ERROR("%s:%s: Error: Tx fifo reset timedout %u us (ch=%d)\n", card->devname, chan->if_name, i*FIFO_RESET_TIMEOUT_US, @@ -4796,7 +4905,7 @@ static void aft_dev_close(sdla_t *card, private_area_t *gchan) */ static void aft_dma_tx_complete (sdla_t *card, private_area_t *chan, int wdt, int reset) { - int err=0; + int tx_complete_cntr=0; DEBUG_TEST("%s: Tx interrupt wdt=%d\n",chan->if_name,wdt); if (!wdt){ @@ -4806,7 +4915,7 @@ static void aft_dma_tx_complete (sdla_t *card, private_area_t *chan, int wdt, in if (chan->channelized_cfg && !chan->hdlc_eng){ aft_tx_dma_voice_handler(chan,wdt,reset); }else{ - err=aft_tx_dma_chain_handler(chan,wdt,reset); + tx_complete_cntr=aft_tx_dma_chain_handler(chan,wdt,reset); } wan_set_bit(0,&chan->idle_start); @@ -4815,23 +4924,33 @@ static void aft_dma_tx_complete (sdla_t *card, private_area_t *chan, int wdt, in return; } - aft_dma_tx(card,chan); + if ( !wdt || (chan->hdlc_eng && !tx_complete_cntr) ) { + aft_dma_tx(card,chan); + } + +#if defined(__WINDOWS__) + /* For LIP layer to wakeup, must always check for free buffers + * and wake the stack. */ + /*if(1){*/ /* TODO: re-run stress test in LIP layer */ -#if 0 - if (WAN_NETIF_QUEUE_STOPPED(chan->common.dev)){ -#else if (wan_chan_dev_stopped(chan)) { -#endif +#else +# if 0 + if (WAN_NETIF_QUEUE_STOPPED(chan->common.dev)){ +# else + if (wan_chan_dev_stopped(chan)) { +# endif +#endif /* __WINDOWS__ */ /* Wakeup stack also wakes up DCHAN, however, for DCHAN we must always call the upper layer and let it decide what to do */ + DEBUG_TEST("%s: STACK STOPPED Pending=%d Max=%d Max/2=%d\n", chan->if_name,wan_skb_queue_len(&chan->wp_tx_pending_list), chan->max_tx_bufs,chan->max_tx_bufs/2); - - + if (wan_skb_queue_len(&chan->wp_tx_pending_list) <= (chan->max_tx_bufs/2)) { wanpipe_wake_stack(chan); } @@ -4871,7 +4990,7 @@ static void aft_tx_post_complete (sdla_t *card, private_area_t *chan, netskb_t * if (reg & AFT_TXDMA_HI_DMA_LENGTH_MASK){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s:%s: Error: TxDMA Length not equal 0 (reg=0x%08X)\n", + DEBUG_ERROR("%s:%s: Error: TxDMA Length not equal 0 (reg=0x%08X)\n", card->devname,chan->if_name,reg); } chan->errstats.Tx_dma_errors++; @@ -4883,14 +5002,14 @@ static void aft_tx_post_complete (sdla_t *card, private_area_t *chan, netskb_t * chan->errstats.Tx_pci_errors++; if (wan_test_bit(AFT_TXDMA_HIDMASTATUS_PCI_M_ABRT,&dma_status)){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s:%s: Tx Error: Abort from Master: pci fatal error!\n", + DEBUG_ERROR("%s:%s: Tx Error: Abort from Master: pci fatal error!\n", card->devname,chan->if_name); } } if (wan_test_bit(AFT_TXDMA_HIDMASTATUS_PCI_T_ABRT,&dma_status)){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s:%s: Tx Error: Abort from Target: pci fatal error!\n", + DEBUG_ERROR("%s:%s: Tx Error: Abort from Target: pci fatal error!\n", card->devname,chan->if_name); } } @@ -4902,7 +5021,7 @@ static void aft_tx_post_complete (sdla_t *card, private_area_t *chan, netskb_t * } if (wan_test_bit(AFT_TXDMA_HIDMASTATUS_PCI_RETRY,&dma_status)){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s:%s: Tx Error: 'Retry' exceeds maximum (64k): pci fatal error!\n", + DEBUG_ERROR("%s:%s: Tx Error: 'Retry' exceeds maximum (64k): pci fatal error!\n", card->devname,chan->if_name); } } @@ -4994,25 +5113,25 @@ static void aft_rx_post_complete (sdla_t *card, private_area_t *chan, if (wan_test_bit(AFT_RXDMA_HIDMASTATUS_PCI_M_ABRT,&dma_status)){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s:%s: Rx Error: Abort from Master: pci fatal error 0x%X!\n", + DEBUG_ERROR("%s:%s: Rx Error: Abort from Master: pci fatal error 0x%X!\n", card->devname,chan->if_name,rx_el->reg); } } if (wan_test_bit(AFT_RXDMA_HIDMASTATUS_PCI_T_ABRT,&dma_status)){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s:%s: Rx Error: Abort from Target: pci fatal error 0x%X!\n", + DEBUG_ERROR("%s:%s: Rx Error: Abort from Target: pci fatal error 0x%X!\n", card->devname,chan->if_name,rx_el->reg); } } if (wan_test_bit(AFT_RXDMA_HIDMASTATUS_PCI_DS_TOUT,&dma_status)){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s:%s: Rx Error: No 'DeviceSelect' from target: pci fatal error 0x%X!\n", + DEBUG_ERROR("%s:%s: Rx Error: No 'DeviceSelect' from target: pci fatal error 0x%X!\n", card->devname,chan->if_name,rx_el->reg); } } if (wan_test_bit(AFT_RXDMA_HIDMASTATUS_PCI_RETRY,&dma_status)){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s:%s: Rx Error: 'Retry' exceeds maximum (64k): pci fatal error 0x%X!\n", + DEBUG_ERROR("%s:%s: Rx Error: 'Retry' exceeds maximum (64k): pci fatal error 0x%X!\n", card->devname,chan->if_name,rx_el->reg); } } @@ -5041,20 +5160,20 @@ static void aft_rx_post_complete (sdla_t *card, private_area_t *chan, /* Checking Rx DMA Frame end bit. (information for api) */ if (!wan_test_bit(AFT_RXDMA_HI_EOF_BIT,&rx_el->reg)){ DEBUG_TEST("%s:%s: RxDMA Intr: End flag missing: MTU Mismatch! Reg=0x%X\n", - card->devname,chan->if_name,rx_el->reg); + card->devname,chan->if_name,rx_el->reg); WAN_NETIF_STATS_INC_RX_FRAME_ERRORS(&chan->common); //chan->if_stats.rx_frame_errors++; chan->opstats.Rx_Data_discard_long_count++; chan->errstats.Rx_hdlc_corrupiton++; card->wandev.stats.rx_errors++; WP_AFT_CHAN_ERROR_STATS(chan->chan_stats,rx_frame_errors); goto rx_comp_error; - - } else { /* Check CRC error flag only if this is the end of Frame */ - + + } else { /* Check CRC error flag only if this is the end of Frame */ + if (wan_test_bit(AFT_RXDMA_HI_FCS_ERR_BIT,&rx_el->reg)){ - DEBUG_TEST("%s:%s: RxDMA Intr: CRC Error! Reg=0x%X Len=%d\n", - card->devname,chan->if_name,rx_el->reg, - (rx_el->reg&AFT_RXDMA_HI_DMA_LENGTH_MASK)>>2); + DEBUG_TEST("%s:%s: RxDMA Intr: CRC Error! Reg=0x%X Len=%d\n", + card->devname,chan->if_name,rx_el->reg, + (rx_el->reg&AFT_RXDMA_HI_DMA_LENGTH_MASK)>>2); WAN_NETIF_STATS_INC_RX_FRAME_ERRORS(&chan->common); //chan->if_stats.rx_frame_errors++; chan->errstats.Rx_crc_err_count++; card->wandev.stats.rx_crc_errors++; @@ -5062,12 +5181,12 @@ static void aft_rx_post_complete (sdla_t *card, private_area_t *chan, wan_set_bit(WP_CRC_ERROR_BIT,&rx_el->pkt_error); data_error = 1; } - + /* Check if this frame is an abort, if it is - * drop it and continue receiving */ + * drop it and continue receiving */ if (wan_test_bit(AFT_RXDMA_HI_FRM_ABORT_BIT,&rx_el->reg)){ DEBUG_TEST("%s:%s: RxDMA Intr: Abort! Reg=0x%X\n", - card->devname,chan->if_name,rx_el->reg); + card->devname,chan->if_name,rx_el->reg); WAN_NETIF_STATS_INC_RX_FRAME_ERRORS(&chan->common); //chan->if_stats.rx_frame_errors++; chan->errstats.Rx_hdlc_corrupiton++; card->wandev.stats.rx_frame_errors++; @@ -5075,7 +5194,7 @@ static void aft_rx_post_complete (sdla_t *card, private_area_t *chan, wan_set_bit(WP_ABORT_ERROR_BIT,&rx_el->pkt_error); data_error = 1; } - + if (chan->common.usedby != API && data_error){ goto rx_comp_error; } @@ -5289,7 +5408,13 @@ static void wp_bh_rx(private_area_t* chan, netskb_t *new_skb, u8 pkt_error, int if(chan->common.usedby == WANPIPE || chan->common.usedby == STACK){ - rx_dpc(chan->common.dev, new_skb, pkt_error, len); + if (wanpipe_lip_rx(chan, new_skb) != 0){ + WAN_NETIF_STATS_INC_RX_DROPPED(&chan->common); /* ++chan->if_stats.rx_dropped; */ + WP_AFT_CHAN_ERROR_STATS(chan->chan_stats,rx_dropped); + wan_skb_free(new_skb); + return; + } + }else{ int err; @@ -5303,7 +5428,7 @@ static void wp_bh_rx(private_area_t* chan, netskb_t *new_skb, u8 pkt_error, int }else{ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: Error Rx pkt headroom %u < %u\n", + DEBUG_ERROR("%s: Error Rx pkt headroom %u < %u\n", chan->if_name, (u32)wan_skb_headroom(new_skb), (u32)sizeof(wp_api_hdr_t)); @@ -5361,7 +5486,7 @@ static void wp_bh_rx(private_area_t* chan, netskb_t *new_skb, u8 pkt_error, int }else{ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: Error Rx pkt headroom %u < %u\n", + DEBUG_ERROR("%s: Error Rx pkt headroom %u < %u\n", chan->if_name, (u32)wan_skb_headroom(new_skb), (u32)sizeof(wp_api_hdr_t)); @@ -5428,7 +5553,7 @@ static void wp_bh_rx(private_area_t* chan, netskb_t *new_skb, u8 pkt_error, int rx_hdr->wp_api_rx_hdr_error_flag=pkt_error; }else{ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: Error Rx pkt headroom %u < %u\n", + DEBUG_ERROR("%s: Error Rx pkt headroom %u < %u\n", chan->if_name, (u32)wan_skb_headroom(new_skb), (u32)sizeof(wp_api_hdr_t)); @@ -5473,7 +5598,7 @@ static void wp_bh_rx(private_area_t* chan, netskb_t *new_skb, u8 pkt_error, int memset(rx_hdr,0,sizeof(wp_api_hdr_t)); }else{ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: Error Rx pkt headroom %u < %u\n", + DEBUG_ERROR("%s: Error Rx pkt headroom %u < %u\n", chan->if_name, (u32)wan_skb_headroom(new_skb), (u32)sizeof(wp_api_hdr_t)); @@ -5513,7 +5638,7 @@ static void wp_bh_rx(private_area_t* chan, netskb_t *new_skb, u8 pkt_error, int card->devname,wan_skb_len(new_skb), chan->tdmv_chan); #else - DEBUG_EVENT("%s: DCHAN Rx Packet critical error TDMV not compiled!\n",card->devname); + DEBUG_ERROR("%s: DCHAN Rx Packet critical error TDMV not compiled!\n",card->devname); #endif wan_skb_free(new_skb); @@ -5522,7 +5647,7 @@ static void wp_bh_rx(private_area_t* chan, netskb_t *new_skb, u8 pkt_error, int } else { if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: DCHAN Rx Packet critical error op not supported\n",card->devname); + DEBUG_ERROR("%s: DCHAN Rx Packet critical error op not supported\n",card->devname); } WAN_NETIF_STATS_INC_RX_DROPPED(&chan->common); /* ++chan->if_stats.rx_dropped; */ WP_AFT_CHAN_ERROR_STATS(chan->chan_stats,rx_dropped); @@ -5542,9 +5667,6 @@ static void wp_bh_rx(private_area_t* chan, netskb_t *new_skb, u8 pkt_error, int }else if (chan->common.usedby == STACK){ -#if defined(__WINDOWS__) - FUNC_NOT_IMPL(); -#else wan_skb_set_csum(new_skb,0); if (wanpipe_lip_rx(chan,new_skb) != 0){ @@ -5553,7 +5675,6 @@ static void wp_bh_rx(private_area_t* chan, netskb_t *new_skb, u8 pkt_error, int wan_skb_free(new_skb); return; } -#endif #if defined(AFT_XMTP2_API_SUPPORT) }else if (chan->common.usedby == XMTP2_API){ @@ -5563,7 +5684,7 @@ static void wp_bh_rx(private_area_t* chan, netskb_t *new_skb, u8 pkt_error, int if (chan->xmtp2_api_index < 0) { if (WAN_NET_RATELIMIT()) { - DEBUG_EVENT("%s: Error: MTP2 Link not configured!\n", + DEBUG_ERROR("%s: Error: MTP2 Link not configured!\n", chan->if_name); } WAN_NETIF_STATS_INC_RX_DROPPED(&chan->common); /* ++chan->if_stats.rx_dropped; */ @@ -5633,6 +5754,37 @@ static void wp_bh_rx(private_area_t* chan, netskb_t *new_skb, u8 pkt_error, int } #endif + +static void wp_init_bert_tx_skb(private_area_t *chan) +{ + u8 *bert_buf; + u32 indx; + + bert_buf = wan_skb_data(chan->tx_bert_skb); + + for ( indx = 0; indx < chan->bert_data_length; ++indx ) { + wp_bert_pop_value(&chan->wp_bert, &bert_buf[indx]); + } +} + + +/* Note that wp_bert_rx() does NOT use tx_bert_skb, so it is + * always safe to call this function (including when tx_bert_skb + * is not allocated), but we never do that. */ +static void wp_bert_rx(private_area_t *chan, netskb_t *bert_skb) +{ + u8 *bert_buf; + u32 indx; + u8 expected_value; + + bert_buf = wan_skb_data(bert_skb); + + for ( indx = 0; indx < chan->bert_data_length; ++indx ) { + wp_bert_push_value(&chan->wp_bert, bert_buf[indx], &expected_value); + } +} + + #if defined(__LINUX__) static void wp_bh (unsigned long data) #elif defined(__WINDOWS__) @@ -5656,8 +5808,20 @@ static void wp_bh (void *data, int pending) card->wandev.stats.collisions++; #endif + AFT_PERF_STAT_INC(card,bh,all); + if (wan_test_bit(CARD_DOWN,&card->wandev.critical)){ - WAN_TASKLET_END((&chan->common.bh_task)); + /* Do not try to touch the chan structure as it + could be in process of going down */ + return; + } + + if (!wan_test_bit(WP_DEV_CONFIG,&chan->up)) { + /* Do not try to touch the chan structure as it + could be in process of going down */ + if (WAN_NET_RATELIMIT()){ + DEBUG_EVENT("wp_bh(): warning: chan not up!\n"); + } return; } @@ -5685,14 +5849,6 @@ static void wp_bh (void *data, int pending) DEBUG_TEST("%s: ------------ BEGIN --------------: %ld\n", __FUNCTION__,(u_int64_t)SYSTEM_TICKS); - if (!wan_test_bit(WP_DEV_CONFIG,&chan->up)){ - if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: wp_bh() chan not up!\n", - chan->if_name); - } - WAN_TASKLET_END((&chan->common.bh_task)); - return; - } do { if (SYSTEM_TICKS-timeout > 2){ @@ -5753,10 +5909,19 @@ static void wp_bh (void *data, int pending) new_skb,TRC_INCOMING_FRM); } - wp_bh_rx(chan, new_skb, pkt_error, len); - }else{ - FUNC_NEWDRV(); + AFT_PERF_STAT_INC(card,bh,rx); + + /* During BERT user will NOT receive any data. */ + if ( wan_test_bit(WP_MAINTENANCE_MODE_BERT, &chan->maintenance_mode_bitmap) ) { + + wp_bert_rx(chan, new_skb); + wan_skb_free(new_skb); + } else { + + wp_bh_rx(chan, new_skb, pkt_error, len); + } } + }while(skb); do { @@ -5772,9 +5937,9 @@ static void wp_bh (void *data, int pending) wan_capture_trace_packet(chan->card, &top_chan->trace_info, skb,TRC_INCOMING_FRM); } -#if defined(__WINDOWS__) - FUNC_NOT_IMPL(); -#else + + AFT_PERF_STAT_INC(card,bh,rx_stack); + if (wanpipe_lip_rx(chan,skb) != 0){ WAN_NETIF_STATS_INC_RX_DROPPED(&chan->common); //++chan->if_stats.rx_dropped; WP_AFT_CHAN_ERROR_STATS(chan->chan_stats,rx_dropped); @@ -5787,7 +5952,7 @@ static void wp_bh (void *data, int pending) WAN_NETIF_STATS_INC_RX_PACKETS(&chan->common); //chan->if_stats.rx_packets++; WAN_NETIF_STATS_INC_RX_BYTES(&chan->common,len); //chan->if_stats.rx_bytes+=len; } -#endif + }while(skb); do { @@ -5804,6 +5969,7 @@ static void wp_bh (void *data, int pending) skb,TRC_INCOMING_FRM); } + AFT_PERF_STAT_INC(card,bh,rx_bri_dchan); wp_bh_rx(chan, skb, 0, wan_skb_len(skb)); }while(skb); @@ -5816,6 +5982,7 @@ static void wp_bh (void *data, int pending) if (!skb) { break; } + AFT_PERF_STAT_INC(card,bh,tx_post); aft_tx_post_complete (chan->card,chan,skb); #if defined(__WINDOWS__) @@ -5868,134 +6035,138 @@ static void wp_bh (void *data, int pending) * ***********************************************************************/ -static void __wp_aft_fifo_per_port_isr(sdla_t *card, u32 rx_status, u32 tx_status) +static int __wp_aft_fifo_per_port_isr(sdla_t *card, u32 rx_status, u32 tx_status) { int num_of_logic_ch; u32 tmp_fifo_reg; - private_area_t *chan, *top_chan=NULL; - int i,fifo=0; + private_area_t *chan=NULL, *top_chan=NULL; + int i,tx_fifo=0,rx_fifo=0; + u8 chan_valid=0; + u16 skip_tx_top_chan=0,skip_rx_top_chan=0; + int irq_handled=0; tx_status&=card->u.aft.active_ch_map; + tx_status&=card->u.aft.logic_ch_map; + rx_status&=card->u.aft.active_ch_map; + rx_status&=card->u.aft.logic_ch_map; num_of_logic_ch=card->u.aft.num_of_time_slots; - if (!wan_test_bit(0,&card->u.aft.comm_enabled)){ - if (tx_status){ - card->wandev.stats.tx_aborted_errors++; - } - if (rx_status){ - card->wandev.stats.rx_over_errors++; - } - return; + if (!tx_status && !rx_status) { + return irq_handled; } - if (tx_status != 0){ + irq_handled=1; - for (i=0;iu.aft.logic_ch_map)){ + if (tx_status) { + AFT_PERF_STAT_INC(card,isr,fifo_tx); + card->wp_tx_fifo_sanity++; + } + if (rx_status) { + AFT_PERF_STAT_INC(card,isr,fifo_rx); + card->wp_rx_fifo_sanity++; + } - chan=(private_area_t*)card->u.aft.dev_to_ch_map[i]; - if (!chan){ - DEBUG_EVENT("Warning: ignoring tx fifo intr: no dev!\n"); - continue; - } + for (i=0;iinterface_down)){ - continue; - } -#if 1 - if (!chan->hdlc_eng && !wan_test_bit(0,&chan->idle_start)){ - DEBUG_TEST("%s: Warning: ignoring tx fifo: dev idle start!\n", - chan->if_name); - continue; - } -#endif - if (IS_BRI_CARD(card) && chan->dchan_time_slot >= 0){ - continue; - } + chan_valid=0; - DEBUG_TEST("%s:%s: Warning TX Fifo Error on LogicCh=%ld Slot=%d!\n", - card->devname,chan->if_name,chan->logic_ch_num,i); + if (wan_test_bit(i,&tx_status)){ + + chan=(private_area_t*)card->u.aft.dev_to_ch_map[i]; + if (!chan){ + DEBUG_EVENT("Warning: ignoring tx fifo intr: no dev!\n"); + continue; + } + chan_valid=1; + + if (wan_test_bit(0,&chan->interface_down)){ + continue; + } + + if (IS_BRI_CARD(card) && chan->dchan_time_slot >= 0){ + continue; + } + + + DEBUG_TEST("%s:%s: Warning TX Fifo Error on LogicCh=%ld Slot=%d!\n", + card->devname,chan->if_name,chan->logic_ch_num,i); #if 0 { - u32 dma_descr,tmp_reg; - dma_descr=(chan->logic_ch_num<<4) + - AFT_PORT_REG(card,AFT_TX_DMA_HI_DESCR_BASE_REG); + u32 dma_descr,tmp_reg; + dma_descr=(chan->logic_ch_num<<4) + + AFT_PORT_REG(card,AFT_TX_DMA_HI_DESCR_BASE_REG); - card->hw_iface.bus_read_4(card->hw,dma_descr, &tmp_reg); + card->hw_iface.bus_read_4(card->hw,dma_descr, &tmp_reg); - DEBUG_EVENT("%s:%s: Warning TX Fifo Error on LogicCh=%ld Slot=%d Reg=0x%X!\n", - card->devname,chan->if_name,chan->logic_ch_num,i,tmp_reg); + DEBUG_EVENT("%s:%s: Warning TX Fifo Error on LogicCh=%ld Slot=%d Reg=0x%X!\n", + card->devname,chan->if_name,chan->logic_ch_num,i,tmp_reg); #if 1 - aft_list_tx_descriptors(chan); - aft_critical_trigger(card); - break; + aft_list_tx_descriptors(chan); + aft_critical_trigger(card); + break; #endif } #endif - top_chan=wan_netif_priv(chan->common.dev); - aft_tx_fifo_under_recover(card,chan); - fifo++; - - WAN_NETIF_STATS_INC_TX_FIFO_ERRORS(&chan->common); //++chan->if_stats.tx_fifo_errors; - WP_AFT_CHAN_ERROR_STATS(chan->chan_stats,tx_fifo_errors); - card->wandev.stats.tx_aborted_errors++; - __sdla_bus_read_4(card->hw,AFT_PORT_REG(card,AFT_TX_FIFO_INTR_PENDING_REG),&tmp_fifo_reg); + top_chan=wan_netif_priv(chan->common.dev); + if (top_chan == chan) { + skip_tx_top_chan++; } + + /* We must handle every fifo error otherwise + we could go into an infinite loop */ + aft_tx_fifo_under_recover(card,chan); + + tx_fifo++; + + WAN_NETIF_STATS_INC_TX_FIFO_ERRORS(&chan->common); //++chan->if_stats.tx_fifo_errors; + WP_AFT_CHAN_ERROR_STATS(chan->chan_stats,tx_fifo_errors); + card->wandev.stats.tx_aborted_errors++; + __sdla_bus_read_4(card->hw,AFT_PORT_REG(card,AFT_TX_FIFO_INTR_PENDING_REG),&tmp_fifo_reg); } - if (fifo) { - if (card->u.aft.global_tdm_irq) { - if (top_chan) { - WAN_NETIF_STATS_INC_TX_FIFO_ERRORS(&top_chan->common); //++chan->if_stats.tx_fifo_errors; - } - } - } - } - fifo=0; + if (wan_test_bit(i,&rx_status)){ - - if (rx_status != 0){ - - for (i=0;iu.aft.logic_ch_map)){ + if (!chan_valid) { chan=(private_area_t*)card->u.aft.dev_to_ch_map[i]; if (!chan){ continue; } + chan_valid=1; if (wan_test_bit(0,&chan->interface_down)){ continue; } - + if (IS_BRI_CARD(card) && chan->dchan_time_slot >= 0){ continue; } + } #ifdef AFT_RX_FIFO_DEBUG -{ + if (0){ u32 dma_descr,tmp1_reg,tmp_reg,cur_dma_ptr; u32 dma_ram_desc=chan->logic_ch_num*4 + AFT_PORT_REG(card,AFT_DMA_CHAIN_RAM_BASE_REG); - + card->hw_iface.bus_read_4(card->hw,dma_ram_desc,&tmp_reg); cur_dma_ptr=aft_dmachain_get_rx_dma_addr(tmp_reg); - + dma_descr=(chan->logic_ch_num<<4) + cur_dma_ptr*AFT_DMA_INDEX_OFFSET + - AFT_PORT_REG(card,AFT_RX_DMA_HI_DESCR_BASE_REG); - - card->hw_iface.bus_read_4(card->hw,dma_descr, &tmp_reg); - card->hw_iface.bus_read_4(card->hw,(dma_descr-4), &tmp1_reg); - - + AFT_PORT_REG(card,AFT_RX_DMA_HI_DESCR_BASE_REG); + + card->hw_iface.bus_read_4(card->hw,dma_descr, &tmp_reg); + card->hw_iface.bus_read_4(card->hw,(dma_descr-4), &tmp1_reg); + + if (wan_test_bit(AFT_RXDMA_HI_GO_BIT,&tmp_reg)){ DEBUG_EVENT("%s: Rx Fifo Go Bit Set DMA=%d Addr=0x%X : HI=0x%08X LO=0x%08X OLO=0x%08X Cfg=0x%08X!\n", card->devname, @@ -6005,45 +6176,52 @@ static void __wp_aft_fifo_per_port_isr(sdla_t *card, u32 rx_status, u32 tx_statu chan->rx_dma_chain_table[chan->rx_chain_indx].dma_addr, 0); } - - DEBUG_EVENT("%s:%s: Warning RX Fifo Error on Ch=%ld End=%d Cur=%d: Reg=0x%X Addr=0x%X!\n", - card->devname,chan->if_name,chan->logic_ch_num, + + DEBUG_ERROR("%s:%s: Warning RX Fifo Error on Ch=%ld End=%d Cur=%d: Reg=0x%X Addr=0x%X!\n", + card->devname,chan->if_name,chan->logic_ch_num, chan->rx_chain_indx,cur_dma_ptr,tmp_reg,dma_descr); -} + } #if 0 + if (0) { aft_display_chain_history(chan); aft_list_descriptors(chan); -#endif -#endif - fifo++; - top_chan=wan_netif_priv(chan->common.dev); - - WAN_NETIF_STATS_INC_RX_FIFO_ERRORS(&chan->common); //++chan->if_stats.rx_fifo_errors; - WAN_NETIF_STATS_INC_RX_OVER_ERRORS(&chan->common); //++chan->if_stats.rx_over_errors; - WP_AFT_CHAN_ERROR_STATS(chan->chan_stats,rx_fifo_errors); - WP_AFT_CHAN_ERROR_STATS(chan->chan_stats,rx_over_errors); - chan->errstats.Rx_overrun_err_count++; - card->wandev.stats.rx_over_errors++; - - aft_rx_fifo_over_recover(card,chan); - wan_set_bit(WP_FIFO_ERROR_BIT, &chan->pkt_error); - - __sdla_bus_read_4(card->hw,AFT_PORT_REG(card,AFT_RX_FIFO_INTR_PENDING_REG),&tmp_fifo_reg); -#if 0 - /* Debuging Code used to stop the line in - * case of fifo errors */ - aft_list_descriptors(chan); - - aft_critical_trigger(card); -#endif } +#endif +#endif + rx_fifo++; + top_chan=wan_netif_priv(chan->common.dev); + if (top_chan == chan) { + skip_rx_top_chan++; + } + + WAN_NETIF_STATS_INC_RX_FIFO_ERRORS(&chan->common); //++chan->if_stats.rx_fifo_errors; + WAN_NETIF_STATS_INC_RX_OVER_ERRORS(&chan->common); //++chan->if_stats.rx_over_errors; + WP_AFT_CHAN_ERROR_STATS(chan->chan_stats,rx_fifo_errors); + WP_AFT_CHAN_ERROR_STATS(chan->chan_stats,rx_over_errors); + chan->errstats.Rx_overrun_err_count++; + card->wandev.stats.rx_over_errors++; + + aft_rx_fifo_over_recover(card,chan); + wan_set_bit(WP_FIFO_ERROR_BIT, &chan->pkt_error); + + __sdla_bus_read_4(card->hw,AFT_PORT_REG(card,AFT_RX_FIFO_INTR_PENDING_REG),&tmp_fifo_reg); +#if 0 + /* Debuging Code used to stop the line in + * case of fifo errors */ + aft_list_descriptors(chan); + + aft_critical_trigger(card); +#endif } + } - - if (fifo) { - if (card->u.aft.global_tdm_irq) { - if (top_chan) { + if (tx_fifo || rx_fifo) { + if (card->u.aft.global_tdm_irq) { + if (top_chan) { + if (tx_fifo && !skip_tx_top_chan) { + WAN_NETIF_STATS_INC_TX_FIFO_ERRORS(&top_chan->common); //++chan->if_stats.tx_fifo_errors; + } else if (rx_fifo && !skip_rx_top_chan) { WAN_NETIF_STATS_INC_RX_FIFO_ERRORS(&top_chan->common); //++chan->if_stats.rx_fifo_errors; WAN_NETIF_STATS_INC_RX_OVER_ERRORS(&top_chan->common); //++chan->if_stats.rx_over_errors; } @@ -6051,13 +6229,14 @@ static void __wp_aft_fifo_per_port_isr(sdla_t *card, u32 rx_status, u32 tx_statu } } - return; + return irq_handled; } -static void wp_aft_fifo_per_port_isr(sdla_t *card) +static int wp_aft_fifo_per_port_isr(sdla_t *card) { u32 rx_status=0, tx_status=0; u32 i; + int irq=0; /* Clear HDLC pending registers */ __sdla_bus_read_4(card->hw, AFT_PORT_REG(card,AFT_TX_FIFO_INTR_PENDING_REG),&tx_status); @@ -6071,14 +6250,14 @@ static void wp_aft_fifo_per_port_isr(sdla_t *card) tmp_card=(sdla_t*)card_list[i]; if (tmp_card && !wan_test_bit(CARD_DOWN,&tmp_card->wandev.critical)) { - __wp_aft_fifo_per_port_isr(tmp_card,rx_status,tx_status); + irq += __wp_aft_fifo_per_port_isr(tmp_card,rx_status,tx_status); } } } else { - __wp_aft_fifo_per_port_isr(card,rx_status,tx_status); + irq=__wp_aft_fifo_per_port_isr(card,rx_status,tx_status); } - return; + return irq; } static sdla_t * aft_find_first_card_in_list(sdla_t *card, int type) @@ -6133,7 +6312,7 @@ static sdla_t * aft_find_first_card_in_list(sdla_t *card, int type) } -static int aft_is_first_card_in_list(sdla_t *card, int type) +static int aft_is_first_card_in_list(sdla_t *card, int type, int fe_isr) { void **card_list; u32 max_number_of_ports, i; @@ -6145,7 +6324,7 @@ static int aft_is_first_card_in_list(sdla_t *card, int type) } else { max_number_of_ports = 8; /* 24 */ } - + card_list=__sdla_get_ptr_isr_array(card->hw); DEBUG_TEST("%s(): card_list ptr: 0x%p\n", __FUNCTION__, card_list); @@ -6178,6 +6357,18 @@ static int aft_is_first_card_in_list(sdla_t *card, int type) break; } + if (fe_isr) { + /* Look for cards that have fe isr enabled */ + if (tmp_card->fe_no_intr) { + /* Ignore cards that are not suppose to generate front end interrupts + For now those are Analog cards */ + continue; + } + } + + DEBUG_TEST("%s(): card_list card %s\n", __FUNCTION__, + tmp_card->devname); + /* check if first valid card in the list matches give card ptr */ if (tmp_card == card) { return 0; @@ -6195,10 +6386,6 @@ static void front_end_interrupt(sdla_t *card, unsigned long reg, int lock) u32 max_number_of_ports, i; sdla_t *tmp_card; - if(IS_FXOFXS_CARD(card)){ - DEBUG_EVENT("%s(): %s: Error: Front End Interrupt on Analog card! Must never happen!\n", __FUNCTION__, card->devname); - return; - } if (IS_BRI_CARD(card)) { max_number_of_ports = MAX_BRI_LINES; /* 24 */ @@ -6217,6 +6404,11 @@ static void front_end_interrupt(sdla_t *card, unsigned long reg, int lock) continue; } + if (tmp_card->fe_no_intr) { + /* Skip cards that i gnore front end interrupts */ + continue; + } + DEBUG_TEST("%s(): card ptr: %s, tmp_card ptr: %s\n", __FUNCTION__, card->devname, tmp_card->devname); if (tmp_card->wandev.fe_iface.check_isr && @@ -6252,8 +6444,6 @@ static int gdma_cnt=0; #define EC_IRQ_TIMEOUT (HZ/32) -int dydbg_tmp_cnt = 0; - static WAN_IRQ_RETVAL wp_aft_global_isr (sdla_t* card) { u32 reg_sec=0,reg=0; @@ -6267,6 +6457,7 @@ static WAN_IRQ_RETVAL wp_aft_global_isr (sdla_t* card) u32 fe_intr=0; u32 max_ports=IS_BRI_CARD(card)?MAX_BRI_LINES:8; u8 check_fe_isr=0; + u8 handle_dma=1; WAN_IRQ_RETVAL_DECL(irq_ret); @@ -6276,13 +6467,10 @@ static WAN_IRQ_RETVAL wp_aft_global_isr (sdla_t* card) WAN_IRQ_RETURN(irq_ret); } -#ifdef WANPIPE_PERFORMANCE_DEBUG - aft_timing_start(card); -#endif -#ifdef AFT_IRQ_STAT_DEBUG - card->wandev.stats.rx_errors++; -#endif + AFT_PERFT_TIMING_START(card,aft_isr_latency); + + AFT_PERF_STAT_INC(card,isr,all); #ifdef AFT_IRQ_DEBUG card->wandev.stats.rx_packets++; @@ -6311,24 +6499,17 @@ static WAN_IRQ_RETVAL wp_aft_global_isr (sdla_t* card) if (wan_test_bit(AFT_CHIPCFG_FE_INTR_STAT_BIT,®)){ -#ifdef WANPIPE_PERFORMANCE_DEBUG - card->wandev.stats.tx_aborted_errors++; -#endif - -#ifdef AFT_IRQ_STAT_DEBUG - card->wandev.stats.rx_dropped++; -#endif + AFT_PERF_STAT_INC(card,isr,fe); if (wan_test_bit(AFT_CHIPCFG_FE_INTR_CFG_BIT,®)) { - DEBUG_ISR("%s: Got Front End Interrupt 0x%08X\n", - card->devname,reg); + DEBUG_ISR("%s: Got Front End Interrupt 0x%08X fe_no_intr=%i\n", + card->devname,reg,card->fe_no_intr); WAN_IRQ_RETVAL_SET(irq_ret, WAN_IRQ_HANDLED); -#ifdef AFT_IRQ_STAT_DEBUG - card->wandev.stats.tx_dropped++; -#endif + AFT_PERF_STAT_INC(card,isr,fe_run); + fe_intr=1; #if defined (__LINUX__) || defined(__FreeBSD__) || defined(__WINDOWS__) @@ -6337,7 +6518,7 @@ static WAN_IRQ_RETVAL wp_aft_global_isr (sdla_t* card) In the list. This way we do not get into a race condition between port task on wanpipe1 versus trigger from wanpipe2. Since Front end interrupt is global interrupt per card */ - if (aft_is_first_card_in_list(card, AFT_CARD_TYPE_ALL) == 0) { + if (aft_is_first_card_in_list(card, AFT_CARD_TYPE_ALL, 1) == 0) { wan_set_bit(AFT_FE_INTR,&card->u.aft.port_task_cmd); WAN_TASKQ_SCHEDULE((&card->u.aft.port_task)); @@ -6347,6 +6528,9 @@ static WAN_IRQ_RETVAL wp_aft_global_isr (sdla_t* card) /* NC: Bug fix we must update the reg value */ __sdla_bus_read_4(card->hw,AFT_PORT_REG(card,AFT_CHIP_CFG_REG), ®); + + DEBUG_TEST("%s: Launching Front End Interrupt\n", + card->devname); } } #else @@ -6359,7 +6543,7 @@ static WAN_IRQ_RETVAL wp_aft_global_isr (sdla_t* card) #endif #endif - } else if (aft_is_first_card_in_list(card,AFT_CARD_TYPE_ALL) == 0 && !card->fe_no_intr) { + } else if (aft_is_first_card_in_list(card,AFT_CARD_TYPE_ALL, 1) == 0 && !card->fe_no_intr) { DEBUG_TEST("%s: Got Front end interrupt but MASK is not set!\n",card->devname); check_fe_isr=1; } @@ -6370,7 +6554,7 @@ static WAN_IRQ_RETVAL wp_aft_global_isr (sdla_t* card) if (check_fe_isr && !card->fe_no_intr) { if (!wan_test_bit(AFT_CHIPCFG_FE_INTR_CFG_BIT,®)) { - if (aft_is_first_card_in_list(card,AFT_CARD_TYPE_ALL) == 0) { + if (aft_is_first_card_in_list(card,AFT_CARD_TYPE_ALL, 1) == 0) { if ((SYSTEM_TICKS - card->front_end_irq_timeout) > HZ) { card->front_end_irq_timeout=SYSTEM_TICKS; __aft_fe_intr_ctrl(card,1); @@ -6395,7 +6579,11 @@ static WAN_IRQ_RETVAL wp_aft_global_isr (sdla_t* card) } #endif - if (card->u.aft.firm_id == AFT_DS_FE_CORE_ID || IS_BRI_CARD(card) || IS_A600_CARD(card) || IS_A700_CARD(card)) { + if (card->u.aft.firm_id == AFT_DS_FE_CORE_ID || + IS_BRI_CARD(card) || + IS_A600_CARD(card) || + IS_B601_CARD(card) || + IS_A700_CARD(card)) { __sdla_bus_read_4(card->hw,AFT_PORT_REG(card, AFT_CHIP_STAT_REG), &a108_reg); @@ -6404,7 +6592,7 @@ static WAN_IRQ_RETVAL wp_aft_global_isr (sdla_t* card) wdt_port_intr = aft_chipcfg_a108_get_wdt_intr_stats(a108_reg); tdmv_port_intr = aft_chipcfg_a108_get_tdmv_intr_stats(a108_reg); - if (!IS_A700_CARD(card)) { + if (!IS_A700_CARD(card) && !IS_B601_CARD(card)) { if (tdmv_port_intr) { tdmv_port_intr=1; } @@ -6454,9 +6642,9 @@ if (serial_reg) { #ifdef DEBUG_CNT if (1){ if (gcnt < 50) { - DEBUG_EVENT("%s: ISR: REG=0x%08X TDM=0x%08X DMA=0x%08X FIFO=0x%08X STAT=0x%08X WDT=0x%08X FREE=0x%08X\n", - card->devname,reg_sec, tdmv_port_intr,dma_port_intr,fifo_port_intr, - status_port_intr,wdt_port_intr,free_run_intr); + DEBUG_EVENT("%s: ISR: REG=0x%08X TDM=0x%08X DMA=0x%08X FIFO=0x%08X STAT=0x%08X WDT=0x%08X FREE=0x%08X\n", + card->devname,reg_sec, tdmv_port_intr,dma_port_intr,fifo_port_intr, + status_port_intr,wdt_port_intr,free_run_intr); } } #endif @@ -6468,29 +6656,42 @@ if (1){ status_port_intr || wdt_port_intr || free_port_intr) { -#ifdef WANPIPE_PERFORMANCE_DEBUG -#warning "KNOWN IRQ DEBUGGING Enabled" - card->wandev.stats.rx_dropped++; + +#if defined(WANPIPE_PERFORMANCE_DEBUG) + AFT_PERF_STAT_INC(card,isr,aft); + if (tdmv_port_intr) { - card->wandev.stats.multicast++; + AFT_PERF_STAT_INC(card,isr,tdm); } + + if (dma_port_intr) { - card->wandev.stats.rx_crc_errors++; + AFT_PERF_STAT_INC(card,isr,dma); } if (wdt_port_intr) { - card->wandev.stats.collisions++; + AFT_PERF_STAT_INC(card,isr,wdt); } if (free_port_intr) { - card->wandev.stats.rx_fifo_errors++; + AFT_PERF_STAT_INC(card,isr,free_run); } + if (status_port_intr) { + AFT_PERF_STAT_INC(card,isr,serial); + } + +#if 0 if (SYSTEM_TICKS - card->debug_timeout >= HZ) { card->debug_timeout=SYSTEM_TICKS; card->wandev.stats.tx_dropped=card->wandev.stats.rx_dropped; card->wandev.stats.rx_dropped=0; } +#endif #endif /* Pass Through */ } else { + + if (!fe_intr) { + AFT_PERF_STAT_INC(card,isr,non_aft); + } #if 0 #warning "UNKONW IRQ DEBUGGING Enabled" card->wandev.stats.rx_dropped++; @@ -6507,18 +6708,24 @@ if (1){ if (wan_test_bit(AFT_LCFG_FIFO_INTR_BIT,&card->u.aft.lcfg_reg) && wan_test_bit(card->wandev.comm_port,&fifo_port_intr)){ -#ifdef AFT_IRQ_STAT_DEBUG - card->wandev.stats.multicast++; -#endif - wp_aft_fifo_per_port_isr(card); - WAN_IRQ_RETVAL_SET(irq_ret, WAN_IRQ_HANDLED); + int irq_handled=wp_aft_fifo_per_port_isr(card); + AFT_PERF_STAT_INC(card,isr,fifo); + /* Only ack the interrupt if true fifo error occoured */ + if (irq_handled) { + WAN_IRQ_RETVAL_SET(irq_ret, WAN_IRQ_HANDLED); + } + } #if 1 if (wan_test_bit(AFT_LCFG_DMA_INTR_BIT,&card->u.aft.lcfg_reg) && wan_test_bit(card->wandev.comm_port,&dma_port_intr)) { - int handle_dma=1; + + handle_dma=1; + + /* This is our interrupt ack it */ + WAN_IRQ_RETVAL_SET(irq_ret, WAN_IRQ_HANDLED); #if !defined(__WINDOWS__) /* Skip the dma per port and run it in loop below */ @@ -6534,7 +6741,6 @@ if (1){ #endif if (handle_dma) { - WAN_IRQ_RETVAL_SET(irq_ret, WAN_IRQ_HANDLED); wp_aft_dma_per_port_isr(card,tdmv_port_intr); } } @@ -6546,6 +6752,7 @@ if (1){ if (wan_test_bit(AFT_TDM_GLOBAL_ISR,&card->u.aft.chip_cfg_status)) { + if (tdmv_port_intr && card->u.aft.global_tdm_irq && !wan_test_bit(aft_chipcfg_get_fifo_reset_bit(card),®)) { @@ -6559,10 +6766,13 @@ if (1){ WAN_IRQ_RETVAL_SET(irq_ret, WAN_IRQ_HANDLED); -#ifdef AFT_IRQ_STAT_DEBUG - card->wandev.stats.rx_crc_errors++; -#endif +#if 0 +#warning "Nenad: Unit Testing Code" + /* Nenad; Unite Testing */ + if (ring_buf_enabled && card->wp_debug_gen_fifo_err_tx == 0) { +#else if (ring_buf_enabled) { +#endif /* NC: Bug fix we must read before writting, reg might have changed above */ __sdla_bus_read_4(card->hw,AFT_PORT_REG(card,AFT_CHIP_CFG_REG), ®); if (card->adptr_type == A104_ADPTR_4TE1 && @@ -6593,9 +6803,7 @@ if (1){ continue; } -#ifdef AFT_IRQ_STAT_DEBUG - tmp_card->wandev.stats.rx_crc_errors++; -#endif + AFT_PERF_STAT_INC(tmp_card,isr,tdm_run); if (IS_A700_CARD(tmp_card)) { if (!wan_test_bit(tmp_card->wandev.comm_port,&tdmv_port_intr)) { @@ -6730,9 +6938,8 @@ if (1){ WAN_IRQ_RETVAL_SET(irq_ret, WAN_IRQ_HANDLED); -#ifdef AFT_IRQ_STAT_DEBUG - card->wandev.stats.rx_crc_errors++; -#endif + AFT_PERF_STAT_INC(card,isr,tdm_run); + #if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE) if (card->wan_tdmv.sc && @@ -6757,10 +6964,14 @@ if (1){ } if (status_port_intr) { + WAN_IRQ_RETVAL_SET(irq_ret, WAN_IRQ_HANDLED); wp_aft_serial_status_isr(card, status_port_intr); } if (free_port_intr) { + AFT_PERFT_TIMING_STOP_AND_CALC(card,kernel_isr_latency); + AFT_PERFT_TIMING_START(card,kernel_isr_latency); + WAN_IRQ_RETVAL_SET(irq_ret, WAN_IRQ_HANDLED); wp_aft_free_timer_status_isr(card, free_port_intr); } @@ -6768,9 +6979,6 @@ if (1){ WAN_IRQ_RETVAL_SET(irq_ret, WAN_IRQ_HANDLED); wp_aft_wdt_per_port_isr(card,1); card->u.aft.wdt_tx_cnt=SYSTEM_TICKS; -#ifdef AFT_IRQ_STAT_DEBUG - card->wandev.stats.rx_fifo_errors++; -#endif } #ifdef AFT_WDT_ENABLE @@ -6778,9 +6986,8 @@ if (1){ SYSTEM_TICKS-card->u.aft.wdt_tx_cnt > (HZ>>2)){ wp_aft_wdt_per_port_isr(card,0); card->u.aft.wdt_tx_cnt=SYSTEM_TICKS; -#ifdef AFT_IRQ_STAT_DEBUG - card->wandev.stats.tx_aborted_errors++; -#endif + + AFT_PERF_STAT_INC(card,isr,wdt_software); } #endif @@ -6795,9 +7002,9 @@ if (1){ if (wan_test_bit(AFT_CHIPCFG_SECURITY_STAT_BIT,®)){ WAN_IRQ_RETVAL_SET(irq_ret, WAN_IRQ_HANDLED); if (++card->u.aft.chip_security_cnt > AFT_MAX_CHIP_SECURITY_CNT){ - DEBUG_EVENT("%s: Critical: AFT Chip Security Compromised: Disabling Driver!(%08X)\n", + DEBUG_ERROR("%s: Critical: AFT Chip Security Compromised: Disabling Driver!(%08X)\n", card->devname, reg); - DEBUG_EVENT("%s: Please call Sangoma Tech Support (www.sangoma.com)!\n", + DEBUG_ERROR("%s: Please call Sangoma Tech Support (www.sangoma.com)!\n", card->devname); aft_critical_trigger(card); @@ -6806,38 +7013,86 @@ if (1){ } else if (aft_hwdev[card->wandev.card_type].aft_check_ec_security(card)){ WAN_IRQ_RETVAL_SET(irq_ret, WAN_IRQ_HANDLED); if (++card->u.aft.chip_security_cnt > AFT_MAX_CHIP_SECURITY_CNT){ - DEBUG_EVENT("%s: Critical: Echo Canceller Chip Security Compromised: Disabling Driver!\n", + DEBUG_ERROR("%s: Critical: Echo Canceller Chip Security Compromised: Disabling Driver!\n", card->devname); - DEBUG_EVENT("%s: Please call Sangoma Tech Support (www.sangoma.com)!\n", + DEBUG_ERROR("%s: Please call Sangoma Tech Support (www.sangoma.com)!\n", card->devname); card->u.aft.chip_security_cnt=0; aft_critical_trigger(card); } - } else if (card->u.aft.firm_id == AFT_DS_FE_CORE_ID && - card->wandev.state == WAN_CONNECTED && - SYSTEM_TICKS-card->u.aft.sec_chk_cnt > HZ) { - - u32 lcfg_reg; + } else if (card->wandev.state == WAN_CONNECTED && + SYSTEM_TICKS-card->u.aft.sec_chk_cnt > (HZ/50)) { + card->u.aft.sec_chk_cnt=SYSTEM_TICKS; - __sdla_bus_read_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG), &lcfg_reg); - card->u.aft.lcfg_reg=lcfg_reg; - if (wan_test_bit(AFT_LCFG_TX_FE_SYNC_STAT_BIT,&lcfg_reg) || - wan_test_bit(AFT_LCFG_RX_FE_SYNC_STAT_BIT,&lcfg_reg)){ - if (++card->u.aft.chip_security_cnt > AFT_MAX_CHIP_SECURITY_CNT){ - DEBUG_EVENT("%s: Critical: A108 Lost Sync with Front End: Disabling Driver (0x%08X : A108S=0x%08X)!\n", - card->devname, - lcfg_reg,a108_reg); - DEBUG_EVENT("%s: Please call Sangoma Tech Support (www.sangoma.com)!\n", - card->devname); - aft_critical_trigger(card); + if (card->u.aft.firm_id == AFT_DS_FE_CORE_ID) { + u32 lcfg_reg; + __sdla_bus_read_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG), &lcfg_reg); + card->u.aft.lcfg_reg=lcfg_reg; + + if (wan_test_bit(AFT_LCFG_TX_FE_SYNC_STAT_BIT,&lcfg_reg) || + wan_test_bit(AFT_LCFG_RX_FE_SYNC_STAT_BIT,&lcfg_reg)){ + if (++card->u.aft.chip_security_cnt > AFT_MAX_CHIP_SECURITY_CNT){ + DEBUG_ERROR("%s: Critical: A108 Lost Sync with Front End: Disabling Driver (0x%08X : A108S=0x%08X)!\n", + card->devname, + lcfg_reg,a108_reg); + DEBUG_ERROR("%s: Please call Sangoma Tech Support (www.sangoma.com)!\n", + card->devname); + + aft_critical_trigger(card); + } + } else { + card->u.aft.chip_security_cnt=0; + + if (wan_test_bit(AFT_TDM_FE_SYNC_CNT,&card->u.aft.chip_cfg_status) && + !aft_fe_loop_back_status(card)) { + u32 sync_cnt = aft_lcfg_get_fe_sync_cnt(lcfg_reg); + if (sync_cnt) { + DEBUG_ERROR("%s: Warning: Front End Lost Synchronization (sync_cnt=%i)\n", + card->devname,sync_cnt); + + aft_lcfg_set_fe_sync_cnt(&lcfg_reg,0); + __sdla_bus_write_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG), lcfg_reg); + __sdla_bus_read_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG), &lcfg_reg); + card->u.aft.lcfg_reg=lcfg_reg; + + if (!wan_test_bit(AFT_FE_RESTART,&card->u.aft.port_task_cmd)) { + if (!wan_test_bit(CARD_PORT_TASK_DOWN,&card->wandev.critical)){ + wan_set_bit(AFT_FE_RESTART,&card->u.aft.port_task_cmd); + WAN_TASKQ_SCHEDULE((&card->u.aft.port_task)); + } + } + } + } } - } else { - card->u.aft.chip_security_cnt=0; } + +#if 0 +#warning "Nenad: Unit Testing Code" + /* Nenad: Unit testing code */ + if (card->wp_rx_fifo_sanity || card->wp_tx_fifo_sanity) { + DEBUG_ERROR("%s: Warning: Excessive Fifo Errors: Resync (rx=%i/tx=%i) (max=%i slots=%i) \n", + card->devname, card->wp_rx_fifo_sanity,card->wp_tx_fifo_sanity, WP_RX_TX_FIFO_SANITY,card->u.aft.num_of_time_slots); + } +#else + if (card->wp_rx_fifo_sanity > WP_RX_TX_FIFO_SANITY || card->wp_tx_fifo_sanity > WP_RX_TX_FIFO_SANITY) { + DEBUG_ERROR("%s: Warning: Excessive Fifo Errors: Resync (rx=%i/tx=%i)\n", + card->devname, card->wp_rx_fifo_sanity,card->wp_tx_fifo_sanity); + if (!wan_test_bit(AFT_FE_RESTART,&card->u.aft.port_task_cmd)) { + if (!wan_test_bit(CARD_PORT_TASK_DOWN,&card->wandev.critical)){ + wan_set_bit(AFT_FE_RESTART,&card->u.aft.port_task_cmd); + WAN_TASKQ_SCHEDULE((&card->u.aft.port_task)); + } + } + } +#endif + + card->wp_rx_fifo_sanity=0; + card->wp_tx_fifo_sanity=0; + } else { card->u.aft.chip_security_cnt=0; } @@ -6848,9 +7103,7 @@ if (1){ aft_global_isr_exit: -#ifdef WANPIPE_PERFORMANCE_DEBUG - aft_timing_stop_calculate_elapsed(card); -#endif + AFT_PERFT_TIMING_STOP_AND_CALC(card,aft_isr_latency); wan_clear_bit(0,&card->in_isr); WAN_IRQ_RETURN(irq_ret); @@ -6952,58 +7205,54 @@ static void wp_aft_serial_status_isr(sdla_t *card, u32 serial_intr_status) static void __wp_aft_per_per_port_isr(sdla_t *card, u32 dma_rx_reg, u32 dma_tx_reg, int tdm) { - private_area_t *chan; - u32 dma_tx_voice=0; - int i; + private_area_t *chan=NULL; + int i, chan_valid=0; #if 0 int timer_wakeup=0; #endif - dma_rx_reg&=card->u.aft.active_ch_map; + dma_rx_reg &= card->u.aft.active_ch_map; dma_rx_reg &= card->u.aft.logic_ch_map; dma_rx_reg &= ~(card->u.aft.tdm_logic_ch_map); - if (!dma_rx_reg) { - goto isr_skb_rx; + dma_tx_reg &= card->u.aft.active_ch_map; + dma_tx_reg &= card->u.aft.logic_ch_map; + dma_tx_reg &= ~(card->u.aft.tdm_logic_ch_map); + + if (!dma_rx_reg && !dma_tx_reg) { + return; } +#ifdef WANPIPE_PERFORMANCE_DEBUG + if (dma_rx_reg) { + AFT_PERF_STAT_INC(card,isr,dma_rx); + AFT_PERFT_TIMING_STOP_AND_CALC(card,kernel_isr_latency); + AFT_PERFT_TIMING_START(card,kernel_isr_latency); + } + + if (dma_tx_reg) { + AFT_PERF_STAT_INC(card,isr,dma_tx); + } +#endif + for (i=0; iu.aft.num_of_time_slots;i++){ + chan_valid=0; + if (wan_test_bit(i,&dma_rx_reg)) { chan=(private_area_t*)card->u.aft.dev_to_ch_map[i]; if (!chan){ - DEBUG_EVENT("%s: Error: No Dev for Rx logical ch=%d\n", + DEBUG_ERROR("%s: Error: No Dev for Rx logical ch=%d\n", card->devname,i); continue; } + chan_valid=1; if (!wan_test_bit(WP_DEV_CONFIG,&chan->up)){ continue; } if (chan->channelized_cfg && !chan->hdlc_eng){ -#if 0 - u32 orig_rx,orig_tx; -#endif - wan_set_bit(i,&dma_tx_voice); - -#if 0 - orig_rx=dma_rx_reg&card->u.aft.tdm_logic_ch_map; - orig_tx=dma_tx_reg&card->u.aft.tdm_logic_ch_map; - - if (orig_rx != orig_tx && tdm) { - DEBUG_EVENT("%s: Warning: Chan %i Rx=0x%08X (0x%08X) TX=0x%08X (0x%08X)\n", - chan->if_name, chan->logic_ch_num+1, orig_rx,dma_rx_reg, orig_tx, dma_tx_reg ); - } -#endif - -#if 0 - if (timer_wakeup==0){ - timer_wakeup++; - /* Updated the Timer device based on our DMA */ - wp_timer_device_reg_tick(card); - } -#endif continue; } @@ -7013,71 +7262,52 @@ static void __wp_aft_per_per_port_isr(sdla_t *card, u32 dma_rx_reg, u32 dma_tx_r DEBUG_TEST("%s: RX Interrupt pend. \n", card->devname); - -#if defined(__WINDOWS__) +#if 0 wan_debug_update_timediff(&card->wan_debug_rx_interrupt_timing, __FUNCTION__); #endif aft_dma_rx_complete(card,chan,0); #ifdef AFT_SPAN_SINGLE_IRQ if (chan->wp_api_op_mode && !chan->hdlc_eng) { - wan_set_bit(i,&dma_tx_voice); + wan_clear_bit(i,&dma_tx_reg); aft_dma_tx_complete(card,chan,0,0); } #endif - -#if 0 - if (chan->cfg.tdmv_master_if && !chan->tdmv_irq_cfg){ - aft_channel_rxintr_ctrl(card,chan,1); - DEBUG_EVENT("%s: Master dev %s Synched to master irq\n", - card->devname,chan->if_name); - chan->tdmv_irq_cfg=1; - } -#endif } - } -isr_skb_rx: - - dma_tx_reg&=card->u.aft.active_ch_map; - dma_tx_reg&=~dma_tx_voice; - dma_tx_reg &= card->u.aft.logic_ch_map; - dma_tx_reg &= ~(card->u.aft.tdm_logic_ch_map); - - if (dma_tx_reg == 0){ - goto isr_skb_tx; - } - - for (i=0; iu.aft.num_of_time_slots ;i++){ if (wan_test_bit(i,&dma_tx_reg)) { - chan=(private_area_t*)card->u.aft.dev_to_ch_map[i]; - if (!chan){ - DEBUG_EVENT("%s: Error: No Dev for Tx logical ch=%d\n", - card->devname,i); - continue; - } - - if (chan->channelized_cfg && !chan->hdlc_eng){ - continue; - } - - if (IS_BRI_CARD(card) && chan->dchan_time_slot >= 0){ - continue; + if (!chan_valid) { + chan=(private_area_t*)card->u.aft.dev_to_ch_map[i]; + if (!chan){ + DEBUG_EVENT("%s: Error: No Dev for Rx logical ch=%d\n", + card->devname,i); + continue; + } + chan_valid=1; + + if (!wan_test_bit(WP_DEV_CONFIG,&chan->up)){ + continue; + } + + if (chan->channelized_cfg && !chan->hdlc_eng){ + continue; + } + + if (IS_BRI_CARD(card) && chan->dchan_time_slot >= 0){ + continue; + } } DEBUG_ISR("---- TX Interrupt pend. --\n"); -#if defined(__WINDOWS__) +#if 0 wan_debug_update_timediff(&card->wan_debug_tx_interrupt_timing, __FUNCTION__); #endif aft_dma_tx_complete(card,chan,0,0); } } -isr_skb_tx: - DEBUG_ISR("---- ISR SKB TX end.-------------------\n"); - return; } @@ -7092,7 +7322,7 @@ static void wp_aft_dma_per_port_isr(sdla_t *card, int tdm) #ifdef AFT_IRQ_STAT_DEBUG - card->wandev.stats.rx_length_errors++; + card->wandev.stats.rx_length_errors++; #endif /* Only enable fifo interrupts after a first @@ -7134,19 +7364,6 @@ static void wp_aft_dma_per_port_isr(sdla_t *card, int tdm) #endif - -#ifdef AFT_FIFO_GEN_DEBUGGING -#ifndef __WINDOWS__ -#warning "AFT_FIFO_GEN_DEBUGGING Enabled" -#endif - if (card->wp_debug_gen_fifo_err) { - if (card->wp_debug_gen_fifo_err++ < 10) { - DEBUG_EVENT("%s:FIFO ERROR: Debugging Enabled ... causing fifo error!\n",card->devname); - } - return; - } -#endif - /* Receive DMA Engine */ __sdla_bus_read_4(card->hw,AFT_PORT_REG(card,AFT_RX_DMA_INTR_PENDING_REG),&dma_rx_reg); __sdla_bus_read_4(card->hw,AFT_PORT_REG(card,AFT_TX_DMA_INTR_PENDING_REG),&dma_tx_reg); @@ -7195,7 +7412,7 @@ static void wp_aft_tdmv_per_port_isr(sdla_t *card) chan=(private_area_t*)card->u.aft.dev_to_ch_map[i]; if (!chan){ - DEBUG_EVENT("%s: Error: No Dev for Rx logical ch=%d\n", + DEBUG_ERROR("%s: Error: No Dev for Rx logical ch=%d\n", card->devname,i); continue; } @@ -7441,9 +7658,6 @@ static void port_set_state (sdla_t *card, u8 state) struct wan_dev_le *devle; netdevice_t *dev; - FUNC_NEWDRV(); - - card->wandev.state = (char)state; #if defined(__WINDOWS__) { @@ -7478,8 +7692,6 @@ static void callback_front_end_state(void *card_id) { sdla_t *card = (sdla_t*)card_id; - FUNC_NEWDRV(); - if (wan_test_bit(CARD_DOWN,&card->wandev.critical)){ return; } @@ -7494,6 +7706,29 @@ static void callback_front_end_state(void *card_id) } +static void callback_front_end_reset(void *card_id) +{ + sdla_t *card = (sdla_t*)card_id; + + if (wan_test_bit(CARD_DOWN,&card->wandev.critical)){ + return; + } + + if (wan_test_bit(AFT_TDM_FE_SYNC_CNT,&card->u.aft.chip_cfg_status)) { + return; + } + + DEBUG_EVENT("%s: Warning: Front End Lost Synchronization\n", + card->devname); + + /* Call the poll task to update the state */ + if (!wan_test_bit(CARD_PORT_TASK_DOWN,&card->wandev.critical)){ + wan_set_bit(AFT_FE_RESTART,&card->u.aft.port_task_cmd); + WAN_TASKQ_SCHEDULE((&card->u.aft.port_task)); + } +} + + /*============================================================ * handle_front_end_state * @@ -7601,6 +7836,10 @@ static void handle_front_end_state(void *card_id,int lock) aft_handle_clock_master(card); +#ifdef SPAN_TIMING_DEBUGGING + //aft_free_running_timer_disable(card); +#endif + port_set_state(card,WAN_CONNECTED); if (!wan_test_bit(CARD_PORT_TASK_DOWN,&card->wandev.critical)){ @@ -7650,6 +7889,9 @@ static void handle_front_end_state(void *card_id,int lock) aft_handle_clock_master(card); +#ifdef SPAN_TIMING_DEBUGGING + aft_free_running_timer_set_enable(card,10); +#endif } } } @@ -7744,18 +7986,25 @@ static void enable_data_error_intr(sdla_t *card) card->hw_iface.bus_read_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG), ®); if (wan_test_bit(AFT_LCFG_FE_IFACE_RESET_BIT,®)){ - DEBUG_EVENT("%s: Warning: Skipping data enable wait for cfg!\n", + DEBUG_WARNING("%s: Warning: Skipping data enable wait for cfg!\n", card->devname); return; } + +#ifdef SPAN_TIMING_DEBUGGING + wan_set_bit(0,&card->u.aft.comm_enabled); + DEBUG_EVENT("%s: SPAN MODE Debugging: not enabling comms!\n",card->devname); + return; +#endif + #if 0 aft_list_dma_chain_regs(card); #endif if (card->u.aft.global_tdm_irq && !wan_test_bit(0,&card->u.aft.tdmv_master_if_up)){ - DEBUG_EVENT("%s: Critical error: Enable Card while Master If Not up!\n", + DEBUG_ERROR("%s: Critical error: Enable Card while Master If Not up!\n", card->devname); } @@ -7779,7 +8028,7 @@ static void enable_data_error_intr(sdla_t *card) err=aft_hwdev[card->wandev.card_type].aft_test_sync(card,0); if (err){ card->hw_iface.bus_read_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG), ®); - DEBUG_EVENT("%s: Error: Front End Interface out of sync! (0x%X)\n", + DEBUG_ERROR("%s: Error: Front End Interface out of sync! (0x%X)\n", card->devname,reg); /*FIXME: How to recover from here, should never happen */ } @@ -7790,6 +8039,13 @@ static void enable_data_error_intr(sdla_t *card) card->hw_iface.bus_write_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG), reg); } + + if (wan_test_bit(AFT_TDM_FE_SYNC_CNT,&card->u.aft.chip_cfg_status)) { + card->hw_iface.bus_read_4(card->hw, AFT_PORT_REG(card,AFT_LINE_CFG_REG), ®); + aft_lcfg_set_fe_sync_cnt(®,0); + card->hw_iface.bus_write_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG), reg); + } + for (i=0; iu.aft.num_of_time_slots;i++){ private_area_t *chan; @@ -7965,6 +8221,8 @@ static void enable_data_error_intr(sdla_t *card) /* Add 2 idle buffers into channel */ aft_dma_tx(card,chan); } + } else { + aft_dma_tx(card,chan); } if (chan->cfg.ss7_enable){ @@ -8115,8 +8373,11 @@ static void disable_data_error_intr(sdla_t *card, unsigned char event) static void aft_rx_fifo_over_recover(sdla_t *card, private_area_t *chan) { -#ifdef AFT_FIFO_GEN_DEBUGGING - card->wp_debug_gen_fifo_err=0; +#ifdef AFT_FIFO_GEN_DEBUGGING_RX + if (card->wp_debug_gen_fifo_err_rx) { + card->wp_debug_gen_fifo_err_rx=0; + DEBUG_EVENT("%s:FIFO RECOVERY: RX FIFO !\n",card->devname); + } #endif /* Igore fifo errors in transpared mode. There is nothing @@ -8177,8 +8438,19 @@ static void aft_rx_fifo_over_recover(sdla_t *card, private_area_t *chan) wanpipe_wake_stack(chan); } -static void aft_tx_fifo_under_recover (sdla_t *card, private_area_t *chan) -{ +void aft_tx_fifo_under_recover (sdla_t *card, private_area_t *chan) +{ + + + +#ifdef AFT_FIFO_GEN_DEBUGGING_TX + if (card->wp_debug_gen_fifo_err_tx) { + card->wp_debug_gen_fifo_err_tx=0; + DEBUG_EVENT("%s:FIFO RECOVERY: TX FIFO !\n",card->devname); + } +#endif + + /* Igore fifo errors in transpared channalized mode. There is nothing that we can do to make it better. */ if (chan->channelized_cfg && !chan->hdlc_eng){ @@ -8195,12 +8467,23 @@ static void aft_tx_fifo_under_recover (sdla_t *card, private_area_t *chan) return; } + /* If running in mixed mode DATA + Voice do not disable DMA */ + if (chan->hdlc_eng && card->u.aft.global_tdm_irq) { + aft_dma_tx_complete(card,chan,0, 1); + aft_free_tx_descriptors(chan); + aft_dma_tx(card,chan); + wanpipe_wake_stack(chan); + return; + } + + #if 0 if (WAN_NET_RATELIMIT()){ DEBUG_EVENT("%s:%s Tx Fifo Recovery!\n", card->devname,chan->if_name); } #endif + /* Enable DMA controler, in order to start the * fifo cleaning */ @@ -8232,7 +8515,7 @@ static int set_chan_state(sdla_t* card, netdevice_t* dev, u8 state) wan_smp_flag_t flags; #endif - FUNC_NEWDRV(); + if (!chan){ if (WAN_NET_RATELIMIT()){ @@ -8332,15 +8615,12 @@ static int set_chan_state(sdla_t* card, netdevice_t* dev, u8 state) #endif if (chan->common.usedby == STACK){ -#if defined(__WINDOWS__) - FUNC_NOT_IMPL(); -#else + if (state == WAN_CONNECTED){ wanpipe_lip_connect(chan,0); }else{ wanpipe_lip_disconnect(chan,0); } -#endif } #if defined(NETGRAPH) @@ -8362,7 +8642,7 @@ static int set_chan_state(sdla_t* card, netdevice_t* dev, u8 state) #if 0 wan_spin_unlock_irq(&card->wandev.lock,&irq_flags); #endif - FUNC_NEWDRV(); + return 0; } @@ -8389,7 +8669,7 @@ static void aft_tx_dma_voice_handler(private_area_t *chan, int wdt, int reset) wan_dma_descr_t *dma_chain; if (wan_test_and_set_bit(TX_HANDLER_BUSY,&chan->dma_status)){ - DEBUG_EVENT("%s: SMP Critical in %s\n", + DEBUG_ERROR("%s: SMP Critical in %s\n", chan->if_name,__FUNCTION__); return; } @@ -8438,14 +8718,14 @@ static void aft_tx_dma_voice_handler(private_area_t *chan, int wdt, int reset) chan->errstats.Tx_pci_errors++; if (wan_test_bit(AFT_TXDMA_HIDMASTATUS_PCI_M_ABRT,&dma_status)){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s:%s: Tx Error: Abort from Master: pci fatal error!\n", + DEBUG_ERROR("%s:%s: Tx Error: Abort from Master: pci fatal error!\n", card->devname,chan->if_name); } } if (wan_test_bit(AFT_TXDMA_HIDMASTATUS_PCI_T_ABRT,&dma_status)){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s:%s: Tx Error: Abort from Target: pci fatal error!\n", + DEBUG_ERROR("%s:%s: Tx Error: Abort from Target: pci fatal error!\n", card->devname,chan->if_name); } } @@ -8457,7 +8737,7 @@ static void aft_tx_dma_voice_handler(private_area_t *chan, int wdt, int reset) } if (wan_test_bit(AFT_TXDMA_HIDMASTATUS_PCI_RETRY,&dma_status)){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s:%s: Tx Error: 'Retry' exceeds maximum (64k): pci fatal error!\n", + DEBUG_ERROR("%s:%s: Tx Error: 'Retry' exceeds maximum (64k): pci fatal error!\n", card->devname,chan->if_name); } } @@ -8495,7 +8775,7 @@ static int aft_tx_dma_chain_diff(private_area_t *chan) DEBUG_EVENT("%s: Tx Chains Curr=%i Pending=%i HW=%i Diff=%i\n", chan->if_name,chan->tx_chain_indx, chan->tx_pending_chain_indx, cur_dma_ptr, - aft_tx_dma_chain_chain_len(chan) + chan->tx_chain_sz ); return 0; @@ -8507,15 +8787,16 @@ static int aft_tx_dma_chain_diff(private_area_t *chan) * aft_tx_dma_chain_handler * */ -static int aft_tx_dma_chain_handler(private_area_t *chan, int wdt, int reset) +static int aft_tx_dma_chain_handler(private_area_t *chan, int wdt, int reset_flag_not_used) { sdla_t *card = chan->card; u32 reg,dma_descr; wan_dma_descr_t *dma_chain; int tx_complete=0; + int idle=0; if (wan_test_and_set_bit(TX_HANDLER_BUSY,&chan->dma_status)){ - DEBUG_EVENT("%s: SMP Critical in %s\n", + DEBUG_ERROR("%s: SMP Critical in %s\n", chan->if_name,__FUNCTION__); return tx_complete; } @@ -8524,6 +8805,7 @@ static int aft_tx_dma_chain_handler(private_area_t *chan, int wdt, int reset) for (;;){ + idle=0; /* If the current DMA chain is in use,then * all chains are busy */ if (!wan_test_bit(0,&dma_chain->init)){ @@ -8565,24 +8847,50 @@ static int aft_tx_dma_chain_handler(private_area_t *chan, int wdt, int reset) } chan->tx_hdlc_rpt_skb=NULL; - } else if (dma_chain->skb){ + } else if(dma_chain->skb == chan->tx_bert_skb) { + + dma_chain->skb=NULL; + + } else if (dma_chain->skb) { + wan_skb_set_csum(dma_chain->skb, reg); wan_skb_queue_tail(&chan->wp_tx_complete_list,dma_chain->skb); dma_chain->skb=NULL; } } else { - if (dma_chain->skb != chan->tx_idle_skb){ - - wan_skb_set_csum(dma_chain->skb, reg); - aft_tx_post_complete(chan->card,chan,dma_chain->skb); - aft_tx_dma_skb_init(chan,dma_chain->skb); + /* the 'tx_bert_skb' check must be first */ + if (dma_chain->skb == chan->tx_bert_skb) { + dma_chain->skb=NULL; + + } else if (dma_chain->skb != chan->tx_idle_skb ) { + + /* make sure buffers not just NOT equal but + * also 'dma_chain->skb' is NOT null */ + if (dma_chain->skb){ + + wan_skb_set_csum(dma_chain->skb, reg); + aft_tx_post_complete(chan->card,chan,dma_chain->skb); + aft_tx_dma_skb_init(chan,dma_chain->skb); + + dma_chain->skb=NULL; + } + } else { + idle=1; } } aft_tx_dma_chain_init(chan,dma_chain); + if (!idle && chan->tx_chain_data_sz > 0) { + chan->tx_chain_data_sz--; + } + + if (chan->tx_chain_sz > 0) { + chan->tx_chain_sz--; + } + if (chan->dma_chain_opmode == WAN_AFT_DMA_CHAIN_SINGLE){ break; } @@ -8612,7 +8920,7 @@ static int aft_dma_chain_tx(wan_dma_descr_t *dma_chain,private_area_t *chan, int { #define dma_descr dma_chain->dma_descr -#define reg dma_chain->reg +#define reg dma_chain->reg #define dma_ch_indx dma_chain->index #define len_align dma_chain->len_align #define card chan->card @@ -8630,9 +8938,16 @@ static int aft_dma_chain_tx(wan_dma_descr_t *dma_chain,private_area_t *chan, int card->hw_iface.bus_read_4(card->hw,dma_descr,®); if (wan_test_bit(AFT_TXDMA_HI_GO_BIT,®)){ +#if 0 + u32 reg_lo,dma_descr_lo; + dma_descr_lo=(chan->logic_ch_num<<4) + (dma_ch_indx*AFT_DMA_INDEX_OFFSET) + + AFT_PORT_REG(card,AFT_TX_DMA_LO_DESCR_BASE_REG); + card->hw_iface.bus_read_4(card->hw,dma_descr_lo,®_lo); /* This can happen during tx fifo error */ - DEBUG_EVENT("%s: Warning: TxDMA GO Ready bit set on dma (chain=0x%X) Desc=0x%X Tx 0x%X\n", - card->devname,dma_descr,dma_ch_indx,reg); + DEBUG_WARNING("%s: Warning: TxDMA GO Ready bit set on dma (chain=0x%X) Idx=0x%X Lch=%i Hi=0x%X Lo=0x%08X\n", + card->devname,dma_descr,dma_ch_indx, chan->logic_ch_num, reg,reg_lo); +#endif + WAN_NETIF_STATS_INC_TX_ERRORS(&chan->common); //chan->if_stats.tx_errors++; /* Nothing we can do here, just reset descriptor and keep going */ card->hw_iface.bus_write_4(card->hw,dma_descr,0); @@ -8782,6 +9097,10 @@ aft_tx_dma_chain_init(private_area_t *chan, wan_dma_descr_t *dma_chain) dma_chain, SDLA_DMA_POSTWRITE); + if (chan->tx_bert_skb == dma_chain->skb) { + dma_chain->skb=NULL; + } + if (dma_chain->skb){ if (!chan->hdlc_eng){ aft_tx_dma_skb_init(chan,dma_chain->skb); @@ -8791,6 +9110,7 @@ aft_tx_dma_chain_init(private_area_t *chan, wan_dma_descr_t *dma_chain) dma_chain->skb=NULL; } } + wan_clear_bit(0,&dma_chain->init); #undef card } @@ -8825,7 +9145,7 @@ static int aft_dma_voice_tx(sdla_t *card, private_area_t *chan) u32 reg, dma_ram_desc; if (wan_test_and_set_bit(TX_DMA_BUSY,&chan->dma_status)){ - DEBUG_EVENT("%s: SMP Critical in %s\n", + DEBUG_ERROR("%s: SMP Critical in %s\n", chan->if_name,__FUNCTION__); return -EBUSY; @@ -8851,7 +9171,7 @@ static int aft_dma_voice_tx(sdla_t *card, private_area_t *chan) * We are only using a single buffer for rx and tx */ dma_chain->skb=wan_skb_dequeue(&chan->wp_rx_free_list); if (!dma_chain->skb){ - DEBUG_EVENT("%s: Warning Tx chain = %d: no free tx bufs\n", + DEBUG_WARNING("%s: Warning Tx chain = %d: no free tx bufs\n", chan->if_name,dma_chain->index); wan_clear_bit(0,&dma_chain->init); err=-EINVAL; @@ -8896,7 +9216,7 @@ static int aft_dma_voice_tx(sdla_t *card, private_area_t *chan) * DMA Reload will be used to tx the Voice frame */ err=aft_dma_chain_tx(dma_chain,chan,1,1); if (err){ - DEBUG_EVENT("%s: Tx dma chain %d overrun error: should never happen!\n", + DEBUG_ERROR("%s: Tx dma chain %d overrun error: should never happen!\n", chan->if_name,dma_chain->index); /* Drop the tx packet here */ @@ -8923,7 +9243,8 @@ int aft_dma_tx (sdla_t *card,private_area_t *chan) wan_dma_descr_t *dma_chain; int tx_data_cnt=0; netskb_t *skb=NULL; - + int idle=0; + if (wan_test_bit(0,&chan->interface_down)) { return -EBUSY; } @@ -8932,15 +9253,28 @@ int aft_dma_tx (sdla_t *card,private_area_t *chan) return aft_dma_voice_tx(card,chan); } +#ifdef AFT_FIFO_GEN_DEBUGGING_TX +#ifndef __WINDOWS__ +#warning "AFT_FIFO_GEN_DEBUGGING_TX Enabled" +#endif + if (card->wp_debug_gen_fifo_err_tx) { + card->wp_debug_gen_fifo_err_tx++; + if (card->wp_debug_gen_fifo_err_tx < 3) { + DEBUG_EVENT("%s:FIFO ERROR: Debugging Enabled ... causing fifo error %i!\n",card->devname,card->wp_debug_gen_fifo_err_tx); + } + return -EBUSY; + } +#endif + if (wan_test_and_set_bit(TX_DMA_BUSY,&chan->dma_status)){ - DEBUG_EVENT("%s: SMP Critical in %s\n", + DEBUG_ERROR("%s: SMP Critical in %s\n", chan->if_name,__FUNCTION__); return -EBUSY; } if (chan->tx_chain_indx >= MAX_AFT_DMA_CHAINS){ - DEBUG_EVENT("%s: MAJOR ERROR: TE1 Tx: Dma tx chain = %d\n", + DEBUG_ERROR("%s: MAJOR ERROR: TE1 Tx: Dma tx chain = %d\n", chan->if_name,chan->tx_chain_indx); return -EBUSY; } @@ -8951,6 +9285,7 @@ int aft_dma_tx (sdla_t *card,private_area_t *chan) * out of the loop before MAX_TX_BUF runs out! */ for (cnt=0;cnttx_dma_chain_table[chan->tx_chain_indx]; /* FIXME: Reset TX Watchdog */ @@ -8963,18 +9298,30 @@ int aft_dma_tx (sdla_t *card,private_area_t *chan) break; } - if(!chan->lip_atm){ - skb=wan_skb_dequeue(&chan->wp_tx_pending_list); - }else{ - skb=atm_tx_skb_dequeue(&chan->wp_tx_pending_list, chan->tx_idle_skb, chan->if_name); + skb=NULL; + + /* During BERT user's data will NOT be transmitted. */ + if ( wan_test_bit(WP_MAINTENANCE_MODE_BERT, &chan->maintenance_mode_bitmap) ) { + + wp_init_bert_tx_skb(chan); + skb = chan->tx_bert_skb; } + if(!skb){ + if(!chan->lip_atm){ + skb=wan_skb_dequeue(&chan->wp_tx_pending_list); + }else{ + skb=atm_tx_skb_dequeue(&chan->wp_tx_pending_list, chan->tx_idle_skb, chan->if_name); + } + } + + idle=0; + if (!skb){ - if (!chan->hdlc_eng){ + if (!chan->hdlc_eng) { if (chan->dma_chain_opmode == WAN_AFT_DMA_CHAIN_IRQ_ALL){ - int chain_diff=aft_tx_dma_chain_chain_len(chan); - if (chain_diff >= 2 || tx_data_cnt) { + if (chan->tx_chain_sz > 0 || tx_data_cnt) { wan_clear_bit(0,&dma_chain->init); wan_clear_bit(TX_DMA_BUSY,&chan->dma_status); break; @@ -8982,11 +9329,11 @@ int aft_dma_tx (sdla_t *card,private_area_t *chan) tx_data_cnt++; } - + idle=1; skb=chan->tx_idle_skb; if (!skb){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: Critical Error: Transparent tx no skb!\n", + DEBUG_ERROR("%s: Critical Error: Transparent tx no skb!\n", chan->if_name); } wan_clear_bit(0,&dma_chain->init); @@ -9032,14 +9379,14 @@ int aft_dma_tx (sdla_t *card,private_area_t *chan) } } else { tx_data_cnt++; - } + }/* if (!skb) */ if ((ulong_ptr_t)wan_skb_data(skb) & 0x03){ err=aft_realign_skb_pkt(chan,skb); if (err){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: Tx Error: Non Aligned packet %p: dropping...\n", + DEBUG_ERROR("%s: Tx Error: Non Aligned packet %p: dropping...\n", chan->if_name,wan_skb_data(skb)); } wan_aft_skb_defered_dealloc(chan,skb); @@ -9055,7 +9402,7 @@ int aft_dma_tx (sdla_t *card,private_area_t *chan) if (!chan->hdlc_eng && (wan_skb_len(skb)%4)){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: Tx Error: Tx Length %d not 32bit aligned: dropping...\n", + DEBUG_ERROR("%s: Tx Error: Tx Length %d not 32bit aligned: dropping...\n", chan->if_name,wan_skb_len(skb)); } wan_aft_skb_defered_dealloc(chan,skb); @@ -9105,7 +9452,7 @@ int aft_dma_tx (sdla_t *card,private_area_t *chan) err=aft_dma_chain_tx(dma_chain,chan,intr,0); if (err){ - DEBUG_EVENT("%s: Tx dma chain %d overrun error: should never happen!\n", + DEBUG_ERROR("%s: Tx dma chain %d overrun error: should never happen!\n", chan->if_name,dma_chain->index); @@ -9146,6 +9493,10 @@ int aft_dma_tx (sdla_t *card,private_area_t *chan) } } #endif + if (!idle) { + chan->tx_chain_data_sz++; + } + chan->tx_chain_sz++; if (chan->dma_chain_opmode == WAN_AFT_DMA_CHAIN_SINGLE){ break; @@ -9266,10 +9617,10 @@ static int aft_dma_voice_rx(sdla_t *card, private_area_t *chan) wan_dma_descr_t *dma_chain; u32 reg, dma_ram_desc; - FUNC_NEWDRV(); + if (wan_test_and_set_bit(RX_DMA_BUSY,&chan->dma_status)){ - DEBUG_EVENT("%s: SMP Critical in %s\n", + DEBUG_ERROR("%s: SMP Critical in %s\n", chan->if_name,__FUNCTION__); return -EBUSY; } @@ -9296,7 +9647,7 @@ static int aft_dma_voice_rx(sdla_t *card, private_area_t *chan) if (!dma_chain->skb){ dma_chain->skb=wan_skb_dequeue(&chan->wp_rx_free_list); if (!dma_chain->skb){ - DEBUG_EVENT("%s: Warning Rx Voice chain = %d: no free rx bufs\n", + DEBUG_WARNING("%s: Warning Rx Voice chain = %d: no free rx bufs\n", chan->if_name,dma_chain->index); wan_clear_bit(0,&dma_chain->init); err=-EINVAL; @@ -9336,7 +9687,7 @@ static int aft_dma_voice_rx(sdla_t *card, private_area_t *chan) err=aft_dma_chain_rx(dma_chain,chan,1,1); if (err){ - DEBUG_EVENT("%s: Rx dma chain %d overrun error: should never happen!\n", + DEBUG_ERROR("%s: Rx dma chain %d overrun error: should never happen!\n", chan->if_name,dma_chain->index); aft_rx_dma_chain_init(chan,dma_chain); WAN_NETIF_STATS_INC_RX_ERRORS(&chan->common); //chan->if_stats.rx_errors++; @@ -9361,8 +9712,21 @@ static int aft_dma_rx(sdla_t *card, private_area_t *chan) return aft_dma_voice_rx(card,chan); } +#ifdef AFT_FIFO_GEN_DEBUGGING_RX +#ifndef __WINDOWS__ +#warning "AFT_FIFO_GEN_DEBUGGING_RX Enabled" +#endif + if (card->wp_debug_gen_fifo_err_rx) { + card->wp_debug_gen_fifo_err_rx++; + if (card->wp_debug_gen_fifo_err_rx < 3) { + DEBUG_EVENT("%s:FIFO ERROR: Debugging Enabled ... causing fifo error %i!\n",card->devname,card->wp_debug_gen_fifo_err_rx); + } + return -EBUSY; + } +#endif + if (wan_test_and_set_bit(RX_DMA_BUSY,&chan->dma_status)){ - DEBUG_EVENT("%s: SMP Critical in %s\n", + DEBUG_ERROR("%s: SMP Critical in %s\n", chan->if_name,__FUNCTION__); return -EBUSY; } @@ -9375,7 +9739,7 @@ static int aft_dma_rx(sdla_t *card, private_area_t *chan) aft_free_rx_complete_list(chan); free_queue_len=wan_skb_queue_len(&chan->wp_rx_free_list); if (free_queue_len < MAX_AFT_DMA_CHAINS){ - DEBUG_EVENT("%s: %s() CRITICAL ERROR: No free rx buffers\n", + DEBUG_ERROR("%s: %s() CRITICAL ERROR: No free rx buffers\n", card->devname,__FUNCTION__); goto te1rx_skip; } @@ -9430,7 +9794,7 @@ static int aft_dma_rx(sdla_t *card, private_area_t *chan) dma_chain->skb=wan_skb_dequeue(&chan->wp_rx_free_list); if (!dma_chain->skb) { - DEBUG_EVENT("%s: Critical Rx chain = %d: no free rx bufs (Free=%d Comp=%d)\n", + DEBUG_ERROR("%s: Critical Rx chain = %d: no free rx bufs (Free=%d Comp=%d)\n", chan->if_name,dma_chain->index, wan_skb_queue_len(&chan->wp_rx_free_list), wan_skb_queue_len(&chan->wp_rx_complete_list)); @@ -9486,7 +9850,7 @@ static int aft_dma_rx(sdla_t *card, private_area_t *chan) err=aft_dma_chain_rx(dma_chain,chan,intr,0); if (err){ - DEBUG_EVENT("%s: Rx dma chain %d overrun error: should never happen!\n", + DEBUG_ERROR("%s: Rx dma chain %d overrun error: should never happen!\n", chan->if_name,dma_chain->index); aft_rx_dma_chain_init(chan,dma_chain); WAN_NETIF_STATS_INC_RX_ERRORS(&chan->common); //chan->if_stats.rx_errors++; @@ -9539,7 +9903,7 @@ static int aft_rx_dma_chain_handler(private_area_t *chan, int reset) u32 dma_ram_desc; if (wan_test_and_set_bit(RX_HANDLER_BUSY,&chan->dma_status)){ - DEBUG_EVENT("%s: SMP Critical in %s\n", + DEBUG_ERROR("%s: SMP Critical in %s\n", chan->if_name,__FUNCTION__); return rx_data_available; } @@ -9570,7 +9934,7 @@ static int aft_rx_dma_chain_handler(private_area_t *chan, int reset) } if (!dma_chain){ - DEBUG_EVENT("%s:%d Assertion Error !!!!\n", + DEBUG_ERROR("%s:%d Assertion Error !!!!\n", __FUNCTION__,__LINE__); break; } @@ -9616,7 +9980,7 @@ static int aft_rx_dma_chain_handler(private_area_t *chan, int reset) if (sizeof(wp_rx_element_t) > wan_skb_headroom(dma_chain->skb)){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: Rx error: rx_el=%u > max head room=%u\n", + DEBUG_ERROR("%s: Rx error: rx_el=%u > max head room=%u\n", chan->if_name, (u32)sizeof(wp_rx_element_t), (u32)wan_skb_headroom(dma_chain->skb)); @@ -9845,7 +10209,7 @@ static void aft_free_rx_descriptors(private_area_t *chan) dma_chain = &chan->rx_dma_chain_table[i]; if (!dma_chain){ - DEBUG_EVENT("%s:%d Assertion Error !!!!\n", + DEBUG_ERROR("%s:%d Assertion Error !!!!\n", __FUNCTION__,__LINE__); break; } @@ -9882,6 +10246,7 @@ static void aft_free_rx_descriptors(private_area_t *chan) wan_clear_bit(0,&dma_chain->init); } + aft_reset_rx_chain_cnt(chan); } static void aft_reset_rx_chain_cnt(private_area_t *chan) @@ -9909,6 +10274,8 @@ static void aft_reset_tx_chain_cnt(private_area_t *chan) card->hw_iface.bus_read_4(card->hw,dma_ram_desc,®); cur_dma_ptr=aft_dmachain_get_tx_dma_addr(reg); chan->tx_pending_chain_indx = chan->tx_chain_indx = (u8)cur_dma_ptr; + chan->tx_chain_data_sz=0; + chan->tx_chain_sz=0; DEBUG_TEST("%s: Setting Tx Index to %d\n", chan->if_name,cur_dma_ptr); @@ -9924,6 +10291,8 @@ static void aft_free_tx_descriptors(private_area_t *chan) u32 i; void *skb; unsigned int dma_cnt=MAX_AFT_DMA_CHAINS; + int limit=2; + int q_size; if (chan->dma_chain_opmode == WAN_AFT_DMA_CHAIN_SINGLE) { dma_cnt=1; @@ -9939,7 +10308,7 @@ static void aft_free_tx_descriptors(private_area_t *chan) dma_chain = &chan->tx_dma_chain_table[i]; if (!dma_chain){ - DEBUG_EVENT("%s:%d Assertion Error !!!!\n", + DEBUG_ERROR("%s:%d Assertion Error !!!!\n", __FUNCTION__,__LINE__); break; } @@ -9955,7 +10324,7 @@ static void aft_free_tx_descriptors(private_area_t *chan) aft_tx_dma_chain_init(chan, dma_chain); } - chan->tx_chain_indx = chan->tx_pending_chain_indx; + aft_reset_tx_chain_cnt(chan); while((skb=wan_skb_dequeue(&chan->wp_tx_complete_list)) != NULL){ if (!chan->hdlc_eng) { @@ -9964,6 +10333,32 @@ static void aft_free_tx_descriptors(private_area_t *chan) wan_aft_skb_defered_dealloc(chan,skb); } } + + if (chan->common.usedby == XMTP2_API) { + limit=4; + } + + q_size=wan_skb_queue_len(&chan->wp_tx_pending_list); + + if (q_size > limit) { + while((skb=wan_skb_dequeue(&chan->wp_tx_pending_list)) != NULL){ + if (!chan->hdlc_eng) { + aft_tx_dma_skb_init(chan,skb); + } else { + wan_aft_skb_defered_dealloc(chan,skb); + } + q_size--; + if (q_size <= limit) { + break; + } + } + } + + DEBUG_TEST("%s:%s: Tx: Freeing Descripors: Comp=%i Pend=%i Chain=%i\n", + card->devname,chan->if_name, + wan_skb_queue_len(&chan->wp_tx_complete_list), + wan_skb_queue_len(&chan->wp_tx_pending_list), + chan->tx_chain_sz); } @@ -10017,13 +10412,8 @@ void aft_dma_max_logic_ch(sdla_t *card) #if defined(__WINDOWS__) static void aft_port_task (IN PKDPC Dpc, IN PVOID card_ptr, IN PVOID SystemArgument1, IN PVOID SystemArgument2) { -#if defined(WAN_SPINLOCK_IS_FASTMUTEX) - /* this will cause __aft_port_task() to run in a low-priority thread */ + /* this will cause __aft_port_task() to run at PASSIVE_LEVEL (low-priority) */ trigger_aft_port_task((sdla_t *)card_ptr); -#else - /* this will run DPC priority */ - __aft_port_task (card_ptr); -#endif } #endif @@ -10055,6 +10445,8 @@ static void aft_port_task (void * card_ptr, int arg) wan_set_bit(CARD_PORT_TASK_RUNNING,&card->wandev.critical); + AFT_PERF_STAT_INC(card,port_task,all); + if (wan_test_bit(CARD_PORT_TASK_DOWN,&card->wandev.critical)){ wan_clear_bit(CARD_PORT_TASK_RUNNING,&card->wandev.critical); return; @@ -10082,6 +10474,8 @@ static void aft_port_task (void * card_ptr, int arg) DEBUG_TEST("%s: PORT TASK: FE INTER\n", card->devname); + AFT_PERF_STAT_INC(card,port_task,fe_isr); + card->hw_iface.hw_lock(card->hw,&smp_flags); aft_fe_intr_ctrl(card, 0); front_end_interrupt(card,0,1); @@ -10099,6 +10493,9 @@ static void aft_port_task (void * card_ptr, int arg) if (wan_test_bit(AFT_FE_POLL,&card->u.aft.port_task_cmd)){ DEBUG_TEST("%s: PORT TASK: FE POLL\n", card->devname); + + AFT_PERF_STAT_INC(card,port_task,fe_poll); + card->hw_iface.hw_lock(card->hw,&smp_flags); if (card->wandev.fe_iface.polling){ @@ -10115,6 +10512,7 @@ static void aft_port_task (void * card_ptr, int arg) } if (wan_test_bit(AFT_FE_LED,&card->u.aft.port_task_cmd)){ + AFT_PERF_STAT_INC(card,port_task,led); DEBUG_TEST("%s: PORT TASK: FE LED\n", card->devname); card->hw_iface.hw_lock(card->hw,&smp_flags); if (card->wandev.state == WAN_CONNECTED){ @@ -10139,6 +10537,8 @@ static void aft_port_task (void * card_ptr, int arg) card->hw_iface.hw_lock(card->hw,&smp_flags); err=0; + AFT_PERF_STAT_INC(card,port_task,rbs); + #ifdef CONFIG_PRODUCT_WANPIPE_TDM_VOICE if (card->wan_tdmv.sc) { WAN_TDMV_CALL(rbsbits_poll, (&card->wan_tdmv, card), err); @@ -10160,9 +10560,13 @@ static void aft_port_task (void * card_ptr, int arg) #if defined(CONFIG_WANPIPE_HWEC) if (wan_test_bit(AFT_FE_EC_POLL,&card->u.aft.port_task_cmd)){ + + AFT_PERF_STAT_INC(card,port_task,ec); + DEBUG_TEST("%s: PORT TASK: FE EC INTR\n", card->devname); if (card->wandev.ec_dev){ int pending = 0; + AFT_PERF_STAT_INC(card,port_task,ec_poll); pending = wanpipe_ec_poll(card->wandev.ec_dev, card); /* Aug 10, 2007 ** If EC poll return 1 (pending), re-schedule port_task again @@ -10180,40 +10584,41 @@ static void aft_port_task (void * card_ptr, int arg) if (wan_test_bit(AFT_FE_RESTART,&card->u.aft.port_task_cmd)){ - wan_spin_lock_irq(&card->wandev.lock,&smp_irq_flags); - wan_clear_bit(AFT_FE_RESTART,&card->u.aft.port_task_cmd); - wan_spin_unlock_irq(&card->wandev.lock,&smp_irq_flags); + AFT_PERF_STAT_INC(card,port_task,restart); +#if 0 +#warning "NENAD Debugging" + /* Nenad: Unit testing code */ + card->wp_debug_gen_fifo_err_rx=0; + card->wp_debug_gen_fifo_err_tx=0; +#endif if (IS_BRI_CARD(card)) { - DEBUG_EVENT("%s: BRI IRQ Restart\n", + DEBUG_EVENT("%s: AFT BRI Sync Restart\n", card->devname); card->hw_iface.hw_lock(card->hw,&smp_flags); wan_spin_lock_irq(&card->wandev.lock,&smp_irq_flags); wan_clear_bit(0,&card->u.aft.comm_enabled); enable_data_error_intr(card); + wan_clear_bit(AFT_FE_RESTART,&card->u.aft.port_task_cmd); wan_spin_unlock_irq(&card->wandev.lock,&smp_irq_flags); card->hw_iface.hw_unlock(card->hw,&smp_flags); } else if (card->fe.fe_status == FE_CONNECTED) { - DEBUG_EVENT("%s: TDM IRQ Restart\n", + DEBUG_EVENT("%s: AFT Sync Restart\n", card->devname); card->hw_iface.hw_lock(card->hw,&smp_flags); - - card->fe.fe_status = FE_DISCONNECTED; - handle_front_end_state(card,1); - - /* BRI has special behaviour, we need to make sure - * that restart will trigger interrupts */ - wan_clear_bit(0,&card->u.aft.comm_enabled); - - card->fe.fe_status = FE_CONNECTED; - handle_front_end_state(card,1); - + wan_spin_lock_irq(&card->wandev.lock,&smp_irq_flags); + /* Purposely leave comm_enabled flag set so that + enable_data_error_intr will restart comms */ + enable_data_error_intr(card); + wan_clear_bit(AFT_FE_RESTART,&card->u.aft.port_task_cmd); + wan_spin_unlock_irq(&card->wandev.lock,&smp_irq_flags); card->hw_iface.hw_unlock(card->hw,&smp_flags); + } } @@ -10221,8 +10626,9 @@ static void aft_port_task (void * card_ptr, int arg) #if defined(AFT_RTP_SUPPORT) if (wan_test_bit(AFT_RTP_TAP_Q,&card->u.aft.port_task_cmd)){ netskb_t *skb; + AFT_PERF_STAT_INC(card,port_task,tap_q); wan_spin_lock_irq(&card->wandev.lock,&smp_irq_flags); - wan_clear_bit(AFT_FE_RESTART,&card->u.aft.port_task_cmd); + wan_clear_bit(AFT_RTP_TAP_Q,&card->u.aft.port_task_cmd); wan_spin_unlock_irq(&card->wandev.lock,&smp_irq_flags); while ((skb=wan_skb_dequeue(&card->u.aft.rtp_tap_list))) { dev_queue_xmit(skb); @@ -10231,6 +10637,7 @@ static void aft_port_task (void * card_ptr, int arg) #endif if (wan_test_bit(AFT_SERIAL_STATUS,&card->u.aft.port_task_cmd)){ + AFT_PERF_STAT_INC(card,port_task,serial_status); wan_spin_lock_irq(&card->wandev.lock,&smp_irq_flags); wan_clear_bit(AFT_SERIAL_STATUS,&card->u.aft.port_task_cmd); wan_spin_unlock_irq(&card->wandev.lock,&smp_irq_flags); @@ -10288,7 +10695,7 @@ void __aft_fe_intr_ctrl(sdla_t *card, int status) if (wan_test_bit(AFT_CHIPCFG_FE_INTR_CFG_BIT,&treg)) { break; } - DEBUG_EVENT("%s: Warning failed to enable front end ... retry %i!\n",card->devname,retry); + DEBUG_WARNING("%s: Warning failed to enable front end ... retry %i!\n",card->devname,retry); } }while(--retry); } @@ -10363,7 +10770,7 @@ static int aft_ss7_tx_mangle(sdla_t *card,private_area_t *chan, netskb_t *skb) if (!chan->tx_ss7_realign_buf){ chan->tx_ss7_realign_buf=wan_malloc(chan->dma_mru); if (!chan->tx_ss7_realign_buf){ - DEBUG_EVENT("%s: Error: Failed to allocate ss7 tx memory buf\n", + DEBUG_ERROR("%s: Error: Failed to allocate ss7 tx memory buf\n", chan->if_name); return -ENOMEM; }else{ @@ -10415,7 +10822,7 @@ static int aft_ss7_tx_mangle(sdla_t *card,private_area_t *chan, netskb_t *skb) wan_skb_trim(skb,0); if (wan_skb_tailroom(skb) < len){ - DEBUG_EVENT("%s: Critical error: SS7 Tx unalign pkt tail room(%d) < len(%d)!\n", + DEBUG_ERROR("%s: Critical error: SS7 Tx unalign pkt tail room(%d) < len(%d)!\n", chan->if_name,wan_skb_tailroom(skb),len); return -ENOMEM; @@ -10486,7 +10893,7 @@ static int aft_tdmv_init(sdla_t *card, wandev_conf_t *conf) int used_cnt; card->hw_iface.getcfg(card->hw, SDLA_HWCPU_USEDCNT, &used_cnt); if (used_cnt > 1) { - DEBUG_EVENT("%s: Error: TDMV Dummy must be configured for first TDM SPAN device\n", + DEBUG_ERROR("%s: Error: TDMV Dummy must be configured for first TDM SPAN device\n", card->devname); return -EINVAL; } @@ -10510,16 +10917,18 @@ static int aft_tdmv_init(sdla_t *card, wandev_conf_t *conf) if (card->u.aft.global_tdm_irq) { if (card->wandev.config_id == WANCONFIG_AFT_ANALOG) { if (IS_A600_CARD(card)) { - valid_firmware_ver=AFT_MIN_A600_FRMW_VER; + valid_firmware_ver = AFT_MIN_A600_FRMW_VER; + } else if (IS_B601_CARD(card)) { + valid_firmware_ver = AFT_MIN_B601_FRMW_VER; } else { - valid_firmware_ver=AFT_MIN_ANALOG_FRMW_VER; + valid_firmware_ver = AFT_MIN_ANALOG_FRMW_VER; } } if (card->u.aft.firm_ver < valid_firmware_ver){ - DEBUG_EVENT("%s: Error: Obselete AFT Firmware version: %X\n", + DEBUG_ERROR("%s: Error: Obselete AFT Firmware version: %X\n", card->devname,card->u.aft.firm_ver); - DEBUG_EVENT("%s: Error: AFT TDMV Support depends on Firmware Ver >= %X\n", + DEBUG_ERROR("%s: Error: AFT TDMV Support depends on Firmware Ver >= %X\n", card->devname,valid_firmware_ver); return -EINVAL; } @@ -10555,7 +10964,7 @@ static int aft_tdmv_if_init(sdla_t *card, private_area_t *chan, wanif_conf_t *co } if (!card->u.aft.global_tdm_irq){ - DEBUG_EVENT("%s: Error: TDMV Span No is not set!\n", + DEBUG_ERROR("%s: Error: TDMV Span No is not set!\n", card->devname); return -EINVAL; } @@ -10577,7 +10986,7 @@ static int aft_tdmv_if_init(sdla_t *card, private_area_t *chan, wanif_conf_t *co #ifdef CONFIG_PRODUCT_WANPIPE_TDM_VOICE WAN_TDMV_CALL(check_mtu, (card, conf->active_ch, &chan->mtu), err); if (err){ - DEBUG_EVENT("Error: TMDV mtu check failed!"); + DEBUG_ERROR("Error: TMDV mtu check failed!"); return -EINVAL; } #endif @@ -10659,7 +11068,7 @@ static int aft_tdmv_if_init(sdla_t *card, private_area_t *chan, wanif_conf_t *co chan->common.dev), channel); if (channel < 0){ - DEBUG_EVENT("%s: Error: Failed to register TDMV channel!\n", + DEBUG_ERROR("%s: Error: Failed to register TDMV channel!\n", chan->if_name); return -EINVAL; @@ -10679,7 +11088,7 @@ static int aft_tdmv_if_init(sdla_t *card, private_area_t *chan, wanif_conf_t *co conf->hwec.enable, chan->common.dev), channel); if (channel < 0){ - DEBUG_EVENT("%s: Error: Failed to register TDMV channel!\n", + DEBUG_ERROR("%s: Error: Failed to register TDMV channel!\n", chan->if_name); return -EINVAL; @@ -10746,7 +11155,6 @@ static void aft_set_channel(sdla_t *card, int ch) #endif - static int aft_voice_span_rx_tx(sdla_t *card, int rotate) { int err; @@ -10820,7 +11228,7 @@ static int aft_dma_rx_tdmv(sdla_t *card, private_area_t *chan) rx_dma_chain = &chan->rx_dma_chain_table[0]; if (!tx_dma_chain || !rx_dma_chain){ - DEBUG_EVENT("%s: %s:%d ASSERT ERROR TxDma=%p RxDma=%p\n", + DEBUG_ERROR("%s: %s:%d ASSERT ERROR TxDma=%p RxDma=%p\n", card->devname,__FUNCTION__,__LINE__, tx_dma_chain,rx_dma_chain); return -EINVAL; @@ -10906,7 +11314,7 @@ defined(AFT_TDMV_BH_ENABLE) rx_bh_dma_chain->skb=wan_skb_dequeue(&chan->wp_rx_free_list); if (!rx_bh_dma_chain->skb){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: Critical TDM BH no free skb\n", + DEBUG_ERROR("%s: Critical TDM BH no free skb\n", chan->if_name); goto aft_tdm_bh_skip; } @@ -10919,7 +11327,7 @@ defined(AFT_TDMV_BH_ENABLE) tx_bh_dma_chain->skb=wan_skb_dequeue(&chan->wp_rx_free_list); if (!tx_bh_dma_chain->skb){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: Critical TDM BH no free skb\n", + DEBUG_ERROR("%s: Critical TDM BH no free skb\n", chan->if_name); goto aft_tdm_bh_skip; } @@ -11046,7 +11454,7 @@ defined(AFT_TDMV_BH_ENABLE) if (WAN_TASKLET_RUNNING((&chan->common.bh_task))){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: Critical Error: TDMV BH Overrun!\n", + DEBUG_ERROR("%s: Critical Error: TDMV BH Overrun!\n", card->devname); } } @@ -11159,10 +11567,13 @@ defined(AFT_TDMV_BH_ENABLE) chan->opstats.Data_bytes_Rx_count+=chan->mru; chan->opstats.Data_frames_Tx_count++; chan->opstats.Data_bytes_Tx_count+=chan->mtu; + chan->chan_stats.rx_packets++; chan->chan_stats.rx_bytes += chan->mru; chan->chan_stats.tx_packets++; chan->chan_stats.tx_bytes += chan->mtu; + + WAN_NETIF_STATS_INC_RX_PACKETS(&chan->common); //chan->if_stats.rx_packets++; WAN_NETIF_STATS_INC_RX_BYTES(&chan->common,chan->mru); //chan->if_stats.rx_bytes += chan->mru; WAN_NETIF_STATS_INC_TX_PACKETS(&chan->common); //chan->if_stats.tx_packets++; @@ -11188,7 +11599,7 @@ static void aft_critical_shutdown (sdla_t *card) { wan_smp_flag_t smp_flags,smp_flags1; - DEBUG_EVENT("%s: Error: Card Critically Shutdown!\n", + DEBUG_ERROR("%s: Error: Card Critically Shutdown!\n", card->devname); if (card->wandev.fe_iface.pre_release){ diff --git a/patches/kdrivers/src/net/aft_core_api_events.c b/patches/kdrivers/src/net/aft_core_api_events.c index d4b1781..89257ab 100644 --- a/patches/kdrivers/src/net/aft_core_api_events.c +++ b/patches/kdrivers/src/net/aft_core_api_events.c @@ -42,7 +42,8 @@ static void wan_aft_api_ringdetect (void* card_id, wan_event_t *event); static int aft_read_rbs_bits(void *chan_ptr, u32 ch, u8 *rbs_bits); static int aft_write_rbs_bits(void *chan_ptr, u32 ch, u8 rbs_bits); static int aft_write_hdlc_frame(void *chan_ptr, netskb_t *skb, wp_api_hdr_t *hdr); - +static int aft_write_hdlc_check(void *chan_ptr, int lock); +static int aft_write_hdlc_timeout(void *chan_ptr, int lock); /*-------------------------------------------------------- * PRIVATE EVENT FUNCTIONS @@ -187,7 +188,7 @@ static void wan_aft_api_hook (void* card_id, wan_event_t *event) int i; if (event->type != WAN_EVENT_RM_LC){ - DEBUG_EVENT("ERROR: %s: Invalid Event type (%04X)!\n", + DEBUG_ERROR("ERROR: %s: Invalid Event type (%04X)!\n", card->devname, event->type); return; } @@ -267,7 +268,7 @@ static void wan_aft_api_ringtrip (void* card_id, wan_event_t *event) int i; if (event->type != WP_API_EVENT_RING_TRIP_DETECT){ - DEBUG_EVENT("ERROR: %s: Invalid Event type (%04X)!\n", + DEBUG_ERROR("ERROR: %s: Invalid Event type (%04X)!\n", card->devname, event->type); return; } @@ -350,7 +351,7 @@ static void wan_aft_api_ringdetect (void* card_id, wan_event_t *event) int i; if (event->type != WAN_EVENT_RM_RING_DETECT){ - DEBUG_EVENT("ERROR: %s: Invalid Event type (%04X)!\n", + DEBUG_ERROR("ERROR: %s: Invalid Event type (%04X)!\n", card->devname, event->type); return; } @@ -422,7 +423,52 @@ static void wan_aft_api_ringdetect (void* card_id, wan_event_t *event) return; } +static int aft_write_hdlc_check(void *chan_ptr, int lock) +{ + private_area_t *chan = (private_area_t *)chan_ptr; + sdla_t *card=chan->card; + wan_smp_flag_t smp_flags; + int rc; + + if (IS_BRI_CARD(card) && (chan->dchan_time_slot >= 0)){ + /* For BRI rely on upper layer checking */ + return 1; + } + if (lock) { + wan_spin_lock_irq(&card->wandev.lock, &smp_flags); + } + rc = wan_chan_dev_stopped(chan); + + if (lock) { + wan_spin_unlock_irq(&card->wandev.lock, &smp_flags); + } + + return rc; +} + +static int aft_write_hdlc_timeout(void *chan_ptr, int lock) +{ + private_area_t *chan = (private_area_t *)chan_ptr; + sdla_t *card=chan->card; + wan_smp_flag_t smp_flags; + + if (IS_BRI_CARD(card)) { + return 0; + } + + if (lock) { + wan_spin_lock_irq(&card->wandev.lock, &smp_flags); + } + + aft_tx_fifo_under_recover(card,chan); + + if (lock) { + wan_spin_unlock_irq(&card->wandev.lock, &smp_flags); + } + + return 0; +} static int aft_write_hdlc_frame(void *chan_ptr, netskb_t *skb, wp_api_hdr_t *hdr) { @@ -500,18 +546,31 @@ static int aft_write_hdlc_frame(void *chan_ptr, netskb_t *skb, wp_api_hdr_t *hd WAN_NETIF_STOP_QUEUE(chan->common.dev); hdr->tx_h.max_tx_queue_length = (u8)chan->max_tx_bufs; hdr->tx_h.current_number_of_frames_in_tx_queue = (u8)wan_skb_queue_len(&chan->wp_tx_pending_list); + + if (chan->dma_chain_opmode != WAN_AFT_DMA_CHAIN_SINGLE) { + hdr->tx_h.current_number_of_frames_in_tx_queue += chan->tx_chain_data_sz; + hdr->tx_h.max_tx_queue_length += MAX_AFT_DMA_CHAINS; + } + + hdr->tx_h.tx_idle_packets = chan->chan_stats.tx_idle_packets; wan_chan_dev_stop(chan); aft_dma_tx(card,chan); wan_spin_unlock_irq(&card->wandev.lock, &smp_flags); return -EBUSY; } - hdr->tx_h.max_tx_queue_length = (u8)chan->max_tx_bufs; - hdr->tx_h.current_number_of_frames_in_tx_queue = (u8)wan_skb_queue_len(&chan->wp_tx_pending_list); - wan_skb_unlink(skb); wan_skb_queue_tail(&chan->wp_tx_pending_list,skb); - aft_dma_tx(card,chan); + aft_dma_tx(card,chan); + + hdr->tx_h.max_tx_queue_length = (u8)chan->max_tx_bufs; + hdr->tx_h.current_number_of_frames_in_tx_queue = (u8)wan_skb_queue_len(&chan->wp_tx_pending_list); + hdr->tx_h.tx_idle_packets = chan->chan_stats.tx_idle_packets; + + if (chan->dma_chain_opmode != WAN_AFT_DMA_CHAIN_SINGLE) { + hdr->tx_h.max_tx_queue_length += MAX_AFT_DMA_CHAINS; + hdr->tx_h.current_number_of_frames_in_tx_queue += chan->tx_chain_data_sz; + } if (wan_skb_queue_len(&chan->wp_tx_pending_list) >= chan->max_tx_bufs){ WAN_NETIF_STOP_QUEUE(chan->common.dev); @@ -553,36 +612,50 @@ int aft_event_ctrl(void *chan_ptr, wan_event_ctrl_t *p_event) memset(event_ctrl, 0, sizeof(wan_event_ctrl_t)); memcpy(event_ctrl, p_event, sizeof(wan_event_ctrl_t)); - if (event_ctrl->type == WAN_EVENT_EC_DTMF && card->wandev.ec_dev){ + switch (event_ctrl->type) { + + case WAN_EVENT_EC_DTMF: + case WAN_EVENT_EC_FAX_DETECT: + + if (card->wandev.ec_dev){ #if defined(CONFIG_WANPIPE_HWEC) - DEBUG_TEST("%s: Event control request EC_DTMF...\n", - chan->if_name); - err = wanpipe_ec_event_ctrl(card->wandev.ec_dev, card, event_ctrl); -#else - DEBUG_EVENT("%s: Error: Hardware EC not compiled! Failed to enable DTMF\n", + DEBUG_TEST("%s: Event control request EC_DTMF...\n", chan->if_name); - err=-EINVAL; + err = wanpipe_ec_event_ctrl(card->wandev.ec_dev, card, event_ctrl); +#else + DEBUG_ERROR("%s: Error: Hardware EC not compiled! Failed to enable DTMF\n", + chan->if_name); + err=-EINVAL; #endif - }else if (chan->card->wandev.fe_iface.event_ctrl){ + } else { + err=-EINVAL; + } + break; - wan_smp_flag_t irq_flags,flags; - DEBUG_TDMAPI("%s: FE Event control request...\n", chan->if_name); + default: - chan->card->hw_iface.hw_lock(chan->card->hw,&flags); - wan_spin_lock_irq(&card->wandev.lock,&irq_flags); + if (chan->card->wandev.fe_iface.event_ctrl){ - err = chan->card->wandev.fe_iface.event_ctrl(&chan->card->fe, event_ctrl); + wan_smp_flag_t irq_flags,flags; + DEBUG_TDMAPI("%s: FE Event control request...\n", chan->if_name); + + chan->card->hw_iface.hw_lock(chan->card->hw,&flags); + wan_spin_lock_irq(&card->wandev.lock,&irq_flags); + + err = chan->card->wandev.fe_iface.event_ctrl(&chan->card->fe, event_ctrl); + + wan_spin_unlock_irq(&card->wandev.lock,&irq_flags); + chan->card->hw_iface.hw_unlock(chan->card->hw,&flags); + + /* Front end interface does not use queue */ + wan_free(event_ctrl); + event_ctrl=NULL; - wan_spin_unlock_irq(&card->wandev.lock,&irq_flags); - chan->card->hw_iface.hw_unlock(chan->card->hw,&flags); - - /* Front end interface does not use queue */ - wan_free(event_ctrl); - event_ctrl=NULL; - - }else{ - DEBUG_EVENT("%s: Unsupported event control request (%X)\n", - chan->if_name, event_ctrl->type); + }else{ + DEBUG_EVENT("%s: Unsupported event control request (%X)\n", + chan->if_name, event_ctrl->type); + } + break; } if (err){ @@ -858,11 +931,13 @@ static int aft_driver_ctrl(void *chan_ptr, int cmd, wanpipe_api_cmd_t *api_cmd) if (!chan) { + DEBUG_ERROR("%s: ERROR: chan=NULL\n",__FUNCTION__); return -ENODEV; } card = chan->card; if (!card) { + DEBUG_ERROR("%s: ERROR: card=NULL\n",__FUNCTION__); return -ENODEV; } @@ -886,19 +961,12 @@ static int aft_driver_ctrl(void *chan_ptr, int cmd, wanpipe_api_cmd_t *api_cmd) chan->chan_stats.max_tx_queue_length = (u8)chan->max_tx_bufs; chan->chan_stats.max_rx_queue_length = (u8)chan->dma_per_ch; - if (chan->dma_chain_opmode == WAN_AFT_DMA_CHAIN_SINGLE){ - /* do not count the queue len */ - } else { - chan->chan_stats.max_tx_queue_length += MAX_AFT_DMA_CHAINS; - } - wptdm_os_lock_irq(&card->wandev.lock, &smp_flags); chan->chan_stats.current_number_of_frames_in_tx_queue = (u8)wan_skb_queue_len(&chan->wp_tx_pending_list); - if (chan->dma_chain_opmode == WAN_AFT_DMA_CHAIN_SINGLE){ - /* do not count the queue len */ - } else { - chan->chan_stats.current_number_of_frames_in_tx_queue += aft_tx_dma_chain_chain_len(chan); + if (chan->dma_chain_opmode != WAN_AFT_DMA_CHAIN_SINGLE){ + chan->chan_stats.current_number_of_frames_in_tx_queue += chan->tx_chain_data_sz; + chan->chan_stats.max_tx_queue_length += MAX_AFT_DMA_CHAINS; } chan->chan_stats.current_number_of_frames_in_rx_queue = (u8)wan_skb_queue_len(&chan->wp_rx_complete_list); @@ -933,20 +1001,28 @@ static int aft_driver_ctrl(void *chan_ptr, int cmd, wanpipe_api_cmd_t *api_cmd) api_cmd->data_len=1; break; - case WP_API_CMD_GEN_FIFO_ERR: - card->wp_debug_gen_fifo_err=1; + case WP_API_CMD_GEN_FIFO_ERR_TX: + DEBUG_EVENT("%s: Span %i enable TX fifo error !\n", + card->devname, card->tdmv_conf.span_no); + card->wp_debug_gen_fifo_err_tx=1; + break; + + case WP_API_CMD_GEN_FIFO_ERR_RX: + DEBUG_EVENT("%s: Span %i enable RX fifo error !\n", + card->devname, card->tdmv_conf.span_no); + card->wp_debug_gen_fifo_err_rx=1; break; case WP_API_CMD_START_CHAN_SEQ_DEBUG: DEBUG_EVENT("%s: Span %i channel sequence deugging enabled !\n", card->devname, card->tdmv_conf.span_no); - card->wp_debug_gen_fifo_err=1; - break; + card->wp_debug_chan_seq=1; + break; case WP_API_CMD_STOP_CHAN_SEQ_DEBUG: DEBUG_EVENT("%s: Span %i channel sequence deugging disabled !\n", card->devname, card->tdmv_conf.span_no); - card->wp_debug_gen_fifo_err=0; + card->wp_debug_chan_seq=0; break; case WP_API_CMD_SET_IDLE_FLAG: @@ -957,12 +1033,13 @@ static int aft_driver_ctrl(void *chan_ptr, int cmd, wanpipe_api_cmd_t *api_cmd) memset(wan_skb_data(chan->tx_idle_skb), chan->idle_flag, wan_skb_len(chan->tx_idle_skb)); }else{ - DEBUG_EVENT("%s: Error: WP_API_CMD_SET_IDLE_FLAG: tx_idle_skb is NULL!\n", + DEBUG_ERROR("%s: Error: WP_API_CMD_SET_IDLE_FLAG: tx_idle_skb is NULL!\n", chan->if_name); } break; default: + DEBUG_ERROR("%s: ERROR: driver_ctrl ioctl %i not supported\n",card->devname,cmd); api_cmd->result=SANG_STATUS_OPTION_NOT_SUPPORTED; err=-EOPNOTSUPP; break; @@ -980,6 +1057,8 @@ int aft_core_tdmapi_event_init(private_area_t *chan) chan->wp_tdm_api_dev->read_rbs_bits = aft_read_rbs_bits; chan->wp_tdm_api_dev->write_rbs_bits = aft_write_rbs_bits; chan->wp_tdm_api_dev->write_hdlc_frame = aft_write_hdlc_frame; + chan->wp_tdm_api_dev->write_hdlc_check = aft_write_hdlc_check; + chan->wp_tdm_api_dev->write_hdlc_timeout = aft_write_hdlc_timeout; chan->wp_tdm_api_dev->pipemon = wan_user_process_udp_mgmt_pkt; chan->wp_tdm_api_dev->driver_ctrl = aft_driver_ctrl; #endif diff --git a/patches/kdrivers/src/net/aft_core_prot.c b/patches/kdrivers/src/net/aft_core_prot.c index 8eb3408..039dfca 100644 --- a/patches/kdrivers/src/net/aft_core_prot.c +++ b/patches/kdrivers/src/net/aft_core_prot.c @@ -112,7 +112,7 @@ int aft_rtp_config(sdla_t *card) } if (card->rtp_conf.rtp_sample < 10 || card->rtp_conf.rtp_sample > 150) { - DEBUG_EVENT("%s: Error: Invalid RTP Sample %d [Min=10 Max=150ms]\n", + DEBUG_ERROR("%s: Error: Invalid RTP Sample %d [Min=10 Max=150ms]\n", card->devname,card->rtp_conf.rtp_sample); goto aft_rtp_init_exit; } @@ -264,7 +264,7 @@ void aft_rtp_tap(void *card_ptr, u8 chan, u8* rx, u8* tx, u32 len) if (!card || !rx || !tx ) { if (WAN_NET_RATELIMIT()) { - DEBUG_EVENT("%s: Internal Error: rtp tap invalid pointers chan %d\n", + DEBUG_ERROR("%s: Internal Error: rtp tap invalid pointers chan %d\n", __FUNCTION__,chan); } return; @@ -283,7 +283,7 @@ void aft_rtp_tap(void *card_ptr, u8 chan, u8* rx, u8* tx, u32 len) if (chan >= 32) { if (WAN_NET_RATELIMIT()) { - DEBUG_EVENT("%s: Internal Error: rtp tap chan out of range %d\n", + DEBUG_ERROR("%s: Internal Error: rtp tap chan out of range %d\n", card->devname,chan); } return; @@ -724,6 +724,10 @@ int aft_tdm_api_init(sdla_t *card, private_area_t *chan, wanif_conf_t *conf) chan->wp_tdm_api_dev->tdm_chan = (u8)chan->if_cnt; } + if (chan->usedby_cfg == MTP1_API) { + chan->wp_tdm_api_dev->operation_mode = WP_TDM_OPMODE_MTP1; + } + /* Set the API interface mode. Used on windows to * indicate legacy API interface */ chan->wp_tdm_api_dev->api_mode = chan->wp_api_iface_mode; @@ -732,6 +736,7 @@ int aft_tdm_api_init(sdla_t *card, private_area_t *chan, wanif_conf_t *conf) DEBUG_EVENT("%s: Memory: TDM API %d\n", card->devname, sizeof(wanpipe_tdm_api_dev_t)); } + err=wanpipe_tdm_api_reg(chan->wp_tdm_api_dev); if (err){ diff --git a/patches/kdrivers/src/net/aft_core_utils.c b/patches/kdrivers/src/net/aft_core_utils.c index 65d6a91..26dccf1 100644 --- a/patches/kdrivers/src/net/aft_core_utils.c +++ b/patches/kdrivers/src/net/aft_core_utils.c @@ -32,8 +32,28 @@ void wan_get_random_mac_address( OUT unsigned char *mac_address ); + +extern +int +sdla_restore_pci_config_space( + IN void *level1_dev_pdx + ) ; + +extern +int wp_get_motherboard_enclosure_serial_number( + OUT char *buf, + IN int buf_length + ); + +extern +int +wp_get_netdev_open_handles_count( + netdevice_t *sdla_net_dev + ); + #endif + #ifdef __WINDOWS__ #define wp_os_lock_irq(card,flags) wan_spin_lock_irq(card,flags) #define wp_os_unlock_irq(card,flags) wan_spin_unlock_irq(card,flags) @@ -89,13 +109,13 @@ int aft_front_end_mismatch_check(sdla_t * card) if (IS_T1_CARD(card)){ if (wan_test_bit(AFT_CHIPCFG_TE1_CFG_BIT,®)){ - DEBUG_EVENT("%s: Global Cfg Error: Initial front end cfg: E1\n", + DEBUG_ERROR("%s: Global Cfg Error: Initial front end cfg: E1\n", card->devname); return -EINVAL; } }else{ if (!wan_test_bit(AFT_CHIPCFG_TE1_CFG_BIT,®)){ - DEBUG_EVENT("%s: Global Cfg Error: Initial front end cfg: T1\n", + DEBUG_ERROR("%s: Global Cfg Error: Initial front end cfg: T1\n", card->devname); return -EINVAL; } @@ -111,22 +131,15 @@ int aft_realign_skb_pkt(private_area_t *chan, netskb_t *skb) int len = wan_skb_len(skb); if (len > chan->dma_mru){ - DEBUG_EVENT("%s: Critical error: Tx unalign pkt(%d) > MTU buf(%d)!\n", + DEBUG_ERROR("%s: Critical error: Tx unalign pkt(%d) > MTU buf(%d)!\n", chan->if_name,len,chan->dma_mru); return -ENOMEM; } if (!chan->tx_realign_buf){ - chan->tx_realign_buf=wan_malloc(chan->dma_mru); - if (!chan->tx_realign_buf){ - DEBUG_EVENT("%s: Error: Failed to allocate tx memory buf\n", - chan->if_name); - return -ENOMEM; - }else{ - DEBUG_EVENT("%s: AFT Realign buffer allocated Len=%d\n", - chan->if_name,chan->dma_mru); - - } + DEBUG_ERROR("%s: Error: realign buf not allocated!\n", + chan->if_name); + return -ENOMEM; } memcpy(chan->tx_realign_buf,data,len); @@ -135,7 +148,7 @@ int aft_realign_skb_pkt(private_area_t *chan, netskb_t *skb) wan_skb_trim(skb,0); if (wan_skb_tailroom(skb) < len){ - DEBUG_EVENT("%s: Critical error: Tx unalign pkt tail room(%i) < unalign len(%i)!\n", + DEBUG_ERROR("%s: Critical error: Tx unalign pkt tail room(%i) < unalign len(%i)!\n", chan->if_name,wan_skb_tailroom(skb),len); return -ENOMEM; @@ -178,13 +191,13 @@ int aft_free_running_timer_set_enable(sdla_t *card, u32 ms) msec=(msec&AFT_FREE_RUN_TIMER_DIVIDER_MASK)<devname,msec,AFT_FREE_RUN_TIMER_DIVIDER_MASK); return -EINVAL; } } else { - DEBUG_EVENT("%s: Error: Free Run Timeout config ms=0\n", + DEBUG_ERROR("%s: Error: Free Run Timeout config ms=0\n", card->devname); return -EINVAL; } @@ -369,6 +382,10 @@ void aft_tx_dma_skb_init(private_area_t *chan, netskb_t *skb) return; } + if (chan->tx_bert_skb == skb) { + return; + } + if (chan->common.usedby == XMTP2_API) { /* Requeue the tx buffer because it came from rx_free_list */ aft_init_requeue_free_skb(chan, skb); @@ -398,7 +415,7 @@ int aft_alloc_rx_dma_buff(sdla_t *card, private_area_t *chan, int num, int irq) } #else if (chan->channelized_cfg && !chan->hdlc_eng){ -#if defined(WANPIPE_64BIT_4G_DMA) || defined(CONFIG_X86_64) +#if (!defined(WANPIPE_64BIT_2G_DMA) && (defined(WANPIPE_64BIT_4G_DMA) || defined(CONFIG_X86_64))) /* On 64bit Systems greater than 4GB we must * allocated our DMA buffers using GFP_DMA * flag */ @@ -659,7 +676,7 @@ int aft_hwec(sdla_t *card, wan_cmd_api_t *api_cmd) return 0; } - + int aft_devel_ioctl(sdla_t *card, struct ifreq *ifr) { wan_cmd_api_t* api_cmd; @@ -671,7 +688,7 @@ int aft_devel_ioctl(sdla_t *card, struct ifreq *ifr) ifr_data_ptr = ifr; #else if (!ifr || !ifr->ifr_data){ - DEBUG_EVENT("%s: Error: No ifr or ifr_data\n",__FUNCTION__); + DEBUG_ERROR("%s: Error: No ifr or ifr_data\n",__FUNCTION__); return -EFAULT; } ifr_data_ptr = ifr->ifr_data; @@ -689,6 +706,7 @@ int aft_devel_ioctl(sdla_t *card, struct ifreq *ifr) case SIOC_WAN_READ_REG: err=aft_read(card, api_cmd); break; + case SIOC_WAN_WRITE_REG: err=aft_write(card, api_cmd); break; @@ -702,27 +720,58 @@ int aft_devel_ioctl(sdla_t *card, struct ifreq *ifr) break; case SIOC_WAN_SET_PCI_BIOS: +#if defined(__WINDOWS__) + /* Restore PCI config space to what it was + * before the firmware update, so access to card + * is possible again. */ + err=sdla_restore_pci_config_space(card); +#else err=aft_write_bios(card, api_cmd); +#endif break; case SIOC_WAN_EC_REG: err = aft_hwec(card, api_cmd); break; + #if defined(__WINDOWS__) case SIOC_WAN_GET_CARD_TYPE: + api_cmd->len = 0; - card->hw_iface.getcfg(card->hw, SDLA_ADAPTERTYPE, (u16*)&api_cmd->data[0]); - card->hw_iface.getcfg(card->hw, SDLA_ADAPTERSUBTYPE, (u16*)&api_cmd->data[sizeof(u16)]); + err = card->hw_iface.getcfg(card->hw, SDLA_ADAPTERTYPE, (u16*)&api_cmd->data[0]); + if(err){ + break; + } + + err = card->hw_iface.getcfg(card->hw, SDLA_ADAPTERSUBTYPE, (u16*)&api_cmd->data[sizeof(u16)]); + if(err){ + break; + } api_cmd->len = 2*sizeof(u16); break; #endif + + case SIOC_WAN_COREREV: + api_cmd->len = 0; + + err = card->hw_iface.getcfg(card->hw, SDLA_COREREV, (u8*)&api_cmd->data[0]); + if(err){ + break; + } + + api_cmd->len = sizeof(u8); + break; + default: - DEBUG_EVENT("%s(): Error: unsupported cmd: %d\n",__FUNCTION__, api_cmd->cmd); + DEBUG_ERROR("%s(): Error: unsupported cmd: %d\n", __FUNCTION__, api_cmd->cmd); + err = -EINVAL; break; } - if (WAN_COPY_TO_USER(ifr_data_ptr, api_cmd,sizeof(wan_cmd_api_t))){ + api_cmd->ret = err; + + if (WAN_COPY_TO_USER(ifr_data_ptr, api_cmd, sizeof(wan_cmd_api_t))){ return -EFAULT; } return err; @@ -843,13 +892,68 @@ int if_change_mtu(netdevice_t *dev, int new_mtu) #endif +#if defined(__WINDOWS__) + +static int digital_loop_test(sdla_t* card, private_area_t* chan, wan_udp_pkt_t* wan_udp_pkt) +{ + wanpipe_tdm_api_dev_t *tdm_api = chan->wp_tdm_api_dev; + int err; + netskb_t *skb; + wp_api_hdr_t hdr; + void *buf; + + memset(&hdr, 0x00, sizeof(hdr)); + hdr.wp_api_hdr_data_length = wan_udp_pkt->wan_udp_data_len; + + if (wan_test_and_set_bit(WP_TDM_HDLC_TX,&tdm_api->critical)){ + + DEBUG_ERROR("%s: Error: %s() WP_TDM_HDLC_TX critical\n", + tdm_api->name, __FUNCTION__); + return SANG_STATUS_GENERAL_ERROR; + } + + if (!tdm_api->write_hdlc_frame || !tdm_api->hdlc_framing) { + + DEBUG_ERROR("%s: Error: %s(): invalid request for non-HDLC channel!\n", + tdm_api->name, __FUNCTION__); + wan_clear_bit(WP_TDM_HDLC_TX,&tdm_api->critical); + return SANG_STATUS_INVALID_DEVICE_REQUEST; + } + + if (chan->common.state != WAN_CONNECTED) { + + wan_clear_bit(WP_TDM_HDLC_TX,&tdm_api->critical); + DEBUG_EVENT("%s: Loop test failed: line not in 'connected' state!\n", + card->devname); + return SANG_STATUS_LINE_DISCONNECTED; + } + + skb = wan_skb_alloc(wan_udp_pkt->wan_udp_data_len); + if (skb == NULL) { + + wan_clear_bit(WP_TDM_HDLC_TX,&tdm_api->critical); + return SANG_STATUS_FAILED_ALLOCATE_MEMORY; + } + + /* wan_skb_push(skb, sizeof(wp_api_hdr_t)); */ /* do NOT push the header! */ + + buf = wan_skb_put(skb, wan_udp_pkt->wan_udp_data_len); + memcpy(buf, wan_udp_pkt->wan_udp_data, wan_udp_pkt->wan_udp_data_len); + + err=tdm_api->write_hdlc_frame(tdm_api->chan, skb, &hdr); + + wan_clear_bit(WP_TDM_HDLC_TX,&tdm_api->critical); + return err; +} + +#else static int digital_loop_test(sdla_t* card,wan_udp_pkt_t* wan_udp_pkt) { netskb_t* skb; netdevice_t* dev; char* buf; private_area_t *chan; - + dev = WAN_DEVLE2DEV(WAN_LIST_FIRST(&card->wandev.dev_head)); if (dev == NULL) { return 1; @@ -861,25 +965,25 @@ static int digital_loop_test(sdla_t* card,wan_udp_pkt_t* wan_udp_pkt) if (chan->common.state != WAN_CONNECTED) { DEBUG_EVENT("%s: Loop test failed: dev not connected!\n", - card->devname); + card->devname); return 2; } - + skb = wan_skb_alloc(wan_udp_pkt->wan_udp_data_len+100); if (skb == NULL) { return 3; } - + switch (chan->common.usedby) { - + case API: wan_skb_push(skb, sizeof(wp_api_hdr_t)); break; - + case STACK: case WANPIPE: break; - + case TDM_VOICE: case TDM_VOICE_API: case TDM_VOICE_DCHAN: @@ -887,35 +991,33 @@ static int digital_loop_test(sdla_t* card,wan_udp_pkt_t* wan_udp_pkt) break; } else { DEBUG_EVENT("%s: Loop test failed: no dchan in TDMV mode!\n", - card->devname); + card->devname); } /* Fall into the default case */ - + default: DEBUG_EVENT("%s: Loop test failed: invalid operation mode!\n", card->devname); wan_skb_free(skb); return 4; } - + buf = wan_skb_put(skb, wan_udp_pkt->wan_udp_data_len); memcpy(buf, wan_udp_pkt->wan_udp_data, wan_udp_pkt->wan_udp_data_len); - + #if defined(__LINUX__) skb->next = skb->prev = NULL; - skb->dev = dev; - skb->protocol = htons(ETH_P_IP); + skb->dev = dev; + skb->protocol = htons(ETH_P_IP); wan_skb_reset_mac_header(skb); - dev_queue_xmit(skb); + dev_queue_xmit(skb); #elif defined(__FreeBSD__) || defined(__OpenBSD__) DEBUG_EVENT("%s: WARNING: Digital loop test mode is not supported!\n", - card->devname); + card->devname); #endif return 0; } - - - +#endif #if defined(AFT_XMTP2_TAP) static int xmtp2km_tap_func(void *prot_ptr, int slot, int dir, unsigned char *data, int len) @@ -946,6 +1048,40 @@ static int xmtp2km_tap_func(void *prot_ptr, int slot, int dir, unsigned char *da #endif +static int wp_bert_allocate_tx_bert_skb(private_area_t* chan) +{ + void *buf; + + if (chan->tx_bert_skb) { + /* the 'tx_bert_skb' already allocated. */ + return 0; + } + + if (chan->hdlc_eng) { + /* For MTU of 2048 dma_mru will be 4096; + * it will result in 1024 bytes being transmitted + * during BERT. */ + chan->bert_data_length = chan->dma_mru / 4; + } else { + chan->bert_data_length = chan->max_idle_size; + } + + chan->tx_bert_skb = wan_skb_alloc(chan->dma_mru); + if (!chan->tx_bert_skb){ + return -EINVAL; + } + + /* reset the tx_bert_skb buffer to the actual mtu size */ + wan_skb_init(chan->tx_bert_skb, sizeof(wp_api_hdr_t)); + wan_skb_trim(chan->tx_bert_skb, 0); + buf=wan_skb_put(chan->tx_bert_skb, chan->bert_data_length); + + memset(buf, 0x00, chan->bert_data_length); + + return 0; +} + + /*============================================================================= * process_udp_mgmt_pkt @@ -1033,11 +1169,11 @@ int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, private_area_t* chan, i /* line_mode: MODE_OPTION_HDLC/MODE_OPTION_BITSTRM/ISDN_BRI_DCHAN */ if (IS_BRI_CARD(card) && (chan->dchan_time_slot >= 0)){ - _snprintf(if_cfg->line_mode, USED_BY_FIELD, "%s", ISDN_BRI_DCHAN); + wp_snprintf(if_cfg->line_mode, USED_BY_FIELD, "%s", ISDN_BRI_DCHAN); } else if (chan->hdlc_eng) { - _snprintf(if_cfg->line_mode, USED_BY_FIELD, "%s", MODE_OPTION_HDLC); + wp_snprintf(if_cfg->line_mode, USED_BY_FIELD, "%s", MODE_OPTION_HDLC); } else { - _snprintf(if_cfg->line_mode, USED_BY_FIELD, "%s", MODE_OPTION_BITSTRM); + wp_snprintf(if_cfg->line_mode, USED_BY_FIELD, "%s", MODE_OPTION_BITSTRM); } if(IS_FXOFXS_MEDIA(&card->fe.fe_cfg)){ @@ -1047,7 +1183,7 @@ int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, private_area_t* chan, i if(mod_num < MAX_REMORA_MODULES){ if_cfg->sub_media = card->fe.rm_param.mod[mod_num].type; }else{ - DEBUG_EVENT("%s(): %s: Error: READ_CONFIGURATION: Invalid mod_num: %d\n", __FUNCTION__, card->devname, mod_num); + DEBUG_ERROR("%s(): %s: Error: READ_CONFIGURATION: Invalid mod_num: %d\n", __FUNCTION__, card->devname, mod_num); } }else if(IS_BRI_CARD(card)){ if(aft_is_bri_te_card(card)){ @@ -1089,13 +1225,9 @@ int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, private_area_t* chan, i break; case WANPIPEMON_AFT_LINK_STATUS: - wan_udp_pkt->wan_udp_return_code = 0; - if (card->wandev.state == WAN_CONNECTED){ - wan_udp_pkt->wan_udp_data[0]=1; - }else{ - wan_udp_pkt->wan_udp_data[0]=0; - } - wan_udp_pkt->wan_udp_data_len=1; + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; + wan_udp_pkt->wan_udp_data[0] = card->wandev.state;/* WAN_CONNECTED/WAN_DISCONNECTED */ + wan_udp_pkt->wan_udp_data_len = 1; break; case WANPIPEMON_AFT_MODEM_STATUS: @@ -1121,10 +1253,28 @@ int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, private_area_t* chan, i break; case WANPIPEMON_DIGITAL_LOOPTEST: +#if defined(__WINDOWS__) + wan_udp_pkt->wan_udp_return_code = + (u8)digital_loop_test(card, chan, wan_udp_pkt); +#else wan_udp_pkt->wan_udp_return_code = (u8)digital_loop_test(card,wan_udp_pkt); +#endif break; - + +#ifdef WANPIPE_PERFORMANCE_DEBUG + case WANPIPEMON_READ_PEFORMANCE_STATS: + memcpy(wan_udp_pkt->wan_udp_data,&card->aft_perf_stats,sizeof(card->aft_perf_stats)); + wan_udp_pkt->wan_udp_data_len=sizeof(card->aft_perf_stats); + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; + break; + + case WANPIPEMON_FLUSH_PEFORMANCE_STATS: + memset(&card->aft_perf_stats,0,sizeof(card->aft_perf_stats)); + wan_udp_pkt->wan_udp_data_len=sizeof(card->aft_perf_stats); + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; + break; +#endif case WANPIPEMON_READ_OPERATIONAL_STATS: memcpy(wan_udp_pkt->wan_udp_data,&chan->chan_stats,sizeof(wp_tdm_chan_stats_t)); wan_udp_pkt->wan_udp_data_len=sizeof(wp_tdm_chan_stats_t); @@ -1204,7 +1354,7 @@ int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, private_area_t* chan, i wan_set_bit (0,&trace_info->tracing_enabled); }else{ - DEBUG_EVENT("%s: Error: AFT trace running!\n", + DEBUG_ERROR("%s: Error: AFT trace running!\n", card->devname); wan_udp_pkt->wan_udp_return_code = 2; } @@ -1246,7 +1396,7 @@ int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, private_area_t* chan, i if(wan_test_bit(0,&trace_info->tracing_enabled)){ trace_info->trace_timeout = SYSTEM_TICKS; }else{ - DEBUG_EVENT("%s: Error AFT trace not enabled\n", + DEBUG_ERROR("%s: Error AFT trace not enabled\n", card->devname); /* set return code */ wan_udp_pkt->wan_udp_return_code = 1; @@ -1382,11 +1532,47 @@ int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, private_area_t* chan, i case WANPIPEMON_ROUTER_UP_TIME: wan_getcurrenttime(&chan->router_up_time, NULL); chan->router_up_time -= chan->router_start_time; - *(u32 *)&wan_udp_pkt->wan_udp_data = chan->router_up_time; + *(wan_time_t *)&wan_udp_pkt->wan_udp_data = chan->router_up_time; wan_udp_pkt->wan_udp_data_len = sizeof(u32); wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; break; + case WANPIPEMON_GEN_FIFO_ERR_TX: + card->wp_debug_gen_fifo_err_tx=1; + + DEBUG_EVENT("%s: Wanipemon Gen TX fifo error\n",card->devname); + wan_udp_pkt->wan_udp_data_len = 0; + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; + break; + + case WANPIPEMON_GEN_FIFO_ERR_RX: + card->wp_debug_gen_fifo_err_rx=1; + + DEBUG_EVENT("%s: Wanipemon Gen RX fifo error\n",card->devname); + wan_udp_pkt->wan_udp_data_len = 0; + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; + break; + + case WANPIPEMON_GEN_FE_SYNC_ERR: + if (wan_test_bit(AFT_TDM_FE_SYNC_CNT,&card->u.aft.chip_cfg_status)) { + u32 reg,cnt; + card->hw_iface.bus_read_4(card->hw, AFT_PORT_REG(card,AFT_LINE_CFG_REG), ®); + cnt = aft_lcfg_get_fe_sync_cnt(reg); + + DEBUG_EVENT("%s: FE SYNC Cnt %i!\n",card->devname,cnt); + + //aft_lcfg_set_fe_sync_cnt(®,1); + //card->hw_iface.bus_write_4(card->hw,AFT_PORT_REG(card,AFT_LINE_CFG_REG), reg); + + wan_udp_pkt->wan_udp_data_len = 0; + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; + } else { + DEBUG_EVENT("%s: Error: WANPIPEMON_GEN_FE_SYNC_ERR not supported on this hw!\n",card->devname); + wan_udp_pkt->wan_udp_data_len = 0; + wan_udp_pkt->wan_udp_return_code = EPFNOSUPPORT; + } + break; + case WAN_GET_PROTOCOL: wan_udp_pkt->wan_udp_data[0] = (u8)card->wandev.config_id; wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; @@ -1412,7 +1598,7 @@ int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, private_area_t* chan, i case WANPIPEMON_GET_OPEN_HANDLES_COUNTER: #if defined(__WINDOWS__) - *(int*)&wan_udp_pkt->wan_udp_data[0] = dev->open_handle_counter; + *(int*)&wan_udp_pkt->wan_udp_data[0] = wp_get_netdev_open_handles_count(dev); #else *(int*)&wan_udp_pkt->wan_udp_data[0] = 1; #endif @@ -1423,7 +1609,7 @@ int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, private_area_t* chan, i #if defined(__WINDOWS__) case WANPIPEMON_EC_IOCTL: if (wan_test_and_set_bit(CARD_HW_EC,&card->wandev.critical)){ - DEBUG_EVENT("%s: Error: EC IOCTL Reentrant!\n", card->devname); + DEBUG_ERROR("%s: Error: EC IOCTL Reentrant!\n", card->devname); return -EBUSY; } @@ -1504,6 +1690,13 @@ int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, private_area_t* chan, i wan_udp_pkt->wan_udp_data_len = ETHER_ADDR_LEN; break; + case WANPIPEMON_GET_BIOS_ENCLOSURE3_SERIAL_NUMBER: + wan_udp_pkt->wan_udp_return_code = + wp_get_motherboard_enclosure_serial_number(wan_udp_pkt->wan_udp_data, + sizeof(wan_udp_pkt->wan_udp_data)); + + wan_udp_pkt->wan_udp_data_len = sizeof(wan_udp_pkt->wan_udp_data); + break; #endif case WANPIPEMON_AFT_CUSTOMER_ID: { @@ -1550,6 +1743,86 @@ int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, private_area_t* chan, i wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; break; + case WANPIPEMON_ENABLE_BERT: + + wan_spin_lock_irq(&card->wandev.lock,&smp_flags); + if ( wan_test_bit(WP_MAINTENANCE_MODE_BERT, &chan->maintenance_mode_bitmap) ) { + wan_udp_pkt->wan_udp_return_code = EBUSY; + wan_spin_unlock_irq(&card->wandev.lock,&smp_flags); + DEBUG_WARNING("%s: Warning: BERT already running!\n", chan->if_name); + break; + } + wan_spin_unlock_irq(&card->wandev.lock,&smp_flags); + + if (wp_bert_allocate_tx_bert_skb(chan)) { + wan_udp_pkt->wan_udp_return_code = ENOMEM; + DEBUG_ERROR("%s: Error: failed memory allocation for BERT!\n", chan->if_name); + break; + } + + { + int err; + + DEBUG_EVENT("%s: Starting BERT (type:%s).\n", chan->if_name, + WP_BERT_DECODE_SEQUENCE_TYPE(wan_udp_pkt->wan_udp_data[0])); + + err = wp_bert_set_sequence_type(&chan->wp_bert, + wan_udp_pkt->wan_udp_data[0] /* sequence type */); + + wan_spin_lock_irq(&card->wandev.lock,&smp_flags); + if(!err) { + /* everything is ready for the test to start */ + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; + wan_set_bit(WP_MAINTENANCE_MODE_BERT, &chan->maintenance_mode_bitmap); + + /* For HDLC channel the first transmission must be initiated, + * after that it will continue transmission automatically. + * This especially important for A101/A102, not so much for A104/A108. */ + if (chan->hdlc_eng) { + aft_dma_tx (card, chan); + } + + } else { + wan_udp_pkt->wan_udp_return_code = err; + } + wan_spin_unlock_irq(&card->wandev.lock,&smp_flags); + } + break; + + case WANPIPEMON_DISABLE_BERT: + DEBUG_EVENT("%s: Stopping BERT.\n", chan->if_name); + + wan_spin_lock_irq(&card->wandev.lock,&smp_flags); + wan_clear_bit(WP_MAINTENANCE_MODE_BERT, &chan->maintenance_mode_bitmap); + wan_spin_unlock_irq(&card->wandev.lock,&smp_flags); + + /* reset internal state of BERT by setting new sequence type */ + wp_bert_set_sequence_type(&chan->wp_bert, WP_BERT_RANDOM_SEQUENCE); + + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; + break; + + case WANPIPEMON_GET_BERT_STATUS: + + if ( !wan_test_bit(WP_MAINTENANCE_MODE_BERT, &chan->maintenance_mode_bitmap) ) { + DEBUG_WARNING("%s: Warning: BERT Status is invalid when BERT is not running!\n", + chan->if_name); + } + + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; + { + wp_bert_status_t *wp_bert_status = (wp_bert_status_t*)&wan_udp_pkt->wan_udp_data[0]; + + wp_bert_status->state = + (wp_bert_is_synchronized(&chan->wp_bert) == 1 ? + WP_BERT_STATUS_IN_SYNCH : WP_BERT_STATUS_OUT_OF_SYNCH); + + wp_bert_status->errors = wp_bert_get_errors(&chan->wp_bert); + wp_bert_status->synchonized_count = wp_bert_get_synchonized_count(&chan->wp_bert); + } + wan_udp_pkt->wan_udp_data_len = sizeof(u8); + break; + default: if ((wan_udp_pkt->wan_udp_command == WAN_GET_MEDIA_TYPE) || ((wan_udp_pkt->wan_udp_command & 0xF0) == WAN_FE_UDP_CMD_START)){ @@ -1572,7 +1845,7 @@ int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, private_area_t* chan, i wan_udp_pkt->wan_udp_data_len = 0; wan_udp_pkt->wan_udp_return_code = WAN_UDP_INVALID_NET_CMD; - DEBUG_EVENT("%s: %s: Error: Unsupported Management command: %x\n", + DEBUG_ERROR("%s: %s: Error: Unsupported Management command: %x\n", __FUNCTION__,card->devname,wan_udp_pkt->wan_udp_command); break; } /* end of switch */ @@ -1629,7 +1902,7 @@ int wan_user_process_udp_mgmt_pkt(void* card_ptr, void* chan_ptr, void *udata) * thus we can release the irq */ if (wan_atomic_read(&chan->udp_pkt_len) > sizeof(wan_udp_pkt_t)){ - DEBUG_EVENT( "%s: Error: Pipemon buf too bit on the way up! %d\n", + DEBUG_ERROR( "%s: Error: Pipemon buf too bit on the way up! %d\n", card->devname,wan_atomic_read(&chan->udp_pkt_len)); wan_atomic_set(&chan->udp_pkt_len,0); return -EINVAL; @@ -1893,7 +2166,7 @@ int aft_handle_clock_master (sdla_t *card_ptr) aft_bri_clock_control(card,0); } else { - DEBUG_EVENT("%s: Internal Error: ctrl_clock feature not available!\n", + DEBUG_ERROR("%s: Internal Error: ctrl_clock feature not available!\n", card->devname); aft_bri_clock_control(card,0); } @@ -1959,7 +2232,11 @@ int aft_handle_clock_master (sdla_t *card_ptr) legacy_tdm_timer_enable = 0; } - if (master_clock_src_found || legacy_tdm_timer_enable == 0) { + if (IS_BRI_CARD(card_ptr)) { + if (master_clock_src_found) { + return 0; + } + } else if (legacy_tdm_timer_enable == 0) { return 0; } @@ -2051,11 +2328,9 @@ void wanpipe_wake_stack(private_area_t* chan) } } else if (chan->common.usedby == STACK){ -#if defined(__WINDOWS__) - FUNC_NOT_IMPL(); -#else + wanpipe_lip_kick(chan,0); -#endif + } else if (chan->wp_api_op_mode || chan->common.usedby == TDM_VOICE_DCHAN){ #ifdef AFT_TDM_API_SUPPORT if (is_tdm_api(chan,chan->wp_tdm_api_dev)){ @@ -2120,7 +2395,7 @@ int aft_find_master_if_and_dchan(sdla_t *card, int *master_if, u32 active_ch) } if (!dchan_found) { - DEBUG_EVENT("%s: Error: TDM DCHAN is out of range 0x%08X\n", + DEBUG_ERROR("%s: Error: TDM DCHAN is out of range 0x%08X\n", card->devname,card->tdmv_conf.dchan); return -EINVAL; } @@ -2275,7 +2550,7 @@ int aft_tdm_ring_rsync(sdla_t *card) chan=(private_area_t*)card->u.aft.dev_to_ch_map[i]; if (!chan){ - DEBUG_EVENT("%s: Error: No Dev for Rx logical ch=%d\n", + DEBUG_ERROR("%s: Error: No Dev for Rx logical ch=%d\n", card->devname,i); continue; } @@ -2371,7 +2646,7 @@ void aft_list_tx_descriptors(private_area_t *chan) dma_chain = &chan->tx_dma_chain_table[i]; if (!dma_chain){ - DEBUG_EVENT("%s:%d Assertion Error !!!!\n", + DEBUG_ERROR("%s:%d Assertion Error !!!!\n", __FUNCTION__,__LINE__); break; } @@ -2466,7 +2741,7 @@ static void aft_list_descriptors(private_area_t *chan) dma_chain = &chan->rx_dma_chain_table[i]; if (!dma_chain){ - DEBUG_EVENT("%s:%d Assertion Error !!!!\n", + DEBUG_ERROR("%s:%d Assertion Error !!!!\n", __FUNCTION__,__LINE__); break; } @@ -2845,7 +3120,7 @@ int aft_register_dump(sdla_t *card) } chan=(private_area_t*)card->u.aft.dev_to_ch_map[i]; if (!chan){ - DEBUG_EVENT("%s: Error: No Dev for Rx logical ch=%d\n", + DEBUG_ERROR("%s: Error: No Dev for Rx logical ch=%d\n", card->devname,i); continue; } @@ -2894,6 +3169,251 @@ int aft_register_dump(sdla_t *card) } +/* Currently the background timer is not used */ + +int aft_background_timer_kill(sdla_t* card) +{ +#if 0 + wan_set_bit(AFT_BG_TIMER_KILL,(void*)&card->u.aft.bg_timer_cmd); + + if (!wan_test_bit(AFT_BG_TIMER_RUNNING,(void*)&card->u.aft.bg_timer_cmd)) { + return 0; + } + + wan_del_timer(&card->u.aft.bg_timer); +#endif + return 0; +} + +int aft_background_timer_add(sdla_t* card, unsigned long delay) +{ +#if 0 + int err; + + if (wan_test_bit(CARD_DOWN,&card->wandev.critical)) { + wan_set_bit(AFT_BG_TIMER_KILL,(void*)&card->u.aft.bg_timer_cmd); + wan_clear_bit(AFT_BG_TIMER_RUNNING,(void*)&card->u.aft.bg_timer_cmd); + return 0; + } + + if (wan_test_bit(AFT_BG_TIMER_KILL,(void*)&card->u.aft.bg_timer_cmd)) { + wan_clear_bit(AFT_BG_TIMER_RUNNING,(void*)&card->u.aft.bg_timer_cmd); + return 0; + } + + if (wan_test_bit(AFT_BG_TIMER_RUNNING,(void*)&card->u.aft.bg_timer_cmd)) { + return 0; + } + + err = wan_add_timer(&card->u.aft.bg_timer, delay * HZ ); + + if (err){ + /* Failed to add timer */ + return -EINVAL; + } + + wan_set_bit(AFT_BG_TIMER_RUNNING,(void*)&card->u.aft.bg_timer_cmd); +#endif + return 0; +} + +#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) +void aft_background_timer_expire(void* pcard) +#elif defined(__WINDOWS__) +void aft_background_timer_expire(IN PKDPC Dpc, void* pcard, void* arg2, void* arg3) +#else +void aft_background_timer_expire(unsigned long pcard) +#endif +{ +#if 0 + sdla_t *card = (sdla_t*)pcard; + + if (wan_test_bit(CARD_DOWN,&card->wandev.critical)) { + wan_set_bit(AFT_BG_TIMER_KILL,(void*)&card->u.aft.bg_timer_cmd); + wan_clear_bit(AFT_BG_TIMER_RUNNING,(void*)&card->u.aft.bg_timer_cmd); + return; + } + + if (wan_test_bit(AFT_BG_TIMER_KILL,(void*)&card->u.aft.bg_timer_cmd)) { + wan_clear_bit(AFT_BG_TIMER_RUNNING,(void*)&card->u.aft.bg_timer_cmd); + return; + } + + if (!wan_test_bit(AFT_BG_TIMER_RUNNING,(void*)&card->u.aft.bg_timer_cmd)){ + /* Somebody clear this bit */ + DEBUG_EVENT("%s: %s() Timer AFT_BG_TIMER_RUNNING is cleared (should never happened)!\n", + card->devname, __FUNCTION__); + return; + } + + wan_clear_bit(AFT_BG_TIMER_RUNNING,(void*)&card->u.aft.bg_timer_cmd); + + DEBUG_EVENT("%s: aft_background_timer_expire() \n",card->devname); + /* Sanity Check */ + if (wan_test_bit(CARD_DOWN,&card->wandev.critical)) { + wan_set_bit(AFT_BG_TIMER_KILL,(void*)&card->u.aft.bg_timer_cmd); + wan_clear_bit(AFT_BG_TIMER_RUNNING,(void*)&card->u.aft.bg_timer_cmd); + return; + } + + if (wan_test_bit(AFT_BG_TIMER_KILL,(void*)&card->u.aft.bg_timer_cmd)) { + wan_clear_bit(AFT_BG_TIMER_RUNNING,(void*)&card->u.aft.bg_timer_cmd); + return; + } + + aft_background_timer_add(card,1); +#endif + + return; +} + +/* Read status of Front End Loop Back (T1/E1) - is it + * enabled or disabled. */ +int aft_fe_loop_back_status(sdla_t *card) +{ + return card->fe.te_param.lb_mode_map; +} + +/** @brief reset */ +int wp_bert_reset(wp_bert_t *bert) +{ + int err = 0; + + bert->m_bSynchronized = 0; + bert->m_uiErrors = 0; + bert->m_uiSynchronizedCount = 0; + bert->m_uiNumberOfIdenticalValues = 0; + + switch(bert->m_eSequenceType) { + case WP_BERT_RANDOM_SEQUENCE: + { + size_t uiSequenceLength + = sizeof(wp_bert_random_sequence) / sizeof(wp_bert_random_sequence[0]); + bert->m_pSequenceBegin = (unsigned char*)&wp_bert_random_sequence[0]; + bert->m_pSequenceEnd = (unsigned char*)&wp_bert_random_sequence[uiSequenceLength]; + bert->m_uiNumberOfIdenticalValuesRequireToSynchronize = uiSequenceLength / 4; + break; + } + + case WP_BERT_ASCENDANT_SEQUENCE: + { + size_t uiSequenceLength + = sizeof(wp_bert_ascendant_sequence) / sizeof(wp_bert_ascendant_sequence[0]); + bert->m_pSequenceBegin = (unsigned char*)&wp_bert_ascendant_sequence[0]; + bert->m_pSequenceEnd = (unsigned char*)&wp_bert_ascendant_sequence[uiSequenceLength]; + bert->m_uiNumberOfIdenticalValuesRequireToSynchronize = uiSequenceLength / 4; + break; + } + + case WP_BERT_DESCENDANT_SEQUENCE: + { + size_t uiSequenceLength + = sizeof(wp_bert_descendant_sequence) / sizeof(wp_bert_descendant_sequence[0]); + bert->m_pSequenceBegin = (unsigned char*)&wp_bert_descendant_sequence[0]; + bert->m_pSequenceEnd = (unsigned char*)&wp_bert_descendant_sequence[uiSequenceLength]; + bert->m_uiNumberOfIdenticalValuesRequireToSynchronize = uiSequenceLength / 4; + break; + } + + default: + DEBUG_ERROR("%s(): Invalid BERT type selected!\n", __FUNCTION__); + err = EINVAL; + } + + bert->m_pNextValue = bert->m_pSequenceBegin; + bert->m_pNextExpectedValue = bert->m_pSequenceBegin; + return err; +} + +/** @brief change the type sequence used by this bert */ +int wp_bert_set_sequence_type(wp_bert_t *bert, wp_bert_sequence_type_t sequence_type) +{ + bert->m_eSequenceType = sequence_type; + return wp_bert_reset(bert); +} + +/** @bried Get the number of times when the BERT entered in the + synchronized state */ +u32 wp_bert_get_synchonized_count(wp_bert_t *bert) +{ + return bert->m_uiSynchronizedCount; +} + +/** @brief Get the number errors */ +u32 wp_bert_get_errors(wp_bert_t *bert) +{ + return bert->m_uiErrors; +} + +/** @brief Returns 1 when the BERT is synchronized */ +u8 wp_bert_is_synchronized(wp_bert_t *bert) +{ + return bert->m_bSynchronized; +} + +void wp_bert_increase_sequence_value_ptr(wp_bert_t *bert, unsigned char **out_pucPtr) +{ + ++(*out_pucPtr);/* increment value at 'out_pucPtr' */ + + if( *out_pucPtr == bert->m_pSequenceEnd ) { + *out_pucPtr = bert->m_pSequenceBegin; /* wrap... */ + } +} + +/** @brief Return the next value to stream toward the remote BERT + entity */ +u8 wp_bert_pop_value(wp_bert_t *bert, u8 *value) +{ + *value = *bert->m_pNextValue; + wp_bert_increase_sequence_value_ptr( bert, &bert->m_pNextValue ); + return *value; +} + + +/** @brief Push value in the BERT. This method return 1 if the + BERT is synchronized and 0 otherwise. The expected value is + also set when the BERT is not synchronized. */ +u8 wp_bert_push_value(wp_bert_t *bert, u8 current_value, u8 *expected_value) +{ + *expected_value = *bert->m_pNextExpectedValue; + if( bert->m_bSynchronized ) { + if( current_value == *bert->m_pNextExpectedValue ) { + wp_bert_increase_sequence_value_ptr( bert, &bert->m_pNextExpectedValue ); + } else { + /* got in synch */ + bert->m_bSynchronized = 0; + bert->m_uiNumberOfIdenticalValues = 0; + bert->m_uiErrors++; + } + } else { + if( current_value == *bert->m_pNextExpectedValue ) { + wp_bert_increase_sequence_value_ptr( bert, &bert->m_pNextExpectedValue ); + if( ++bert->m_uiNumberOfIdenticalValues + == bert->m_uiNumberOfIdenticalValuesRequireToSynchronize ) { + /* got out of synch */ + bert->m_bSynchronized = 1; + ++bert->m_uiSynchronizedCount; + } + } else { + bert->m_uiNumberOfIdenticalValues = 0; + } + bert->m_uiErrors++; + } + + return bert->m_bSynchronized; +} + +/** @brief print current state of BERT */ +void wp_bert_print_state(wp_bert_t *bert) +{ + DEBUG_EVENT("BERT: isSynchronized=%s, errors=%d, synchronizedCount=%d\n", + (wp_bert_is_synchronized(bert) == 1 ? "Yes":"No"), + wp_bert_get_errors(bert), + wp_bert_get_synchonized_count(bert)); +} + + +/************** EOF *************/ diff --git a/patches/kdrivers/src/net/sdla_8te1.c b/patches/kdrivers/src/net/sdla_8te1.c index c09cc5c..ec3218e 100644 --- a/patches/kdrivers/src/net/sdla_8te1.c +++ b/patches/kdrivers/src/net/sdla_8te1.c @@ -36,17 +36,28 @@ ** sdla_8te1.c WANPIPE(tm) Multiprotocol WAN Link Driver. ** 8 ports T1/E1 board configuration. ** -** Author: Alex Feldman +** Author: Alex Feldman ** ** ============================================================================ -** Date Name Label Description +** Date Name Label Description ** ============================================================================ -** 02-18-06 Alex Feldman Initial version. -** 07-10-07 Alex Feldman EBIT Enable auto E-bit support. -** Nov 23, 2007 Alex Feldman UNFRM Add support E1 Unframe mode for E1 -** interface. -** Nov 23, 2007 Alex Feldman TXTRI Add support for TX Tri-state. +** +** Feb 01, 2010 David Rokhvarg PATTERN_LEN +** Fixed division-by-zero error in BERT +** test, when test pattern was *not* +** initialized by user-mode application. +** ** Feb 06, 2008 Alex Feldman E1_120 Adjust waveform for E1, 120 ohm. +** +** Nov 23, 2007 Alex Feldman TXTRI Add support for TX Tri-state. +** +** Nov 23, 2007 Alex Feldman UNFRM Add support E1 Unframe mode for E1 +** interface. +** +** 07-10-07 Alex Feldman EBIT Enable auto E-bit support. +** +** 02-18-06 Alex Feldman Initial version. + ******************************************************************************/ /****************************************************************************** @@ -127,9 +138,11 @@ (int)fe_line_no, \ (int)sdla_ds_te1_address(fe,fe_line_no,(reg))) -#define WAN_TE1_FRAMED_ALARMS (WAN_TE_BIT_ALARM_RED | WAN_TE_BIT_ALARM_LOF | WAN_TE_BIT_ALARM_LIU_LOS) +#define WAN_T1_FRAMED_ALARMS (WAN_TE_BIT_ALARM_RED | WAN_TE_BIT_ALARM_LOF) +#define WAN_E1_FRAMED_ALARMS (WAN_TE_BIT_ALARM_RED | WAN_TE_BIT_ALARM_LOF) // | WAN_TE_BIT_ALARM_LIU_LOS) /*Nov 23, 2007 UNFRM */ -#define WAN_TE1_UNFRAMED_ALARMS (WAN_TE_BIT_ALARM_RED | WAN_TE_BIT_ALARM_LOS) +#define WAN_TE1_UNFRAMED_ALARMS (WAN_TE_BIT_ALARM_RED | \ + WAN_TE_BIT_ALARM_LOS) #define IS_T1_ALARM(alarm) \ (alarm & \ @@ -467,7 +480,7 @@ static int sdla_ds_te1_device_id(sdla_fe_t* fe) fe->fe_max_ports = 2; break; default: - DEBUG_EVENT("%s: ERROR: Unsupported DS %s CHIP (%02X:%02X)\n", + DEBUG_ERROR("%s: ERROR: Unsupported DS %s CHIP (%02X:%02X)\n", fe->name, FE_MEDIA_DECODE(fe), fe->fe_chip_id, @@ -679,7 +692,7 @@ static int sdla_ds_t1_cfg_verify(void* pfe) WAN_FE_FRAME(fe) = WAN_FR_ESF; break; default: - DEBUG_EVENT("%s: Error: Invalid %s FE Framing type (%X)\n", + DEBUG_ERROR("%s: Error: Invalid %s FE Framing type (%X)\n", fe->name, FE_MEDIA_DECODE(fe), WAN_FE_FRAME(fe)); @@ -697,7 +710,7 @@ static int sdla_ds_t1_cfg_verify(void* pfe) WAN_FE_LCODE(fe) = WAN_LCODE_B8ZS; break; default: - DEBUG_EVENT("%s: Error: Invalid %s FE Line code type (%X)\n", + DEBUG_ERROR("%s: Error: Invalid %s FE Line code type (%X)\n", fe->name, FE_MEDIA_DECODE(fe), WAN_FE_LCODE(fe)); @@ -721,7 +734,7 @@ static int sdla_ds_t1_cfg_verify(void* pfe) WAN_TE1_LBO(fe) = WAN_T1_LBO_0_DB; break; default: - DEBUG_EVENT("%s: Error: Invalid %s LBO value (%X)\n", + DEBUG_ERROR("%s: Error: Invalid %s LBO value (%X)\n", fe->name, FE_MEDIA_DECODE(fe), WAN_TE1_LBO(fe)); @@ -789,7 +802,7 @@ static int sdla_ds_e1_cfg_verify(void* pfe) WAN_FE_FRAME(fe) = WAN_FR_CRC4; break; default: - DEBUG_EVENT("%s: Error: Invalid %s FE Framing type (%X)\n", + DEBUG_ERROR("%s: Error: Invalid %s FE Framing type (%X)\n", fe->name, FE_MEDIA_DECODE(fe), WAN_FE_FRAME(fe)); @@ -806,7 +819,7 @@ static int sdla_ds_e1_cfg_verify(void* pfe) WAN_FE_LCODE(fe) = WAN_LCODE_HDB3; break; default: - DEBUG_EVENT("%s: Error: Invalid %s FE Line code type (%X)\n", + DEBUG_ERROR("%s: Error: Invalid %s FE Line code type (%X)\n", fe->name, FE_MEDIA_DECODE(fe), WAN_FE_LCODE(fe)); @@ -824,7 +837,7 @@ static int sdla_ds_e1_cfg_verify(void* pfe) WAN_TE1_LBO(fe) = WAN_E1_120; break; default: - DEBUG_EVENT("%s: Error: Invalid %s LBO value (%X)\n", + DEBUG_ERROR("%s: Error: Invalid %s LBO value (%X)\n", fe->name, FE_MEDIA_DECODE(fe), WAN_TE1_LBO(fe)); @@ -841,7 +854,7 @@ static int sdla_ds_e1_cfg_verify(void* pfe) WAN_TE1_SIG_MODE(fe) = WAN_TE1_SIG_CCS; break; default: - DEBUG_EVENT("%s: Error: Invalid E1 Signalling type (%X)\n", + DEBUG_ERROR("%s: Error: Invalid E1 Signalling type (%X)\n", fe->name, WAN_TE1_SIG_MODE(fe)); return -EINVAL; @@ -1282,8 +1295,9 @@ static int sdla_ds_te1_chip_config(void* pfe) if (IS_FE_TXTRISTATE(fe)){ DEBUG_EVENT("%s: Disable TX (tri-state mode)\n", fe->name); - }else{ - WRITE_REG(REG_LMCR, BIT_LMCR_TE); + }else{ + /* Sep 17, 2009 - Auto AIS transmition (experimental) */ + WRITE_REG(REG_LMCR, BIT_LMCR_ATAIS | BIT_LMCR_TE); } /* INIT RBS bits to 1 */ @@ -1414,7 +1428,7 @@ static int sdla_ds_te1_config(void* pfe) }else if (IS_E1_FEMEDIA(fe)){ err = sdla_ds_e1_cfg_verify(fe); }else{ - DEBUG_EVENT("%s: Error: Invalid FE Media type (%X)\n", + DEBUG_ERROR("%s: Error: Invalid FE Media type (%X)\n", fe->name, WAN_FE_MEDIA(fe)); err =-EINVAL; @@ -1550,7 +1564,6 @@ static int sdla_ds_te1_pre_release(void* pfe) wan_free(fe->swirq); fe->swirq = NULL; } - return 0; } @@ -1670,7 +1683,16 @@ sdla_ds_te1_sigctrl(sdla_fe_t *fe, int sig_mode, unsigned long ch_map, int mode) ******************************************************************************/ static u_int32_t sdla_ds_t1_is_alarm(sdla_fe_t *fe, u_int32_t alarms) { - return (alarms & WAN_TE1_FRAMED_ALARMS); + u_int32_t alarm_mask = WAN_T1_FRAMED_ALARMS; + + /* Alex Feb 27, 2008 + ** Special case for customer that uses + ** YEL alarm for protocol control */ + if (fe->fe_cfg.cfg.te_cfg.ignore_yel_alarm == WANOPT_NO){ + alarm_mask |= WAN_TE_BIT_RAI_ALARM; + } + + return (alarms & alarm_mask); } /****************************************************************************** @@ -1693,8 +1715,16 @@ static u_int32_t sdla_ds_e1_is_alarm(sdla_fe_t *fe, u_int32_t alarms) WAN_TE_BIT_ALARM_LIU_LOS); } }else{ - alarm_mask = WAN_TE1_FRAMED_ALARMS; + alarm_mask = WAN_E1_FRAMED_ALARMS; } + + /* Alex Feb 27, 2008 + ** Special case for customer that uses + ** YEL alarm for protocol control */ + if (fe->fe_cfg.cfg.te_cfg.ignore_yel_alarm == WANOPT_NO){ + alarm_mask |= WAN_TE_BIT_RAI_ALARM; + } + return (alarms & alarm_mask); } @@ -1720,66 +1750,75 @@ static int sdla_ds_te1_set_status(sdla_fe_t* fe, u_int32_t alarms) if (valid_rx_alarms){ if (fe->fe_status != FE_DISCONNECTED){ - new_fe_status = FE_DISCONNECTED; + new_fe_status = FE_DISCONNECTED; } }else{ if (fe->fe_status != FE_CONNECTED){ - new_fe_status = FE_CONNECTED; + new_fe_status = FE_CONNECTED; } } if (fe->fe_status == new_fe_status){ + + if (fe->te_param.tx_yel_alarm && valid_rx_alarms == WAN_TE_BIT_RAI_ALARM){ + sdla_ds_te1_clear_alarms(fe, WAN_TE_BIT_YEL_ALARM); + } + fe->te_param.status_cnt = 0; - DEBUG_TE1("%s: %s %s...%d\n", + DEBUG_TE1("%s: (1) %s %s...%d\n", + fe->name, + FE_MEDIA_DECODE(fe), + WAN_FE_STATUS_DECODE(fe), + fe->te_param.status_cnt); + return 0; + } + if (new_fe_status == FE_CONNECTED){ + if (fe->te_param.status_cnt > WAN_TE1_STATUS_THRESHOLD){ + if (fe->te_param.tx_yel_alarm){ + sdla_ds_te1_clear_alarms(fe, WAN_TE_BIT_ALARM_YEL); + } + DEBUG_EVENT("%s: %s connected!\n", + fe->name, + FE_MEDIA_DECODE(fe)); + fe->fe_status = FE_CONNECTED; + if (card->wandev.te_report_alarms){ + card->wandev.te_report_alarms( + card, + fe->fe_alarm); + } + }else{ + + DEBUG_TE1("%s: (2) %s %s...%d\n", fe->name, FE_MEDIA_DECODE(fe), WAN_FE_STATUS_DECODE(fe), fe->te_param.status_cnt); - return 0; - } - if (new_fe_status == FE_CONNECTED){ - if (fe->te_param.status_cnt > WAN_TE1_STATUS_THRESHOLD){ - if (fe->te_param.tx_yel_alarm){ - sdla_ds_te1_clear_alarms(fe, WAN_TE_BIT_ALARM_YEL); - } - DEBUG_EVENT("%s: %s connected!\n", - fe->name, - FE_MEDIA_DECODE(fe)); - fe->fe_status = FE_CONNECTED; - if (card->wandev.te_report_alarms){ - card->wandev.te_report_alarms( - card, - fe->fe_alarm); - } - }else{ - if (!fe->te_param.status_cnt){ - DEBUG_TE1("%s: %s connecting...\n", - fe->name, - FE_MEDIA_DECODE(fe)); - } - fe->te_param.status_cnt ++; - fe->fe_status = FE_DISCONNECTED; - DEBUG_TE1("%s: %s connecting...%d\n", - fe->name, - FE_MEDIA_DECODE(fe), - fe->te_param.status_cnt); - } - }else{ - DEBUG_EVENT("%s: %s disconnected!\n", - fe->name, - FE_MEDIA_DECODE(fe)); - fe->fe_status = FE_DISCONNECTED; - if (fe->te_param.tx_yel_alarm){ - sdla_ds_te1_set_alarms(fe, WAN_TE_BIT_ALARM_YEL); - } - fe->te_param.status_cnt = 0; - if (card->wandev.te_report_alarms){ - card->wandev.te_report_alarms(card, fe->fe_alarm); - } - } + fe->te_param.status_cnt ++; + fe->fe_status = FE_DISCONNECTED; + } + }else{ + DEBUG_EVENT("%s: %s disconnected!\n", + fe->name, + FE_MEDIA_DECODE(fe)); + fe->fe_status = FE_DISCONNECTED; - /* Front-End state changed */ + /* Special case, if remote alarms is ONLY RAI, then do not transmit yellow */ + if (!fe->te_param.tx_yel_alarm && !(valid_rx_alarms == WAN_TE_BIT_RAI_ALARM)){ + sdla_ds_te1_set_alarms(fe, WAN_TE_BIT_ALARM_YEL); + } + + /* Special case, loopback if only valid alarm is RAI and we already transmitted yellow, + then we must clear yellow */ + if (fe->te_param.tx_yel_alarm && valid_rx_alarms == WAN_TE_BIT_RAI_ALARM){ + sdla_ds_te1_clear_alarms(fe, WAN_TE_BIT_YEL_ALARM); + } + + fe->te_param.status_cnt = 0; + if (card->wandev.te_report_alarms){ + card->wandev.te_report_alarms(card, fe->fe_alarm); + } + } return 1; } @@ -2125,7 +2164,7 @@ static int sdla_ds_te1_set_alarms(sdla_fe_t* fe, u_int32_t alarms) if (IS_T1_FEMEDIA(fe)){ value = READ_REG(REG_TCR1); if (!(value & BIT_TCR1_T1_TRAI)){ - DEBUG_TE1("%s: Enable transmit RAI alarm\n", + DEBUG_EVENT("%s: Enable transmit RAI alarm\n", fe->name); WRITE_REG(REG_TCR1, value | BIT_TCR1_T1_TRAI); fe->te_param.tx_yel_alarm = 1; @@ -2160,7 +2199,7 @@ static int sdla_ds_te1_clear_alarms(sdla_fe_t* fe, u_int32_t alarms) if (IS_T1_FEMEDIA(fe)){ value = READ_REG(REG_TCR1); if (value & BIT_TCR1_T1_TRAI){ - DEBUG_TE1("%s: Disable transmit RAI alarm\n", + DEBUG_EVENT("%s: Disable transmit RAI alarm\n", fe->name); WRITE_REG(REG_TCR1, value & ~BIT_TCR1_T1_TRAI); fe->te_param.tx_yel_alarm = 0; @@ -2770,11 +2809,11 @@ sdla_ds_te1_intr_ctrl(sdla_fe_t *fe, int dummy, u_int8_t type, u_int8_t mode, un /* In-band loop codes */ if (IS_T1_FEMEDIA(fe)){ - mask = BIT_RIM3_T1_LDNC|BIT_RIM3_T1_LDND | + mask = BIT_RIM3_T1_LDNC|BIT_RIM3_T1_LDND | BIT_RIM3_T1_LUPC|BIT_RIM3_T1_LUPD; WRITE_REG(REG_RIM3, mask); - WRITE_REG(REG_RLS3, 0xFF); - } + WRITE_REG(REG_RLS3, 0xFF); + } //WRITE_REG(REG_RIM4, BIT_RIM4_TIMER); WRITE_REG(REG_RLS4, 0xFF); @@ -2911,22 +2950,32 @@ static int sdla_ds_te1_fr_rxintr_rls1(sdla_fe_t *fe, int silent) } if (WAN_FE_FRAME(fe) != WAN_FR_UNFRAMED){ if (rim1 & (BIT_RIM1_RLOFC | BIT_RIM1_RLOFD)){ - if (rls1 & (BIT_RLS1_RLOFC|BIT_RLS1_RLOFD)){ + if (rls1 & (BIT_RLS1_RLOFC|BIT_RLS1_RLOFD)){ if (rrts1 & BIT_RRTS1_RLOF){ + sdla_ds_te1_swirq_trigger( fe, WAN_TE1_SWIRQ_TYPE_ALARM_LOF, WAN_TE1_SWIRQ_SUBTYPE_ALARM_ON, WAN_T1_ALARM_THRESHOLD_LOF_ON); + }else{ + + if (fe->fe_status == FE_CONNECTED){ + sdla_t *card = (sdla_t*)fe->card; + if (card && card->wandev.te_link_reset){ + card->wandev.te_link_reset(card); + } + } + sdla_ds_te1_swirq_trigger( fe, WAN_TE1_SWIRQ_TYPE_ALARM_LOF, WAN_TE1_SWIRQ_SUBTYPE_ALARM_OFF, WAN_T1_ALARM_THRESHOLD_LOF_OFF); } - } - } + } + } } WRITE_REG(REG_RLS1, rls1); return 0; @@ -3269,7 +3318,7 @@ static int sdla_ds_te1_bert_intr(sdla_fe_t *fe, int silent) if (blsr & BIT_BLSR_BBED && bsim & BIT_BSIM_BBED){ if (!silent && WAN_NET_RATELIMIT()) - DEBUG_EVENT("%s: BERT bit error detected!\n", + DEBUG_ERROR("%s: BERT bit error detected!\n", fe->name); } if (blsr & BIT_BLSR_BBCO && bsim & BIT_BSIM_BBCO){ @@ -3277,7 +3326,7 @@ static int sdla_ds_te1_bert_intr(sdla_fe_t *fe, int silent) fe->name); } if (blsr & BIT_BLSR_BECO && bsim & BIT_BSIM_BECO){ - if (!silent) DEBUG_EVENT("%s: BERT Error Counter overflows!\n", + if (!silent) DEBUG_ERROR("%s: BERT Error Counter overflows!\n", fe->name); } if (blsr & BIT_BLSR_BRA1 && bsim & BIT_BSIM_BRA1){ @@ -3474,6 +3523,7 @@ static void sdla_ds_te1_timer(unsigned long pfe) wan_spin_unlock_irq(&fe->lockirq,&smp_flags); if (!empty || fe->swirq_map){ + DEBUG_TEST("%s: TE1 timer!\n", fe->name); if (wan_test_and_set_bit(TE_TIMER_EVENT_PENDING,(void*)&fe->te_param.critical)){ DEBUG_EVENT("%s: TE1 timer event is pending!\n", fe->name); return; @@ -3578,15 +3628,15 @@ sdla_ds_te1_add_event(sdla_fe_t *fe, sdla_fe_timer_event_t *event) cnt ++; } if (tmp == NULL){ - DEBUG_EVENT("%s: ERROR: Internal Error!!!\n", fe->name); + DEBUG_ERROR("%s: ERROR: Internal Error!!!\n", fe->name); wan_clear_bit(event->type,(void*)&fe->event_map); wan_spin_unlock_irq(&fe->lockirq, &smp_flags); return -EINVAL; } if (cnt > WAN_FE_MAX_QEVENT_LEN){ - DEBUG_EVENT("%s: ERROR: Too many events in event queue!\n", + DEBUG_ERROR("%s: ERROR: Too many events in event queue!\n", fe->name); - DEBUG_EVENT("%s: ERROR: Dropping new event type %d!\n", + DEBUG_ERROR("%s: ERROR: Dropping new event type %d!\n", fe->name, event->type); wan_clear_bit(event->type,(void*)&fe->event_map); wan_spin_unlock_irq(&fe->lockirq, &smp_flags); @@ -3595,7 +3645,7 @@ sdla_ds_te1_add_event(sdla_fe_t *fe, sdla_fe_timer_event_t *event) } WAN_LIST_INSERT_AFTER(tmp, tevent, next); } - DEBUG_TE1("%s: Add new DS Event=0x%X (%d sec)\n", + DEBUG_TEST("%s: Add new DS Event=0x%X (%d sec)\n", fe->name, event->type,event->delay); wan_spin_unlock_irq(&fe->lockirq, &smp_flags); return 0; @@ -3620,7 +3670,7 @@ static int sdla_ds_te1_swirq_link(sdla_fe_t* fe) if (!WAN_STIMEOUT(swirq->start, swirq->delay)){ /* Timeout is not expired yet */ - DEBUG_TE1("%s: T1/E1 link swirq (%s) is not ready (%ld:%ld:%d)...\n", + DEBUG_TEST("%s: T1/E1 link swirq (%s) is not ready (%ld:%ld:%d)...\n", fe->name, WAN_TE1_SWIRQ_SUBTYPE_DECODE(swirq->subtype), swirq->start,SYSTEM_TICKS,swirq->delay*HZ); wan_set_bit(WAN_TE1_SWIRQ_TYPE_LINK, (void*)&fe->swirq_map); @@ -3666,7 +3716,7 @@ static int sdla_ds_te1_swirq_link(sdla_fe_t* fe) subtype = WAN_TE1_SWIRQ_SUBTYPE_LINKREADY; delay = POLLING_TE1_TIMER; }else{ - /* Get alarm status before enabling interrupts */ + /* Read alarm status before enabling fe interrupt */ sdla_ds_te1_read_alarms(fe, WAN_FE_ALARM_READ|WAN_FE_ALARM_UPDATE); /* Enable Basic Interrupt ** Enable automatic update pmon counters */ @@ -3725,7 +3775,7 @@ static int sdla_ds_te1_swirq_alarm(sdla_fe_t* fe, int type) WAN_ASSERT(fe->swirq == NULL); swirq = &fe->swirq[type]; - DEBUG_TE1("%s: %s swirq (%s)\n", + DEBUG_TEST("%s: %s swirq (%s)\n", fe->name, WAN_TE1_SWIRQ_TYPE_DECODE(type), WAN_TE1_SWIRQ_SUBTYPE_DECODE(swirq->subtype)); @@ -3744,7 +3794,7 @@ static int sdla_ds_te1_swirq_alarm(sdla_fe_t* fe, int type) wan_clear_bit(1, (void*)&swirq->pending); update = WAN_TRUE; }else{ - DEBUG_TE1("%s: %s swirq (%s) is not ready (%ld:%ld:%d)...\n", + DEBUG_TEST("%s: %s swirq (%s) is not ready (%ld:%ld:%d)...\n", fe->name, WAN_TE1_SWIRQ_TYPE_DECODE(type), WAN_TE1_SWIRQ_SUBTYPE_DECODE(swirq->subtype), @@ -3767,7 +3817,7 @@ static int sdla_ds_te1_swirq_alarm(sdla_fe_t* fe, int type) wan_clear_bit(1, (void*)&swirq->pending); update = WAN_TRUE; }else{ - DEBUG_TE1("%s: %s swirq (%s) is not ready (%ld:%ld:%d)...\n", + DEBUG_TEST("%s: %s swirq (%s) is not ready (%ld:%ld:%d)...\n", fe->name, WAN_TE1_SWIRQ_TYPE_DECODE(type), WAN_TE1_SWIRQ_SUBTYPE_DECODE(swirq->subtype), @@ -3778,7 +3828,7 @@ static int sdla_ds_te1_swirq_alarm(sdla_fe_t* fe, int type) } if (update == WAN_TRUE){ - DEBUG_TE1("%s: %s swirq (%s) updating state ...\n", + DEBUG_TEST("%s: %s swirq (%s) updating state ...\n", fe->name, WAN_TE1_SWIRQ_TYPE_DECODE(type), WAN_TE1_SWIRQ_SUBTYPE_DECODE(swirq->subtype)); @@ -3825,12 +3875,12 @@ static int sdla_ds_te1_swirq_trigger(sdla_fe_t* fe, int type, int subtype, int d fe->swirq[type].subtype); if (!wan_test_and_set_bit(1, (void*)&fe->swirq[type].pending)){ /* new swirq */ - fe->swirq[type].subtype = subtype; + fe->swirq[type].subtype = (u8)subtype; fe->swirq[type].start = SYSTEM_TICKS; }else{ /* already in a process */ if (fe->swirq[type].subtype != subtype){ - fe->swirq[type].subtype = subtype; + fe->swirq[type].subtype = (u8)subtype; fe->swirq[type].start = SYSTEM_TICKS; } } @@ -4078,6 +4128,7 @@ static int sdla_ds_te1_txlbcode_done(sdla_fe_t *fe) } wan_clear_bit(fe->te_param.lb_tx_mode, &fe->te_param.lb_mode_map); + DEBUG_TE1("%s: T1 %s loopback code sent.\n", fe->name, WAN_TE1_BOC_LB_CODE_DECODE(fe->te_param.lb_tx_code)); @@ -4175,16 +4226,16 @@ static int sdla_ds_te1_pmon(sdla_fe_t *fe, int action) DEBUG_EVENT("%s: Line Code Viilation:\t\t%d\n", fe->name, pmon->lcv_errors); if (IS_T1_FEMEDIA(fe)){ - DEBUG_EVENT("%s: Bit error events:\t\t%d\n", + DEBUG_ERROR("%s: Bit error events:\t\t%d\n", fe->name, pmon->bee_errors); DEBUG_EVENT("%s: Frames out of sync:\t\t%d\n", fe->name, pmon->oof_errors); }else{ - DEBUG_EVENT("%s: CRC4 errors:\t\t\t%d\n", + DEBUG_ERROR("%s: CRC4 errors:\t\t\t%d\n", fe->name, pmon->crc4_errors); - DEBUG_EVENT("%s: Frame Alignment signal errors:\t%d\n", + DEBUG_ERROR("%s: Frame Alignment signal errors:\t%d\n", fe->name, pmon->fas_errors); - DEBUG_EVENT("%s: Far End Block errors:\t\t%d\n", + DEBUG_ERROR("%s: Far End Block errors:\t\t%d\n", fe->name, pmon->feb_errors); } } @@ -4242,7 +4293,7 @@ static int sdla_ds_te1_boc(sdla_fe_t *fe, int mode) break; case UNIVLB_DEACTIVATE_CODE: - DEBUG_EVENT("%s: Received T1 %s code from far end.(%08X)\n", + DEBUG_EVENT("%s: Received T1 %s code from far end.(LB Mode Map: %08X)\n", fe->name, WAN_TE1_BOC_LB_CODE_DECODE(boc),fe->te_param.lb_mode_map); if (wan_test_bit(WAN_TE1_LINELB_MODE, &fe->te_param.lb_mode_map)){ sdla_ds_te1_set_lb(fe, WAN_TE1_LINELB_MODE, WAN_TE1_LB_DISABLE, ENABLE_ALL_CHANNELS); @@ -4353,6 +4404,33 @@ static int sdla_ds_te1_rxlevel(sdla_fe_t* fe) return 0; } +/* + ****************************************************************************** + * sdla_ds_te1_liu_alb() + * + * Description: check if port configuration allows to enable/disable Loop Back + * Arguments: + * Returns: 1 - yes it is ok to enable LB; 0 - it is not possible to enable LB + ****************************************************************************** + */ +static int sdla_is_loop_back_valid_for_clock_mode(sdla_fe_t* fe, unsigned char cmd) +{ + if (cmd == WAN_TE1_LB_ENABLE) { + + if (WAN_TE1_CLK(fe) == WAN_MASTER_CLK || WAN_TE1_REFCLK(fe)) { + /* If Master clock OR referencing another port, which + * must be Master clock, it is ok to enable LB. */ + return 1; + }else{ + /* can not enable LB */ + return 0; + } + } else { + /* It is always ok to disable LB */ + return 1; + } +} + /* ****************************************************************************** * sdla_ds_te1_liu_alb() @@ -4369,11 +4447,12 @@ static int sdla_ds_te1_liu_alb(sdla_fe_t* fe, unsigned char cmd) WAN_ASSERT(fe->write_fe_reg == NULL); WAN_ASSERT(fe->read_fe_reg == NULL); - if (cmd == WAN_TE1_LB_ENABLE && WAN_TE1_CLK(fe) != WAN_MASTER_CLK){ - DEBUG_EVENT("%s: ERROR: You must be configured to Master Clock to execute this Loopback type!\n", - fe->name); + if (!sdla_is_loop_back_valid_for_clock_mode(fe, cmd)) { + DEBUG_ERROR("%s: %s(): ERROR: You must be configured to Master Clock to execute this Loopback type!\n", + fe->name, __FUNCTION__); return -EINVAL; } + value = READ_REG(REG_LMCR); if (cmd == WAN_TE1_LB_ENABLE){ value |= BIT_LMCR_ALB; @@ -4400,11 +4479,12 @@ static int sdla_ds_te1_liu_llb(sdla_fe_t* fe, unsigned char cmd) WAN_ASSERT(fe->write_fe_reg == NULL); WAN_ASSERT(fe->read_fe_reg == NULL); - if (cmd == WAN_TE1_LB_ENABLE && WAN_TE1_CLK(fe) != WAN_MASTER_CLK){ - DEBUG_EVENT("%s: ERROR: You must be configured to Master Clock to execute this Loopback type!\n", - fe->name); + if (!sdla_is_loop_back_valid_for_clock_mode(fe, cmd)) { + DEBUG_ERROR("%s: %s(): ERROR: You must be configured to Master Clock to execute this Loopback type!\n", + fe->name, __FUNCTION__); return -EINVAL; } + value = READ_REG(REG_LMCR); if (cmd == WAN_TE1_LB_ENABLE){ value |= BIT_LMCR_LLB; @@ -4446,6 +4526,20 @@ static int sdla_ds_te1_liu_rlb(sdla_fe_t* fe, unsigned char cmd) * sdla_ds_te1_fr_flb() * * Description: + Bit 0: Framer Loopback (FLB). is it the "Local" LB?? + 0 = loopback disabled + 1 = loopback enabled + + This loopback is useful in testing and debugging applications. + In FLB, the DS26528 loops data from the transmit side back to the receive side. + When FLB is enabled, the following will occur: + 1) (T1 mode) An unframed all-ones code will be transmitted at TTIP and TRING. + (E1 mode) Normal data will be transmitted at TTIP and TRING. + 2) Data at RTIP and RRING will be ignored. + 3) All receive-side signals will take on timing synchronous with TCLK instead of RCLK. + 4) Note that it is not acceptable to have RCLK tied to TCLK during this loopback because this will cause an + unstable condition. + * Arguments: * Returns: ****************************************************************************** @@ -4457,9 +4551,9 @@ static int sdla_ds_te1_fr_flb(sdla_fe_t* fe, unsigned char cmd) WAN_ASSERT(fe->write_fe_reg == NULL); WAN_ASSERT(fe->read_fe_reg == NULL); - if (cmd == WAN_TE1_LB_ENABLE && WAN_TE1_CLK(fe) != WAN_MASTER_CLK){ - DEBUG_EVENT("%s: ERROR: You must be configured to Master Clock to execute this Loopback type!\n", - fe->name); + if (!sdla_is_loop_back_valid_for_clock_mode(fe, cmd)) { + DEBUG_ERROR("%s: %s(): ERROR: You must be configured to Master Clock to execute this Loopback type!\n", + fe->name, __FUNCTION__); return -EINVAL; } @@ -4478,6 +4572,23 @@ static int sdla_ds_te1_fr_flb(sdla_fe_t* fe, unsigned char cmd) * sdla_ds_te1_fr_plb() * * Description: + Bit 1: Payload Loopback (PLB). + 0 = loopback disabled + 1 = loopback enabled + + When PLB is enabled, the following will occur: + 1) Data will be transmitted from the TTIP and TRING pins synchronous with RCLK instead of TCLK. + 2) All the receive-side signals will continue to operate normally. + 3) The TCHCLK and TCHBLK signals are forced low. + 4) Data at the TSER, TDATA, and TSIG pins is ignored. + 5) The TLCLK signal will become synchronous with RCLK instead of TCLK. + In a PLB situation, the DS26528 loops the 192 bits (248 for E1) of payload data (with BPVs corrected) from the + receive section back to the transmit section. The transmitter follows the frame alignment provided by the receiver. + The receive frame boundary is automatically fed into the transmit section, such that the transmit frame position is + locked to the receiver (i.e., TSYNC is sourced from RSYNC). The FPS framing pattern, CRC-6 calculation, and the + FDL bits (FAS word, Si, Sa, E-bits, and CRC-4 for E1) are not looped back. Rather, they are reinserted by the + DS26528 (i.e., the transmit section will modify the payload as if it was input at TSER). + * Arguments: * Returns: ****************************************************************************** @@ -4520,17 +4631,19 @@ sdla_ds_te1_tx_lb(sdla_fe_t* fe, u_int8_t type, u_int8_t mode) fe->name, WAN_TE1_LB_MODE_DECODE(type)); return WAN_FE_LBMODE_RC_FAILED; } + if (wan_test_bit(LINELB_WAITING,(void*)&fe->te_param.critical)){ DEBUG_TE1("%s: Still waiting for far end to send loopback signal back!\n", fe->name); return WAN_FE_LBMODE_RC_TXBUSY; } - if (WAN_TE1_CLK(fe) != WAN_MASTER_CLK){ - DEBUG_EVENT("%s: ERROR: You must be configured to Master Clock to execute this Loopback type!\n", - fe->name); + if (!sdla_is_loop_back_valid_for_clock_mode(fe, WAN_TE1_LB_ENABLE)) { + DEBUG_ERROR("%s: %s(): ERROR: You must be configured to Master Clock to execute this Loopback type!\n", + fe->name, __FUNCTION__); return WAN_FE_LBMODE_RC_FAILED; } + fe->te_param.lb_tx_mode = type; fe->te_param.lb_tx_cmd = mode; if (type == WAN_TE1_TX_LINELB_MODE){ @@ -4627,31 +4740,36 @@ sdla_ds_te1_set_lb(sdla_fe_t *fe, u_int8_t type, u_int8_t mode, u_int32_t chan_m WAN_ASSERT(fe->read_fe_reg == NULL); if (!chan_map) chan_map = WAN_TE1_ACTIVE_CH(fe); switch(type){ - case WAN_TE1_LIU_ALB_MODE: + case WAN_TE1_LIU_ALB_MODE: /* Analog (Local) LB () */ err = sdla_ds_te1_liu_alb(fe, mode); break; - case WAN_TE1_LIU_LLB_MODE: + case WAN_TE1_LIU_LLB_MODE: /* Local LB */ err = sdla_ds_te1_liu_llb(fe, mode); break; - //case WAN_TE1_LIU_RLB_MODE: - // err = sdla_ds_te1_liu_rlb(fe, mode); - // break; - case WAN_TE1_LIU_DLB_MODE: + case WAN_TE1_LIU_RLB_MODE:/* Remote LB - issued LOCALLY */ + err = sdla_ds_te1_liu_rlb(fe, mode); + break; + case WAN_TE1_LIU_DLB_MODE:/* Dual LB */ if (!(err = sdla_ds_te1_liu_llb(fe, mode))){ err = sdla_ds_te1_liu_rlb(fe, mode); } break; - case WAN_TE1_LIU_RLB_MODE: - case WAN_TE1_LINELB_MODE: + case WAN_TE1_LINELB_MODE:/* Remote LB (allb/dllb commands). + * If issued REMOTELY (T1 only) by sending LB command + * from master clock device the right thing to do is + * activate LIU Line Loopback. + * Can be issued LOCALLY for both T1 and E1. + * */ err = sdla_ds_te1_liu_rlb(fe, mode); break; case WAN_TE1_PAYLB_MODE: err = sdla_ds_te1_fr_plb(fe, mode); break; - case WAN_TE1_DDLB_MODE: - err = sdla_ds_te1_fr_flb(fe, mode); + case WAN_TE1_DDLB_MODE: /* Diagnostic (Local) LB (adlb/ddlb commands) */ + /*err = sdla_ds_te1_fr_flb(fe, mode); March 31, 2010: replaced by sdla_ds_te1_liu_alb() */ + err = sdla_ds_te1_liu_alb(fe, mode); break; - case WAN_TE1_PCLB_MODE: + case WAN_TE1_PCLB_MODE:/* Per-channel LB */ err = sdla_ds_te1_pclb(fe, mode, chan_map); break; default: @@ -4699,7 +4817,13 @@ static u32 sdla_ds_te1_get_lbmode(sdla_fe_t *fe) }else if (lmcr & BIT_LMCR_LLB){ wan_set_bit(WAN_TE1_LIU_LLB_MODE, &type_map); } - if (lmcr & BIT_LMCR_RLB) wan_set_bit(WAN_TE1_LINELB_MODE, &type_map); + + //DavidR: this is misleading!! + //if (lmcr & BIT_LMCR_RLB) wan_set_bit(WAN_TE1_LINELB_MODE, &type_map); + + //this is what it should be: + if (lmcr & BIT_LMCR_RLB) wan_set_bit(WAN_TE1_LIU_RLB_MODE, &type_map); + rcr3 = READ_REG(REG_RCR3); if (rcr3 & BIT_RCR3_FLB) wan_set_bit(WAN_TE1_DDLB_MODE, &type_map); @@ -4712,6 +4836,7 @@ static u32 sdla_ds_te1_get_lbmode(sdla_fe_t *fe) if (wan_test_bit(WAN_TE1_TX_LINELB_MODE, &fe->te_param.lb_mode_map)){ wan_set_bit(WAN_TE1_TX_LINELB_MODE, &type_map); } + return type_map; } @@ -4732,9 +4857,14 @@ static int sdla_ds_te1_udp_lb(sdla_fe_t *fe, char *data) switch(lb->type){ case WAN_TE1_TX_LINELB_MODE: case WAN_TE1_TX_PAYLB_MODE: - lb->rc = sdla_ds_te1_tx_lb(fe, lb->type, lb->mode); + lb->rc = (u8)sdla_ds_te1_tx_lb(fe, lb->type, lb->mode); return 0; break; + + /*case WAN_TE1_LINELB_MODE: + DEBUG_WARNING("%s: Warning: %s %s should not be used Locally. This loopback should be controlled by Remote side.\n", + fe->name, WAN_TE1_LB_ACTION_DECODE(lb->mode), WAN_TE1_LB_MODE_DECODE(lb->type));*/ + default: err = sdla_ds_te1_set_lb(fe, lb->type, lb->mode, lb->chan_map); break; @@ -4889,7 +5019,7 @@ static int sdla_ds_te1_bert_status(sdla_fe_t *fe, sdla_te_bert_stats_t *bert_sta static int sdla_ds_te1_bert_stop(sdla_fe_t *fe) { - DEBUG_EVENT("%s: Stopping Bit-Error-Test ...\n", fe->name); + DEBUG_ERROR("%s: Stopping Bit-Error-Test ...\n", fe->name); WRITE_REG(REG_BSIM, 0x00); WRITE_REG(REG_RXPC, READ_REG(REG_RXPC) & ~BIT_RXPC_RBPEN); @@ -4965,7 +5095,7 @@ static int sdla_ds_te1_bert_eib(sdla_fe_t *fe, sdla_te_bert_t *bert) } WP_DELAY(100); if (bert->cmd == WAN_TE_BERT_CMD_EIB){ - DEBUG_EVENT("%s: Insert Bit Errors: %s\n", + DEBUG_ERROR("%s: Insert Bit Errors: %s\n", fe->name, WAN_TE_BERT_EIB_DECODE(bert->un.cfg.eib)); } WRITE_REG(REG_BC2, value); @@ -4989,9 +5119,10 @@ static int sdla_ds_te1_bert_config(sdla_fe_t *fe, sdla_te_bert_t *bert) NUM_OF_E1_TIMESLOTS; DEBUG_EVENT("%s: Starting Bit-Error-Test ...\n", fe->name); + if (bert->verbose){ DEBUG_EVENT("%s: BERT command : %s\n", fe->name, WAN_TE_BERT_CMD_DECODE(bert->cmd)); - DEBUG_EVENT("%s: BERT channel list : %08X\n", fe->name, bert->un.cfg.chan_map); + DEBUG_EVENT("%s: BERT channel list : %08X (FE chans: %08X)\n", fe->name, bert->un.cfg.chan_map, WAN_TE1_ACTIVE_CH(fe)); DEBUG_EVENT("%s: BERT pattern type : %s\n", fe->name, WAN_TE_BERT_PATTERN_DECODE(bert->un.cfg.pattern_type)); DEBUG_EVENT("%s: BERT pattern length : %d\n", fe->name, bert->un.cfg.pattern_len); DEBUG_EVENT("%s: BERT pattern : %08X\n",fe->name, bert->un.cfg.pattern); @@ -5026,10 +5157,11 @@ static int sdla_ds_te1_bert_config(sdla_fe_t *fe, sdla_te_bert_t *bert) if (bert->un.cfg.chan_map == ENABLE_ALL_CHANNELS){ bert->un.cfg.chan_map = WAN_TE1_ACTIVE_CH(fe); } - for(i = 1; i <= channel_range; i++){ + + for(i = 1 /* DavidR: starting from bit one!? */; i <= channel_range; i++){ int off, shift; u_int8_t val; - if (wan_test_bit(i, (void*)&bert->un.cfg.chan_map) && wan_test_bit(i,(void*)&active_ch)){ + if (wan_test_bit(i, (void*)&bert->un.cfg.chan_map) && wan_test_bit(i, (void*)&active_ch)){ off = (i-1) / 8; shift = (i-1) % 8; val = READ_REG(REG_TBPCS1+off); @@ -5056,16 +5188,17 @@ static int sdla_ds_te1_bert_config(sdla_fe_t *fe, sdla_te_bert_t *bert) break; case WAN_TE_BERT_PATTERN_REPETITIVE: value = BIT_BC1_PS2; - if (bert->un.cfg.pattern_len > 32){ - DEBUG_EVENT("%s: ERROR: Invalid BERT pattern length %d bits (0-32)!\n", - fe->name, bert->un.cfg.pattern_len); + if (bert->un.cfg.pattern_len > 32 || + bert->un.cfg.pattern_len < 1 /* prevent zero-length pattern length */){ + DEBUG_ERROR("%s: ERROR: Invalid BERT pattern length of %d bits. (Must be between 1 and 32)!\n", + fe->name, bert->un.cfg.pattern_len); return -EINVAL; } break; case WAN_TE_BERT_PATTERN_WORD: value = BIT_BC1_PS2 | BIT_BC1_PS0; if (bert->un.cfg.count >= 256){ - DEBUG_EVENT("%s: ERROR: Invalid BERT Alternating Word Count %d (1-256)!\n", + DEBUG_ERROR("%s: ERROR: Invalid BERT Alternating Word Count %d (1-256)!\n", fe->name, bert->un.cfg.count); return -EINVAL; } @@ -5077,7 +5210,7 @@ static int sdla_ds_te1_bert_config(sdla_fe_t *fe, sdla_te_bert_t *bert) value = BIT_BC1_PS2 | BIT_BC1_PS1 | BIT_BC1_PS0; break; default: - DEBUG_EVENT("%s: Error: Invalid BERT pattern type %s\n", + DEBUG_ERROR("%s: Error: Invalid BERT pattern type %s\n", fe->name, WAN_TE_BERT_PATTERN_DECODE(bert->un.cfg.pattern_type)); return -EINVAL; @@ -5090,7 +5223,7 @@ static int sdla_ds_te1_bert_config(sdla_fe_t *fe, sdla_te_bert_t *bert) int i = 0; u16 repeat = 0; - if (bert->un.cfg.pattern_len < 17){ + if (bert->un.cfg.pattern_len && bert->un.cfg.pattern_len < 17){ repeat = 32 / bert->un.cfg.pattern_len; for(i=0; i < repeat; i++){ bert->un.cfg.pattern |= (pattern << bert->un.cfg.pattern_len); @@ -5171,7 +5304,7 @@ static int sdla_ds_te1_bert(sdla_fe_t *fe, sdla_te_bert_t *bert) switch(bert->cmd){ case WAN_TE_BERT_CMD_START: if (wan_test_bit(WAN_TE_BERT_FLAG_READY, &fe->te_param.bert_flag)){ - DEBUG_EVENT("%s: ERROR: BERT test is still running!\n", + DEBUG_ERROR("%s: ERROR: BERT test is still running!\n", fe->name); bert->rc = WAN_TE_BERT_RC_RUNNING; return 0; @@ -5181,7 +5314,7 @@ static int sdla_ds_te1_bert(sdla_fe_t *fe, sdla_te_bert_t *bert) case WAN_TE_BERT_CMD_EIB: case WAN_TE_BERT_CMD_RESET: if (!wan_test_bit(WAN_TE_BERT_FLAG_READY, &fe->te_param.bert_flag)){ - DEBUG_EVENT("%s: ERROR: BERT test is not running!\n", + DEBUG_ERROR("%s: ERROR: BERT test is not running!\n", fe->name); bert->rc = WAN_TE_BERT_RC_STOPPED; return 0; @@ -5191,7 +5324,7 @@ static int sdla_ds_te1_bert(sdla_fe_t *fe, sdla_te_bert_t *bert) case WAN_TE_BERT_CMD_RUNNING: break; default: - DEBUG_EVENT("%s: ERROR: Invalid BERT command (%02X)\n", + DEBUG_ERROR("%s: ERROR: Invalid BERT command (%02X)\n", fe->name, bert->cmd); bert->rc = WAN_TE_BERT_RC_EINVAL; return 0; @@ -5430,14 +5563,27 @@ static int sdla_ds_te1_udp(sdla_fe_t *fe, void* p_udp_cmd, unsigned char* data) break; case WAN_FE_BERT_MODE: + + if (fe->fe_status != FE_CONNECTED){ + DEBUG_WARNING("%s: Warning: Bit-Error-Test can not run when line is in \"Disconnected\" state.\n", fe->name); + udp_cmd->wan_cmd_return_code = SANG_STATUS_LINE_DISCONNECTED; + break; + } + + if (IS_E1_FEMEDIA(fe)){ + DEBUG_WARNING("%s: Warning: Transmission of LB Activation Code not supported in E1 mode.\n", fe->name); + udp_cmd->wan_cmd_return_code = SANG_STATUS_OPTION_NOT_SUPPORTED; + break; + } + sdla_ds_te1_bert(fe, (sdla_te_bert_t*)&data[0]); - udp_cmd->wan_cmd_data_len = sizeof(sdla_te_bert_t); + udp_cmd->wan_cmd_data_len = sizeof(sdla_te_bert_t); udp_cmd->wan_cmd_return_code = WAN_CMD_OK; break; default: udp_cmd->wan_cmd_return_code = WAN_UDP_INVALID_CMD; - udp_cmd->wan_cmd_data_len = 0; + udp_cmd->wan_cmd_data_len = 0; break; } return 0; diff --git a/patches/kdrivers/src/net/sdla_adsl.c b/patches/kdrivers/src/net/sdla_adsl.c index ddfc364..7f4e2f1 100644 --- a/patches/kdrivers/src/net/sdla_adsl.c +++ b/patches/kdrivers/src/net/sdla_adsl.c @@ -85,7 +85,6 @@ #if defined(__WINDOWS__) -#include "array_queue.h" #include "sdladrv_private.h" /* prototypes of global functions */ extern void @@ -93,6 +92,18 @@ wan_get_random_mac_address( OUT unsigned char *mac_address ); +extern +int wp_get_motherboard_enclosure_serial_number( + OUT char *buf, + IN int buf_length + ); + +extern +int +wp_get_netdev_open_handles_count( + netdevice_t *sdla_net_dev + ); + #endif /* @@ -141,6 +152,8 @@ wan_get_random_mac_address( # error "Undefined LIST_FIRST_MCLIST/LIST_NEXT_MCLIST macros!" #endif +WAN_DECLARE_NETDEV_OPS(wan_netdev_ops) + enum { TX_BUSY_SET }; @@ -203,15 +216,8 @@ static int del_if (wan_device_t*, netdevice_t*); static int process_udp_cmd(netdevice_t*,wan_udp_hdr_t*); #if defined(__WINDOWS__) -static void process_bh( - IN PKDPC Dpc, - IN PVOID arg, - IN PVOID not_used1, - IN PVOID not_used2 - ); - -static int adsl_init(void* priv); - +KDEFERRED_ROUTINE process_bh; +static int adsl_init(void* priv); #else #if defined(__LINUX__) @@ -420,6 +426,7 @@ static int new_if (wan_device_t* wandev, netdevice_t* ifp, wanif_conf_t* conf) #elif defined(__LINUX__) || defined(__WINDOWS__) #if defined(__LINUX__) + WAN_NETDEV_OPS_BIND(ifp,wan_netdev_ops); WAN_TASKQ_INIT((&adsl->common.wanpipe_task), 0, process_bh, ifp); #elif defined(__WINDOWS__) /* # define WAN_TASKQ_INIT(task, priority, func, arg) */ @@ -431,11 +438,12 @@ static int new_if (wan_device_t* wandev, netdevice_t* ifp, wanif_conf_t* conf) card->hw_iface.getcfg(card->hw, SDLA_MEMBASE, &ifp->mem_start); card->hw_iface.getcfg(card->hw, SDLA_MEMEND, &ifp->mem_end); - ifp->open = &adsl_open; - ifp->stop = &adsl_close; + WAN_NETDEV_OPS_OPEN(ifp,wan_netdev_ops,&adsl_open); + WAN_NETDEV_OPS_STOP(ifp,wan_netdev_ops,&adsl_close); #if defined(__WINDOWS__) ifp->hard_start_xmit = &adsl_if_send;/* will call adsl_output() */ + ifp->init = &adsl_init; adsl->common.card = card; @@ -444,20 +452,21 @@ static int new_if (wan_device_t* wandev, netdevice_t* ifp, wanif_conf_t* conf) wpabs_trace_info_init(&adsl->trace_info, MAX_TRACE_QUEUE); ifp->trace_info = &adsl->trace_info; #else - ifp->hard_start_xmit = &adsl_output; + WAN_NETDEV_OPS_XMIT(ifp,wan_netdev_ops,&adsl_output); + #endif adsl->common.dev = ifp; - ifp->get_stats = &adsl_stats; + WAN_NETDEV_OPS_STATS(ifp,wan_netdev_ops,&adsl_stats); #if !defined(__WINDOWS__) - ifp->set_multicast_list = &adsl_multicast; + WAN_NETDEV_OPS_SET_MULTICAST_LIST(ifp,wan_netdev_ops,&adsl_multicast); #endif - ifp->tx_timeout = &adsl_tx_timeout; + WAN_NETDEV_OPS_TIMEOUT(ifp,wan_netdev_ops,&adsl_tx_timeout); ifp->watchdog_timeo = (1*HZ); - ifp->do_ioctl = adsl_ioctl; + WAN_NETDEV_OPS_IOCTL(ifp,wan_netdev_ops,adsl_ioctl); #endif/*#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)*/ adsl->common.card = card; @@ -564,20 +573,21 @@ static int new_if (wan_device_t* wandev, netdevice_t* ifp, wanif_conf_t* conf) } ifp->tx_queue_len = 100; - ifp->open = &adsl_open; - ifp->stop = &adsl_close; - ifp->hard_start_xmit = &adsl_output; - ifp->get_stats = &adsl_stats; - ifp->set_multicast_list = &adsl_multicast; - ifp->tx_timeout = &adsl_tx_timeout; + WAN_NETDEV_OPS_OPEN(ifp,wan_netdev_ops,&adsl_open); + WAN_NETDEV_OPS_STOP(ifp,wan_netdev_ops,&adsl_close); + WAN_NETDEV_OPS_XMIT(ifp,wan_netdev_ops,&adsl_output); + WAN_NETDEV_OPS_STATS(ifp,wan_netdev_ops,&adsl_stats); + WAN_NETDEV_OPS_SET_MULTICAST_LIST(ifp,wan_netdev_ops,&adsl_multicast); + WAN_NETDEV_OPS_TIMEOUT(ifp,wan_netdev_ops,&adsl_tx_timeout); ifp->watchdog_timeo = (1*HZ); - ifp->do_ioctl = adsl_ioctl; + WAN_NETDEV_OPS_IOCTL(ifp,wan_netdev_ops,adsl_ioctl); + #endif break; #endif /* #if !defined(__WINDOWS__) */ default: - DEBUG_EVENT("%s: Error invalid EncapMode %i\n", + DEBUG_ERROR("%s: Error invalid EncapMode %i\n", card->devname,card->u.adsl.EncapMode); del_if(wandev,ifp); return -EINVAL; @@ -611,8 +621,11 @@ static int del_if (wan_device_t* wandev, netdevice_t* ifp) /* Initialize interrupt handler pointer */ card->isr = NULL; + #if defined(__WINDOWS__) - ;/* do nothing */ + /* free the private area */ + wan_free(adsl); + return 0; #else /* Initialize network interface */ if (card->u.adsl.EncapMode == RFC_MODE_PPP_VC || @@ -627,7 +640,7 @@ static int del_if (wan_device_t* wandev, netdevice_t* ifp) if (adsl->common.prot_ptr){ wp_sppp_detach(ifp); - ifp->do_ioctl = NULL; + WAN_NETDEV_OPS_IOCTL(ifp,wan_netdev_ops,NULL); wan_free(adsl->common.prot_ptr); adsl->common.prot_ptr= NULL; @@ -636,6 +649,7 @@ static int del_if (wan_device_t* wandev, netdevice_t* ifp) } #endif/* #if !defined(__WINDOWS__)*/ + #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) #if 0 ifp->if_output = NULL; @@ -864,8 +878,7 @@ static int adsl_open(netdevice_t* ifp) { int status = 0; - DBG_DSL_NOT_IMPLD(); -#if defined (__LINUX__) +#if defined (__LINUX__) || defined(__WINDOWS__) adsl_private_area_t* adsl = wan_netif_priv(ifp); #endif return status; @@ -882,10 +895,10 @@ static int adsl_open(netdevice_t* ifp) int adsl_close(netdevice_t* ifp) { int status = 0; -#if defined (__LINUX__) + +#if defined (__LINUX__) || defined(__WINDOWS__) adsl_private_area_t* adsl = wan_netif_priv(ifp); #endif - DBG_DSL_NOT_IMPLD(); return status; } @@ -1176,7 +1189,7 @@ adsl_output(netdevice_t* dev, netskb_t* skb, struct sockaddr* dst, struct rtentr } if (!adsl){ - DEBUG_EVENT("%s: Error: TxLan: No private adapter !\n", + DEBUG_ERROR("%s: Error: TxLan: No private adapter !\n", wan_netif_name(dev)); #if defined(__LINUX__) || defined(__WINDOWS__) wan_skb_free(skb); @@ -1193,7 +1206,7 @@ adsl_output(netdevice_t* dev, netskb_t* skb, struct sockaddr* dst, struct rtentr case RFC_MODE_PPP_LLC: case RFC_MODE_STACK_VC: if (wan_skb_len(skb) <= 2){ - DEBUG_EVENT("%s: Error: TxLan: PPP pkt len <= 2! (len=%i)\n", + DEBUG_ERROR("%s: Error: TxLan: PPP pkt len <= 2! (len=%i)\n", wan_netif_name(dev),wan_skb_len(skb)); wan_skb_print(skb); @@ -1741,13 +1754,13 @@ static int adsl_ioctl(netdevice_t* ifp, struct ifreq *ifr, int command) int error = 0; if (!wan_netif_up(ifp)) { - DEBUG_EVENT("%s: Error: ADSL Device is not up\n", + DEBUG_ERROR("%s: Error: ADSL Device is not up\n", wan_netif_name(ifp)); return -ENETDOWN; } if (!adsl || !adsl->common.card ) { - DEBUG_EVENT("%s: Error: ADSL Device is not configured\n", + DEBUG_ERROR("%s: Error: ADSL Device is not configured\n", wan_netif_name(ifp)); return -EFAULT; } @@ -1780,7 +1793,7 @@ static int adsl_ioctl(netdevice_t* ifp, struct ifreq *ifr, int command) wan_udp_pkt=(wan_udp_pkt_t*)&adsl->udp_pkt_data[0]; if (WAN_COPY_FROM_USER(&wan_udp_pkt->wan_udp_hdr,ifr->ifr_data,sizeof(wan_udp_hdr_t))){ - DEBUG_EVENT("%s: Error: Failed to copy memory from USER space!\n", + DEBUG_ERROR("%s: Error: Failed to copy memory from USER space!\n", card->devname); wan_atomic_set(&adsl->udp_pkt_len,0); error = -EFAULT; @@ -1794,7 +1807,7 @@ static int adsl_ioctl(netdevice_t* ifp, struct ifreq *ifr, int command) * thus we can release the irq */ if (wan_atomic_read(&adsl->udp_pkt_len) > sizeof(wan_udp_pkt_t)){ - DEBUG_EVENT("%s: Error: Pipemon buf too big on the way up! %i\n", + DEBUG_ERROR("%s: Error: Pipemon buf too big on the way up! %i\n", card->devname,wan_atomic_read(&adsl->udp_pkt_len)); wan_atomic_set(&adsl->udp_pkt_len,0); error = -EINVAL; @@ -1802,7 +1815,7 @@ static int adsl_ioctl(netdevice_t* ifp, struct ifreq *ifr, int command) } if (WAN_COPY_TO_USER(ifr->ifr_data,&wan_udp_pkt->wan_udp_hdr,sizeof(wan_udp_hdr_t))){ - DEBUG_EVENT("%s: Error: Failed to copy memory to USER space!\n", + DEBUG_ERROR("%s: Error: Failed to copy memory to USER space!\n", card->devname); wan_atomic_set(&adsl->udp_pkt_len,0); error = -EFAULT; @@ -1935,7 +1948,7 @@ VOID process_bh( static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* netdev, adsl_private_area_t* chan) { wan_udp_pkt_t *wan_udp_pkt; - unsigned long tv; + wan_time_t tv; int rc = SANG_STATUS_SUCCESS; wan_udp_pkt = (wan_udp_pkt_t*)chan->udp_pkt_data; @@ -1951,7 +1964,7 @@ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* netdev, adsl_private_ break; case GET_OPEN_HANDLES_COUNTER: - *(int*)&wan_udp_pkt->wan_udp_data[0] = netdev->open_handle_counter; + *(int*)&wan_udp_pkt->wan_udp_data[0] = wp_get_netdev_open_handles_count(netdev); wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; wan_udp_pkt->wan_udp_data_len = sizeof(int); break; @@ -1960,7 +1973,7 @@ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* netdev, adsl_private_ wan_getcurrenttime( &tv, NULL ); chan->router_up_time = tv - chan->router_start_time; - *(unsigned long *)&wan_udp_pkt->wan_udp_data = + *(wan_time_t *)&wan_udp_pkt->wan_udp_data = chan->router_up_time; wan_udp_pkt->wan_udp_data_len = sizeof(unsigned long); wan_udp_pkt->wan_udp_return_code = 0; @@ -2004,9 +2017,9 @@ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* netdev, adsl_private_ case WAN_GET_HW_MAC_ADDR: DEBUG_UDP("WAN_GET_HW_MAC_ADDR\n"); +#if 0 /* Some S518 cards have duplicate MAC addresses - can NOT use them * on Bridged networks. Use randomly generated MAC address instead. */ -#if 0 wan_get_random_mac_address(wan_udp_pkt->wan_udp_data); #else { @@ -2020,14 +2033,21 @@ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* netdev, adsl_private_ wan_udp_pkt->wan_udp_data_len = ETHER_ADDR_LEN; break; + case WANPIPEMON_GET_BIOS_ENCLOSURE3_SERIAL_NUMBER: + wan_udp_pkt->wan_udp_return_code = + wp_get_motherboard_enclosure_serial_number(wan_udp_pkt->wan_udp_data, + sizeof(wan_udp_pkt->wan_udp_data)); + + wan_udp_pkt->wan_udp_data_len = sizeof(wan_udp_pkt->wan_udp_data); + break; + default: wan_udp_pkt->wan_udp_data_len = 0; wan_udp_pkt->wan_udp_return_code = 0xCD; - DEBUG_EVENT( - "%s: Warning: Unknowd Management command: 0x%X\n", - netdev->name, wan_udp_pkt->wan_udp_command); + DEBUG_WARNING("%s(): %s: Warning: Unknown Management command: 0x%X\n", + __FUNCTION__, netdev->name, wan_udp_pkt->wan_udp_command); break; - }//end of switch + }/* switch () */ return rc; } @@ -2264,7 +2284,7 @@ static int process_udp_cmd(netdevice_t* ifp, wan_udp_hdr_t* udp_hdr) wan_set_bit (0,&trace_info->tracing_enabled); }else{ - DEBUG_EVENT("%s: Error: ADSL trace running!\n", + DEBUG_ERROR("%s: Error: ADSL trace running!\n", card->devname); udp_hdr->wan_udphdr_return_code = 2; } @@ -2297,7 +2317,7 @@ static int process_udp_cmd(netdevice_t* ifp, wan_udp_hdr_t* udp_hdr) if(wan_test_bit(0,&trace_info->tracing_enabled)){ trace_info->trace_timeout = SYSTEM_TICKS; }else{ - DEBUG_EVENT("%s: Error ADSL trace not enabled\n", + DEBUG_ERROR("%s: Error ADSL trace not enabled\n", card->devname); /* set return code */ udp_hdr->wan_udphdr_return_code = 1; @@ -2466,24 +2486,24 @@ static int wanpipe_attach_sppp(sdla_t *card, netdevice_t *dev, wanif_conf_t *con #endif #if defined(__WINDOWS__) -////////////////////////////////////////////////////////////////////////// -//////////// RX + static int netif_rx(netdevice_t *sdla_net_dev, netskb_t *rx_skb) { adsl_private_area_t *chan = (adsl_private_area_t*)wan_netif_priv(sdla_net_dev); - sdla_t *card = (sdla_t*)chan->common.card; DBG_ADSL_RX("%s()\n", __FUNCTION__); -// wan_skb_print(rx_skb); - - rx_dpc(sdla_net_dev, rx_skb, 0, wan_skb_len(rx_skb)); + if(wanpipe_lip_rx(chan, rx_skb)){ + chan->if_stats.rx_dropped++; + wan_skb_free(rx_skb); + }else{ + chan->if_stats.rx_packets++; + chan->if_stats.rx_bytes += wan_skb_len(rx_skb); + } return 0; } -////////////////////////////////////////////////////////////////////////// -//////////// TX static int adsl_if_send(netskb_t* skb, netdevice_t* dev) { adsl_private_area_t *chan = (adsl_private_area_t*)wan_netif_priv(dev); @@ -2500,7 +2520,7 @@ static int adsl_if_send(netskb_t* skb, netdevice_t* dev) if(adsl_output(skb, dev)){ - DBG_ADSL_TX("%s():%s:Warning: adsl_output() failed. Dropping TX data!\n", + DBG_ADSL_TX("%s():%s: Warning: adsl_output() failed. Dropping TX data!\n", __FUNCTION__, dev->name); WAN_NETIF_START_QUEUE(dev); diff --git a/patches/kdrivers/src/net/sdla_adsl_tty.c b/patches/kdrivers/src/net/sdla_adsl_tty.c index 0f171a5..f0fc269 100644 --- a/patches/kdrivers/src/net/sdla_adsl_tty.c +++ b/patches/kdrivers/src/net/sdla_adsl_tty.c @@ -16,7 +16,6 @@ #if defined(__WINDOWS__) -#include #include #endif @@ -144,13 +143,13 @@ static int adsl_wan_open(ttystruct_t *tp, struct file *pFile) line = MINOR(tp->device) - tp->driver.minor_start; if ((line < 0) || (line >= ADSL_NR_PORTS)){ - DEBUG_EVENT("wanpipe: Error: Invalid tty line %d!\n", line); + DEBUG_ERROR("wanpipe: Error: Invalid tty line %d!\n", line); status = -ENODEV; goto adsl_wan_open_exit; } if (!tty_card_map[line]){ - DEBUG_EVENT("wanpipe: Error: No adapter in tty map for minor %d!\n", line); + DEBUG_ERROR("wanpipe: Error: No adapter in tty map for minor %d!\n", line); return -ENODEV; } @@ -174,12 +173,12 @@ static void adsl_wan_close(ttystruct_t *tp, struct file *pFile) line = MINOR(tp->device) - tp->driver.minor_start; if ((line < 0) || (line >= ADSL_NR_PORTS)){ - DEBUG_EVENT("wanpipe: Error: Invalid tty line %d!\n", line); + DEBUG_ERROR("wanpipe: Error: Invalid tty line %d!\n", line); return; } if (!tty_card_map[line]){ - DEBUG_EVENT("wanpipe: Error: No adapter in tty map for minor %d!\n", line); + DEBUG_ERROR("wanpipe: Error: No adapter in tty map for minor %d!\n", line); return; } @@ -290,13 +289,13 @@ int adsl_wan_register(void *driver_data, ttydriver_t *tp = &serial_driver; if (minor_no >= ADSL_NR_PORTS){ - DEBUG_EVENT("%s: Error Illegal Minor TTY number (0-%d): %i\n", + DEBUG_ERROR("%s: Error Illegal Minor TTY number (0-%d): %i\n", devname,ADSL_NR_PORTS,minor_no); return -EINVAL; } if (tty_card_map[minor_no] != NULL){ - DEBUG_EVENT("%s: Error TTY Minor %i, already in use\n", + DEBUG_ERROR("%s: Error TTY Minor %i, already in use\n", devname,minor_no); return -EBUSY; } diff --git a/patches/kdrivers/src/net/sdla_aft_te1_ss7.c b/patches/kdrivers/src/net/sdla_aft_te1_ss7.c index c72acac..275d0d9 100644 --- a/patches/kdrivers/src/net/sdla_aft_te1_ss7.c +++ b/patches/kdrivers/src/net/sdla_aft_te1_ss7.c @@ -700,7 +700,7 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) card->devname,chan->if_name); }else{ - DEBUG_EVENT( "%s:%s: Error: Invalid operation mode [WANPIPE|API|BRIDGE|BRIDGE_NODE]\n", + DEBUG_ERROR( "%s:%s: Error: Invalid operation mode [WANPIPE|API|BRIDGE|BRIDGE_NODE]\n", card->devname,chan->if_name); err=-EINVAL; goto new_if_error; @@ -724,7 +724,7 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) if (!chan->hdlc_eng){ if (card->u.xilinx.cfg.mru&0x03){ - DEBUG_EVENT("%s:%s: Error, Transparent MTU must be word aligned!\n", + DEBUG_ERROR("%s:%s: Error, Transparent MTU must be word aligned!\n", card->devname,chan->if_name); err = -EINVAL; goto new_if_error; @@ -780,7 +780,7 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) chan->dma_mru = xilinx_valid_mtu(chan->dma_mru+100); if (!chan->dma_mru){ - DEBUG_EVENT("%s:%s: Error invalid MTU %i mru %i\n", + DEBUG_ERROR("%s:%s: Error invalid MTU %i mru %i\n", card->devname, chan->if_name, card->wandev.mtu,card->u.xilinx.cfg.mru); @@ -1688,7 +1688,7 @@ wanpipe_xilinx_ioctl(netdevice_t *dev, struct ifreq *ifr, int cmd) * thus we can release the irq */ if (wan_atomic_read(&chan->udp_pkt_len) > sizeof(wan_udp_pkt_t)){ - DEBUG_EVENT( "%s: Error: Pipemon buf too bit on the way up! %i\n", + DEBUG_ERROR( "%s: Error: Pipemon buf too bit on the way up! %i\n", card->devname,wan_atomic_read(&chan->udp_pkt_len)); wan_atomic_set(&chan->udp_pkt_len,0); return -EINVAL; @@ -1747,7 +1747,7 @@ static int xilinx_chip_configure(sdla_t *card) wan_set_bit(INTERFACE_TYPE_T1_E1_BIT,®); wan_set_bit(FRONT_END_FRAME_FLAG_ENABLE_BIT,®); }else{ - DEBUG_EVENT("%s: Error: Xilinx doesn't support non T1/E1 interface!\n", + DEBUG_ERROR("%s: Error: Xilinx doesn't support non T1/E1 interface!\n", card->devname); return -EINVAL; } @@ -1829,7 +1829,7 @@ static int xilinx_chip_configure(sdla_t *card) card->devname); break; default: - DEBUG_EVENT("%s: Error Invalid Security ID=0x%X\n", + DEBUG_ERROR("%s: Error Invalid Security ID=0x%X\n", card->devname,adptr_security); //return -EINVAL; } @@ -1866,7 +1866,7 @@ static int xilinx_chip_configure(sdla_t *card) err=aft_core_ready(card); if (err != 0){ - DEBUG_EVENT("%s: Error: HDLC Core Not Ready!\n", + DEBUG_ERROR("%s: Error: HDLC Core Not Ready!\n", card->devname); card->hw_iface.bus_read_4(card->hw,XILINX_CHIP_CFG_REG, ®); @@ -1912,7 +1912,7 @@ static int xilinx_chip_configure(sdla_t *card) card->hw_iface.bus_read_4(card->hw,XILINX_HDLC_TX_INTR_PENDING_REG, &tmp); card->hw_iface.bus_read_4(card->hw,XILINX_CHIP_CFG_REG, (u32*)®); if (wan_test_bit(DMA_INTR_FLAG,®)){ - DEBUG_EVENT("%s: Error: Active DMA Interrupt Pending. !\n", + DEBUG_ERROR("%s: Error: Active DMA Interrupt Pending. !\n", card->devname); reg = 0; @@ -1922,7 +1922,7 @@ static int xilinx_chip_configure(sdla_t *card) return err; } if (wan_test_bit(ERROR_INTR_FLAG,®)){ - DEBUG_EVENT("%s: Error: Active Error Interrupt Pending. !\n", + DEBUG_ERROR("%s: Error: Active Error Interrupt Pending. !\n", card->devname); reg = 0; @@ -2344,7 +2344,7 @@ static int xilinx_init_rx_dev_fifo(sdla_t *card, private_area_t *chan, unsigned } if (timeout){ - DEBUG_EVENT("%s:%s: Error: Rx fifo reset timedout %u us\n", + DEBUG_ERROR("%s:%s: Error: Rx fifo reset timedout %u us\n", card->devname,chan->if_name,i*FIFO_RESET_TIMEOUT_US); }else{ DEBUG_TEST("%s:%s: Rx Fifo reset successful %u us\n", @@ -2387,7 +2387,7 @@ static int xilinx_init_tx_dev_fifo(sdla_t *card, private_area_t *chan, unsigned } if (timeout){ - DEBUG_EVENT("%s:%s: Error: Tx fifo reset timedout %u us\n", + DEBUG_ERROR("%s:%s: Error: Tx fifo reset timedout %u us\n", card->devname,chan->if_name,i*FIFO_RESET_TIMEOUT_US); }else{ DEBUG_TEST("%s:%s: Tx Fifo reset successful %u us\n", @@ -2434,21 +2434,21 @@ static int xilinx_dma_rx(sdla_t *card, private_area_t *chan) card->hw_iface.bus_read_4(card->hw,dma_descr, ®); if (wan_test_bit(RxDMA_HI_DMA_GO_READY_BIT,®)){ - DEBUG_EVENT("%s: Error: RxDMA GO Ready bit set on dma Rx\n", + DEBUG_ERROR("%s: Error: RxDMA GO Ready bit set on dma Rx\n", card->devname); return -EFAULT; } #endif if (chan->rx_dma_skb){ - DEBUG_EVENT("%s: Critial Error: Rx Dma Buf busy!\n",chan->if_name); + DEBUG_ERROR("%s: Critial Error: Rx Dma Buf busy!\n",chan->if_name); return -EINVAL; } chan->rx_dma_skb = wan_skb_dequeue(&chan->wp_rx_free_list); if (!chan->rx_dma_skb){ - DEBUG_EVENT("%s: Critical Error no rx dma buf Free=%i Comp=%i!\n", + DEBUG_ERROR("%s: Critical Error no rx dma buf Free=%i Comp=%i!\n", chan->if_name,wan_skb_queue_len(&chan->wp_rx_free_list), wan_skb_queue_len(&chan->wp_rx_complete_list)); @@ -2463,7 +2463,7 @@ static int xilinx_dma_rx(sdla_t *card, private_area_t *chan) chan->rx_dma_skb = wan_skb_dequeue(&chan->wp_rx_free_list); if (!chan->rx_dma_skb){ - DEBUG_EVENT("%s: Critical Error no STILL NO MEM!\n", + DEBUG_ERROR("%s: Critical Error no STILL NO MEM!\n", chan->if_name); return -ENOMEM; } @@ -2479,7 +2479,7 @@ static int xilinx_dma_rx(sdla_t *card, private_area_t *chan) PCI_DMA_FROMDEVICE)); if (!bus_addr){ - DEBUG_EVENT("%s: %s Critical error pci_map_single() failed!\n", + DEBUG_ERROR("%s: %s Critical error pci_map_single() failed!\n", chan->if_name,__FUNCTION__); return -EINVAL; } @@ -2645,7 +2645,7 @@ static int xilinx_dma_tx (sdla_t *card, private_area_t *chan) card->hw_iface.bus_read_4(card->hw,dma_descr, ®); if (wan_test_bit(TxDMA_HI_DMA_GO_READY_BIT,®)){ - DEBUG_EVENT("%s: Error: TxDMA GO Ready bit set on dma Tx 0x%X\n", + DEBUG_ERROR("%s: Error: TxDMA GO Ready bit set on dma Tx 0x%X\n", card->devname,reg); wan_clear_bit(TX_BUSY,&chan->dma_status); return -EFAULT; @@ -2704,7 +2704,7 @@ static int xilinx_dma_tx (sdla_t *card, private_area_t *chan) ss7_len=skb->csum; if ((chan->tx_dma_addr + (skb->len-ss7_len)) & 0x03){ - DEBUG_EVENT("%s: Error: Tx SS7 Prot Ptr not aligned to 32bit boudary!\n", + DEBUG_ERROR("%s: Error: Tx SS7 Prot Ptr not aligned to 32bit boudary!\n", card->devname); pci_unmap_single(NULL, @@ -2726,7 +2726,7 @@ static int xilinx_dma_tx (sdla_t *card, private_area_t *chan) } if (chan->tx_dma_addr & 0x03){ - DEBUG_EVENT("%s: Error: Tx Ptr not aligned to 32bit boudary!\n", + DEBUG_ERROR("%s: Error: Tx Ptr not aligned to 32bit boudary!\n", card->devname); pci_unmap_single(NULL, @@ -2866,7 +2866,7 @@ static void xilinx_dma_tx_complete (sdla_t *card, private_area_t *chan) if (!chan->tx_dma_skb){ if (chan->hdlc_eng){ - DEBUG_EVENT("%s: Critical Error: Tx DMA intr: no tx skb !\n", + DEBUG_ERROR("%s: Critical Error: Tx DMA intr: no tx skb !\n", card->devname); wan_clear_bit(TX_BUSY,&chan->dma_status); return; @@ -2913,7 +2913,7 @@ static void xilinx_dma_tx_complete (sdla_t *card, private_area_t *chan) #if 0 if (reg & TxDMA_HI_DMA_PCI_ERROR_RETRY_TOUT){ - DEBUG_EVENT("%s:%s: PCI Error: 'Retry' exceeds maximum (64k): Reg=0x%X!\n", + DEBUG_ERROR("%s:%s: PCI Error: 'Retry' exceeds maximum (64k): Reg=0x%X!\n", card->devname,chan->if_name,reg); if (++chan->pci_retry < 3){ @@ -2974,12 +2974,12 @@ static void xilinx_tx_post_complete (sdla_t *card, private_area_t *chan, netskb_ /* Checking Tx DMA Go bit. Has to be '0' */ if (wan_test_bit(TxDMA_HI_DMA_GO_READY_BIT,®)){ - DEBUG_EVENT("%s:%s: Error: TxDMA Intr: GO bit set on Tx intr\n", + DEBUG_ERROR("%s:%s: Error: TxDMA Intr: GO bit set on Tx intr\n", card->devname,chan->if_name); } if (reg & TxDMA_HI_DMA_DATA_LENGTH_MASK){ - DEBUG_EVENT("%s:%s: Error: TxDMA Length not equal 0 \n", + DEBUG_ERROR("%s:%s: Error: TxDMA Length not equal 0 \n", card->devname,chan->if_name); } #if 0 @@ -2987,20 +2987,20 @@ static void xilinx_tx_post_complete (sdla_t *card, private_area_t *chan, netskb_ if (reg&TxDMA_HI_DMA_PCI_ERROR_MASK){ if (reg & TxDMA_HI_DMA_PCI_ERROR_M_ABRT){ - DEBUG_EVENT("%s:%s: Tx Error: Abort from Master: pci fatal error!\n", + DEBUG_ERROR("%s:%s: Tx Error: Abort from Master: pci fatal error!\n", card->devname,chan->if_name); } if (reg & TxDMA_HI_DMA_PCI_ERROR_T_ABRT){ - DEBUG_EVENT("%s:%s: Tx Error: Abort from Target: pci fatal error!\n", + DEBUG_ERROR("%s:%s: Tx Error: Abort from Target: pci fatal error!\n", card->devname,chan->if_name); } if (reg & TxDMA_HI_DMA_PCI_ERROR_DS_TOUT){ - DEBUG_EVENT("%s:%s: Tx Warning: PCI Latency Timeout!\n", + DEBUG_WARNING("%s:%s: Tx Warning: PCI Latency Timeout!\n", card->devname,chan->if_name); goto tx_post_ok; } if (reg & TxDMA_HI_DMA_PCI_ERROR_RETRY_TOUT){ - DEBUG_EVENT("%s:%s: Tx Error: 'Retry' exceeds maximum (64k): pci fatal error!\n", + DEBUG_ERROR("%s:%s: Tx Error: 'Retry' exceeds maximum (64k): pci fatal error!\n", card->devname,chan->if_name); } } @@ -3044,7 +3044,7 @@ static void xilinx_dma_rx_complete (sdla_t *card, private_area_t *chan) wan_clear_bit(0,&chan->rx_dma); if (!chan->rx_dma_skb){ - DEBUG_EVENT("%s: Critical Error: rx_dma_skb\n",chan->if_name); + DEBUG_ERROR("%s: Critical Error: rx_dma_skb\n",chan->if_name); return; } @@ -3118,7 +3118,7 @@ static void xilinx_rx_post_complete (sdla_t *card, private_area_t *chan, /* Checking Rx DMA Go bit. Has to be '0' */ if (wan_test_bit(RxDMA_HI_DMA_GO_READY_BIT,&rx_el->reg)){ - DEBUG_EVENT("%s:%s: Error: RxDMA Intr: GO bit set on Rx intr\n", + DEBUG_ERROR("%s:%s: Error: RxDMA Intr: GO bit set on Rx intr\n", card->devname,chan->if_name); chan->if_stats.rx_errors++; goto rx_comp_error; @@ -3128,23 +3128,23 @@ static void xilinx_rx_post_complete (sdla_t *card, private_area_t *chan, if (rx_el->reg&RxDMA_HI_DMA_PCI_ERROR_MASK){ if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_M_ABRT){ - DEBUG_EVENT("%s:%s: Rx Error: Abort from Master: pci fatal error!\n", + DEBUG_ERROR("%s:%s: Rx Error: Abort from Master: pci fatal error!\n", card->devname,chan->if_name); } if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_T_ABRT){ - DEBUG_EVENT("%s:%s: Rx Error: Abort from Target: pci fatal error!\n", + DEBUG_ERROR("%s:%s: Rx Error: Abort from Target: pci fatal error!\n", card->devname,chan->if_name); } if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_DS_TOUT){ - DEBUG_EVENT("%s:%s: Rx Error: No 'DeviceSelect' from target: pci fatal error!\n", + DEBUG_ERROR("%s:%s: Rx Error: No 'DeviceSelect' from target: pci fatal error!\n", card->devname,chan->if_name); } if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_RETRY_TOUT){ - DEBUG_EVENT("%s:%s: Rx Error: 'Retry' exceeds maximum (64k): pci fatal error!\n", + DEBUG_ERROR("%s:%s: Rx Error: 'Retry' exceeds maximum (64k): pci fatal error!\n", card->devname,chan->if_name); } - DEBUG_EVENT("%s: RXDMA PCI ERROR = 0x%x\n",chan->if_name,rx_el->reg); + DEBUG_ERROR("%s: RXDMA PCI ERROR = 0x%x\n",chan->if_name,rx_el->reg); chan->if_stats.rx_errors++; goto rx_comp_error; } @@ -3170,7 +3170,7 @@ static void xilinx_rx_post_complete (sdla_t *card, private_area_t *chan, if (wan_test_bit(RxDMA_HI_DMA_CRC_ERROR_BIT,&rx_el->reg)){ if (net_ratelimit()){ - DEBUG_EVENT("%s:%s: RxDMA Intr: CRC Error! Reg=0x%X\n", + DEBUG_ERROR("%s:%s: RxDMA Intr: CRC Error! Reg=0x%X\n", card->devname,chan->if_name,rx_el->reg); } chan->if_stats.rx_errors++; @@ -3311,7 +3311,7 @@ static char request_xilinx_logical_channel_num (sdla_t *card, private_area_t *ch } if (card->u.xilinx.dev_to_ch_map[(unsigned char)logic_ch]){ - DEBUG_EVENT("%s: Error, request logical ch=%i map busy\n", + DEBUG_ERROR("%s: Error, request logical ch=%i map busy\n", card->devname,logic_ch); return -1; } @@ -3494,7 +3494,7 @@ static void wp_bh (unsigned long data) memset(rx_hdr,0,sizeof(api_rx_hdr_t)); rx_hdr->error_flag=pkt_error; }else{ - DEBUG_EVENT("%s: Error Rx pkt headroom %i < %i\n", + DEBUG_ERROR("%s: Error Rx pkt headroom %i < %i\n", chan->if_name, wan_skb_headroom(new_skb), sizeof(api_rx_hdr_t)); @@ -3512,7 +3512,7 @@ static void wp_bh (unsigned long data) #endif if (wan_api_rx(chan,new_skb) != 0){ if (net_ratelimit()){ - DEBUG_EVENT("%s: Error: Rx Socket busy!\n", + DEBUG_ERROR("%s: Error: Rx Socket busy!\n", chan->if_name); } ++chan->if_stats.rx_dropped; @@ -3575,7 +3575,7 @@ static int fifo_error_interrupt(sdla_t *card, unsigned long reg) card->hw_iface.bus_read_4(card->hw,XILINX_HDLC_RX_INTR_PENDING_REG,&rx_status); if (card->wandev.state != WAN_CONNECTED){ - DEBUG_EVENT("%s: Warning: Ignoring Error Intr: link disc!\n", + DEBUG_ERROR("%s: Warning: Ignoring Error Intr: link disc!\n", card->devname); return 0; } @@ -3589,24 +3589,24 @@ static int fifo_error_interrupt(sdla_t *card, unsigned long reg) chan=(private_area_t*)card->u.xilinx.dev_to_ch_map[i]; if (!chan){ - DEBUG_EVENT("Warning: ignoring tx error intr: no dev!\n"); + DEBUG_ERROR("Warning: ignoring tx error intr: no dev!\n"); continue; } if (!(chan->common.dev->flags&IFF_UP)){ - DEBUG_EVENT("%s: Warning: ignoring tx error intr: dev down 0x%X UP=0x%X!\n", + DEBUG_ERROR("%s: Warning: ignoring tx error intr: dev down 0x%X UP=0x%X!\n", chan->common.dev->name,chan->common.state,chan->ignore_modem); continue; } if (chan->common.state != WAN_CONNECTED){ - DEBUG_EVENT("%s: Warning: ignoring tx error intr: dev disc!\n", + DEBUG_ERROR("%s: Warning: ignoring tx error intr: dev disc!\n", chan->common.dev->name); continue; } if (!chan->hdlc_eng && !wan_test_bit(0,&chan->idle_start)){ - DEBUG_EVENT("%s: Warning: ignoring tx error intr: dev init error!\n", + DEBUG_ERROR("%s: Warning: ignoring tx error intr: dev init error!\n", chan->common.dev->name); if (chan->hdlc_eng){ xilinx_tx_fifo_under_recover(card,chan); @@ -3614,7 +3614,7 @@ static int fifo_error_interrupt(sdla_t *card, unsigned long reg) continue; } - DEBUG_EVENT("%s:%s: Warning TX Fifo Error on LogicCh=%li Slot=%i!\n", + DEBUG_ERROR("%s:%s: Warning TX Fifo Error on LogicCh=%li Slot=%i!\n", card->devname,chan->if_name,chan->logic_ch_num,i); xilinx_tx_fifo_under_recover(card,chan); @@ -3633,18 +3633,18 @@ static int fifo_error_interrupt(sdla_t *card, unsigned long reg) } if (!(chan->common.dev->flags&IFF_UP)){ - DEBUG_EVENT("%s: Warning: ignoring rx error intr: dev down 0x%X UP=0x%X!\n", + DEBUG_ERROR("%s: Warning: ignoring rx error intr: dev down 0x%X UP=0x%X!\n", chan->common.dev->name,chan->common.state,chan->ignore_modem); continue; } if (chan->common.state != WAN_CONNECTED){ - DEBUG_EVENT("%s: Warning: ignoring rx error intr: dev disc!\n", + DEBUG_ERROR("%s: Warning: ignoring rx error intr: dev disc!\n", chan->common.dev->name); continue; } - DEBUG_EVENT("%s:%s: Warning RX Fifo Error on LCh=%li Slot=%i RxCL=%i RxFL=%i RxDMA=%i\n", + DEBUG_ERROR("%s:%s: Warning RX Fifo Error on LCh=%li Slot=%i RxCL=%i RxFL=%i RxDMA=%i\n", card->devname,chan->if_name,chan->logic_ch_num,i, wan_skb_queue_len(&chan->wp_rx_complete_list), wan_skb_queue_len(&chan->wp_rx_free_list), @@ -3793,13 +3793,13 @@ static void wp_xilinx_isr (sdla_t* card) if (wan_test_bit(i,&dma_rx_reg) && test_bit(i,&card->u.xilinx.logic_ch_map)){ chan=(private_area_t*)card->u.xilinx.dev_to_ch_map[i]; if (!chan){ - DEBUG_EVENT("%s: Error: No Dev for Rx logical ch=%i\n", + DEBUG_ERROR("%s: Error: No Dev for Rx logical ch=%i\n", card->devname,i); continue; } if (!(chan->common.dev->flags&IFF_UP)){ - DEBUG_EVENT("%s: Error: Dev not up for Rx logical ch=%i\n", + DEBUG_ERROR("%s: Error: Dev not up for Rx logical ch=%i\n", card->devname,i); continue; } @@ -3834,13 +3834,13 @@ isr_skb_rx: if (wan_test_bit(i,&dma_tx_reg) && test_bit(i,&card->u.xilinx.logic_ch_map)){ chan=(private_area_t*)card->u.xilinx.dev_to_ch_map[i]; if (!chan){ - DEBUG_EVENT("%s: Error: No Dev for Tx logical ch=%i\n", + DEBUG_ERROR("%s: Error: No Dev for Tx logical ch=%i\n", card->devname,i); continue; } if (!(chan->common.dev->flags&IFF_UP)){ - DEBUG_EVENT("%s: Error: Dev not up for Tx logical ch=%i\n", + DEBUG_ERROR("%s: Error: Dev not up for Tx logical ch=%i\n", card->devname,i); continue; } @@ -3952,7 +3952,7 @@ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, set_bit (0,&trace_info->tracing_enabled); }else{ - DEBUG_EVENT("%s: Error: ATM trace running!\n", + DEBUG_ERROR("%s: Error: ATM trace running!\n", card->devname); wan_udp_pkt->wan_udp_return_code = 2; } @@ -3987,7 +3987,7 @@ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, if(wan_test_bit(0,&trace_info->tracing_enabled)){ trace_info->trace_timeout = SYSTEM_TICKS; }else{ - DEBUG_EVENT("%s: Error ATM trace not enabled\n", + DEBUG_ERROR("%s: Error ATM trace not enabled\n", card->devname); /* set return code */ wan_udp_pkt->wan_udp_return_code = 1; @@ -4310,7 +4310,7 @@ static int xilinx_read(sdla_t *card,struct ifreq *ifr) struct sdla_hdlc_api api_cmd; if (!ifr || !ifr->ifr_data){ - DEBUG_EVENT("%s: Error: No ifr or ifr_data\n",__FUNCTION__); + DEBUG_ERROR("%s: Error: No ifr or ifr_data\n",__FUNCTION__); return -EFAULT; } @@ -4346,7 +4346,7 @@ static int xilinx_write(sdla_t *card,struct ifreq *ifr) struct sdla_hdlc_api api_cmd; if (!ifr || !ifr->ifr_data){ - DEBUG_EVENT("%s: Error: No ifr or ifr_data\n",__FUNCTION__); + DEBUG_ERROR("%s: Error: No ifr or ifr_data\n",__FUNCTION__); return -EFAULT; } @@ -4399,7 +4399,7 @@ static int xilinx_write_bios(sdla_t *card,struct ifreq *ifr) struct sdla_hdlc_api api_cmd; if (!ifr || !ifr->ifr_data){ - DEBUG_EVENT("%s: Error: No ifr or ifr_data\n",__FUNCTION__); + DEBUG_ERROR("%s: Error: No ifr or ifr_data\n",__FUNCTION__); return -EINVAL; } @@ -4743,7 +4743,7 @@ static int xilinx_write_ctrl_hdlc(sdla_t *card, u32 timeslot, u8 reg_off, u32 da } if ((jiffies-timeout) > 1){ - DEBUG_EVENT("%s: Error: Access to timeslot %i timed out!\n", + DEBUG_ERROR("%s: Error: Access to timeslot %i timed out!\n", card->devname,ts_orig); return -EIO; } @@ -4833,7 +4833,7 @@ static int request_fifo_baddr_and_size(sdla_t *card, private_area_t *chan) fifo_size=map_fifo_baddr_and_size(card,req_fifo_size,&chan->fifo_base_addr); if (fifo_size == 0 || chan->fifo_base_addr == 31){ - DEBUG_EVENT("%s:%s: Error: Failed to obtain fifo size %i or addr %i \n", + DEBUG_ERROR("%s:%s: Error: Failed to obtain fifo size %i or addr %i \n", card->devname,chan->if_name,fifo_size,chan->fifo_base_addr); return -EINVAL; } @@ -4850,7 +4850,7 @@ static int request_fifo_baddr_and_size(sdla_t *card, private_area_t *chan) } if (fifo_size != req_fifo_size){ - DEBUG_EVENT("%s:%s: Warning: Failed to obtain the req fifo %i got %i\n", + DEBUG_WARNING("%s:%s: Warning: Failed to obtain the req fifo %i got %i\n", card->devname,chan->if_name,req_fifo_size,fifo_size); } diff --git a/patches/kdrivers/src/net/sdla_aft_te3.c b/patches/kdrivers/src/net/sdla_aft_te3.c index b6dfece..1b40a2c 100644 --- a/patches/kdrivers/src/net/sdla_aft_te3.c +++ b/patches/kdrivers/src/net/sdla_aft_te3.c @@ -104,6 +104,8 @@ enum { * 0=Remove Disabled */ +WAN_DECLARE_NETDEV_OPS(wan_netdev_ops) + static int aft_rx_copyback=1000; @@ -850,7 +852,7 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) }else{ - DEBUG_EVENT( "%s:%s: Error: Invalid operation mode [WANPIPE|API|BRIDGE|BRIDGE_NODE]\n", + DEBUG_ERROR( "%s:%s: Error: Invalid operation mode [WANPIPE|API|BRIDGE|BRIDGE_NODE]\n", card->devname,chan->if_name); err=-EINVAL; goto new_if_error; @@ -866,7 +868,7 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) chan->single_dma_chain=1; if (card->wandev.mtu&0x03){ - DEBUG_EVENT("%s:%s: Error, Transparent MTU must be word aligned!\n", + DEBUG_ERROR("%s:%s: Error, Transparent MTU must be word aligned!\n", card->devname,chan->if_name); err = -EINVAL; goto new_if_error; @@ -909,7 +911,7 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) chan->dma_mtu = xilinx_valid_mtu(chan->dma_mtu); if (!chan->dma_mtu){ - DEBUG_EVENT("%s:%s: Error invalid MTU %i mru %i\n", + DEBUG_ERROR("%s:%s: Error invalid MTU %i mru %i\n", card->devname, chan->if_name, card->wandev.mtu,card->u.xilinx.cfg.mru); @@ -997,7 +999,15 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) * can return an error */ wan_netif_set_priv(dev,chan); #if defined(__LINUX__) - dev->init = &if_init; + WAN_NETDEV_OPS_BIND(dev,wan_netdev_ops); + WAN_NETDEV_OPS_INIT(dev,wan_netdev_ops,&if_init); + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&if_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&if_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&if_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&if_stats); + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&if_tx_timeout); + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,if_do_ioctl); + WAN_NETDEV_OPS_MTU(dev,wan_netdev_ops,if_change_mtu); # ifdef WANPIPE_GENERIC if_init(dev); # endif @@ -1178,21 +1188,23 @@ static int if_init (netdevice_t* dev) #endif /* Initialize device driver entry points */ - dev->open = &if_open; - dev->stop = &if_close; + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&if_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&if_close); + #ifdef WANPIPE_GENERIC hdlc = dev_to_hdlc(dev); hdlc->xmit = if_send; #else - dev->hard_start_xmit = &if_send; + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&if_send); #endif - dev->get_stats = &if_stats; + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&if_stats); #if defined(LINUX_2_4)||defined(LINUX_2_6) - dev->tx_timeout = &if_tx_timeout; + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&if_tx_timeout); + dev->watchdog_timeo = 2*HZ; #endif - dev->do_ioctl = if_do_ioctl; - dev->change_mtu = if_change_mtu; + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,if_do_ioctl); + WAN_NETDEV_OPS_MTU(dev,wan_netdev_ops,if_change_mtu); if (chan->common.usedby == BRIDGE || chan->common.usedby == BRIDGE_NODE){ @@ -1599,7 +1611,7 @@ static int if_send(netdevice_t *dev, netskb_t *skb, struct sockaddr *dst,struct if (chan->common.state != WAN_CONNECTED){ WAN_NETIF_STOP_QUEUE(dev); if (WAN_NETIF_CARRIER_OK(dev)){ - DEBUG_EVENT("%s: Critical Error: Carrier on on tx dev down\n", + DEBUG_ERROR("%s: Critical Error: Carrier on on tx dev down\n", chan->if_name); } wan_netif_set_ticks(dev, SYSTEM_TICKS); @@ -1891,7 +1903,7 @@ if_do_ioctl(netdevice_t *dev, struct ifreq *ifr, wan_ioctl_cmd_t cmd) * thus we can release the irq */ if (wan_atomic_read(&chan->udp_pkt_len) > sizeof(wan_udp_pkt_t)){ - DEBUG_EVENT( "%s: Error: Pipemon buf too bit on the way up! %i\n", + DEBUG_ERROR( "%s: Error: Pipemon buf too bit on the way up! %i\n", card->devname,wan_atomic_read(&chan->udp_pkt_len)); wan_atomic_set(&chan->udp_pkt_len,0); return -EINVAL; @@ -1967,7 +1979,7 @@ static int xilinx_init_rx_dev_fifo(sdla_t *card, private_area_t *chan, unsigned } if (timeout){ - DEBUG_EVENT("%s:%s: Error: Rx fifo reset timedout %u us\n", + DEBUG_ERROR("%s:%s: Error: Rx fifo reset timedout %u us\n", card->devname,chan->if_name,i*FIFO_RESET_TIMEOUT_US); }else{ DEBUG_TEST("%s:%s: Rx Fifo Reset Successful\n", @@ -2016,7 +2028,7 @@ static int xilinx_init_tx_dev_fifo(sdla_t *card, private_area_t *chan, unsigned } if (timeout){ - DEBUG_EVENT("%s:%s: Error: Tx fifo reset timedout %u us\n", + DEBUG_ERROR("%s:%s: Error: Tx fifo reset timedout %u us\n", card->devname,chan->if_name,i*FIFO_RESET_TIMEOUT_US); }else{ DEBUG_TEST("%s:%s: Tx Fifo Reset Successful\n", @@ -2183,23 +2195,23 @@ static void xilinx_tx_post_complete (sdla_t *card, private_area_t *chan, netskb_ chan->errstats.Tx_pci_errors++; if (reg & TxDMA_HI_DMA_PCI_ERROR_M_ABRT){ - DEBUG_EVENT("%s:%s: Tx Error: Abort from Master: pci fatal error!\n", + DEBUG_ERROR("%s:%s: Tx Error: Abort from Master: pci fatal error!\n", card->devname,chan->if_name); } if (reg & TxDMA_HI_DMA_PCI_ERROR_T_ABRT){ - DEBUG_EVENT("%s:%s: Tx Error: Abort from Target: pci fatal error!\n", + DEBUG_ERROR("%s:%s: Tx Error: Abort from Target: pci fatal error!\n", card->devname,chan->if_name); } if (reg & TxDMA_HI_DMA_PCI_ERROR_DS_TOUT){ if (WAN_NET_RATELIMIT()) { - DEBUG_EVENT("%s:%s: Tx Warning: PCI Latency Timeout!\n", + DEBUG_WARNING("%s:%s: Tx Warning: PCI Latency Timeout!\n", card->devname,chan->if_name); } chan->errstats.Tx_pci_latency++; goto tx_post_ok; } if (reg & TxDMA_HI_DMA_PCI_ERROR_RETRY_TOUT){ - DEBUG_EVENT("%s:%s: Tx Error: 'Retry' exceeds maximum (64k): pci fatal error!\n", + DEBUG_ERROR("%s:%s: Tx Error: 'Retry' exceeds maximum (64k): pci fatal error!\n", card->devname,chan->if_name); } } @@ -2250,28 +2262,28 @@ static int aft_check_pci_errors(sdla_t *card, private_area_t *chan, wp_rx_elemen if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_M_ABRT){ if (WAN_NET_RATELIMIT()) { - DEBUG_EVENT("%s:%s: Rx Error: Abort from Master: pci fatal error!\n", + DEBUG_ERROR("%s:%s: Rx Error: Abort from Master: pci fatal error!\n", card->devname,chan->if_name); } pci_err=1; } if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_T_ABRT){ if (WAN_NET_RATELIMIT()) { - DEBUG_EVENT("%s:%s: Rx Error: Abort from Target: pci fatal error!\n", + DEBUG_ERROR("%s:%s: Rx Error: Abort from Target: pci fatal error!\n", card->devname,chan->if_name); } pci_err=1; } if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_DS_TOUT){ if (WAN_NET_RATELIMIT()) { - DEBUG_EVENT("%s:%s: Rx Error: No 'DeviceSelect' from target: pci fatal error!\n", + DEBUG_ERROR("%s:%s: Rx Error: No 'DeviceSelect' from target: pci fatal error!\n", card->devname,chan->if_name); } pci_err=1; } if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_RETRY_TOUT){ if (WAN_NET_RATELIMIT()) { - DEBUG_EVENT("%s:%s: Rx Error: 'Retry' exceeds maximum (64k): pci fatal error!\n", + DEBUG_ERROR("%s:%s: Rx Error: 'Retry' exceeds maximum (64k): pci fatal error!\n", card->devname,chan->if_name); } pci_err=1; @@ -2279,7 +2291,7 @@ static int aft_check_pci_errors(sdla_t *card, private_area_t *chan, wp_rx_elemen if (!pci_err) { if (WAN_NET_RATELIMIT()) { - DEBUG_EVENT("%s: RXDMA Unknown PCI ERROR = 0x%x\n",chan->if_name,rx_el->reg); + DEBUG_ERROR("%s: RXDMA Unknown PCI ERROR = 0x%x\n",chan->if_name,rx_el->reg); } } @@ -2328,7 +2340,7 @@ static void xilinx_rx_post_complete (sdla_t *card, private_area_t *chan, /* Checking Rx DMA Go bit. Has to be '0' */ if (wan_test_bit(RxDMA_HI_DMA_GO_READY_BIT,&rx_el->reg)){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s:%s: Error: RxDMA Intr: GO bit set on Rx intr\n", + DEBUG_ERROR("%s:%s: Error: RxDMA Intr: GO bit set on Rx intr\n", card->devname,chan->if_name); } chan->if_stats.rx_errors++; @@ -2614,7 +2626,7 @@ static void wp_bh (void* data, int dummy) }else{ int hroom=wan_skb_headroom(new_skb); int rx_sz=sizeof(wp_api_hdr_t); - DEBUG_EVENT("%s: Error Rx pkt headroom %i < %i\n", + DEBUG_ERROR("%s: Error Rx pkt headroom %i < %i\n", chan->if_name, hroom, rx_sz); @@ -2708,7 +2720,7 @@ static int fifo_error_interrupt(sdla_t *card, u32 reg, u32 tx_status, u32 rx_sta if (card->wandev.state != WAN_CONNECTED){ - DEBUG_EVENT("%s: Warning: Ignoring Error Intr: link disc!\n", + DEBUG_ERROR("%s: Warning: Ignoring Error Intr: link disc!\n", card->devname); return 0; } @@ -2729,14 +2741,14 @@ static int fifo_error_interrupt(sdla_t *card, u32 reg, u32 tx_status, u32 rx_sta chan=(private_area_t*)card->u.xilinx.dev_to_ch_map[i]; if (!chan){ if (WAN_NET_RATELIMIT()) { - DEBUG_EVENT("Warning: ignoring tx error intr: no dev!\n"); + DEBUG_ERROR("Warning: ignoring tx error intr: no dev!\n"); } continue; } if (!wan_test_bit(0,&chan->up)){ if (WAN_NET_RATELIMIT()) { - DEBUG_EVENT("%s: Warning: ignoring tx error intr: dev down 0x%X UP=0x%X!\n", + DEBUG_ERROR("%s: Warning: ignoring tx error intr: dev down 0x%X UP=0x%X!\n", wan_netif_name(chan->common.dev),chan->common.state,chan->ignore_modem); } continue; @@ -2744,7 +2756,7 @@ static int fifo_error_interrupt(sdla_t *card, u32 reg, u32 tx_status, u32 rx_sta if (chan->common.state != WAN_CONNECTED){ if (WAN_NET_RATELIMIT()) { - DEBUG_EVENT("%s: Warning: ignoring tx error intr: dev disc!\n", + DEBUG_ERROR("%s: Warning: ignoring tx error intr: dev disc!\n", wan_netif_name(chan->common.dev)); } continue; @@ -2752,7 +2764,7 @@ static int fifo_error_interrupt(sdla_t *card, u32 reg, u32 tx_status, u32 rx_sta if (!chan->hdlc_eng && !wan_test_bit(0,&chan->idle_start)){ if (WAN_NET_RATELIMIT()) { - DEBUG_EVENT("%s: Warning: ignoring tx error intr: dev init error!\n", + DEBUG_ERROR("%s: Warning: ignoring tx error intr: dev init error!\n", wan_netif_name(chan->common.dev)); } if (chan->hdlc_eng){ @@ -2780,13 +2792,13 @@ static int fifo_error_interrupt(sdla_t *card, u32 reg, u32 tx_status, u32 rx_sta } if (!wan_test_bit(0,&chan->up)){ - DEBUG_EVENT("%s: Warning: ignoring rx error intr: dev down 0x%X UP=0x%X!\n", + DEBUG_ERROR("%s: Warning: ignoring rx error intr: dev down 0x%X UP=0x%X!\n", wan_netif_name(chan->common.dev),chan->common.state,chan->ignore_modem); continue; } if (chan->common.state != WAN_CONNECTED){ - DEBUG_EVENT("%s: Warning: ignoring rx error intr: dev disc!\n", + DEBUG_ERROR("%s: Warning: ignoring rx error intr: dev disc!\n", wan_netif_name(chan->common.dev)); continue; } @@ -2830,7 +2842,7 @@ static void front_end_interrupt(sdla_t *card, unsigned long reg) /* FIXME HANDLE T3 Interrupt */ WAN_FECALL(&card->wandev, isr, (&card->fe)); }else{ - DEBUG_EVENT("%s: Internal Error (Never should happened)!\n", + DEBUG_ERROR("%s: Internal Error (Never should happened)!\n", card->devname); } @@ -3001,13 +3013,13 @@ static WAN_IRQ_RETVAL wp_aft_te3_isr (sdla_t* card) chan=(private_area_t*)card->u.xilinx.dev_to_ch_map[i]; if (!chan){ - DEBUG_EVENT("%s: Error: No Dev for Rx logical ch=%i\n", + DEBUG_ERROR("%s: Error: No Dev for Rx logical ch=%i\n", card->devname,i); continue; } if (!wan_test_bit(0,&chan->up)){ - DEBUG_EVENT("%s: Error: Dev not up for Rx logical ch=%i\n", + DEBUG_ERROR("%s: Error: Dev not up for Rx logical ch=%i\n", card->devname,i); continue; } @@ -3047,13 +3059,13 @@ isr_skb_rx: if (wan_test_bit(i,&dma_tx_reg) && wan_test_bit(i,&card->u.xilinx.logic_ch_map)){ chan=(private_area_t*)card->u.xilinx.dev_to_ch_map[i]; if (!chan){ - DEBUG_EVENT("%s: Error: No Dev for Tx logical ch=%i\n", + DEBUG_ERROR("%s: Error: No Dev for Tx logical ch=%i\n", card->devname,i); continue; } if (!wan_test_bit(0,&chan->up)){ - DEBUG_EVENT("%s: Error: Dev not up for Tx logical ch=%i\n", + DEBUG_ERROR("%s: Error: Dev not up for Tx logical ch=%i\n", card->devname,i); continue; } @@ -3211,7 +3223,7 @@ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, wan_set_bit (0,&trace_info->tracing_enabled); }else{ - DEBUG_EVENT("%s: Error: TE3 trace already running!\n", + DEBUG_ERROR("%s: Error: TE3 trace already running!\n", card->devname); wan_udp_pkt->wan_udp_return_code = 2; } @@ -3246,7 +3258,7 @@ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, if(wan_test_bit(0,&trace_info->tracing_enabled)){ trace_info->trace_timeout = SYSTEM_TICKS; }else{ - DEBUG_EVENT("%s: Error TE3 trace not enabled\n", + DEBUG_ERROR("%s: Error TE3 trace not enabled\n", card->devname); /* set return code */ wan_udp_pkt->wan_udp_return_code = 1; @@ -3728,7 +3740,7 @@ static int aft_devel_ioctl(sdla_t *card,struct ifreq *ifr) int err; if (!ifr || !ifr->ifr_data){ - DEBUG_EVENT("%s: Error: No ifr or ifr_data\n",__FUNCTION__); + DEBUG_ERROR("%s: Error: No ifr or ifr_data\n",__FUNCTION__); return -EINVAL; } @@ -4367,7 +4379,7 @@ static int xilinx_t3_exar_chip_configure(sdla_t *card) wan_clear_bit(INTERFACE_MODE_E3_G832,®); } }else{ - DEBUG_EVENT("%s: Error: T3 Exar doesn't support non T3/E3 interface!\n", + DEBUG_ERROR("%s: Error: T3 Exar doesn't support non T3/E3 interface!\n", card->devname); return -EINVAL; } @@ -4429,14 +4441,14 @@ static int xilinx_t3_exar_chip_configure(sdla_t *card) if (card->wandev.fe_iface.post_init){ err=card->wandev.fe_iface.post_init(&card->fe); } else { - DEBUG_EVENT("%s: Internal Error (%s:%d)\n", + DEBUG_ERROR("%s: Internal Error (%s:%d)\n", card->devname, __FUNCTION__,__LINE__); return -EINVAL; } }else{ - DEBUG_EVENT("%s: Internal Error (%s:%d)\n", + DEBUG_ERROR("%s: Internal Error (%s:%d)\n", card->devname, __FUNCTION__,__LINE__); return -EINVAL; @@ -4465,7 +4477,7 @@ static int xilinx_t3_exar_chip_configure(sdla_t *card) xilinx_delay(1); if (err != 0){ - DEBUG_EVENT("%s: Error: HDLC Core Not Ready!\n", + DEBUG_ERROR("%s: Error: HDLC Core Not Ready!\n", card->devname); card->hw_iface.bus_read_4(card->hw,XILINX_CHIP_CFG_REG, ®); @@ -4512,7 +4524,7 @@ static int xilinx_t3_exar_chip_configure(sdla_t *card) card->hw_iface.bus_read_4(card->hw, XILINX_CHIP_CFG_REG, (u32*)®); if (wan_test_bit(DMA_INTR_FLAG,®)){ - DEBUG_EVENT("%s: Error: Active DMA Interrupt Pending. !\n", + DEBUG_ERROR("%s: Error: Active DMA Interrupt Pending. !\n", card->devname); reg = 0; @@ -4522,7 +4534,7 @@ static int xilinx_t3_exar_chip_configure(sdla_t *card) return err; } if (wan_test_bit(ERROR_INTR_FLAG,®)){ - DEBUG_EVENT("%s: Error: Active Error Interrupt Pending. !\n", + DEBUG_ERROR("%s: Error: Active Error Interrupt Pending. !\n", card->devname); reg = 0; @@ -4868,7 +4880,7 @@ static int aft_dma_chain_tx(aft_dma_chain_t *dma_chain,private_area_t *chan, int card->hw_iface.bus_read_4(card->hw,dma_descr,®); if (wan_test_bit(TxDMA_HI_DMA_GO_READY_BIT,®)){ - DEBUG_EVENT("%s: Error: TxDMA GO Ready bit set on dma (chain=%i) Tx 0x%X\n", + DEBUG_ERROR("%s: Error: TxDMA GO Ready bit set on dma (chain=%i) Tx 0x%X\n", card->devname,dma_ch_indx,reg); card->hw_iface.bus_write_4(card->hw,dma_descr,0); return -EBUSY; @@ -5009,7 +5021,7 @@ static int aft_realign_skb_pkt(private_area_t *chan, netskb_t *skb) int len = wan_skb_len(skb); if (len > chan->dma_mtu){ - DEBUG_EVENT("%s: Critical error: Tx unalign pkt(%d) > MTU buf(%d)!\n", + DEBUG_ERROR("%s: Critical error: Tx unalign pkt(%d) > MTU buf(%d)!\n", chan->if_name,len,chan->dma_mtu); return -ENOMEM; } @@ -5017,7 +5029,7 @@ static int aft_realign_skb_pkt(private_area_t *chan, netskb_t *skb) if (!chan->tx_realign_buf){ chan->tx_realign_buf=wan_malloc(chan->dma_mtu); if (!chan->tx_realign_buf){ - DEBUG_EVENT("%s: Error: Failed to allocate tx memory buf\n", + DEBUG_ERROR("%s: Error: Failed to allocate tx memory buf\n", chan->if_name); return -ENOMEM; }else{ @@ -5033,7 +5045,7 @@ static int aft_realign_skb_pkt(private_area_t *chan, netskb_t *skb) wan_skb_trim(skb,0); if (wan_skb_tailroom(skb) < len){ - DEBUG_EVENT("%s: Critical error: Tx unalign pkt tail room(%i) < unalign len(%i)!\n", + DEBUG_ERROR("%s: Critical error: Tx unalign pkt tail room(%i) < unalign len(%i)!\n", chan->if_name,wan_skb_tailroom(skb),len); return -ENOMEM; @@ -5073,7 +5085,7 @@ static int xilinx_dma_te3_tx (sdla_t *card,private_area_t *chan) } if (chan->tx_chain_indx >= MAX_AFT_DMA_CHAINS){ - DEBUG_EVENT("%s: MAJOR ERROR: TE3 Tx: Dma tx chain = %i\n", + DEBUG_ERROR("%s: MAJOR ERROR: TE3 Tx: Dma tx chain = %i\n", chan->if_name,chan->tx_chain_indx); if (!chan->single_dma_chain){ aft_enable_tx_watchdog(card,AFT_TX_TIMEOUT); @@ -5101,7 +5113,7 @@ static int xilinx_dma_te3_tx (sdla_t *card,private_area_t *chan) skb=chan->tx_idle_skb; if (!skb) { if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: Error: Tx Idle not allocated\n", + DEBUG_ERROR("%s: Error: Tx Idle not allocated\n", card->devname); } wan_clear_bit(0,&dma_chain->init); @@ -5121,7 +5133,7 @@ static int xilinx_dma_te3_tx (sdla_t *card,private_area_t *chan) err=aft_realign_skb_pkt(chan,skb); if (err){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: Tx Error: Non Aligned packet %p: dropping...\n", + DEBUG_ERROR("%s: Tx Error: Non Aligned packet %p: dropping...\n", chan->if_name,wan_skb_data(skb)); } wan_skb_free(skb); @@ -5186,7 +5198,7 @@ static int xilinx_dma_te3_tx (sdla_t *card,private_area_t *chan) err=aft_dma_chain_tx(dma_chain,chan,intr); if (err){ - DEBUG_EVENT("%s: Tx dma chain %i overrun error: should never happen!\n", + DEBUG_ERROR("%s: Tx dma chain %i overrun error: should never happen!\n", chan->if_name,dma_chain->index); aft_tx_dma_chain_init(chan,dma_chain); chan->if_stats.tx_errors++; @@ -5335,7 +5347,7 @@ static int xilinx_dma_rx(sdla_t *card, private_area_t *chan, int gcur_ptr) aft_free_rx_complete_list(chan); free_queue_len=wan_skb_queue_len(&chan->wp_rx_free_list); if (free_queue_len == 0){ - DEBUG_EVENT("%s: %s() CRITICAL ERROR: No free rx buffers\n", + DEBUG_ERROR("%s: %s() CRITICAL ERROR: No free rx buffers\n", card->devname,__FUNCTION__); goto te3_rx_skip; } @@ -5431,7 +5443,7 @@ static int xilinx_dma_rx(sdla_t *card, private_area_t *chan, int gcur_ptr) err=aft_dma_chain_rx(dma_chain,chan,intr); if (err){ - DEBUG_EVENT("%s: Rx dma chain %i overrun error: should never happen!\n", + DEBUG_ERROR("%s: Rx dma chain %i overrun error: should never happen!\n", chan->if_name,dma_chain->index); aft_rx_dma_chain_init(chan,dma_chain); chan->if_stats.rx_dropped++; @@ -5498,7 +5510,7 @@ static void aft_rx_dma_chain_handler(private_area_t *chan, int wtd, int reset) dma_chain = &chan->rx_dma_chain_table[chan->rx_pending_chain_indx]; if (!dma_chain){ - DEBUG_EVENT("%s:%d Assertion Error !!!!\n", + DEBUG_ERROR("%s:%d Assertion Error !!!!\n", __FUNCTION__,__LINE__); break; } @@ -5667,7 +5679,7 @@ static void aft_dma_te3_set_intr(aft_dma_chain_t *dma_chain, private_area_t *cha /* If the current DMA chain is in use,then * all chains are busy */ if (!wan_test_bit(0,&dma_chain->init)){ - DEBUG_EVENT("%s: (%s) Error: Pending chain %i empty!\n", + DEBUG_ERROR("%s: (%s) Error: Pending chain %i empty!\n", chan->if_name,__FUNCTION__,dma_chain->index); return; } @@ -5680,7 +5692,7 @@ static void aft_dma_te3_set_intr(aft_dma_chain_t *dma_chain, private_area_t *cha * is in process of being Received, thus * all are busy */ if (!wan_test_bit(RxDMA_HI_DMA_GO_READY_BIT,®)){ - DEBUG_EVENT("%s: (%s) Error: Pending chain %i Go bit is not set!\n", + DEBUG_ERROR("%s: (%s) Error: Pending chain %i Go bit is not set!\n", chan->if_name,__FUNCTION__,dma_chain->index); return; } @@ -5756,7 +5768,7 @@ static void aft_list_descriptors(private_area_t *chan) dma_chain = &chan->rx_dma_chain_table[i]; if (!dma_chain){ - DEBUG_EVENT("%s:%d Assertion Error !!!!\n", + DEBUG_ERROR("%s:%d Assertion Error !!!!\n", __FUNCTION__,__LINE__); break; } @@ -5808,7 +5820,7 @@ static void aft_list_tx_descriptors(private_area_t *chan) dma_chain = &chan->tx_dma_chain_table[i]; if (!dma_chain){ - DEBUG_EVENT("%s:%d Assertion Error !!!!\n", + DEBUG_ERROR("%s:%d Assertion Error !!!!\n", __FUNCTION__,__LINE__); break; } @@ -5845,7 +5857,7 @@ static void aft_free_rx_descriptors(private_area_t *chan) dma_chain = &chan->rx_dma_chain_table[i]; if (!dma_chain){ - DEBUG_EVENT("%s:%d Assertion Error !!!!\n", + DEBUG_ERROR("%s:%d Assertion Error !!!!\n", __FUNCTION__,__LINE__); break; } @@ -5929,7 +5941,7 @@ static void aft_free_tx_descriptors(private_area_t *chan) dma_chain = &chan->tx_dma_chain_table[i]; if (!dma_chain){ - DEBUG_EVENT("%s:%d Assertion Error !!!!\n", + DEBUG_ERROR("%s:%d Assertion Error !!!!\n", __FUNCTION__,__LINE__); break; } @@ -6096,7 +6108,7 @@ static void aft_critical_shutdown (sdla_t *card) printk(KERN_ERR "%s: Error: Card Critically Shutdown!\n", card->devname); #else - DEBUG_EVENT("%s: Error: Card Critically Shutdown!\n", + DEBUG_ERROR("%s: Error: Card Critically Shutdown!\n", card->devname); #endif @@ -6164,7 +6176,7 @@ static int aft_hdlc_core_ready(sdla_t *card) } if (err){ - DEBUG_EVENT("%s: Critical Error: HDLC Core not ready!\n", + DEBUG_ERROR("%s: Critical Error: HDLC Core not ready!\n", card->devname); } return err; diff --git a/patches/kdrivers/src/net/sdla_asyhdlc.c b/patches/kdrivers/src/net/sdla_asyhdlc.c index 7edabfc..da27a41 100644 --- a/patches/kdrivers/src/net/sdla_asyhdlc.c +++ b/patches/kdrivers/src/net/sdla_asyhdlc.c @@ -103,6 +103,8 @@ enum { #define PORT(x) (x == 0 ? "PRIMARY" : "SECONDARY" ) #define MAX_BH_BUFF 10 +WAN_DECLARE_NETDEV_OPS(wan_netdev_ops) + /******Data Structures*****************************************************/ @@ -837,7 +839,15 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) * finished successfully. DO NOT place any code below that * can return an error */ - dev->init = &if_init; + WAN_NETDEV_OPS_BIND(dev,wan_netdev_ops); + WAN_NETDEV_OPS_INIT(dev,wan_netdev_ops,&if_init); + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&if_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&if_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&if_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&if_stats); + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&if_tx_timeout); + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,if_do_ioctl); + wan_netif_set_priv(dev,chdlc_priv_area); set_bit(0,&chdlc_priv_area->config_chdlc); @@ -913,15 +923,15 @@ static int if_init (netdevice_t* dev) wan_device_t* wandev = &card->wandev; /* Initialize device driver entry points */ - dev->open = &if_open; - dev->stop = &if_close; - dev->hard_start_xmit = &if_send; - dev->get_stats = &if_stats; + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&if_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&if_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&if_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&if_stats); #if defined(LINUX_2_4)||defined(LINUX_2_6) - dev->tx_timeout = &if_tx_timeout; + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&if_tx_timeout); dev->watchdog_timeo = TX_TIMEOUT; #endif - dev->do_ioctl = if_do_ioctl; + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,if_do_ioctl); if (chdlc_priv_area->common.usedby == BRIDGE || chdlc_priv_area->common.usedby == BRIDGE_NODE){ diff --git a/patches/kdrivers/src/net/sdla_atm.c b/patches/kdrivers/src/net/sdla_atm.c index 152c90e..96effe2 100644 --- a/patches/kdrivers/src/net/sdla_atm.c +++ b/patches/kdrivers/src/net/sdla_atm.c @@ -44,6 +44,8 @@ #define ATM_TIMER_TIMEOUT 1 #define POLL_DELAY_TIMEOUT 1 +WAN_DECLARE_NETDEV_OPS(wan_netdev_ops) + /* Private critical flags */ enum { POLL_CRIT = PRIV_CRIT, @@ -443,15 +445,15 @@ int wp_atm_init (sdla_t* card, wandev_conf_t* conf) if((card->wandev.comm_port == WANOPT_PRI)) { /* For Primary Port 0 */ if (conf->mtu < MIN_WP_PRI_MTU){ - DEBUG_EVENT("%s: Warning: Limiting MTU to Min=%i\n", + DEBUG_WARNING("%s: Warning: Limiting MTU to Min=%i\n", card->devname,MIN_WP_PRI_MTU); conf->mtu=MIN_WP_PRI_MTU; }else if (conf->mtu > MAX_WP_PRI_MTU){ - DEBUG_EVENT("%s: Warning: Limiting MTU to Max=%i\n", + DEBUG_WARNING("%s: Warning: Limiting MTU to Max=%i\n", card->devname,MIN_WP_PRI_MTU); conf->mtu=MAX_WP_PRI_MTU; }else{ - DEBUG_EVENT("%s: Warning: Limiting MTU to Max=%i\n", + DEBUG_WARNING("%s: Warning: Limiting MTU to Max=%i\n", card->devname,MIN_WP_PRI_MTU); } card->wandev.mtu = conf->mtu; @@ -755,7 +757,7 @@ static int new_if (wan_device_t* wandev, struct net_device* dev, wanif_conf_t* c int err = 0; if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)) { - DEBUG_EVENT( "%s: Error: Invalid interface name!\n", + DEBUG_ERROR( "%s: Error: Invalid interface name!\n", card->devname); return -EINVAL; } @@ -826,7 +828,7 @@ static int new_if (wan_device_t* wandev, struct net_device* dev, wanif_conf_t* c card->devname,priv_area->if_name); }else{ - DEBUG_EVENT( "%s:%s: Error: Invalid operation mode [WANPIPE|API|BRIDGE|BRIDGE_NODE]\n", + DEBUG_ERROR( "%s:%s: Error: Invalid operation mode [WANPIPE|API|BRIDGE|BRIDGE_NODE]\n", card->devname,priv_area->if_name); err=-EINVAL; goto new_if_error; @@ -923,7 +925,13 @@ static int new_if (wan_device_t* wandev, struct net_device* dev, wanif_conf_t* c /* Only setup the dev pointer once the new_if function has * finished successfully. DO NOT place any code below that * can return an error */ - dev->init = &if_init; + WAN_NETDEV_OPS_BIND(dev,wan_netdev_ops); + WAN_NETDEV_OPS_INIT(dev,wan_netdev_ops,&if_init); + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&if_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&if_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&if_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&if_stats); + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&if_tx_timeout); wan_netif_set_priv(dev,priv_area); /* Increment the number of network interfaces @@ -1036,16 +1044,17 @@ static int if_init (struct net_device* dev) wan_device_t* wandev = &card->wandev; /* Initialize device driver entry points */ - dev->open = &if_open; - dev->stop = &if_close; - dev->hard_start_xmit = &if_send; - dev->get_stats = &if_stats; + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&if_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&if_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&if_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&if_stats); + + #if defined(LINUX_2_4)||defined(LINUX_2_6) - dev->tx_timeout = &if_tx_timeout; + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&if_tx_timeout); dev->watchdog_timeo = TX_TIMEOUT; #endif - dev->do_ioctl = if_do_ioctl; - + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,if_do_ioctl); if (priv_area->cfg.encap_mode == RFC_MODE_BRIDGED_ETH_LLC || priv_area->cfg.encap_mode == RFC_MODE_BRIDGED_ETH_VC){ @@ -1607,7 +1616,7 @@ static int if_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) * thus we can release the irq */ if (wan_atomic_read(&chan->udp_pkt_len) > sizeof(wan_udp_pkt_t)){ - DEBUG_EVENT( "%s: Error: Pipemon buf too bit on the way up! %i\n", + DEBUG_ERROR( "%s: Error: Pipemon buf too bit on the way up! %i\n", card->devname,wan_atomic_read(&chan->udp_pkt_len)); wan_atomic_set(&chan->udp_pkt_len,0); return -EINVAL; @@ -2276,7 +2285,7 @@ static int wp_handle_out_of_sync_condition(sdla_t *card, unsigned char irq) err = card->hw_iface.cmd(card->hw, card->mbox_off, mb); if (err != CMD_OK){ frmw_error(card, err, mb); - DEBUG_EVENT("%s: Error: Failed to re-syncing adapter !\n", + DEBUG_ERROR("%s: Error: Failed to re-syncing adapter !\n", card->devname); /* We failed to resync for some reason, get out @@ -2326,7 +2335,7 @@ static void wp_handle_rx_packets(netskb_t *skb) sdla_t *card; if (!chan){ - DEBUG_EVENT("%s:%d Error, Rx packet has no dev pointer (skb->dev==NULL)\n", + DEBUG_ERROR("%s:%d Error, Rx packet has no dev pointer (skb->dev==NULL)\n", __FUNCTION__,__LINE__); wan_skb_free(skb); return; @@ -3054,7 +3063,7 @@ static int process_udp_mgmt_pkt(sdla_t* card, struct net_device* dev, wan_set_bit (0,&trace_info->tracing_enabled); }else{ - DEBUG_EVENT("%s: Error: ATM trace running!\n", + DEBUG_ERROR("%s: Error: ATM trace running!\n", card->devname); udp_hdr->wan_udphdr_return_code = 2; } @@ -3090,7 +3099,7 @@ static int process_udp_mgmt_pkt(sdla_t* card, struct net_device* dev, if(wan_test_bit(0,&trace_info->tracing_enabled)){ trace_info->trace_timeout = SYSTEM_TICKS; }else{ - DEBUG_EVENT("%s: Error ATM trace not enabled\n", + DEBUG_ERROR("%s: Error ATM trace not enabled\n", card->devname); /* set return code */ udp_hdr->wan_udphdr_return_code = 1; diff --git a/patches/kdrivers/src/net/sdla_bitstrm.c b/patches/kdrivers/src/net/sdla_bitstrm.c index f688d3b..6865cd7 100644 --- a/patches/kdrivers/src/net/sdla_bitstrm.c +++ b/patches/kdrivers/src/net/sdla_bitstrm.c @@ -141,6 +141,9 @@ atomic_t rx_data; # undef HDLC_IDLE_ABORT #endif +WAN_DECLARE_NETDEV_OPS(wan_netdev_ops) + + /******Data Structures*****************************************************/ /* This structure is placed in the private data area of the device structure. @@ -970,7 +973,7 @@ static int update (wan_device_t* wandev) #endif #if 0 - if (bstrm_priv_area->common.usedby == SWITCH && bstrm_priv_area->sw_dev){ + if (bstrm_priv_area->common.usedby == WP_SWITCH && bstrm_priv_area->sw_dev){ bitstrm_private_area_t *sw_chan= (bitstrm_private_area_t *)bstrm_priv_area->sw_dev->priv; sdla_t *sw_card=sw_chan->card; @@ -1237,7 +1240,7 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) #endif } else if( strcmp(conf->usedby, "SWITCH") == 0) { - bstrm_priv_area->common.usedby=SWITCH; + bstrm_priv_area->common.usedby=WP_SWITCH; bstrm_priv_area->protocol=0; DEBUG_EVENT( "%s: Running in SWITCH mode !\n", wandev->name); @@ -1267,7 +1270,15 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) /* prepare network device data space for registration */ - dev->init = &if_init; + WAN_NETDEV_OPS_BIND(dev,wan_netdev_ops); + WAN_NETDEV_OPS_INIT(dev,wan_netdev_ops,&if_init); + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&if_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&if_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&if_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&if_stats); + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&if_tx_timeout); + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,&if_do_ioctl); + wan_netif_set_priv(dev,bstrm_priv_area); bstrm_priv_area->common.dev = dev; @@ -1282,7 +1293,7 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) bstrm_priv_area->tx_flag= 0x7E; //card->u.b.cfg.monosync_tx_time_fill_char; bstrm_priv_area->tx_flag_idle= 0x7E; //card->u.b.cfg.monosync_tx_time_fill_char; - if (bstrm_priv_area->common.usedby == SWITCH){ + if (bstrm_priv_area->common.usedby == WP_SWITCH){ strncpy(bstrm_priv_area->sw_if_name,conf->sw_dev_name,WAN_IFNAME_SZ); } @@ -1529,15 +1540,15 @@ static int if_init (netdevice_t* dev) wan_device_t* wandev = &card->wandev; /* Initialize device driver entry points */ - dev->open = &if_open; - dev->stop = &if_close; - dev->hard_start_xmit = &if_send; - dev->get_stats = &if_stats; + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&if_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&if_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&if_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&if_stats); #if defined(LINUX_2_4)||defined(LINUX_2_6) - dev->tx_timeout = &if_tx_timeout; + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&if_tx_timeout); dev->watchdog_timeo = TX_TIMEOUT; #endif - dev->do_ioctl = &if_do_ioctl; + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,&if_do_ioctl); /* Initialize media-specific parameters */ if (!bstrm_priv_area->protocol){ @@ -1554,7 +1565,7 @@ static int if_init (netdevice_t* dev) dev->type = ARPHRD_PPP; } - if (bstrm_priv_area->common.usedby == SWITCH){ + if (bstrm_priv_area->common.usedby == WP_SWITCH){ dev->mtu = card->u.b.cfg.max_length_tx_data_block; }else{ if (!bstrm_priv_area->protocol){ @@ -1929,7 +1940,7 @@ static int if_open (netdevice_t* dev) protocol_start(card,dev); - if (bstrm_priv_area->common.usedby == SWITCH){ + if (bstrm_priv_area->common.usedby == WP_SWITCH){ int err; err=bstrm_bind_dev_switch(card,bstrm_priv_area,bstrm_priv_area->sw_if_name); if (err){ @@ -1999,7 +2010,7 @@ static int if_close (netdevice_t* dev) protocol_stop(card,dev); - if (bstrm_priv_area->common.usedby == SWITCH){ + if (bstrm_priv_area->common.usedby == WP_SWITCH){ wan_spin_lock_irq(&card->wandev.lock,&smp_flags); bstrm_unbind_dev_switch(card,bstrm_priv_area); wan_spin_unlock_irq(&card->wandev.lock,&smp_flags); @@ -2219,7 +2230,7 @@ static int if_send (struct sk_buff* skb, netdevice_t* dev) wan_skb_unlink(skb); - if (bstrm_priv_area->common.usedby == SWITCH){ + if (bstrm_priv_area->common.usedby == WP_SWITCH){ wanpipe_switch_datascope_tx_up(bstrm_priv_area,skb); } @@ -2233,7 +2244,7 @@ static int if_send (struct sk_buff* skb, netdevice_t* dev) skb_pull(skb,sizeof(api_tx_hdr_t)); } - if (bstrm_priv_area->common.usedby != SWITCH && + if (bstrm_priv_area->common.usedby != WP_SWITCH && bstrm_priv_area->hdlc_eng){ if (hdlc_encode(bstrm_priv_area,&skb) != 0){ ++card->wandev.stats.tx_dropped; @@ -2263,7 +2274,7 @@ static int if_send (struct sk_buff* skb, netdevice_t* dev) bstrm_priv_area->ifstats.tx_bytes += skb->len; spin_lock_irqsave(&card->wandev.lock,smp_flags); - if (bstrm_priv_area->common.usedby != SWITCH && + if (bstrm_priv_area->common.usedby != WP_SWITCH && bstrm_priv_area->hdlc_eng){ bstrm_priv_area->tx_idle_flag = bstrm_priv_area->tx_flag_idle; } @@ -2671,7 +2682,7 @@ static void bstrm_tx_bh (unsigned long data) goto tx_bh_exit; } - if (chan->common.usedby == SWITCH && + if (chan->common.usedby == WP_SWITCH && test_bit(WAIT_DEVICE_BUFFERS,&chan->tq_control)){ DEBUG_EVENT("%s: Warning: Tx Intr while waiting for buffers\n", chan->common.dev->name); @@ -2681,7 +2692,7 @@ static void bstrm_tx_bh (unsigned long data) if (chan->common.usedby == API){ start_net_queue(chan->common.dev); wan_wakeup_api(chan); - }else if (chan->common.usedby == SWITCH){ + }else if (chan->common.usedby == WP_SWITCH){ start_net_queue(chan->common.dev); }else{ wake_net_dev(chan->common.dev); @@ -2741,14 +2752,14 @@ static void bstrm_tx_bh (unsigned long data) lapb_protocol.lapb_mark_bh(chan->annexg_dev); } #endif - }else if (chan->common.usedby == SWITCH){ + }else if (chan->common.usedby == WP_SWITCH){ start_net_queue(chan->common.dev); }else{ wake_net_dev(chan->common.dev); } } - if (chan->common.usedby == SWITCH && + if (chan->common.usedby == WP_SWITCH && test_bit(WAIT_DEVICE_BUFFERS,&chan->tq_control)){ card->u.b.tx_scratch_buf[card->u.b.tx_scratch_buf_len]=chan->tx_idle_flag; card->u.b.tx_scratch_buf_len++; @@ -2876,7 +2887,7 @@ static void bstrm_rx_bh (unsigned long data) continue; } - if (chan->common.usedby == SWITCH){ + if (chan->common.usedby == WP_SWITCH){ bstrm_bh_data_tx_up(card,skb,chan); @@ -2967,9 +2978,9 @@ static void bstrm_rx_bh (unsigned long data) * HDLC decoder or API. For now just push it * into the used queue */ - if (chan->common.usedby == SWITCH){ + if (chan->common.usedby == WP_SWITCH){ //if (skb->len != 0){ - // DEBUG_EVENT( "%s: SWITCH Sending %i, orig skb %i\n", + // DEBUG_EVENT( "%s: WP_SWITCH Sending %i, orig skb %i\n", // card->devname,chan->rx_skb->len, skb->len); //} @@ -3066,8 +3077,7 @@ static void bstrm_switch_send(sdla_t *card, bitstrm_private_area_t * chan, struc card->wandev.stats.rx_errors++; chan->ifstats.rx_errors++; }else{ - if (!chan->sw_dev->hard_start_xmit || - chan->sw_dev->hard_start_xmit(new_skb,chan->sw_dev) != 0){ + if (!WAN_NETDEV_TEST_XMIT(chan->sw_dev) || WAN_NETDEV_XMIT(new_skb,chan->sw_dev) != 0){ kfree(new_skb); DEBUG_EVENT( "%s: Failed to send on SW dev %s\n", chan->if_name,chan->sw_dev->name); @@ -3099,11 +3109,11 @@ static int bstrm_bh_data_tx_up(sdla_t*card, struct sk_buff *non_te1_skb, bitstrm if ((chan=chan_ptr) != NULL){ dev=chan->common.dev; - /* Channel exists, the SWITCH code is trying + /* Channel exists, the WP_SWITCH code is trying * to send a packet up the stack. Thus - * check if this device is actually in SWITCH + * check if this device is actually in WP_SWITCH * mode */ - if (dev && chan->common.usedby == SWITCH){ + if (dev && chan->common.usedby == WP_SWITCH){ /* If API socket is not bound in * then exit */ @@ -3165,9 +3175,9 @@ static int bstrm_bh_data_tx_up(sdla_t*card, struct sk_buff *non_te1_skb, bitstrm } } - /* This is just a precaution, SWITCH device + /* This is just a precaution, WP_SWITCH device * shouldnt be here */ - if (chan->common.usedby == SWITCH){ + if (chan->common.usedby == WP_SWITCH){ goto tx_up_skb_recover; }else if (chan->common.usedby == API){ @@ -3705,7 +3715,7 @@ static void tx_up_decode_pkt(bitstrm_private_area_t *chan) } #endif - }else if (chan->common.usedby==API || chan->common.usedby==SWITCH){ + }else if (chan->common.usedby==API || chan->common.usedby==WP_SWITCH){ buf = skb_put(skb,sizeof(api_rx_hdr_t)); memset(buf, 0, sizeof(api_rx_hdr_t)); @@ -4966,7 +4976,7 @@ static void port_set_state (sdla_t *card, int state) bstrm_priv_area = wan_netif_priv(dev); if (IS_TE1_CARD(card) && - bstrm_priv_area->common.usedby == SWITCH && + bstrm_priv_area->common.usedby == WP_SWITCH && state == WAN_CONNECTED){ bitstrm_private_area_t *sw_chan; @@ -5051,7 +5061,7 @@ static void port_set_state (sdla_t *card, int state) skb_queue_tail(&bstrm_priv_area->rx_free_queue,skb); } - if (bstrm_priv_area->common.usedby==SWITCH){ + if (bstrm_priv_area->common.usedby==WP_SWITCH){ set_bit(WAIT_DEVICE_BUFFERS,&bstrm_priv_area->tq_control); } } @@ -5385,7 +5395,7 @@ static int bstrm_bind_dev_switch (sdla_t *card, bitstrm_private_area_t*chan, cha return -EINVAL; } - if (((wanpipe_common_t*)wan_netif_priv(sw_dev))->usedby != SWITCH){ + if (((wanpipe_common_t*)wan_netif_priv(sw_dev))->usedby != WP_SWITCH){ DEBUG_EVENT( "%s: Error: Device %s not configured for switching\n", card->devname, sw_dev->name); @@ -5576,7 +5586,7 @@ static int protocol_shutdown (sdla_t *card, netdevice_t *dev) chan->protocol == WANCONFIG_CHDLC){ wp_sppp_detach(dev); - dev->do_ioctl = NULL; + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,NULL); if (chan->common.prot_ptr){ kfree(chan->common.prot_ptr); diff --git a/patches/kdrivers/src/net/sdla_bri.c b/patches/kdrivers/src/net/sdla_bri.c index 61852c7..a399fcf 100644 --- a/patches/kdrivers/src/net/sdla_bri.c +++ b/patches/kdrivers/src/net/sdla_bri.c @@ -51,13 +51,10 @@ # include "aft_core.h" /* for 'private_area_t' */ # include "sdla_bri.h" -#undef DEBUG_BRI -#define DEBUG_BRI if(0)DEBUG_EVENT /* DEBUG macro definitions */ #define DEBUG_HFC_INIT if(0)DEBUG_EVENT #define DEBUG_HFC_MODE if(0)DEBUG_EVENT -#define DEBUG_HFC_S0_STATES if(0)DEBUG_EVENT #define DEBUG_HFC_IRQ if(0)DEBUG_EVENT #define DEBUG_HFC_SU_IRQ if(0)DEBUG_EVENT @@ -83,8 +80,6 @@ #define DEBUG_LOOPB if(0)DEBUG_EVENT -#define COLOGNE_TECH_SUPPORT_LOGIC_NT 1 - #define FIFO_THRESHOLD_INDEX 1 typedef enum _nt_states{ @@ -120,13 +115,13 @@ static u8 validate_fe_line_no(sdla_fe_t *fe, const char *caller_name) static int32_t validate_physical_mod_no(u32 mod_no, const char *caller_name) { if(mod_no % 2){ - DEBUG_EVENT("%s(): Error: mod_no (%d) is not divisible by 2!!\n", + DEBUG_ERROR("%s(): Error: mod_no (%d) is not divisible by 2!!\n", caller_name, mod_no); return 1; } if(mod_no >= MAX_BRI_LINES){ - DEBUG_EVENT("%s(): Error: mod_no (%d) is greate than maximum of %d!!\n", + DEBUG_ERROR("%s(): Error: mod_no (%d) is greate than maximum of %d!!\n", caller_name, mod_no, MAX_BRI_LINES - 1); return 1; } @@ -439,7 +434,7 @@ static int32_t reset_chip(sdla_fe_t *fe, u32 mod_no) } if (!(maximum_reset_wait_counter)) { - DEBUG_EVENT("%s: %s(): Error: chip initialization sequence timeout!\n", + DEBUG_ERROR("%s: %s(): Error: chip initialization sequence timeout!\n", fe->name, __FUNCTION__); return 1; } @@ -489,7 +484,7 @@ static int32_t __config_clock_routing(sdla_fe_t *fe, u32 mod_no, u8 master_mode) } if (fe->bri_param.mod[mod_no].type != MOD_TYPE_TE && master_mode) { - DEBUG_EVENT("%s: Module %d: error configuring clock routing on NT\n", + DEBUG_ERROR("%s: Module %d: error configuring clock routing on NT\n", fe->name, mod_no); return 1; } @@ -621,7 +616,7 @@ static int32_t init_xfhc(sdla_fe_t *fe, u32 mod_no) { sdla_bri_param_t *bri = &fe->bri_param; wp_bri_module_t *bri_module; - int32_t err = 0, i, timeout = 1000 /*0x2000*/; + int32_t err = 0, i; u8 port_no, bchan; reg_a_su_ctrl0 a_su_ctrl0; @@ -927,13 +922,13 @@ static int32_t check_f0cl_increment(sdla_fe_t *fe, u8 old_f0cl, u8 new_f0cl, int /* should be between 70 and 90 over 10ms time */ if(*diff == 0){ - DEBUG_EVENT("%s: PCM ERROR: BRI Modlue NO CLOCK found! 125us pulse f0cl diff: %d\n", + DEBUG_ERROR("%s: PCM ERROR: BRI Modlue NO CLOCK found! 125us pulse f0cl diff: %d\n", fe->name, *diff); return 1; } if(*diff > 150 || *diff < 70){ - DEBUG_EVENT("%s: PCM Warning 125us pulse count f0cl diff: %d\n", + DEBUG_WARNING("%s: PCM Warning 125us pulse count f0cl diff: %d\n", fe->name, *diff); } @@ -1293,7 +1288,7 @@ static int xhfc_read_fifo_dchan(sdla_fe_t *fe, u8 mod_no, /* check crc */ if (buf[(*idx) - 1]) { if (0 && WAN_NET_RATELIMIT()) { - DEBUG_EVENT("%s: %s(): CRC-error in frame in mod_no %d, port_no %d!\n", + DEBUG_ERROR("%s: %s(): CRC-error in frame in mod_no %d, port_no %d!\n", fe->name, __FUNCTION__, mod_no, port->idx); } *idx = 0; @@ -1547,7 +1542,7 @@ static u_int8_t scan_modules(sdla_fe_t *fe, u_int8_t rm_no) DEBUG_BRI_INIT("mod_no_index (line number) on CARD (should be 0-23): %d\n", mod_no_index); if(mod_no_index >= MAX_BRI_LINES){ - DEBUG_EVENT("%s: Error: Module %d/%d exceeds maximum (%d)\n", + DEBUG_ERROR("%s: Error: Module %d/%d exceeds maximum (%d)\n", fe->name, mod_no_index, mod_no_index, MAX_BRI_LINES); return 0; } @@ -1614,7 +1609,7 @@ static int32_t scan_remoras_and_modules(void* pfe) } if(modules_counter == 0){ - DEBUG_EVENT("%s: Error: modules counter is zero!\n", fe->name); + DEBUG_ERROR("%s: Error: modules counter is zero!\n", fe->name); return 1; }else{ DEBUG_EVENT("%s: Total number of modules: %d.\n", fe->name, modules_counter); @@ -1748,7 +1743,7 @@ static int32_t wp_bri_config(void *pfe) fe->fe_status = FE_UNITIALIZED; if(validate_fe_line_no(fe, __FUNCTION__)){ - DEBUG_EVENT("%s: %s: Error: Invalid Front End Line number %i !\n", + DEBUG_ERROR("%s: %s: Error: Invalid Front End Line number %i !\n", fe->name, FE_MEDIA_DECODE(fe), WAN_FE_LINENO(fe)+1); return 1; } @@ -1821,7 +1816,7 @@ static int32_t wp_bri_config(void *pfe) break; default: - DEBUG_EVENT("%s(): %s: Error: Module %d (AFT Line: %d): Not Installed!!\n", + DEBUG_ERROR("%s(): %s: Error: Module %d (AFT Line: %d): Not Installed!!\n", __FUNCTION__, fe->name, REPORT_MOD_NO(mod_no), aft_line_no); return 1; } @@ -1855,7 +1850,7 @@ static int32_t wp_bri_config(void *pfe) break; default: - DEBUG_EVENT("%s(): %s: Warning: Module %d (AFT Line: %d): Not Installed.\n", + DEBUG_WARNING("%s(): %s: Warning: Module %d (AFT Line: %d): Not Installed.\n", __FUNCTION__, fe->name, REPORT_MOD_NO(mod_no), aft_line_no); break; } @@ -1875,7 +1870,7 @@ static int32_t wp_bri_config(void *pfe) } break; default: - DEBUG_EVENT("%s(): %s: Warning: Module %d (AFT Line: %d): Not Installed.\n", + DEBUG_WARNING("%s(): %s: Warning: Module %d (AFT Line: %d): Not Installed.\n", __FUNCTION__, fe->name, REPORT_MOD_NO(mod_no), aft_line_no); break; } @@ -2525,7 +2520,7 @@ static u32 wp_bri_active_map(sdla_fe_t* fe, u8 line_no) BRI_FUNC(); if(line_no >= 2){ - DEBUG_EVENT("%s: %s(): Error: Line number %d is out of range!\n", + DEBUG_ERROR("%s: %s(): Error: Line number %d is out of range!\n", fe->name, __FUNCTION__, line_no); return 0; } @@ -2834,7 +2829,7 @@ static int wp_bri_control(sdla_fe_t *fe, u32 command) break; default: - DEBUG_EVENT("%s(): %s: Error: invalid command '%i'requested!\n", + DEBUG_ERROR("%s(): %s: Error: invalid command '%i'requested!\n", __FUNCTION__, fe->name, command); rc = 1; break; @@ -2899,15 +2894,8 @@ static int wp_bri_set_fe_status(sdla_fe_t *fe, unsigned char new_status) sdla_bri_set_status(fe, mod_no, port_no, FE_CONNECTED); } else { xhfc_ph_command(fe, port_ptr, HFC_L1_ACTIVATE_NT); -#if COLOGNE_TECH_SUPPORT_LOGIC_NT /* After activation, state will automatically change to G2, where * T1 will be started. Don't start T1 here as recommended by Table 5.4. */ -#else - /* Start T1 deactivation timer - if line is not synchronized before T1 expires, - * the timer will de-activate the line. If line already Active, no change of state will - * occur. */ - l1_timer_start_t1(port_ptr); -#endif } } break; @@ -2916,7 +2904,7 @@ static int wp_bri_set_fe_status(sdla_fe_t *fe, unsigned char new_status) DEBUG_HFC_S0_STATES("L2->L1 -- DEACTIVATE REQUEST\n"); if (port_ptr->mode & PORT_MODE_TE) { /* no deact request in TE mode ! */ - DEBUG_EVENT("%s(): %s: Error: 'deactivate' request is invalid for TE!\n", + DEBUG_ERROR("%s(): %s: Error: 'deactivate' request is invalid for TE!\n", __FUNCTION__, fe->name); rc = 1; } else { @@ -2925,7 +2913,7 @@ static int wp_bri_set_fe_status(sdla_fe_t *fe, unsigned char new_status) break; default: - DEBUG_EVENT("%s(): %s: Error: invalid new status '%d' (%s) requested!\n", + DEBUG_ERROR("%s(): %s: Error: invalid new status '%d' (%s) requested!\n", __FUNCTION__, fe->name, new_status, FE_STATUS_DECODE(new_status)); rc = 1; break; @@ -3152,7 +3140,9 @@ static u8 __su_new_state(sdla_fe_t *fe, u8 mod_no, u8 port_no) bri_xhfc_port_t *port_ptr; sdla_bri_param_t *bri = &fe->bri_param; wp_bri_module_t *bri_module; - u8 connected = 0; + u8 connected = 0; + u8 current_fe_status = fe->fe_status; + BRI_FUNC(); @@ -3220,7 +3210,7 @@ static u8 __su_new_state(sdla_fe_t *fe, u8 mod_no, u8 port_no) } else if (port_ptr->mode & PORT_MODE_NT) { DEBUG_HFC_S0_STATES("NT G%d\n", port_ptr->l1_state); -#if COLOGNE_TECH_SUPPORT_LOGIC_NT + /* S/T state transition based Cologne recomendations: * T1 is a counter that can be reset. An other stop function is not needed. * There are only 3 states of T1: @@ -3265,8 +3255,9 @@ static u8 __su_new_state(sdla_fe_t *fe, u8 mod_no, u8 port_no) * where the line is deactivated. */ wan_clear_bit(HFC_L1_ACTIVATED, &port_ptr->l1_flags); } - /* In any case the line is in 'disconnected' state. */ - connected = 0; + + /* In any case, do NOT change the line state. */ + connected = (current_fe_status == FE_CONNECTED ? 1 : 0); break; case NT_STATE_ACTIVE_G3: @@ -3284,71 +3275,10 @@ static u8 __su_new_state(sdla_fe_t *fe, u8 mod_no, u8 port_no) break; default: - DEBUG_EVENT("%s: Error: invalid NT State: %d.\n", fe->name, port_ptr->l1_state); + DEBUG_ERROR("%s: Error: invalid NT State: %d.\n", fe->name, port_ptr->l1_state); break; } -#else - if(!port_ptr->su_state.bit.v_su_info0){ - /* If INFO0 is not received, stop T1 timer */ - l1_timer_stop_t1(port_ptr); - } - - /* S/T state transitions based on Table 5.4 */ - switch (port_ptr->l1_state) - { - case (1):/* Deactivated */ - if(!port_ptr->su_state.bit.v_su_info0){ - /* We are NOT receiving INFO0 that means we ARE receiving INFO1. - * Start T1. - * Automatic transition to G2 is expected. */ - l1_timer_start_t1(port_ptr); - } - wan_clear_bit(HFC_L1_ACTIVATED, &port_ptr->l1_flags); - connected = 0; - break; - - case (2):/* Pending Activation */ - if(!port_ptr->su_state.bit.v_su_info0){ - /* We are NOT receiving INFO0 and very likely we are receiving INFO3. - * That means link is on the way UP. Next state should be G3. - * - * Automatic G2->G3 transition is allowed by init_xfhc() - - * V_G2_G3_EN was set in the register A_SU_CTRL1.*/ - }else{ - /* Link on the way DOWN, but no automatic state change. - * The state change will be triggered by T1 expiration, - * where the line is deactivated. */ - wan_clear_bit(HFC_L1_ACTIVATED, &port_ptr->l1_flags); - } - /* In any case the line is in 'disconnected' state. */ - connected = 0; - break; - - case (3):/* Active */ - if( (port_ptr->su_state.bit.v_su_info0) || - (!port_ptr->su_state.bit.v_su_fr_sync)){ - /* If receiving INFO0 or Lost Framing, chip will automatically go into G2. */ - /* Wait for G2 to go into disconnected state, so here stay 'connected'. */ - wan_set_bit(HFC_L1_ACTIVATED, &port_ptr->l1_flags); - connected = 1; - }else if(port_ptr->su_state.bit.v_su_fr_sync){ - /* Got synchronized. No automatic state change expected. */ - wan_set_bit(HFC_L1_ACTIVATED, &port_ptr->l1_flags); - connected = 1; - } - break; - - case (4):/* Pending Deactivation */ - /* No action required. */ - wan_clear_bit(HFC_L1_ACTIVATED, &port_ptr->l1_flags); - connected = 0; - break; - - default: - break; - } -#endif if(connected == 1){ DEBUG_HFC_S0_STATES("NT: l1->l2 -- ACTIVATE INDICATION\n"); }else{ @@ -3544,7 +3474,7 @@ static int32_t xhfc_interrupt(sdla_fe_t *fe, u8 mod_no) DEBUG_HFC_IRQ("%s(): chan ptr: 0x%p\n", __FUNCTION__, chan); if (!chan){ - DEBUG_EVENT("%s: Error: No Dev for Rx logical ch=%d\n", + DEBUG_ERROR("%s: Error: No Dev for Rx logical ch=%d\n", card->devname, BRI_DCHAN_LOGIC_CHAN); break; } diff --git a/patches/kdrivers/src/net/sdla_bri_tdmv.c b/patches/kdrivers/src/net/sdla_bri_tdmv.c index 2541bfb..1c1244c 100644 --- a/patches/kdrivers/src/net/sdla_bri_tdmv.c +++ b/patches/kdrivers/src/net/sdla_bri_tdmv.c @@ -361,7 +361,7 @@ static int wp_tdmv_bri_hwec_create(struct dahdi_chan *chan, if(chan->chanpos == 1 || chan->chanpos == 2){ err = card->wandev.ec_enable(card, 1, chan->chanpos); }else{ - DEBUG_EVENT("[TDMV_BRI]: %s: %s(): Warning: invalid fe_channel %d!!\n", + DEBUG_WARNING("[TDMV_BRI]: %s: %s(): Warning: invalid fe_channel %d!!\n", wr->devname, __FUNCTION__, chan->chanpos); err = 0; } @@ -398,7 +398,7 @@ static void wp_tdmv_bri_hwec_free(struct dahdi_chan *chan, struct dahdi_echocan_ if(chan->chanpos == 1 || chan->chanpos == 2) { card->wandev.ec_enable(card, 0, chan->chanpos); } else { - DEBUG_EVENT("[TDMV_BRI]: %s: %s(): Warning: invalid fe_channel %d!!\n", + DEBUG_WARNING("[TDMV_BRI]: %s: %s(): Warning: invalid fe_channel %d!!\n", wr->devname, __FUNCTION__, chan->chanpos); } } else { @@ -438,7 +438,7 @@ static int wp_bri_zap_hwec(struct zt_chan *chan, int enable) if (chan->chanpos == 1 || chan->chanpos == 2){ err = card->wandev.ec_enable(card, enable, chan->chanpos); }else{ - DEBUG_EVENT("[TDMV_BRI]: %s: %s(): Warning: invalid fe_channel %d!!\n", + DEBUG_WARNING("[TDMV_BRI]: %s: %s(): Warning: invalid fe_channel %d!!\n", wr->devname, __FUNCTION__, chan->chanpos); err = 0; } @@ -852,7 +852,7 @@ static int wp_tdmv_bri_reg( wan_set_bit(channo, &wr->timeslot_map); if (i == wr->max_timeslots){ - DEBUG_EVENT("%s: Error: TDMV iface %s failed to configure for 0x%08X timeslots!\n", + DEBUG_ERROR("%s: Error: TDMV iface %s failed to configure for 0x%08X timeslots!\n", card->devname, wan_netif_name(dev), active_ch); @@ -882,7 +882,7 @@ static int wp_tdmv_bri_reg( __FUNCTION__, card->u.aft.tdmv_dchan, channo, wr->dchan); if(wr->dchan != 3){ - DEBUG_EVENT("%s:%s: Error: 'dchan' (%d) not equal 3!\n", + DEBUG_ERROR("%s:%s: Error: 'dchan' (%d) not equal 3!\n", card->devname, wan_netif_name(dev), wr->dchan); return -EINVAL; } @@ -966,7 +966,7 @@ static int wp_tdmv_bri_remove(void* pcard) return -EINVAL; } if (wr && wr->usecount){ - DEBUG_EVENT("%s: ERROR: Wanpipe is still used by Asterisk!\n", + DEBUG_ERROR("%s: ERROR: Wanpipe is still used by Asterisk!\n", card->devname); return -EINVAL; } @@ -1395,7 +1395,7 @@ static int wp_tdmv_rx_dchan(wan_tdmv_t *wan_tdmv, int channo, buf = ms->readbuf[ms->inreadbuf]; left = ms->blocksize - ms->readidx[ms->inreadbuf]; if (len + 2 > left) { - DEBUG_EVENT("%s: ERROR: Not ehough space for RX HDLC packet (%d:%d)!\n", + DEBUG_ERROR("%s: ERROR: Not ehough space for RX HDLC packet (%d:%d)!\n", wp->devname, len+2, left); wan_spin_unlock_irq(&chan->lock, &smp_flags); return -EINVAL; diff --git a/patches/kdrivers/src/net/sdla_chdlc.c b/patches/kdrivers/src/net/sdla_chdlc.c index 0276120..3d98015 100644 --- a/patches/kdrivers/src/net/sdla_chdlc.c +++ b/patches/kdrivers/src/net/sdla_chdlc.c @@ -105,6 +105,7 @@ enum { #define PORT(x) (x == 0 ? "PRIMARY" : "SECONDARY" ) #define MAX_BH_BUFF 10 +WAN_DECLARE_NETDEV_OPS(wan_netdev_ops) /******Data Structures*****************************************************/ @@ -1106,7 +1107,15 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) * finished successfully. DO NOT place any code below that * can return an error */ - dev->init = &if_init; + WAN_NETDEV_OPS_BIND(dev,wan_netdev_ops); + WAN_NETDEV_OPS_INIT(dev,wan_netdev_ops,&if_init); + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&if_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&if_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&if_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&if_stats); + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&if_tx_timeout); + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,&if_do_ioctl); + wan_netif_set_priv(dev,chdlc_priv_area); set_bit(0,&chdlc_priv_area->config_chdlc); @@ -1182,15 +1191,17 @@ static int if_init (netdevice_t* dev) wan_device_t* wandev = &card->wandev; /* Initialize device driver entry points */ - dev->open = &if_open; - dev->stop = &if_close; - dev->hard_start_xmit = &if_send; - dev->get_stats = &if_stats; + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&if_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&if_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&if_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&if_stats); #if defined(LINUX_2_4)||defined(LINUX_2_6) - dev->tx_timeout = &if_tx_timeout; + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&if_tx_timeout); + dev->watchdog_timeo = TX_TIMEOUT; #endif - dev->do_ioctl = if_do_ioctl; + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,&if_do_ioctl); + if (chdlc_priv_area->common.usedby == BRIDGE || chdlc_priv_area->common.usedby == BRIDGE_NODE){ @@ -4574,7 +4585,10 @@ static void tty_poll_task (struct work_struct *work) if ((tty=card->tty)==NULL) return; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)) + ops = tty->ldisc->ops; +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) ops = tty->ldisc.ops; #else ops = &tty->ldisc; @@ -5382,7 +5396,9 @@ static void wanpipe_tty_flush_buffer(struct tty_struct *tty) wake_up_interruptible(&tty->poll_wait); #endif if (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)){ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)) + const struct tty_ldisc_ops *ops = tty->ldisc->ops; +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)) const struct tty_ldisc_ops *ops = tty->ldisc.ops; #else const struct tty_ldisc *ops = &tty->ldisc; diff --git a/patches/kdrivers/src/net/sdla_ec.c b/patches/kdrivers/src/net/sdla_ec.c index 2c84377..76b307e 100644 --- a/patches/kdrivers/src/net/sdla_ec.c +++ b/patches/kdrivers/src/net/sdla_ec.c @@ -113,7 +113,7 @@ void *wan_ec_config(void *pcard, int max_channels) WAN_LIST_FOREACH(ec, &wan_ec_head, next){ WAN_LIST_FOREACH(ec_dev, &ec->ec_dev, next){ if (ec_dev->card == NULL || ec_dev->card == card){ - DEBUG_EVENT("%s: Internal Error (%s:%d)\n", + DEBUG_ERROR("%s: Internal Error (%s:%d)\n", card->devname, __FUNCTION__,__LINE__); return NULL; @@ -133,7 +133,7 @@ void *wan_ec_config(void *pcard, int max_channels) } ec_dev_new = wan_malloc(sizeof(wan_ec_dev_t)); if (ec_dev_new == NULL){ - DEBUG_EVENT("%s: ERROR: Failed to allocate memory (%s:%d)!\n", + DEBUG_ERROR("%s: ERROR: Failed to allocate memory (%s:%d)!\n", card->devname, __FUNCTION__,__LINE__); return NULL; @@ -145,7 +145,7 @@ void *wan_ec_config(void *pcard, int max_channels) /* First device for current Oct6100 chip */ ec = wan_malloc(sizeof(wan_ec_t)); if (ec == NULL){ - DEBUG_EVENT("%s: ERROR: Failed to allocate memory (%s:%d)!\n", + DEBUG_ERROR("%s: ERROR: Failed to allocate memory (%s:%d)!\n", card->devname, __FUNCTION__,__LINE__); return NULL; @@ -384,7 +384,7 @@ wan_ec_read_internal_dword(wan_ec_dev_t *ec_dev, u32 addr1, u32 *data) WAN_ASSERT(card == NULL); addr = convert_addr(addr1); if (addr == 0x00){ - DEBUG_EVENT("%s: %s:%d: Internal Error (EC off %X)\n", + DEBUG_ERROR("%s: %s:%d: Internal Error (EC off %X)\n", card->devname, __FUNCTION__,__LINE__, addr1); @@ -411,7 +411,7 @@ wan_ec_write_internal_dword(wan_ec_dev_t *ec_dev, u32 addr1, u32 data) WAN_ASSERT(card == NULL); addr = convert_addr(addr1); if (addr == 0x00){ - DEBUG_EVENT("%s: %s:%d: Internal Error (EC off %X)\n", + DEBUG_ERROR("%s: %s:%d: Internal Error (EC off %X)\n", card->devname, __FUNCTION__,__LINE__, addr1); @@ -821,7 +821,7 @@ int wan_ec_dev_ioctl(void *ec_arg, void *data) ec_dev = WAN_LIST_FIRST(&ec->ec_dev); if (ec_dev == NULL){ - DEBUG_EVENT("%s: Internal Error (%s:%d)\n", + DEBUG_ERROR("%s: Internal Error (%s:%d)\n", ec->name, __FUNCTION__,__LINE__); return -EINVAL; diff --git a/patches/kdrivers/src/net/sdla_fr.c b/patches/kdrivers/src/net/sdla_fr.c index 1c88064..818040e 100644 --- a/patches/kdrivers/src/net/sdla_fr.c +++ b/patches/kdrivers/src/net/sdla_fr.c @@ -319,6 +319,7 @@ typedef struct fr_channel #define TMR_INT_ENABLED_UPDATE_DLCI 0x40 #define TMR_INT_ENABLED_TE 0x80 +WAN_DECLARE_NETDEV_OPS(wan_netdev_ops) #pragma pack(1) @@ -1317,7 +1318,14 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) chan->trap_state = SNMP_FR_DISABLED; chan->trap_max_rate = 0; - dev->init = &if_init; + WAN_NETDEV_OPS_BIND(dev,wan_netdev_ops); + WAN_NETDEV_OPS_INIT(dev,wan_netdev_ops,&if_init); + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&if_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&if_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&if_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&if_stats); + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&if_tx_timeout); + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,&if_do_ioctl); wan_netif_set_priv(dev,chan); chan->common.dev = dev; @@ -1456,12 +1464,14 @@ static int if_init (netdevice_t* dev) wan_device_t* wandev = &card->wandev; /* Initialize device driver entry points */ - dev->open = &if_open; - dev->stop = &if_close; - dev->hard_start_xmit = &if_send; - dev->get_stats = &if_stats; + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&if_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&if_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&if_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&if_stats); + #if defined(LINUX_2_4) || defined(LINUX_2_6) - dev->tx_timeout = &if_tx_timeout; + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&if_tx_timeout); + dev->watchdog_timeo = TX_TIMEOUT; #endif @@ -1518,7 +1528,7 @@ static int if_init (netdevice_t* dev) card->hw_iface.getcfg(card->hw, SDLA_MEMEND, &dev->mem_end); //ALEX_TODAY wandev->maddr + wandev->msize - 1; /* SNMP */ - dev->do_ioctl = if_do_ioctl; + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,&if_do_ioctl); return 0; } diff --git a/patches/kdrivers/src/net/sdla_pos.c b/patches/kdrivers/src/net/sdla_pos.c index 31618d0..a703348 100644 --- a/patches/kdrivers/src/net/sdla_pos.c +++ b/patches/kdrivers/src/net/sdla_pos.c @@ -782,7 +782,7 @@ static int if_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (mb->wan_pos_data_len > 0){ if (mb->wan_pos_data_len > 1030){ - DEBUG_EVENT("MAJORE ERROR !! DATA LEN > 1030\n"); + DEBUG_ERROR("MAJORE ERROR !! DATA LEN > 1030\n"); err=-EFAULT; break; } @@ -821,7 +821,7 @@ static int if_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (mb->wan_pos_data_len>0) { if (mb->wan_pos_data_len > 1030){ - DEBUG_EVENT("MAJORE ERROR !! DATA LEN > 1030\n"); + DEBUG_ERROR("MAJORE ERROR !! DATA LEN > 1030\n"); err=-EFAULT; break; } diff --git a/patches/kdrivers/src/net/sdla_ppp.c b/patches/kdrivers/src/net/sdla_ppp.c index 024a61a..d4e4a2b 100644 --- a/patches/kdrivers/src/net/sdla_ppp.c +++ b/patches/kdrivers/src/net/sdla_ppp.c @@ -132,6 +132,8 @@ #define PPP_MAX_MTU 4000 /* maximum MTU */ #define PPP_HDR_LEN 1 +WAN_DECLARE_NETDEV_OPS(wan_netdev_ops) + /* Private critical flags */ enum { POLL_CRIT = PRIV_CRIT @@ -793,7 +795,14 @@ static int new_if(wan_device_t *wandev, netdevice_t *dev, wanif_conf_t *conf) goto new_if_error; } - dev->init = &if_init; + WAN_NETDEV_OPS_BIND(dev,wan_netdev_ops); + WAN_NETDEV_OPS_INIT(dev,wan_netdev_ops,&if_init); + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&if_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&if_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&if_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&if_stats); + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&if_tx_timeout); + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,&if_do_ioctl); wan_netif_set_priv(dev,ppp_priv_area); dev->mtu = wp_min(dev->mtu, card->wandev.mtu); ppp_priv_area->ppp_state=WAN_DISCONNECTED; @@ -888,12 +897,13 @@ static int if_init(netdevice_t *dev) wan_device_t *wandev = &card->wandev; /* Initialize device driver entry points */ - dev->open = &if_open; - dev->stop = &if_close; - dev->hard_start_xmit = &if_send; - dev->get_stats = &if_stats; + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&if_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&if_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&if_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&if_stats); + #if defined(LINUX_2_4)||defined(LINUX_2_6) - dev->tx_timeout = &if_tx_timeout; + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&if_tx_timeout); dev->watchdog_timeo = TX_TIMEOUT; #endif @@ -935,7 +945,7 @@ static int if_init(netdevice_t *dev) dev->tx_queue_len = 100; /* SNMP */ - dev->do_ioctl = if_do_ioctl; + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,&if_do_ioctl); return 0; } diff --git a/patches/kdrivers/src/net/sdla_remora.c b/patches/kdrivers/src/net/sdla_remora.c index 5fc5160..6ee8cd8 100644 --- a/patches/kdrivers/src/net/sdla_remora.c +++ b/patches/kdrivers/src/net/sdla_remora.c @@ -122,7 +122,9 @@ enum { WP_RM_POLL_READ, WP_RM_POLL_WRITE, WP_RM_POLL_RXSIG_OFFHOOK, - WP_RM_POLL_RXSIG_ONHOOK + WP_RM_POLL_RXSIG_ONHOOK, + WP_RM_POLL_SET_TX_GAIN, + WP_RM_POLL_SET_RX_GAIN }; #define WP_RM_POLL_DECODE(type) \ @@ -151,6 +153,8 @@ enum { ((type) == WP_RM_POLL_WRITE) ? "FE Write": \ ((type) == WP_RM_POLL_RXSIG_OFFHOOK) ? "RX Sig Off-hook": \ ((type) == WP_RM_POLL_RXSIG_ONHOOK) ? "RX Sig On-hook": \ + ((type) == WP_RM_POLL_SET_TX_GAIN) ? "Set Tx Gain": \ + ((type) == WP_RM_POLL_SET_RX_GAIN) ? "Set Rx Gain": \ "Unknown RM poll event" @@ -319,7 +323,7 @@ static struct fxo_mode { { "CHINA", 0, 0, 0, 0, 0, 0, 0x3, 0xf, }, { "COLUMBIA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, { "CROATIA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, - { "CYRPUS", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "CYPRUS", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, { "CZECH", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, { "DENMARK", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, { "ECUADOR", 0, 0, 0, 0, 0, 0x3, 0, 0, }, @@ -433,6 +437,11 @@ static int wp_remora_ringtone(sdla_fe_t*, int); static int wp_remora_congestiontone(sdla_fe_t*, int); static int wp_remora_disabletone(sdla_fe_t*, int); +static int wp_remora_set_fxo_txgain(sdla_fe_t*,int txgain, int mod_no); +static int wp_remora_set_fxo_rxgain(sdla_fe_t*,int rxgain, int mod_no); +static int wp_remora_set_fxs_txgain(sdla_fe_t*,int txgain, int mod_no); +static int wp_remora_set_fxs_rxgain(sdla_fe_t*,int rxgain, int mod_no); + #if defined(AFT_TDM_API_SUPPORT) static int wp_remora_watchdog(void *card_ptr); #endif @@ -452,9 +461,7 @@ static void wait_just_a_bit(int foo, int fast) start_ticks = SYSTEM_TICKS + foo; while(SYSTEM_TICKS < start_ticks){ WP_DELAY(100); -# if defined(__LINUX__) if (!fast) WP_SCHEDULE(foo, "A200"); -# endif } #endif } @@ -480,7 +487,7 @@ wp_proslic_setreg_indirect(sdla_fe_t *fe, int mod_no, unsigned char address, uns } if (READ_RM_REG(mod_no, 31) & 0x1) { - DEBUG_EVENT("%s: Critical error:ProSLIC indirect write failed (mod:0x%X add:0x%X)\n", + DEBUG_ERROR("%s: Critical error:ProSLIC indirect write failed (mod:0x%X add:0x%X)\n", fe->name, mod_no, address); return -EINVAL; } @@ -504,7 +511,7 @@ wp_proslic_getreg_indirect(sdla_fe_t *fe, int mod_no, unsigned char address) } if (READ_RM_REG(mod_no, 31) & 0x1) { - DEBUG_EVENT("%s: Critical error:ProSLIC indirect read failed (mod:%X)\n", + DEBUG_ERROR("%s: Critical error:ProSLIC indirect read failed (mod:%X)\n", fe->name, mod_no); return -EINVAL; } @@ -584,7 +591,7 @@ static int wp_remora_chain_enable(sdla_fe_t *fe) WAN_ASSERT_RC(fe->reset_fe == NULL,0); - if (IS_A600(fe)) { + if (IS_A600(fe) || IS_B601(fe)) { for(mod_no = 0; mod_no < NUM_A600_ANALOG_FXO_PORTS; mod_no++) { fe->rm_param.mod[mod_no].type = MOD_TYPE_FXO; } @@ -662,7 +669,6 @@ static int wp_remora_chain_enable(sdla_fe_t *fe) static int wp_init_proslic_insane(sdla_fe_t *fe, int mod_no) { unsigned char value; - value = READ_RM_REG(mod_no, 0); if ((value & 0x30) >> 4){ DEBUG_RM("%s: Proslic on module %d is not a Si3210 (%02X)!\n", @@ -682,7 +688,7 @@ static int wp_init_proslic_insane(sdla_fe_t *fe, int mod_no) value & 0x0F); return -1; } - + /* Step 8 */ value = READ_RM_REG(mod_no, 8); if (value != 0x2) { DEBUG_EVENT( @@ -714,13 +720,13 @@ static int wp_powerup_proslic(sdla_fe_t *fe, int mod_no, int fast) { wp_remora_fxs_t *fxs; wan_ticks_t start_ticks; - int loopcurrent = 20, lim; + int loopcurrent = 20, lim,i; unsigned char vbat; DEBUG_CFG("%s: PowerUp SLIC initialization...\n", fe->name); fxs = &fe->rm_param.mod[mod_no].u.fxs; /* set the period of the DC-DC converter to 1/64 kHz START OUT SLOW*/ - WRITE_RM_REG(mod_no, 92, 0xf5); + WRITE_RM_REG(mod_no, 92, 0xf5); /*0xf5 for BJT range */ if (fast) return 0; @@ -729,14 +735,16 @@ static int wp_powerup_proslic(sdla_fe_t *fe, int mod_no, int fast) WRITE_RM_REG(mod_no, 14, 0x0); /* DIFF DEMO 0x10 */ start_ticks = SYSTEM_TICKS; + i=0; while((vbat = READ_RM_REG(mod_no, 82)) < 0xC0){ + i++; /* Wait no more than 500ms */ - if ((SYSTEM_TICKS - start_ticks) > HZ/2){ + if ( i > 10 ){ break; } wait_just_a_bit(HZ/10, fast); } - + /* Step 12-c */ if (vbat < 0xc0){ if (fxs->proslic_power == PROSLIC_POWER_UNKNOWN){ DEBUG_EVENT( @@ -777,9 +785,10 @@ static int wp_powerup_proslic(sdla_fe_t *fe, int mod_no, int fast) WRITE_RM_REG(mod_no, 93, 0x99); /* DC-DC Calibration */ /* Wait for DC-DC Calibration to complete */ - start_ticks = SYSTEM_TICKS; + i=0; while(0x80 & READ_RM_REG(mod_no, 93)){ - if ((SYSTEM_TICKS - start_ticks) > 2*HZ){ + i++; + if (i > 20){ DEBUG_EVENT( "%s: Module %d: Timeout waiting for DC-DC calibration (%02X)\n", fe->name, mod_no+1, @@ -796,14 +805,13 @@ static int wp_proslic_powerleak_test(sdla_fe_t *fe, int mod_no, int fast) { wan_ticks_t start_ticks; unsigned char vbat; - DEBUG_CFG("%s: PowerLeak ProSLIC testing...\n", fe->name); /* powerleak */ WRITE_RM_REG(mod_no, 64, 0); WRITE_RM_REG(mod_no, 14, 0x10); - /* wait for 1 s */ + start_ticks = SYSTEM_TICKS; - wait_just_a_bit(HZ, fast); + wait_just_a_bit(HZ/40, fast); vbat = READ_RM_REG(mod_no, 82); if (vbat < 0x6){ DEBUG_EVENT( @@ -857,25 +865,28 @@ static int wp_proslic_manual_calibrate(sdla_fe_t *fe, int mod_no, int fast) WRITE_RM_REG(mod_no, 21, 0x00); WRITE_RM_REG(mod_no, 22, 0x00); WRITE_RM_REG(mod_no, 23, 0x00); + /* Step 13 */ WRITE_RM_REG(mod_no, 64, 0x00); /* Step 14 */ - WRITE_RM_REG(mod_no, 97, 0x18); + WRITE_RM_REG(mod_no, 97, 0x1E); WRITE_RM_REG(mod_no, 96, 0x47); /* Step 15 */ - start_ticks = SYSTEM_TICKS; + i=0; while(READ_RM_REG(mod_no, 96) != 0){ - if ((SYSTEM_TICKS - start_ticks) > 800){ + i++; + if (i > 50){ DEBUG_EVENT( "%s: Module %d: Timeout on SLIC calibration (15)!\n", fe->name, mod_no+1); return -1; } - wait_just_a_bit(HZ/10, fast); + wait_just_a_bit(HZ/40, fast); + } - wait_just_a_bit(HZ/10, fast); + wp_proslic_setreg_indirect(fe, mod_no, 88, 0x00); wp_proslic_setreg_indirect(fe, mod_no, 89, 0x00); wp_proslic_setreg_indirect(fe, mod_no, 90, 0x00); @@ -885,26 +896,26 @@ static int wp_proslic_manual_calibrate(sdla_fe_t *fe, int mod_no, int fast) /* Step 16 */ /* Insert manual calibration for sangoma Si3210 */ - WRITE_RM_REG(mod_no, 98, 0x10); - WRITE_RM_REG(mod_no, 99, 0x10); for (i = 0x1f; i > 0; i--){ WRITE_RM_REG(mod_no, 98, i); - wait_just_a_bit(4, fast); + wait_just_a_bit(HZ/25, fast); if ((READ_RM_REG(mod_no, 88)) == 0){ break; } } for (i = 0x1f; i > 0; i--){ WRITE_RM_REG(mod_no, 99, i); - wait_just_a_bit(4, fast); + wait_just_a_bit(HZ/25, fast); if ((READ_RM_REG(mod_no, 89)) == 0){ break; } } +#if 0 WRITE_RM_REG(mod_no, 64, 0x01); wait_just_a_bit(HZ, fast); WRITE_RM_REG(mod_no, 64, 0x00); +#endif /* Step 17 */ WRITE_RM_REG(mod_no, 23, 0x04); @@ -915,10 +926,11 @@ static int wp_proslic_manual_calibrate(sdla_fe_t *fe, int mod_no, int fast) WRITE_RM_REG(mod_no, 96, 0x40); /* Step 19 */ - wait_just_a_bit(HZ*2, fast); + i=0; start_ticks = SYSTEM_TICKS; while(READ_RM_REG(mod_no, 96) != 0){ - if ((SYSTEM_TICKS - start_ticks) > 400){ + i++; + if (i > 50){ DEBUG_EVENT( "%s: Module %d: Timeout on SLIC calibration (%ld:%ld)!\n", fe->name, mod_no+1, @@ -926,7 +938,7 @@ static int wp_proslic_manual_calibrate(sdla_fe_t *fe, int mod_no, int fast) (unsigned long)SYSTEM_TICKS); return -1; } - wait_just_a_bit(HZ/10, fast); + wait_just_a_bit(HZ/40, fast); } DEBUG_RM("%s: Module %d: Calibration is done\n", fe->name, mod_no+1); @@ -974,7 +986,7 @@ int wp_init_proslic(sdla_fe_t *fe, int mod_no, int fast, int sane) } wp_proslic_setreg_indirect(fe, mod_no, 97,0); - /* Steo 10 */ + /* Step 10 */ WRITE_RM_REG(mod_no, 8, 0); /*DIGIUM*/ WRITE_RM_REG(mod_no, 108, 0xeb); /*DIGIUM*/ WRITE_RM_REG(mod_no, 67, 0x17); @@ -1024,9 +1036,11 @@ int wp_init_proslic(sdla_fe_t *fe, int mod_no, int fast, int sane) return -1; } #endif + +#if 1 /* Doesn't look neccessary to calibration again! */ /* Perform DC-DC calibration */ WRITE_RM_REG(mod_no, 93, 0x99); - wait_just_a_bit(10, fast); + wait_just_a_bit(HZ/100, fast); value = READ_RM_REG(mod_no, 107); if ((value < 0x2) || (value > 0xd)) { DEBUG_EVENT( @@ -1035,7 +1049,8 @@ int wp_init_proslic(sdla_fe_t *fe, int mod_no, int fast, int sane) mod_no+1, value); WRITE_RM_REG(mod_no, 107, 0x8); - } + } +#endif /* Save calibration vectors */ for (x=0;xfe_cfg.tdmv_law == WAN_TDMV_ALAW){ WRITE_RM_REG(mod_no, 1, 0x20); }else if (fe->fe_cfg.tdmv_law == WAN_TDMV_MULAW){ @@ -1263,7 +1277,7 @@ int wp_init_proslic(sdla_fe_t *fe, int mod_no, int fast, int sane) #endif WRITE_RM_REG(mod_no, 64, 0x1); - wait_just_a_bit(HZ, fast); + if (READ_RM_REG(mod_no, 81) < 0x0A){ DEBUG_EVENT( "%s: Module %d: TIP/RING is too low on FXS %d!\n", @@ -1397,47 +1411,10 @@ int wp_init_voicedaa(sdla_fe_t *fe, int mod_no, int fast, int sane) /* Take values for fxotxgain and fxorxgain and apply them to module */ if (fe->fe_cfg.cfg.remora.fxo_txgain) { - if (fe->fe_cfg.cfg.remora.fxo_txgain >= -150 && fe->fe_cfg.cfg.remora.fxo_txgain < 0) { - DEBUG_EVENT("%s: Module %d: Adjust TX Gain to %2d.%d dB\n", - fe->name, mod_no+1, - fe->fe_cfg.cfg.remora.fxo_txgain / 10, - fe->fe_cfg.cfg.remora.fxo_txgain % -10); - WRITE_RM_REG(mod_no, 38, 16 + (fe->fe_cfg.cfg.remora.fxo_txgain/-10)); - if(fe->fe_cfg.cfg.remora.fxo_txgain % 10) { - WRITE_RM_REG(mod_no, 40, 16 + (-fe->fe_cfg.cfg.remora.fxo_txgain%10)); - } + wp_remora_set_fxo_txgain(fe,fe->fe_cfg.cfg.remora.fxo_txgain,mod_no); } - else if (fe->fe_cfg.cfg.remora.fxo_txgain <= 120 && fe->fe_cfg.cfg.remora.fxo_txgain > 0) { - DEBUG_EVENT("%s: Module %d: Adjust TX Gain to %2d.%d dB\n", - fe->name, mod_no+1, - fe->fe_cfg.cfg.remora.fxo_txgain / 10, - fe->fe_cfg.cfg.remora.fxo_txgain % 10); - WRITE_RM_REG(mod_no, 38, fe->fe_cfg.cfg.remora.fxo_txgain/10); - if (fe->fe_cfg.cfg.remora.fxo_txgain % 10){ - WRITE_RM_REG(mod_no, 40, (fe->fe_cfg.cfg.remora.fxo_txgain % 10)); - } - } - } if (fe->fe_cfg.cfg.remora.fxo_rxgain) { - if (fe->fe_cfg.cfg.remora.fxo_rxgain >= -150 && fe->fe_cfg.cfg.remora.fxo_rxgain < 0) { - DEBUG_EVENT("%s: Module %d: Adjust RX Gain to %2d.%d dB\n", - fe->name, mod_no+1, - fe->fe_cfg.cfg.remora.fxo_rxgain / 10, - (-1) * (fe->fe_cfg.cfg.remora.fxo_rxgain % 10)); - WRITE_RM_REG(mod_no, 39, 16 + (fe->fe_cfg.cfg.remora.fxo_rxgain/-10)); - if(fe->fe_cfg.cfg.remora.fxo_rxgain%10) { - WRITE_RM_REG(mod_no, 41, 16 + (-fe->fe_cfg.cfg.remora.fxo_rxgain%10)); - } - }else if (fe->fe_cfg.cfg.remora.fxo_rxgain <= 120 && fe->fe_cfg.cfg.remora.fxo_rxgain > 0) { - DEBUG_EVENT("%s: Module %d: Adjust RX Gain to %2d.%d dB\n", - fe->name, mod_no+1, - fe->fe_cfg.cfg.remora.fxo_rxgain / 10, - fe->fe_cfg.cfg.remora.fxo_rxgain % 10); - WRITE_RM_REG(mod_no, 39, fe->fe_cfg.cfg.remora.fxo_rxgain/10); - if(fe->fe_cfg.cfg.remora.fxo_rxgain % 10) { - WRITE_RM_REG(mod_no, 41, (fe->fe_cfg.cfg.remora.fxo_rxgain%10)); - } - } + wp_remora_set_fxo_rxgain(fe,fe->fe_cfg.cfg.remora.fxo_rxgain,mod_no); } /* NZ -- crank the tx gain up by 7 dB */ @@ -1451,6 +1428,9 @@ int wp_init_voicedaa(sdla_fe_t *fe, int mod_no, int fast, int sane) if (fe->fe_cfg.cfg.remora.rm_mode == WAN_RM_TAPPING) { DEBUG_EVENT("%s: Module %d: FXO Tapping enabled.\n", fe->name, mod_no+1); } + if (fe->fe_cfg.cfg.remora.rm_lcm == WANOPT_YES) { + DEBUG_EVENT("%s: Module %d: FXO Loop Current Measure (LCM enabled).\n", fe->name, mod_no+1); + } if (fe->fe_cfg.cfg.remora.rm_mode == WAN_RM_TAPPING) { if (!fe->fe_cfg.cfg.remora.ohthresh) { @@ -1562,7 +1542,7 @@ static int wp_remora_config(void *pfe) DEBUG_EVENT("%s: Configuring FXS/FXO Front End ...\n", fe->name); - if (IS_A600(fe)) { + if (IS_A600(fe) || IS_B601(fe)) { fe->rm_param.max_fe_channels = NUM_A600_ANALOG_PORTS; } else if (IS_A700(fe)) { fe->rm_param.max_fe_channels = NUM_A700_ANALOG_PORTS; @@ -1580,9 +1560,7 @@ static int wp_remora_config(void *pfe) if (wp_remora_opermode(fe)){ return -EINVAL; } - - wait_just_a_bit(HZ, 0); - + /* Reset SPI interface */ fe->reset_fe(fe); @@ -1662,7 +1640,7 @@ retry_cfg: "ERROR: %s: Configuration is failed for all FXO/FXS modules!\n", fe->name); }else{ - DEBUG_EVENT("ERROR: %s: No FXO/FXS modules are found!\n", + DEBUG_ERROR("ERROR: %s: No FXO/FXS modules are found!\n", fe->name); } return -EINVAL; @@ -2206,7 +2184,7 @@ static int wp_remora_event_exec(sdla_fe_t* fe, sdla_fe_timer_event_t *fe_event) WRITE_RM_REG(mod_no, 5, 0x9); } else { err=-EINVAL; - DEBUG_EVENT("%s: Error Invalid Module Type %i: Module %d: txsig START.\n", + DEBUG_ERROR("%s: Error Invalid Module Type %i: Module %d: txsig START.\n", fe->name, fe->rm_param.mod[mod_no].type, mod_no+1); } break; @@ -2235,7 +2213,7 @@ static int wp_remora_event_exec(sdla_fe_t* fe, sdla_fe_timer_event_t *fe_event) WRITE_RM_REG(mod_no, 5, 0x9); } else { err=-EINVAL; - DEBUG_EVENT("%s: Error Invalid Module Type %i: Module %d: off-hook.\n", + DEBUG_ERROR("%s: Error Invalid Module Type %i: Module %d: off-hook.\n", fe->name, fe->rm_param.mod[mod_no].type, mod_no+1); } break; @@ -2266,7 +2244,7 @@ static int wp_remora_event_exec(sdla_fe_t* fe, sdla_fe_timer_event_t *fe_event) WRITE_RM_REG(mod_no, 5, 0x8); } else { err=-EINVAL; - DEBUG_EVENT("%s: Error Invalid Module Type %i: Module %d: on-hook.\n", + DEBUG_ERROR("%s: Error Invalid Module Type %i: Module %d: on-hook.\n", fe->name, fe->rm_param.mod[mod_no].type, mod_no+1); } break; @@ -2279,7 +2257,7 @@ static int wp_remora_event_exec(sdla_fe_t* fe, sdla_fe_timer_event_t *fe_event) }else{ fe->rm_param.mod[mod_no].u.fxs.idletxhookstate = 0x2; } - if (fe->rm_param.mod[mod_no].u.fxs.lasttxhook == 0x1) { + if (fe->rm_param.mod[mod_no].u.fxs.lasttxhook == 0x1 ||fe->rm_param.mod[mod_no].u.fxs.lasttxhook == 0x5 ) { /* Apply the change if appropriate */ if (fe->fe_cfg.cfg.remora.reversepolarity){ fe->rm_param.mod[mod_no].u.fxs.lasttxhook = 0x6; @@ -2294,15 +2272,22 @@ static int wp_remora_event_exec(sdla_fe_t* fe, sdla_fe_timer_event_t *fe_event) } break; case WP_RM_POLL_SETPOLARITY: + if(fe_event->rm_event.polarity != 0 && fe_event->rm_event.polarity != 1){ + DEBUG_EVENT("%s: Module %d: invalid Polarity value %d\n", + fe->name, mod_no+1, + fe_event->rm_event.polarity); + err=-SANG_STATUS_INVALID_PARAMETER; + break; + } /* Can't change polarity while ringing or when open */ if ((fe->rm_param.mod[mod_no].u.fxs.lasttxhook == 0x04) || (fe->rm_param.mod[mod_no].u.fxs.lasttxhook == 0x00)){ err=-EBUSY; break; } - - if ((fe_event->mode && !fe->fe_cfg.cfg.remora.reversepolarity) || - (!fe_event->mode && fe->fe_cfg.cfg.remora.reversepolarity)){ + + if ((fe_event->rm_event.polarity && !fe->fe_cfg.cfg.remora.reversepolarity) || + (!fe_event->rm_event.polarity && fe->fe_cfg.cfg.remora.reversepolarity)){ fe->rm_param.mod[mod_no].u.fxs.lasttxhook |= 0x04; }else{ fe->rm_param.mod[mod_no].u.fxs.lasttxhook &= ~0x04; @@ -2371,9 +2356,25 @@ static int wp_remora_event_exec(sdla_fe_t* fe, sdla_fe_timer_event_t *fe_event) card->wandev.event_callback.hook(card, &event); } break; + case WP_RM_POLL_SET_TX_GAIN: + /* */ + if (fe->rm_param.mod[mod_no].type == MOD_TYPE_FXO){ + err=wp_remora_set_fxo_txgain(fe,fe_event->rm_event.rm_gain,mod_no); + }else if(fe->rm_param.mod[mod_no].type == MOD_TYPE_FXS){ + err=wp_remora_set_fxs_txgain(fe,fe_event->rm_event.rm_gain,mod_no); + } + break; + case WP_RM_POLL_SET_RX_GAIN: + /* */ + if (fe->rm_param.mod[mod_no].type == MOD_TYPE_FXO){ + err=wp_remora_set_fxo_rxgain(fe,fe_event->rm_event.rm_gain,mod_no); + }else if(fe->rm_param.mod[mod_no].type == MOD_TYPE_FXS){ + err=wp_remora_set_fxs_rxgain(fe,fe_event->rm_event.rm_gain,mod_no); + } + break; default: err=-EINVAL; - DEBUG_EVENT("ERROR: %s: Invalid poll event type %X!\n", + DEBUG_ERROR("ERROR: %s: Invalid poll event type %X!\n", fe->name, fe_event->type); break; } @@ -2498,7 +2499,7 @@ wp_remora_add_event(sdla_fe_t *fe, sdla_fe_timer_event_t *fe_event) if (!WAN_LIST_NEXT(tmp, next)) break; } if (tmp == NULL){ - DEBUG_EVENT("%s: Internal Error!!!\n", fe->name); + DEBUG_ERROR("%s: Internal Error!!!\n", fe->name); wan_spin_unlock_irq(&fe->lockirq, &smp_flags); return -EINVAL; } @@ -2523,7 +2524,7 @@ wp_remora_event_verification(sdla_fe_t *fe, wan_event_ctrl_t *ectrl) int mod_no = ectrl->mod_no-1; if (!wan_test_bit(mod_no, &fe->rm_param.module_map)) { - DEBUG_EVENT("%s: Error: Received event unused module (mod_no:%d)\n",fe->name, mod_no); + DEBUG_ERROR("%s: Error: Received event unused module (mod_no:%d)\n",fe->name, mod_no); return 0; } @@ -2542,6 +2543,8 @@ wp_remora_event_verification(sdla_fe_t *fe, wan_event_ctrl_t *ectrl) case WAN_EVENT_RM_TXSIG_ONHOOK: case WAN_EVENT_RM_ONHOOKTRANSFER: case WAN_EVENT_RM_SETPOLARITY: + case WAN_EVENT_RM_SET_TX_GAIN: + case WAN_EVENT_RM_SET_RX_GAIN: break; default: DEBUG_EVENT( @@ -2556,10 +2559,12 @@ wp_remora_event_verification(sdla_fe_t *fe, wan_event_ctrl_t *ectrl) case WAN_EVENT_RM_TXSIG_START: case WAN_EVENT_RM_TXSIG_OFFHOOK: case WAN_EVENT_RM_TXSIG_ONHOOK: + case WAN_EVENT_RM_SET_TX_GAIN: + case WAN_EVENT_RM_SET_RX_GAIN: break; default: DEBUG_EVENT( - "%s: Module %d: Remora Invalid Event for FXO modulei (%X)\n", + "%s: Module %d: Remora Invalid Event for FXO module (%X)\n", fe->name,mod_no+1, ectrl->type); return -EINVAL; @@ -2654,6 +2659,15 @@ wp_remora_event_ctrl(sdla_fe_t *fe, wan_event_ctrl_t *ectrl) break; case WAN_EVENT_RM_SETPOLARITY: fe_event.type = WP_RM_POLL_SETPOLARITY; + fe_event.rm_event.polarity = ectrl->polarity; + break; + case WAN_EVENT_RM_SET_TX_GAIN: + fe_event.type = WP_RM_POLL_SET_TX_GAIN; + fe_event.rm_event.rm_gain = ectrl->rm_gain; + break; + case WAN_EVENT_RM_SET_RX_GAIN: + fe_event.type = WP_RM_POLL_SET_RX_GAIN; + fe_event.rm_event.rm_gain = ectrl->rm_gain; break; default: DEBUG_EVENT("%s: Module %d: Executing Invalid %s event (%s:%X)!\n", @@ -2796,7 +2810,7 @@ static int wp_remora_regdump(sdla_fe_t* fe, unsigned char *data) mod_no = rm_udp->mod_no; if (!wan_test_bit(mod_no, &fe->rm_param.module_map)) { - DEBUG_EVENT("%s: Error: Attempt to get regdump on unused module (mod_no:%d)\n",fe->name, mod_no); + DEBUG_ERROR("%s: Error: Attempt to get regdump on unused module (mod_no:%d)\n",fe->name, mod_no); return 0; } @@ -2832,7 +2846,7 @@ static int wp_remora_stats(sdla_fe_t* fe, unsigned char *data) mod_no = rm_udp->mod_no; if (!wan_test_bit(mod_no, &fe->rm_param.module_map)) { - DEBUG_EVENT("%s: Error: Attempt to get stats on unused module (mod_no:%d)\n",fe->name, mod_no); + DEBUG_ERROR("%s: Error: Attempt to get stats on unused module (mod_no:%d)\n",fe->name, mod_no); return 0; } @@ -2852,6 +2866,148 @@ static int wp_remora_stats(sdla_fe_t* fe, unsigned char *data) return sizeof(wan_remora_udp_t); } +static int wp_remora_set_fxo_txgain(sdla_fe_t *fe, int txgain, int mod_no) +{ + + if (txgain >= -150 && txgain < 0) { + DEBUG_EVENT("%s: Module %d: Adjust TX Gain to -%2d.%d dB\n", + fe->name, mod_no+1, + (-1) * (txgain / 10), + (-1)* (txgain % 10)); + WRITE_RM_REG(mod_no, 38, 16 + (txgain/-10)); + if(txgain % 10) { + WRITE_RM_REG(mod_no, 40, 16 + (-txgain%10)); + } + }else if (txgain <= 120 && txgain > 0) { + DEBUG_EVENT("%s: Module %d: Adjust TX Gain to %2d.%d dB\n", + fe->name, mod_no+1, + txgain / 10, + txgain % 10); + WRITE_RM_REG(mod_no, 38, txgain/10); + if (txgain % 10){ + WRITE_RM_REG(mod_no, 40, (txgain % 10)); + } + }else if (txgain == 0) { + DEBUG_EVENT("%s: Module %d: Set TX Gain to %2d.%d dB\n", + fe->name, mod_no+1, + txgain / 10, + txgain % 10); + WRITE_RM_REG(mod_no, 38, 0); + WRITE_RM_REG(mod_no, 40, 0); + }else{ + DEBUG_EVENT("%s: Module %d: Invalid txgain value %d\n", + fe->name, mod_no+1, + txgain); + return 1; + + } + + return 0; +} + +static int wp_remora_set_fxo_rxgain(sdla_fe_t *fe, int rxgain, int mod_no) +{ + + if (rxgain >= -150 && rxgain < 0) { + DEBUG_EVENT("%s: Module %d: Adjust RX Gain to -%2d.%d dB\n", + fe->name, mod_no+1, + (-1) * (rxgain / 10), + (-1) * (rxgain % 10)); + WRITE_RM_REG(mod_no, 39, 16 + (rxgain/-10)); + if(rxgain%10) { + WRITE_RM_REG(mod_no, 41, 16 + (-rxgain%10)); + } + }else if (rxgain <= 120 && rxgain > 0) { + DEBUG_EVENT("%s: Module %d: Adjust RX Gain to %2d.%d dB\n", + fe->name, mod_no+1, + rxgain / 10, + rxgain % 10); + WRITE_RM_REG(mod_no, 39, rxgain/10); + if(rxgain % 10) { + WRITE_RM_REG(mod_no, 41, (rxgain%10)); + } + }else if (rxgain == 0) { + DEBUG_EVENT("%s: Module %d: Set RX Gain to %2d.%d dB\n", + fe->name, mod_no+1, + rxgain / 10, + rxgain % 10); + WRITE_RM_REG(mod_no, 39, 0); + WRITE_RM_REG(mod_no, 41, 0); + }else{ + DEBUG_EVENT("%s: Module %d: Invalid rxgain value %d!\n", + fe->name, mod_no+1, + rxgain); + return 1; + + } + return 0; +} + +static int wp_remora_set_fxs_txgain(sdla_fe_t *fe, int txgain, int mod_no) +{ + unsigned char value; + + value = READ_RM_REG(mod_no, 9); + switch (txgain) { + case 35: + value &= 0xF3; + value |= 0x8; + break; + case -35: + value &= 0xF3; + value |= 0x4; + break; + case 0: + value &= 0xF3; + break; + default: + DEBUG_EVENT("%s: Module %d: Invalid txgain value %d!\n", + fe->name, mod_no+1,txgain); + return 1; + break; + return 0; + } + + DEBUG_EVENT("%s: Module %d: Adjust TX Gain to %s\n", + fe->name, mod_no+1, + (txgain == 35) ? "3.5dB": + (txgain == -35) ? "-3.5dB":"0dB"); + WRITE_RM_REG(mod_no, 9, value); + + return 0; +} +static int wp_remora_set_fxs_rxgain(sdla_fe_t *fe, int rxgain, int mod_no) +{ + + unsigned char value; + value = READ_RM_REG(mod_no, 9); + switch (rxgain) { + case 35: + value &= 0xFC; + value |= 0x2; + break; + case -35: + value &= 0xFC; + value |= 0x01; + break; + case 0: + value &= 0xFC; + break; + default: + DEBUG_EVENT("%s: Module %d: Invalid rxgain value %d!\n", + fe->name, mod_no+1,rxgain); + return 1; + break; + } + DEBUG_EVENT("%s: Module %d: Adjust RX Gain to %s\n", + fe->name, mod_no+1, + (rxgain == 35) ? "3.5dB": + (rxgain == -35) ? "-3.5dB":"0dB"); + WRITE_RM_REG(mod_no, 9, value); + + return 0; +} + /* ****************************************************************************** * wp_remora_udp() diff --git a/patches/kdrivers/src/net/sdla_remora_analog.c b/patches/kdrivers/src/net/sdla_remora_analog.c index 23779a5..01fee14 100644 --- a/patches/kdrivers/src/net/sdla_remora_analog.c +++ b/patches/kdrivers/src/net/sdla_remora_analog.c @@ -12,6 +12,7 @@ * 2 of the License, or (at your option) any later version. * ============================================================================ * Oct 20, 2008 Jignesh Patel Initial version based on sdla_remora_tdmv.c + * Sep 22, 2009 Moises Silva Added TDMV Alarm reporting on line disconnect ****************************************************************************** */ @@ -45,6 +46,7 @@ static void wp_remora_voicedaa_tapper_check_hook(sdla_fe_t *fe, int mod_no); #endif static int ohdebounce = 16; +#define DEFAULT_CURRENT_THRESH 5 /*Anything under this is considered "on-hook" for active call on FXO */ @@ -159,6 +161,7 @@ static int wp_tdmv_remora_voicedaa_check_hook(sdla_fe_t *fe, int mod_no) unsigned char res; #endif signed char b; + signed char lc; int poopy = 0; wp_remora_fxo_t *fxo = NULL; #if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE) @@ -295,7 +298,7 @@ static int wp_tdmv_remora_voicedaa_check_hook(sdla_fe_t *fe, int mod_no) if (fe->rm_param.mod[mod_no].u.fxo.statusdebounce >= FXO_LINK_DEBOUNCE){ if (fe->rm_param.mod[mod_no].u.fxo.status != FE_DISCONNECTED){ DEBUG_EVENT( - "%s: Module %d: FXO Line is disconnnected!\n", + "%s: Module %d: FXO Line is disconnected!\n", fe->name, mod_no + 1); fe->rm_param.mod[mod_no].u.fxo.status = FE_DISCONNECTED; @@ -310,6 +313,15 @@ static int wp_tdmv_remora_voicedaa_check_hook(sdla_fe_t *fe, int mod_no) } } #endif +#if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE) + if (card->u.aft.tdmv_zaptel_cfg) { + zt_alarm_channel(&wr->chans[mod_no], ZT_ALARM_RED); + DEBUG_TDMV("%s: Module %d: TDMV RED ALARM on span %d!\n", + fe->name, + mod_no + 1, + wr->span.spanno); + } +#endif } fe->rm_param.mod[mod_no].u.fxo.statusdebounce = FXO_LINK_DEBOUNCE; @@ -333,12 +345,76 @@ static int wp_tdmv_remora_voicedaa_check_hook(sdla_fe_t *fe, int mod_no) card->wandev.event_callback.linkstatus(card, &event); } } +#endif +#if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE) + if (card->u.aft.tdmv_zaptel_cfg) { + zt_alarm_channel(&wr->chans[mod_no], ZT_ALARM_NONE); + DEBUG_TDMV("%s: Module %d: TDMV NONE ALARM on span %d!\n", + fe->name, + mod_no + 1, + wr->span.spanno); + } #endif } fe->rm_param.mod[mod_no].u.fxo.statusdebounce = 0; } } - + /*If current measure is enabled check measure once we are Off-hook */ + if (fe->fe_cfg.cfg.remora.rm_lcm == WANOPT_YES) { +#if defined(REG_SHADOW) + lc= fe->rm_param.reg3shadow[mod_no]; +#else + lc= READ_RM_REG(mod_no, 28); +#endif + lc=(11*lc)/10; /* lc=current on the line, equivalent of lc*1.1*/ + if (DEFAULT_CURRENT_THRESH > lc){ + if(fxo->offhook && !(fxo->i_debounce)){ + if (card->u.aft.tdmv_zaptel_cfg) { +#if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE) + DEBUG_TDMV( + "%s: Module %d: FXO Line current %d !!\n", + fe->name, + mod_no + 1, + lc); + + DEBUG_TDMV( + "%s: Module %d: Signalled On Hook span %d (%u) using LCM)\n", + fe->name, + mod_no + 1, + wr->span.spanno,(u32)SYSTEM_TICKS); + zt_hooksig(&wr->chans[mod_no], ZT_RXSIG_ONHOOK); +#endif + } else { +#ifdef AFT_TDM_API_SUPPORT + DEBUG_RM( + "%s: Module %d: FXO Line current %d (%u)!!\n", + fe->name, + mod_no + 1, + lc,(u32)SYSTEM_TICKS); + DEBUG_RM("%s: Module %d: On-Hook status using LCM!\n", + fe->name, + mod_no + 1); + event.type = WAN_EVENT_RM_LC; + event.channel = mod_no+1; + event.rxhook = WAN_EVENT_RXHOOK_ON; + if (card->wandev.event_callback.hook){ + card->wandev.event_callback.hook( + card, &event); + } +#endif + } + fxo->i_debounce=fe->rm_param.battdebounce; /*current debounce same as battry debounce*/ + } + + }else{ + fxo->i_debounce=fe->rm_param.battdebounce; + } + if(fxo->offhook) /*only during off-hook */{ + if (fxo->i_debounce) + fxo->i_debounce--; + } + } + if (abs(b) < fe->rm_param.battthresh) { /*Check for fe */ fxo->nobatttimer++; #if 0 @@ -367,29 +443,30 @@ static int wp_tdmv_remora_voicedaa_check_hook(sdla_fe_t *fe, int mod_no) #ifdef JAPAN if ((!fxo->ohdebounce) && fxo->offhook) { - - if (card->u.aft.tdmv_zaptel_cfg) { + if (fe->fe_cfg.cfg.remora.rm_lcm != WANOPT_YES) { + if (card->u.aft.tdmv_zaptel_cfg) { #if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE) - zt_hooksig(&wr->chans[mod_no], ZT_RXSIG_ONHOOK); - DEBUG_TDMV( - "%s: Module %d: Signalled On Hook span %d\n", - fe->name, - mod_no + 1, - wr->span.spanno); + zt_hooksig(&wr->chans[mod_no], ZT_RXSIG_ONHOOK); + DEBUG_TDMV( + "%s: Module %d: Signalled On Hook span %d\n", + fe->name, + mod_no + 1, + wr->span.spanno); #endif - } else { + } else { #ifdef AFT_TDM_API_SUPPORT - DEBUG_RM("%s: Module %d: On-Hook status!\n", - fe->name, - mod_no + 1); - event.type = WAN_EVENT_RM_LC; - event.channel = mod_no+1; - event.rxhook = WAN_EVENT_RXHOOK_ON; - if (card->wandev.event_callback.hook){ - card->wandev.event_callback.hook( - card, &event); - } + DEBUG_RM("%s: Module %d: On-Hook status!\n", + fe->name, + mod_no + 1); + event.type = WAN_EVENT_RM_LC; + event.channel = mod_no+1; + event.rxhook = WAN_EVENT_RXHOOK_ON; + if (card->wandev.event_callback.hook){ + card->wandev.event_callback.hook( + card, &event); + } #endif + } } #ifdef ZERO_BATT_RING @@ -397,30 +474,31 @@ static int wp_tdmv_remora_voicedaa_check_hook(sdla_fe_t *fe, int mod_no) #endif } #else - - if (card->u.aft.tdmv_zaptel_cfg) { + if (fe->fe_cfg.cfg.remora.rm_lcm != WANOPT_YES) { + if (card->u.aft.tdmv_zaptel_cfg) { #if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE) - DEBUG_TDMV( - "%s: Module %d: Signalled On Hook span %d\n", - fe->name, - mod_no + 1, - wr->span.spanno); - zt_hooksig(&wr->chans[mod_no], ZT_RXSIG_ONHOOK); + DEBUG_TDMV( + "%s: Module %d: Signalled On Hook span %d\n", + fe->name, + mod_no + 1, + wr->span.spanno); + zt_hooksig(&wr->chans[mod_no], ZT_RXSIG_ONHOOK); #endif - } else { + } else { #ifdef AFT_TDM_API_SUPPORT - DEBUG_RM("%s: Module %d: On-Hook status!\n", - fe->name, - mod_no + 1); - event.type = WAN_EVENT_RM_LC; - event.channel = mod_no+1; - event.rxhook = WAN_EVENT_RXHOOK_ON; - if (card->wandev.event_callback.hook){ - card->wandev.event_callback.hook( - card, &event); - } + DEBUG_RM("%s: Module %d: On-Hook status!\n", + fe->name, + mod_no + 1); + event.type = WAN_EVENT_RM_LC; + event.channel = mod_no+1; + event.rxhook = WAN_EVENT_RXHOOK_ON; + if (card->wandev.event_callback.hook){ + card->wandev.event_callback.hook( + card, &event); + } #endif - } + } + } #endif fxo->battdebounce = fe->rm_param.battdebounce; } else if (!fxo->battery) @@ -684,7 +762,7 @@ static int wp_tdmv_remora_proslic_check_hook(sdla_fe_t *fe, int mod_no) &event); } } else { - DEBUG_RM( + DEBUG_RM( "RM: %s: Module %d: Off-Hook status!\n", fe->name, mod_no+1); fxs->itimer=0; @@ -733,6 +811,9 @@ static int wp_tdmv_remora_proslic_check_hook(sdla_fe_t *fe, int mod_no) card, &event); } + /*need for mwi tranfer in TDMAPI mode */ + fxs->lasttxhook=fxs->idletxhookstate; + fxs->lasttxhook_update=1; } @@ -973,7 +1054,7 @@ static void wp_remora_voicedaa_tapper_check_hook(sdla_fe_t *fe, int mod_no) if (fe->rm_param.mod[mod_no].u.fxo.statusdebounce >= FXO_LINK_DEBOUNCE) { if (fe->rm_param.mod[mod_no].u.fxo.status != FE_DISCONNECTED) { DEBUG_EVENT( - "%s: Module %d: FXO Line is disconnnected!\n", + "%s: Module %d: FXO Line is disconnected (tapper)!\n", fe->name, mod_no + 1); fe->rm_param.mod[mod_no].u.fxo.status = FE_DISCONNECTED; @@ -1085,8 +1166,6 @@ static void wp_remora_voicedaa_tapper_check_hook(sdla_fe_t *fe, int mod_no) card->wandev.event_callback.hook( card, &event); } - - fe->rm_param.mod[mod_no].u.fxo.offhook = 0; fe->rm_param.mod[mod_no].u.fxo.ohdebounce = ohdebounce; } @@ -1179,8 +1258,9 @@ int wp_tdmv_remora_rx_tx_span_common(void *pcard ) if (card->wandev.event_callback.hook){ card->wandev.event_callback.hook( card, &event); - } + fxs->lasttxhook =fxs->idletxhookstate; + fxs->lasttxhook_update=1; } } @@ -1247,9 +1327,6 @@ int wp_tdmv_remora_rx_tx_span_common(void *pcard ) /* Read first shadow reg */ WRITE_RM_REG(x, 5, fe->rm_param.reg0shadow[x]); fe->rm_param.reg0shadow_update[x] = 0; - - - } #endif } @@ -1298,6 +1375,8 @@ int wp_tdmv_remora_rx_tx_span_common(void *pcard ) fe->rm_param.reg1shadow[x] = READ_RM_REG(x, 29); /* Read third shadow reg */ fe->rm_param.reg2shadow[x] = READ_RM_REG(x, 34); + /* Read fourth shadow reg */ + fe->rm_param.reg3shadow[x] = READ_RM_REG(x, 28); } #endif diff --git a/patches/kdrivers/src/net/sdla_remora_tdmv.c b/patches/kdrivers/src/net/sdla_remora_tdmv.c index b09b28a..1ee0561 100644 --- a/patches/kdrivers/src/net/sdla_remora_tdmv.c +++ b/patches/kdrivers/src/net/sdla_remora_tdmv.c @@ -1061,7 +1061,7 @@ static int wp_tdmv_remora_remove(void* pcard) return -EINVAL; } if (wr && wr->usecount){ - DEBUG_EVENT("%s: ERROR: Wanpipe is still used by Asterisk!\n", + DEBUG_ERROR("%s: ERROR: Wanpipe is still used by Asterisk!\n", card->devname); return -EINVAL; } @@ -1337,7 +1337,7 @@ static void wp_tdmv_remora_tone (void* card_id, wan_event_t *event) wr = wan_tdmv->sc; if (event->channel <= 0) { - DEBUG_EVENT("%s: Error: wp_tdmv_remora_tone() Invalid Event Channel = %i\n", + DEBUG_ERROR("%s: Error: wp_tdmv_remora_tone() Invalid Event Channel = %i\n", card->devname, event->channel); return; } @@ -1387,7 +1387,7 @@ static void wp_tdmv_remora_tone (void* card_id, wan_event_t *event) (SYSTEM_TICKS - wr->ec_fax_detect_timeout[fechan]) >= card->tdmv_conf.hw_fax_detect*HZ) { #ifdef WAN_DEBUG_TDMAPI if (WAN_NET_RATELIMIT()) { - DEBUG_EVENT("%s: Warning: Ignoring Fax detect during call (s%dc%d) - Call Time: %ld Max: %d!\n", + DEBUG_WARNING("%s: Warning: Ignoring Fax detect during call (s%dc%d) - Call Time: %ld Max: %d!\n", card->devname, wr->spanno+1, event->channel, diff --git a/patches/kdrivers/src/net/sdla_serial.c b/patches/kdrivers/src/net/sdla_serial.c index eb83b95..86d731f 100644 --- a/patches/kdrivers/src/net/sdla_serial.c +++ b/patches/kdrivers/src/net/sdla_serial.c @@ -34,59 +34,12 @@ # include "aft_core.h" /* Map of Zaptel -> DAHDI definitions */ - #undef DEBUG_SERIAL #define DEBUG_SERIAL if(0)DEBUG_EVENT /* DEBUG macro definitions */ -#define DEBUG_HFC_INIT if(0)DEBUG_EVENT -#define DEBUG_HFC_MODE if(0)DEBUG_EVENT -#define DEBUG_HFC_S0_STATES if(0)DEBUG_EVENT -#define DEBUG_HFC_IRQ if(0)DEBUG_EVENT -#define DEBUG_HFC_SU_IRQ if(0)DEBUG_EVENT - -#define DEBUG_HFC_TX if(0)DEBUG_EVENT -#define DEBUG_TX_DATA if(0)DEBUG_EVENT -#define TX_EXTRA_DBG if(0)DEBUG_EVENT -#define TX_FAST_DBG if(0)DEBUG_EVENT - -#define DEBUG_HFC_RX if(0)DEBUG_EVENT -#define RX_EXTRA_DBG if(0)DEBUG_EVENT -#define DEBUG_RX1 if(0)DEBUG_EVENT -#define DBG_RX_DATA if(0)DEBUG_EVENT - -#define DEBUG_HFC_CLOCK if(0)DEBUG_EVENT - -#define BUILD_MOD_TESTER 0 /* for Production Test */ -#define DBG_MODULE_TESTER if(0)DEBUG_EVENT - -#define NT_STATE_FUNC() if(0)DEBUG_EVENT("%s(): line: %d\n", __FUNCTION__, __LINE__) -#define CLOCK_FUNC() if(0)DEBUG_EVENT("%s(): line: %d\n", __FUNCTION__, __LINE__) - -#define DBG_SPI if(0)DEBUG_EVENT - -#define DEBUG_FE_STATUS if(0)DEBUG_EVENT - #define SERIAL_FUNC() if(0)DEBUG_EVENT("%s(): line: %d\n", __FUNCTION__, __LINE__) -/* Timer interrupt counter - used by activation timer T3 */ -#define HFC_TIMER_COUNTER_T3 2 - -#undef NT_T1_COUNT - -#define FIFO_THRESHOLD_INDEX 1 - -#define LINE_STABILITY_THRESHOLD 3 - -enum { - WAITING_TO_STABILIZE=1, - LINE_STABLE, - LINE_DISCONNECTED -}; - -#define CHECK_DATA 0 - - int aft_serial_write_cpld(void *card, unsigned short off,u_int16_t data); unsigned char aft_serial_read_cpld(void *card, unsigned short cpld_off); @@ -356,7 +309,7 @@ static int32_t wp_serial_config(void *pfe) case WANOPT_NRZI: break; default: - DEBUG_EVENT("%s: A140: Error: Unsupported line coding mode 0x%X\n", + DEBUG_ERROR("%s: A140: Error: Unsupported line coding mode 0x%X\n", card->devname, card->wandev.line_coding); return -1; @@ -382,7 +335,7 @@ static int32_t wp_serial_config(void *pfe) cpld_reg=0x09; break; default: - DEBUG_EVENT("%s: Error: Invalid Serial Port Number! (%i) \n", + DEBUG_ERROR("%s: Error: Invalid Serial Port Number! (%i) \n", card->devname,WAN_FE_LINENO(fe)); return -EINVAL; }; @@ -395,7 +348,7 @@ static int32_t wp_serial_config(void *pfe) if (wan_test_bit(2,&cpld_reg)) { /* In this case port is trying to configure for V35 * where previous port already configured for X21 */ - DEBUG_EVENT("%s: Error: Invalid V35 Configuration, Previous Port configured for X21\n", + DEBUG_ERROR("%s: Error: Invalid V35 Configuration, Previous Port configured for X21\n", card->devname); return -EINVAL; } @@ -416,7 +369,7 @@ static int32_t wp_serial_config(void *pfe) } } else { if (wan_test_bit(2,&cpld_reg_val)) { - DEBUG_EVENT("%s: Error: Clocking configuration mismatch!\n", + DEBUG_ERROR("%s: Error: Clocking configuration mismatch!\n", card->devname); DEBUG_EVENT("%s: Ports 1&3 and 2&4 must use same clock source!\n", card->devname); @@ -454,7 +407,7 @@ static int32_t wp_serial_config(void *pfe) break; default: - DEBUG_EVENT("%s: Error: Invalid Serial Card Type 0x%X\n", + DEBUG_ERROR("%s: Error: Invalid Serial Card Type 0x%X\n", card->devname,card->adptr_type); return -1; } @@ -491,7 +444,7 @@ static int32_t wp_serial_config(void *pfe) break; default: /* Should never happen because we check above */ - DEBUG_EVENT("%s: A140: Error: Unsupported line coding mode 0x%X\n", + DEBUG_ERROR("%s: A140: Error: Unsupported line coding mode 0x%X\n", card->devname, card->wandev.line_coding); return -1; @@ -964,7 +917,7 @@ int aft_serial_write_cpld(void *pcard, unsigned short off,u_int16_t data) sdla_t *card = (sdla_t*)pcard; if (card->hw_iface.fe_test_and_set_bit(card->hw,0)){ - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", card->devname, __FUNCTION__,__LINE__); return 0x00; } @@ -984,7 +937,7 @@ unsigned char aft_serial_read_cpld(void *pcard, unsigned short cpld_off) sdla_t *card = (sdla_t*)pcard; if (card->hw_iface.fe_test_and_set_bit(card->hw,0)){ - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", card->devname, __FUNCTION__,__LINE__); return 0x00; } diff --git a/patches/kdrivers/src/net/sdla_tdmv.c b/patches/kdrivers/src/net/sdla_tdmv.c index 31e25d9..1b26f63 100644 --- a/patches/kdrivers/src/net/sdla_tdmv.c +++ b/patches/kdrivers/src/net/sdla_tdmv.c @@ -140,6 +140,13 @@ static unsigned char wp_tdmv_ulaw[] = { STRUCTURES AND TYPEDEFS ****************************************************************************** */ + +typedef struct wp_tdmv_rbs +{ + int chan; + unsigned char data; +}wp_tdmv_rbs_t; + typedef struct wp_tdmv_pvt_area { sdla_t* card; @@ -180,14 +187,18 @@ typedef struct wp_tdmv_pvt_area int timeslots; /* configured timeslots */ unsigned long sig_timeslot_map; int sig_timeslots; + wan_skb_queue_t rbs_tx_q; +#if 0 unsigned long rbs_tx_status; unsigned long rbs_tx1_status; unsigned char rbs_tx[31]; unsigned char rbs_tx1[31]; +#endif unsigned char rbs_rx[31]; unsigned long rbs_rx_pending; u32 intcount; - u32 rbscount; + unsigned long rbs_sanity_timeout; + unsigned long rbs_timeout; unsigned int brt_ctrl; unsigned char hwec; unsigned char echo_off; @@ -420,6 +431,7 @@ static int wp_tdmv_create(void* pcard, wan_tdmv_conf_t *tdmv_conf) wp->max_rxtx_len = 0; wan_spin_lock_irq_init(&wp->lockirq, "wan_te1tdmv_lock"); wan_spin_lock_irq_init(&wp->tx_rx_lockirq, "wan_te1tdmv_txrx_lock"); + wan_skb_queue_init(&wp->rbs_tx_q); #ifdef DAHDI_ISSUES for (i = 0; i < sizeof(wp->chans)/sizeof(wp->chans[0]); i++) { wp->chans_ptrs[i] = &wp->chans[i]; @@ -465,7 +477,7 @@ static int wp_tdmv_reg( void *pcard, wp = wan_tdmv->sc; if (wan_test_bit(WP_TDMV_REGISTER, &wp->flags)){ - DEBUG_EVENT("%s: Error: Master device has already been configured!\n", + DEBUG_ERROR("%s: Error: Master device has already been configured!\n", card->devname); return -EINVAL; } @@ -485,7 +497,7 @@ static int wp_tdmv_reg( void *pcard, } if (!cnt){ - DEBUG_EVENT("%s: Error: TDMV iface %s configured with 0 timeslots!\n", + DEBUG_ERROR("%s: Error: TDMV iface %s configured with 0 timeslots!\n", card->devname, wan_netif_name(dev)); return -EINVAL; } @@ -497,7 +509,7 @@ static int wp_tdmv_reg( void *pcard, channo = 0; #if 0 if (is_last != WAN_TRUE){ - DEBUG_EVENT("%s: Error: Unchannelized interface must be the Master If (slots=%i)!\n", + DEBUG_ERROR("%s: Error: Unchannelized interface must be the Master If (slots=%i)!\n", wp->devname,cnt); return -EINVAL; } @@ -510,7 +522,7 @@ static int wp_tdmv_reg( void *pcard, if (wp->dchan_map){ if (dev == NULL){ - DEBUG_EVENT("%s: ERROR: Device pointer is NULL for D-chan!\n", + DEBUG_ERROR("%s: ERROR: Device pointer is NULL for D-chan!\n", wp->devname); return -EINVAL; } @@ -521,7 +533,7 @@ static int wp_tdmv_reg( void *pcard, /* Channelized implementation */ if (wan_test_bit(channo, &wp->dchan_map)){ if (dev == NULL){ - DEBUG_EVENT("%s: ERROR: Device pointer is NULL for D-chan!\n", + DEBUG_ERROR("%s: ERROR: Device pointer is NULL for D-chan!\n", wp->devname); return -EINVAL; } @@ -625,7 +637,7 @@ static int wp_tdmv_remove(void* pcard) return -EINVAL; } if (wp && wp->usecount){ - DEBUG_EVENT("%s: ERROR: Wanpipe is still used by Asterisk (cnt=%i)!\n", + DEBUG_ERROR("%s: ERROR: Wanpipe is still used by Asterisk (cnt=%i)!\n", card->devname,wp->usecount); return -EINVAL; } @@ -660,12 +672,19 @@ static int wp_tdmv_state(void* pcard, int state) switch(state){ case WAN_CONNECTED: + wp->rbs_rx_pending = wp->sig_timeslot_map; wan_set_bit(WP_TDMV_UP, &wp->flags); + memset(wp->rbs_rx,0,sizeof(wp->rbs_rx)); + wan_clear_bit(WP_TDMV_RBS_BUSY, &wp->flags); + wan_skb_queue_purge(&wp->rbs_tx_q); break; case WAN_DISCONNECTED: wan_clear_bit(WP_TDMV_UP, &wp->flags); wp->rbs_rx_pending = wp->sig_timeslot_map; + memset(wp->rbs_rx,0,sizeof(wp->rbs_rx)); + wan_clear_bit(WP_TDMV_RBS_BUSY, &wp->flags); + wan_skb_queue_purge(&wp->rbs_tx_q); break; } return 0; @@ -1044,7 +1063,7 @@ static int wp_tdmv_rx_tx(void* pcard, netskb_t* skb) if (wan_skb_len(skb) != wp->max_rxtx_len){ if (WAN_NET_RATELIMIT()) { - DEBUG_EVENT("%s: Internal Error[%s:%d]: Wrong buffer lenght %d (%d)!\n", + DEBUG_ERROR("%s: Internal Error[%s:%d]: Wrong buffer lenght %d (%d)!\n", wp->devname, __FUNCTION__,__LINE__, wan_skb_len(skb), @@ -1064,13 +1083,6 @@ static int wp_tdmv_rx_tx(void* pcard, netskb_t* skb) wan_skb_data(skb), wan_skb_len(skb)); - if (wan_test_bit(WP_TDMV_RBS_UPDATE, &wp->flags)){ - if (card->wandev.fe_iface.report_rbsbits){ - card->wandev.fe_iface.report_rbsbits(&card->fe); - } - wan_clear_bit(WP_TDMV_RBS_UPDATE, &wp->flags); - wan_clear_bit(WP_TDMV_RBS_BUSY, &wp->flags); - } return wp->max_rxtx_len; } @@ -1642,7 +1654,9 @@ static int wp_tdmv_rbsbits(struct zt_chan *chan, int bits) wp_tdmv_softc_t *wp = NULL; sdla_t *card = NULL; unsigned char ABCD_bits = 0x00; - + wp_tdmv_rbs_t *rbs_pkt; + netskb_t *rbs_skb; + /* Byte offset */ WAN_ASSERT2(chan == NULL, 0); if ((wp = chan->pvt) == NULL) return 0; @@ -1672,6 +1686,22 @@ static int wp_tdmv_rbsbits(struct zt_chan *chan, int bits) (ABCD_bits & WAN_RBS_SIG_C) ? 1 : 0, (ABCD_bits & WAN_RBS_SIG_D) ? 1 : 0); + rbs_skb=wan_skb_alloc(sizeof(wp_tdmv_rbs_t)); + if (!rbs_skb) { + DEBUG_EVENT("%s: Critical Error: TX RBS overrun for channel %d\n", + wp->devname, + chan->chanpos); + return 0; + } + rbs_pkt=(wp_tdmv_rbs_t*)wan_skb_put(rbs_skb,sizeof(wp_tdmv_rbs_t)); + + DEBUG_TEST("%s: TX RBS Chan=%02i Data=0x%02X\n", + wp->devname, chan->chanpos, ABCD_bits); + rbs_pkt->chan=chan->chanpos; + rbs_pkt->data=ABCD_bits; + wan_skb_queue_tail(&wp->rbs_tx_q, rbs_skb); + +#if 0 if (wan_test_and_set_bit(chan->chanpos-1, &wp->rbs_tx_status)){ if (ABCD_bits == wp->rbs_tx[chan->chanpos-1]){ return 0; @@ -1680,7 +1710,7 @@ static int wp_tdmv_rbsbits(struct zt_chan *chan, int bits) if (ABCD_bits == wp->rbs_tx1[chan->chanpos-1]){ return 0; } - DEBUG_EVENT("%s: Critical Error: TX RBS for channel %d\n", + DEBUG_EVENT("%s: Critical Error: TX RBS overrun for channel %d\n", wp->devname, chan->chanpos); } @@ -1688,6 +1718,7 @@ static int wp_tdmv_rbsbits(struct zt_chan *chan, int bits) }else{ wp->rbs_tx[chan->chanpos-1] = ABCD_bits; } +#endif #if 0 wan_set_bit(7, &ABCD_bits); if (wan_test_and_set_bit(7, &wp->rbs_tx[chan->chanpos-1])){ @@ -1718,6 +1749,7 @@ static int wp_tdmv_rbsbits(struct zt_chan *chan, int bits) */ static int wp_tdmv_is_rbsbits(wan_tdmv_t *wan_tdmv) { + int ret=0; wp_tdmv_softc_t *wp = NULL; WAN_ASSERT(wan_tdmv->sc == NULL); @@ -1728,32 +1760,30 @@ static int wp_tdmv_is_rbsbits(wan_tdmv_t *wan_tdmv) return 0; } - if (wan_test_and_set_bit(WP_TDMV_RBS_BUSY, &wp->flags)){ - /* RBS read still in progress or not ready*/ - return 0; - } - - if (wp->rbs_tx_status || wp->rbs_tx1_status){ - return 1; - } - if (!IS_TDMV_UP(wp)){ wan_clear_bit(WP_TDMV_RBS_BUSY, &wp->flags); return 0; } - /* Increment RX/TX interrupt counter */ - wp->rbscount++; + if (wan_skb_queue_len(&wp->rbs_tx_q)) { + ret=1; + } + + if (wan_test_and_set_bit(WP_TDMV_RBS_BUSY, &wp->flags)){ + /* RBS read still in progress or not ready*/ + return ret; + } - /* RBS_POLL - ** Update RBS bits now (we don't have to do very often) */ - if (!(wp->rbscount & 0xF)){ - return 1; + /* Check rbs every 20ms */ + if ((SYSTEM_TICKS - wp->rbs_timeout) > HZ/50) { + wp->rbs_timeout = SYSTEM_TICKS; + ret=1; + return ret; } /* Wait for the next time */ wan_clear_bit(WP_TDMV_RBS_BUSY, &wp->flags); - return 0; + return ret; } /****************************************************************************** @@ -1761,17 +1791,19 @@ static int wp_tdmv_is_rbsbits(wan_tdmv_t *wan_tdmv) ** ** DONE */ + static int wp_tdmv_rbsbits_poll(wan_tdmv_t *wan_tdmv, void *card1) { sdla_t *card = (sdla_t*)card1; wp_tdmv_softc_t *wp = NULL; int i, x; + unsigned char status=0; WAN_ASSERT(wan_tdmv->sc == NULL); wp = wan_tdmv->sc; /* TX rbsbits */ - if (wp->rbs_tx_status || wp->rbs_tx1_status){ + if (wan_skb_queue_len(&wp->rbs_tx_q)) { wp_tdmv_tx_rbsbits(wp); } @@ -1779,30 +1811,60 @@ static int wp_tdmv_rbsbits_poll(wan_tdmv_t *wan_tdmv, void *card1) wan_clear_bit(WP_TDMV_RBS_BUSY, &wp->flags); return 0; } + if (wp->rbs_rx_pending){ - DEBUG_TEST("%s: %s:%d: Reading RBS (pending)\n", - wp->devname, - __FUNCTION__,__LINE__); + + DEBUG_TEST("%s: %s:%d: Reading RBS pending=0x%08X max_time_slot=%i\n", + wp->devname,__FUNCTION__,__LINE__, wp->rbs_rx_pending,wp->max_timeslots); + for(i=0; i < wp->max_timeslots;i++){ if (wan_test_bit(i, &wp->rbs_rx_pending)){ wan_clear_bit(i, &wp->rbs_rx_pending); + + DEBUG_TEST("%s: %s:%d: Reading RBS (pending=0x%08X) maxts=%i chan=%i\n", + wp->devname, + __FUNCTION__,__LINE__, + wp->rbs_rx_pending,wp->max_timeslots,i+1); + card->wandev.fe_iface.read_rbsbits( &card->fe, i+1, WAN_TE_RBS_UPDATE|WAN_TE_RBS_REPORT); } } - wan_set_bit(WP_TDMV_RBS_UPDATE, &wp->flags); + + wan_clear_bit(WP_TDMV_RBS_BUSY, &wp->flags); return 0; } /* RX rbsbits */ - DEBUG_TEST("%s: %s:%d: Reading RBS (%s)\n", + + DEBUG_TEST("%s: %s:%d: Reading RBS \n", wp->devname, - __FUNCTION__,__LINE__, - (wp->rbscount % 1000) ? "Normal" : "Sanity"); - if (wp->rbscount % 1000 == 0){ - for(x = 0; x < wp->max_timeslots; x++){ + __FUNCTION__,__LINE__); + +#if 1 + + /* NENAD: The check_rbs seems to be broken for + E1 channel 31. Default to reading rbs + each time intead of using check_rbs */ + for (x=0; x < wp->max_timeslots; x++) { + if (wan_test_bit(x, &wp->sig_timeslot_map)) { + status = card->wandev.fe_iface.read_rbsbits( + &card->fe, + x+1, + WAN_TE_RBS_UPDATE|WAN_TE_RBS_REPORT); + } + } + + wan_clear_bit(WP_TDMV_RBS_BUSY, &wp->flags); + + +#else + + if (SYSTEM_TICKS - wp->rbs_sanity_timeout > HZ) { + wp->rbs_sanity_timeout=SYSTEM_TICKS; + for (x = 0; x < wp->max_timeslots; x++){ if (wan_test_bit(x, &wp->sig_timeslot_map)){ card->wandev.fe_iface.read_rbsbits( &card->fe, @@ -1810,13 +1872,14 @@ static int wp_tdmv_rbsbits_poll(wan_tdmv_t *wan_tdmv, void *card1) WAN_TE_RBS_UPDATE); } } - }else{ + } else { if (card->wandev.fe_iface.check_rbsbits == NULL){ - DEBUG_EVENT("%s: Internal Error [%s:%d]!\n", + DEBUG_ERROR("%s: Internal Error [%s:%d]!\n", wp->devname, __FUNCTION__,__LINE__); return -EINVAL; } + card->wandev.fe_iface.check_rbsbits( &card->fe, 1, wp->sig_timeslot_map, 0); @@ -1826,13 +1889,18 @@ static int wp_tdmv_rbsbits_poll(wan_tdmv_t *wan_tdmv, void *card1) card->wandev.fe_iface.check_rbsbits( &card->fe, 17, wp->sig_timeslot_map, 0); - if (wp->ise1){ + if (wp->ise1){ card->wandev.fe_iface.check_rbsbits( &card->fe, 25, wp->sig_timeslot_map, 0); } } + wan_set_bit(WP_TDMV_RBS_UPDATE, &wp->flags); + +#endif + + return 0; } @@ -1844,10 +1912,14 @@ static int wp_tdmv_rbsbits_poll(wan_tdmv_t *wan_tdmv, void *card1) static int wp_tdmv_tx_rbsbits(wp_tdmv_softc_t *wp) { sdla_t *card; - int x; + //int x; + wp_tdmv_rbs_t *rbs_pkt; + netskb_t *rbs_skb; WAN_ASSERT2(wp->card == NULL, 0); card = (sdla_t*)wp->card; + +#if 0 for(x=0;xmax_timeslots;x++){ if (wan_test_bit(x, &wp->rbs_tx_status)){ card->wandev.fe_iface.set_rbsbits( @@ -1864,24 +1936,24 @@ static int wp_tdmv_tx_rbsbits(wp_tdmv_softc_t *wp) } } } -#if 0 - for(x=0;xmax_timeslots;x++){ - if (wan_test_bit(7, &wp->rbs_tx[x])){ - card->wandev.fe_iface.set_rbsbits( - &wp->card->fe, - x+1, - wp->rbs_tx[x]); - wan_clear_bit(7, &wp->rbs_tx[x]); - if (wan_test_bit(7, &wp->rbs_tx1[x])){ - card->wandev.fe_iface.set_rbsbits( - &wp->card->fe, - x+1, - wp->rbs_tx1[x]); - wan_clear_bit(7, &wp->rbs_tx1[x]); - } - } +#else + + while ((rbs_skb=wan_skb_dequeue(&wp->rbs_tx_q))) { + rbs_pkt=(wp_tdmv_rbs_t*)wan_skb_data(rbs_skb); + + DEBUG_TEST("%s: TX RBS HW Chan=%02i Data=0x%02X\n", + wp->devname, rbs_pkt->chan, rbs_pkt->data); + + card->wandev.fe_iface.set_rbsbits( + &card->fe, + rbs_pkt->chan, + rbs_pkt->data); + + wan_skb_free(rbs_skb); } + #endif + return 0; } @@ -2229,6 +2301,7 @@ static void wp_tdmv_release(wp_tdmv_softc_t *wp) wan_clear_bit(WP_TDMV_REGISTER, &wp->flags); zt_unregister(&wp->span); wan_clear_bit(WP_TDMV_REGISTER, &wp->flags); + wan_skb_queue_purge(&wp->rbs_tx_q); } wan_free(wp); } @@ -2274,7 +2347,7 @@ static int wp_tdmv_rx_dchan(wan_tdmv_t *wan_tdmv, int channo, WAN_ASSERT(wp == NULL); if (!wan_test_bit(channo, &wp->dchan_map)) { - DEBUG_EVENT("%s: Internal Error: DCHAN Mismatch channo=%i 0x%08X\n", + DEBUG_ERROR("%s: Internal Error: DCHAN Mismatch channo=%i 0x%08X\n", wp->devname,channo,wp->dchan_map); return -EINVAL; } @@ -2309,7 +2382,7 @@ static int wp_tdmv_rx_dchan(wan_tdmv_t *wan_tdmv, int channo, buf = ms->readbuf[ms->inreadbuf]; left = ms->blocksize - ms->readidx[ms->inreadbuf]; if (len + 2 > left) { - DEBUG_EVENT("%s: ERROR: Not ehough space for RX HDLC packet (%d:%d)!\n", + DEBUG_ERROR("%s: ERROR: Not ehough space for RX HDLC packet (%d:%d)!\n", wp->devname, len+2, left); wan_spin_unlock_irq(&chan->lock, &smp_flags); return -EINVAL; @@ -2379,10 +2452,10 @@ static void wp_tdmv_tx_hdlc_hard(struct zt_chan *chan) data = wan_skb_put(skb, size); res = zt_hdlc_getbuf(chan, data, &size); if (res == 0){ - DEBUG_EVENT("%s: ERROR: TX HW DCHAN %d bytes (res %d)\n", + DEBUG_ERROR("%s: ERROR: TX HW DCHAN %d bytes (res %d)\n", wp->devname, size, res); } - err = wp->dchan_dev->hard_start_xmit(skb, wp->dchan_dev); + err = WAN_NETDEV_XMIT(skb,wp->dchan_dev); if (err){ wan_skb_free(skb); } @@ -2704,16 +2777,6 @@ static int wp_tdmv_rx_tx_span(void *pcard) wp->max_rxtx_len); } - if (wan_test_bit(WP_TDMV_RBS_UPDATE, &wp->flags)){ - DEBUG_TEST("%s: %s:%d: Updating RBS status \n", - wp->devname, - __FUNCTION__,__LINE__); - if (card->wandev.fe_iface.report_rbsbits){ - card->wandev.fe_iface.report_rbsbits(&card->fe); - } - wan_clear_bit(WP_TDMV_RBS_UPDATE, &wp->flags); - wan_clear_bit(WP_TDMV_RBS_BUSY, &wp->flags); - } return 0; } @@ -2744,7 +2807,7 @@ static void wp_tdmv_callback_tone (void* card_id, wan_event_t *event) wp = wan_tdmv->sc; if (event->channel <= 0) { - DEBUG_EVENT("%s: Error: wp_tdmv_callback_tone() Invalid Event Channel = %i\n", + DEBUG_ERROR("%s: Error: wp_tdmv_callback_tone() Invalid Event Channel = %i\n", card->devname, event->channel); return; } @@ -2753,7 +2816,7 @@ static void wp_tdmv_callback_tone (void* card_id, wan_event_t *event) fechan=event->channel-1; if (event->type != WAN_EVENT_EC_DTMF){ - DEBUG_EVENT("ERROR: %s: Invalid event type %X!\n", + DEBUG_ERROR("ERROR: %s: Invalid event type %X!\n", card->devname, event->type); return; } @@ -2792,7 +2855,7 @@ static void wp_tdmv_callback_tone (void* card_id, wan_event_t *event) (SYSTEM_TICKS - wp->ec_fax_detect_timeout[fechan]) >= card->tdmv_conf.hw_fax_detect*HZ) { #ifdef WAN_DEBUG_TDMAPI if (WAN_NET_RATELIMIT()) { - DEBUG_EVENT("%s: Warning: Ignoring Fax detect during call (s%dc%d) - Call Time: %ld Max: %d!\n", + DEBUG_WARNING("%s: Warning: Ignoring Fax detect during call (s%dc%d) - Call Time: %ld Max: %d!\n", card->devname, wp->spanno+1, event->channel, diff --git a/patches/kdrivers/src/net/sdla_te1.c b/patches/kdrivers/src/net/sdla_te1.c index a91f1a0..6cc38f0 100644 --- a/patches/kdrivers/src/net/sdla_te1.c +++ b/patches/kdrivers/src/net/sdla_te1.c @@ -4149,7 +4149,7 @@ static int sdla_t1_cfg_verify(void* pfe) WAN_FE_FRAME(fe) = WAN_FR_ESF; break; default: - DEBUG_EVENT("%s: Error: Invalid %s FE Framing type (%X)\n", + DEBUG_ERROR("%s: Error: Invalid %s FE Framing type (%X)\n", fe->name, FE_MEDIA_DECODE(fe), WAN_FE_FRAME(fe)); @@ -4167,7 +4167,7 @@ static int sdla_t1_cfg_verify(void* pfe) WAN_FE_LCODE(fe) = WAN_LCODE_B8ZS; break; default: - DEBUG_EVENT("%s: Error: Invalid %s FE Line code type (%X)\n", + DEBUG_ERROR("%s: Error: Invalid %s FE Line code type (%X)\n", fe->name, FE_MEDIA_DECODE(fe), WAN_FE_LCODE(fe)); @@ -4191,7 +4191,7 @@ static int sdla_t1_cfg_verify(void* pfe) WAN_TE1_LBO(fe) = WAN_T1_LBO_0_DB; break; default: - DEBUG_EVENT("%s: Error: Invalid %s LBO value (%X)\n", + DEBUG_ERROR("%s: Error: Invalid %s LBO value (%X)\n", fe->name, FE_MEDIA_DECODE(fe), WAN_TE1_LBO(fe)); @@ -4222,7 +4222,7 @@ static int sdla_e1_cfg_verify(void* pfe) WAN_FE_FRAME(fe) = WAN_FR_CRC4; break; default: - DEBUG_EVENT("%s: Error: Invalid %s FE Framing type (%X)\n", + DEBUG_ERROR("%s: Error: Invalid %s FE Framing type (%X)\n", fe->name, FE_MEDIA_DECODE(fe), WAN_FE_FRAME(fe)); @@ -4239,7 +4239,7 @@ static int sdla_e1_cfg_verify(void* pfe) WAN_FE_LCODE(fe) = WAN_LCODE_HDB3; break; default: - DEBUG_EVENT("%s: Error: Invalid %s FE Line code type (%X)\n", + DEBUG_ERROR("%s: Error: Invalid %s FE Line code type (%X)\n", fe->name, FE_MEDIA_DECODE(fe), WAN_FE_LCODE(fe)); @@ -4257,7 +4257,7 @@ static int sdla_e1_cfg_verify(void* pfe) WAN_TE1_LBO(fe) = WAN_E1_120; break; default: - DEBUG_EVENT("%s: Error: Invalid %s LBO value (%X)\n", + DEBUG_ERROR("%s: Error: Invalid %s LBO value (%X)\n", fe->name, FE_MEDIA_DECODE(fe), WAN_TE1_LBO(fe)); @@ -4274,7 +4274,7 @@ static int sdla_e1_cfg_verify(void* pfe) WAN_TE1_SIG_MODE(fe) = WAN_TE1_SIG_CCS; break; default: - DEBUG_EVENT("%s: Error: Invalid E1 Signalling type (%X)\n", + DEBUG_ERROR("%s: Error: Invalid E1 Signalling type (%X)\n", fe->name, WAN_TE1_SIG_MODE(fe)); return -EINVAL; @@ -4314,7 +4314,7 @@ static int sdla_te_chip_config(sdla_fe_t *fe) break; default: - DEBUG_EVENT("%s: ERROR: Unsupported T1/E1 CHIP (0x%02X)\n", + DEBUG_ERROR("%s: ERROR: Unsupported T1/E1 CHIP (0x%02X)\n", fe->name, (fe->fe_chip_id >> 5)); return -EINVAL; } @@ -4386,7 +4386,7 @@ static int sdla_te_config(void* pfe) } break; default: - DEBUG_EVENT("%s: ERROR: Unsupported PMC %s CHIP (%02X)\n", + DEBUG_ERROR("%s: ERROR: Unsupported PMC %s CHIP (%02X)\n", fe->name, FE_MEDIA_DECODE(fe), (fe->fe_chip_id >> 5)); @@ -4398,7 +4398,7 @@ static int sdla_te_config(void* pfe) }else if (IS_E1_FEMEDIA(fe)){ err = sdla_e1_cfg_verify(fe); }else{ - DEBUG_EVENT("%s: Error: Invalid FE Media type (%X)\n", + DEBUG_ERROR("%s: Error: Invalid FE Media type (%X)\n", fe->name, WAN_FE_MEDIA(fe)); err =-EINVAL; @@ -6412,7 +6412,7 @@ sdla_te_add_event(sdla_fe_t *fe, sdla_fe_timer_event_t *fe_event) if (!WAN_LIST_NEXT(tmp, next)) break; } if (tmp == NULL){ - DEBUG_EVENT("%s: Internal Error!!!\n", fe->name); + DEBUG_ERROR("%s: Internal Error!!!\n", fe->name); wan_spin_unlock_irq(&fe->lockirq,&smp_flags); return -EINVAL; } @@ -6640,7 +6640,7 @@ static int sdla_te_tx_lb(sdla_fe_t* fe, u_int8_t mode, u_int8_t cmd) return -EINVAL; } if (WAN_FE_FRAME(fe) != WAN_FR_ESF){ - DEBUG_EVENT("%s: ERROR: TX loopback code is available only for T1 ESF mode!\n", + DEBUG_ERROR("%s: ERROR: TX loopback code is available only for T1 ESF mode!\n", fe->name); return -EINVAL; } diff --git a/patches/kdrivers/src/net/sdla_te3.c b/patches/kdrivers/src/net/sdla_te3.c index 443089d..f09724a 100644 --- a/patches/kdrivers/src/net/sdla_te3.c +++ b/patches/kdrivers/src/net/sdla_te3.c @@ -1345,18 +1345,30 @@ static int sdla_te3_add_timer(sdla_fe_t* fe, unsigned long delay) { int err=0; - if (wan_test_bit(TE_TIMER_KILL,(void*)&fe->te_param.critical) || - wan_test_bit(TE_TIMER_RUNNING,(void*)&fe->te_param.critical)) { + if (wan_test_bit(TE_TIMER_KILL,(void*)&fe->te3_param.critical)) { + DEBUG_EVENT("WARNING: %s: %s() Timer has been killed!\n", + fe->name,__FUNCTION__); return 0; } + + if (wan_test_bit(TE_TIMER_RUNNING,(void*)&fe->te3_param.critical)) { + DEBUG_ERROR("Error: %s: %s() Timer already running!\n", + fe->name,__FUNCTION__); + return 0; + } + + wan_set_bit(TE_TIMER_RUNNING,(void*)&fe->te3_param.critical); + err = wan_add_timer(&fe->timer, delay * HZ / 1000); if (err){ + wan_clear_bit(TE_TIMER_RUNNING,(void*)&fe->te3_param.critical); + DEBUG_ERROR("Error: %s: %s() Failed to add Timer err=%i!\n", + fe->name,__FUNCTION__,err); /* Failed to add timer */ return -EINVAL; } - wan_set_bit(TE_TIMER_RUNNING,(void*)&fe->te_param.critical); return 0; } @@ -1375,25 +1387,25 @@ static void sdla_te3_timer(unsigned long pfe) wan_device_t *wandev = &card->wandev; DEBUG_TEST("[TE3] %s: TE3 timer!\n", fe->name); - if (wan_test_bit(TE_TIMER_KILL,(void*)&fe->te_param.critical)){ - wan_clear_bit(TE_TIMER_RUNNING,(void*)&fe->te_param.critical); + if (wan_test_bit(TE_TIMER_KILL,(void*)&fe->te3_param.critical)){ + wan_clear_bit(TE_TIMER_RUNNING,(void*)&fe->te3_param.critical); + DEBUG_EVENT("WARNING: %s: Timer has been killed!\n", + fe->name); return; } - if (!wan_test_bit(TE_TIMER_RUNNING,(void*)&fe->te_param.critical)){ + if (!wan_test_bit(TE_TIMER_RUNNING,(void*)&fe->te3_param.critical)){ /* Somebody clear this bit */ DEBUG_EVENT("WARNING: %s: Timer bit is cleared (should never happened)!\n", fe->name); return; } - wan_clear_bit(TE_TIMER_RUNNING,(void*)&fe->te_param.critical); + wan_clear_bit(TE_TIMER_RUNNING,(void*)&fe->te3_param.critical); if (wandev->fe_enable_timer){ wandev->fe_enable_timer(fe->card); } - sdla_te3_add_timer(fe, 1000); - return; } @@ -1405,7 +1417,7 @@ static int sdla_te3_post_init(void* pfe) /* Initialize and start T1/E1 timer */ - wan_set_bit(TE_TIMER_KILL,(void*)&fe->te_param.critical); + wan_set_bit(TE_TIMER_KILL,(void*)&fe->te3_param.critical); wan_init_timer( &fe->timer, @@ -1413,7 +1425,7 @@ static int sdla_te3_post_init(void* pfe) (wan_timer_arg_t)fe); /* Initialize T1/E1 timer */ - wan_clear_bit(TE_TIMER_KILL,(void*)&fe->te_param.critical); + wan_clear_bit(TE_TIMER_KILL,(void*)&fe->te3_param.critical); /* Start T1/E1 timer */ if (IS_E3(&fe->fe_cfg)){ @@ -1430,11 +1442,11 @@ static int sdla_te3_pre_release(void* pfe) /* Kill TE timer poll command */ - wan_set_bit(TE_TIMER_KILL,(void*)&fe->te_param.critical); - if (wan_test_bit(TE_TIMER_RUNNING,(void*)&fe->te_param.critical)){ + wan_set_bit(TE_TIMER_KILL,(void*)&fe->te3_param.critical); + if (wan_test_bit(TE_TIMER_RUNNING,(void*)&fe->te3_param.critical)){ wan_del_timer(&fe->timer); } - wan_clear_bit(TE_TIMER_RUNNING,(void*)&fe->te_param.critical); + wan_clear_bit(TE_TIMER_RUNNING,(void*)&fe->te3_param.critical); return 0; } diff --git a/patches/kdrivers/src/net/sdla_usb_remora.c b/patches/kdrivers/src/net/sdla_usb_remora.c index aad2713..8c8d57f 100644 --- a/patches/kdrivers/src/net/sdla_usb_remora.c +++ b/patches/kdrivers/src/net/sdla_usb_remora.c @@ -305,7 +305,7 @@ static struct fxo_mode { { "CHINA", 0, 0, 0, 0, 0, 0, 0x3, 0xf, }, { "COLUMBIA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, { "CROATIA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, - { "CYRPUS", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "CYPRUS", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, { "CZECH", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, { "DENMARK", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, { "ECUADOR", 0, 0, 0, 0, 0, 0x3, 0, 0, }, @@ -1531,7 +1531,7 @@ retry_cfg: "ERROR: %s: Configuration is failed for all FXO/FXS modules!\n", fe->name); }else{ - DEBUG_EVENT("ERROR: %s: No FXO/FXS modules are found!\n", + DEBUG_ERROR("ERROR: %s: No FXO/FXS modules are found!\n", fe->name); } return -EINVAL; @@ -2191,7 +2191,7 @@ static int wp_usb_remora_polling(sdla_fe_t* fe) } break; default: - DEBUG_EVENT("ERROR: %s: Invalid poll event type %X!\n", + DEBUG_ERROR("ERROR: %s: Invalid poll event type %X!\n", fe->name, fe_event->type); break; } @@ -2289,7 +2289,7 @@ wp_usb_remora_add_event(sdla_fe_t *fe, sdla_fe_timer_event_t *fe_event) // if (!WAN_LIST_NEXT(tmp, next)) break; // } // if (tmp == NULL){ -// DEBUG_EVENT("%s: Internal Error!!!\n", fe->name); +// DEBUG_ERROR("%s: Internal Error!!!\n", fe->name); // wan_spin_unlock_irq(&fe->lockirq, &smp_flags); // return -EINVAL; // } diff --git a/patches/kdrivers/src/net/sdla_usb_remora_tdmv.c b/patches/kdrivers/src/net/sdla_usb_remora_tdmv.c index caa59a1..1e5176f 100644 --- a/patches/kdrivers/src/net/sdla_usb_remora_tdmv.c +++ b/patches/kdrivers/src/net/sdla_usb_remora_tdmv.c @@ -34,6 +34,9 @@ # include "zapcompat.h" /* Map of Zaptel -> DAHDI definitions */ #endif + +#if defined(CONFIG_PRODUCT_WANPIPE_USB) + /******************************************************************************* ** DEFINES AND MACROS *******************************************************************************/ @@ -1543,7 +1546,7 @@ static int wp_usb_tdmv_remora_remove(void* pcard) return -EINVAL; } if (wr && wr->usecount){ - DEBUG_EVENT("%s: ERROR: Wanpipe is still used by Asterisk!\n", + DEBUG_ERROR("%s: ERROR: Wanpipe is still used by Asterisk!\n", card->devname); return -EINVAL; } @@ -1924,3 +1927,5 @@ static void wp_usb_tdmv_remora_tone (void* card_id, wan_event_t *event) } return; } + +#endif /* #if defined(CONFIG_PRODUCT_WANPIPE_USB) */ diff --git a/patches/kdrivers/src/net/sdla_x25.c b/patches/kdrivers/src/net/sdla_x25.c index 50fed93..a09504f 100644 --- a/patches/kdrivers/src/net/sdla_x25.c +++ b/patches/kdrivers/src/net/sdla_x25.c @@ -163,6 +163,8 @@ #define MAX_X25_TRACE_QUEUE 100 +WAN_DECLARE_NETDEV_OPS(wan_netdev_ops) + /* Private critical flags */ enum { POLL_CRIT = PRIV_CRIT @@ -1219,7 +1221,14 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) chan->network_number = 0xDEADBEEF; /* prepare network device data space for registration */ - dev->init = &if_init; + WAN_NETDEV_OPS_BIND(dev,wan_netdev_ops); + WAN_NETDEV_OPS_INIT(dev,wan_netdev_ops,&if_init); + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&if_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&if_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&if_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&if_stats); + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,&x25_ioctl); + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&if_tx_timeout); init_x25_channel_struct(chan); @@ -1336,15 +1345,15 @@ static int if_init (netdevice_t* dev) wan_device_t* wandev = &card->wandev; /* Initialize device driver entry points */ - dev->open = &if_open; - dev->stop = &if_close; - dev->hard_start_xmit = &if_send; - dev->get_stats = &if_stats; - dev->do_ioctl = &x25_ioctl; + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&if_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&if_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&if_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&if_stats); + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,&x25_ioctl); #if defined(LINUX_2_4)||defined(LINUX_2_6) if (chan->common.usedby != API){ - dev->tx_timeout = &if_tx_timeout; + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&if_tx_timeout); dev->watchdog_timeo = TX_TIMEOUT; } #endif @@ -6234,7 +6243,6 @@ static int process_udp_mgmt_pkt(sdla_t *card,netdevice_t *local_dev) switch (wan_udp_pkt->wan_udp_command) { - case XPIPE_FLUSH_DRIVER_STATS: init_x25_channel_struct(chan); init_global_statistics(card); diff --git a/patches/kdrivers/src/net/sdla_xilinx.c b/patches/kdrivers/src/net/sdla_xilinx.c index fbabb48..c337403 100644 --- a/patches/kdrivers/src/net/sdla_xilinx.c +++ b/patches/kdrivers/src/net/sdla_xilinx.c @@ -25,7 +25,38 @@ #include "wanpipe_tdm_api.h" #include "wanpipe_api_hdr.h" #include "sdla_xilinx.h" + +#ifdef __WINDOWS__ +# define PCI_DMA_FROMDEVICE 0 +# define PCI_DMA_TODEVICE 1 +extern +int +set_netdev_state( + sdla_t* card, + netdevice_t* sdla_net_dev, + int state + ); + +extern +void +wan_get_random_mac_address( + OUT unsigned char *mac_address + ); + +extern +int wp_get_motherboard_enclosure_serial_number( + OUT char *buf, + IN int buf_length + ); + +extern +int +sdla_restore_pci_config_space( + IN void *level1_dev_pdx + ) ; + +#endif /* #define XILINX_A010 1 */ @@ -71,16 +102,18 @@ enum { #endif #if defined(__LINUX__) -#define AFT_TDM_API_SUPPORT 1 +# define AFT_TDM_API_SUPPORT 1 +#elif defined(__WINDOWS__) +# define AFT_TDM_API_SUPPORT 1 #else -#undef AFT_TDM_API_SUPPORT +# undef AFT_TDM_API_SUPPORT #endif #define AFT_MAX_CHIP_SECURITY_CNT 100 #define AFT_MIN_FRMW_VER 24 - +WAN_DECLARE_NETDEV_OPS(wan_netdev_ops) static int aft_rx_copyback=1000; @@ -88,13 +121,13 @@ static int aft_rx_copyback=1000; /******Data Structures*****************************************************/ /* This structure is placed in the private data area of the device structure. - * The card structure used to occupy the private area but now the following - * structure will incorporate the card structure along with Protocol specific data - */ +* The card structure used to occupy the private area but now the following +* structure will incorporate the card structure along with Protocol specific data +*/ typedef struct private_area { - wanpipe_common_t common; + wanpipe_common_t common;/* MUST be at the top */ wan_skb_queue_t wp_tx_free_list; wan_skb_queue_t wp_tx_pending_list; @@ -131,8 +164,8 @@ typedef struct private_area unsigned char interface_down; /* Polling task queue. Each interface - * has its own task queue, which is used - * to defer events from the interrupt */ + * has its own task queue, which is used + * to defer events from the interrupt */ wan_taskq_t poll_task; wan_timer_info_t poll_delay_timer; @@ -199,6 +232,8 @@ typedef struct private_area unsigned int max_tx_bufs; struct private_area *next; + + wan_skb_queue_t wp_dealloc_list; }private_area_t; /* Route Status options */ @@ -211,7 +246,12 @@ typedef struct private_area #define WP_NO_WAIT 1 /* Function interface between WANPIPE layer and kernel */ +#ifdef __WINDOWS__ +wan_iface_t wan_iface; +extern int aft_core_tdmapi_event_init(private_area_t *chan); +#else extern wan_iface_t wan_iface; +#endif /* variable for keeping track of enabling/disabling FT1 monitor status */ /* static int rCount; */ @@ -220,10 +260,10 @@ extern void disable_irq(unsigned int); extern void enable_irq(unsigned int); /**SECTOIN************************************************** - * - * Function Prototypes - * - ***********************************************************/ +* +* Function Prototypes +* +***********************************************************/ int wp_xilinx_default_devcfg(sdla_t* card, wandev_conf_t* conf); int wp_xilinx_default_ifcfg(sdla_t* card, wanif_conf_t* conf); @@ -239,12 +279,12 @@ static int wanpipe_xilinx_open (netdevice_t* dev); static int wanpipe_xilinx_close (netdevice_t* dev); static int wanpipe_xilinx_ioctl(netdevice_t*, struct ifreq*, wan_ioctl_cmd_t); -#if defined(__LINUX__) +#if defined(__LINUX__) || defined(__WINDOWS__) static int wanpipe_xilinx_send (netskb_t* skb, netdevice_t* dev); static struct net_device_stats* wanpipe_xilinx_ifstats (netdevice_t* dev); #else static int wan_aft_output(netdevice_t*, netskb_t*, struct sockaddr*, - struct rtentry*); +struct rtentry*); #endif static void handle_front_end_state(void* card_id); @@ -262,14 +302,16 @@ static WAN_IRQ_RETVAL wp_xilinx_isr (sdla_t* card); /* Bottom half handlers */ #if defined(__LINUX__) static void wp_bh (unsigned long); +#elif defined(__WINDOWS__) +static void wp_bh (IN PKDPC Dpc, IN PVOID data, IN PVOID SystemArgument1, IN PVOID SystemArgument2); #else static void wp_bh(void *data, int pending); #endif /* Miscellaneous functions */ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, - private_area_t*, - int local_dev); + private_area_t*, + int local_dev); static int xilinx_chip_configure(sdla_t *card); static int xilinx_chip_unconfigure(sdla_t *card); @@ -283,26 +325,26 @@ static void xilinx_dma_tx_complete (sdla_t *card, private_area_t *chan); static void xilinx_dma_rx_complete (sdla_t *card, private_area_t *chan); static void xilinx_dma_max_logic_ch(sdla_t *card); static int xilinx_init_rx_dev_fifo(sdla_t *card, private_area_t *chan, - unsigned char); + unsigned char); static void xilinx_init_tx_dma_descr(sdla_t *card, private_area_t *chan); static int xilinx_init_tx_dev_fifo(sdla_t *card, - private_area_t *chan, unsigned char); + private_area_t *chan, unsigned char); static void xilinx_tx_post_complete (sdla_t *card, - private_area_t *chan, netskb_t *skb); + private_area_t *chan, netskb_t *skb); static void xilinx_rx_post_complete (sdla_t *card, private_area_t *chan, - netskb_t *skb, - netskb_t **new_skb, - unsigned char *pkt_error); + netskb_t *skb, + netskb_t **new_skb, + unsigned char *pkt_error); static char request_xilinx_logical_channel_num (sdla_t *card, - private_area_t *chan, long *free_ch); + private_area_t *chan, long *free_ch); static void free_xilinx_logical_channel_num (sdla_t *card, int logic_ch); static unsigned char read_cpld(sdla_t *card, unsigned short cpld_off); static unsigned char write_cpld(sdla_t *card, unsigned short cpld_off, - unsigned char cpld_data); + unsigned char cpld_data); static int aft_devel_ioctl(sdla_t *card,struct ifreq *ifr); static int xilinx_write_bios(sdla_t *card,wan_cmd_api_t *api_cmd); @@ -324,8 +366,8 @@ static int set_chan_state(sdla_t* card, netdevice_t* dev, int state); static int fifo_error_interrupt(sdla_t *card, u32 reg, u32 tx_err, u32 rx_err); static int request_fifo_baddr_and_size(sdla_t *card, private_area_t *chan); static int map_fifo_baddr_and_size(sdla_t *card, - unsigned char fifo_size, - unsigned char *addr); + unsigned char fifo_size, + unsigned char *addr); static int free_fifo_baddr_and_size (sdla_t *card, private_area_t *chan); static int update_comms_stats(sdla_t* card); @@ -333,8 +375,8 @@ static int update_comms_stats(sdla_t* card); static void aft_red_led_ctrl(sdla_t *card, int mode); static int protocol_init (sdla_t*card,netdevice_t *dev, - private_area_t *chan, - wanif_conf_t* conf); + private_area_t *chan, + wanif_conf_t* conf); static int protocol_stop (sdla_t *card, netdevice_t *dev); static int protocol_start (sdla_t *card, netdevice_t *dev); @@ -357,12 +399,14 @@ static int aft_realign_skb_pkt(private_area_t *chan, netskb_t *skb); #if defined(__LINUX__) # if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) -static void aft_port_task (void * card_ptr); +static void xilinx_aft_port_task (void * card_ptr); # else -static void aft_port_task (struct work_struct *work); +static void xilinx_aft_port_task (struct work_struct *work); # endif +#elif defined(__WINDOWS__) +KDEFERRED_ROUTINE xilinx_aft_port_task; #else -static void aft_port_task (void * card_ptr, int arg); +static void xilinx_aft_port_task (void * card_ptr, int arg); #endif static void aft_fe_intr_ctrl(sdla_t *card, int status); @@ -404,10 +448,10 @@ static void xilinx_delay(int sec) } /**SECTION********************************************************* - * - * Public Functions - * - ******************************************************************/ +* +* Public Functions +* +******************************************************************/ int wp_xilinx_default_devcfg(sdla_t* card, wandev_conf_t* conf) { @@ -433,24 +477,24 @@ int wp_xilinx_default_ifcfg(sdla_t* card, wanif_conf_t* conf) /*============================================================================ - * wp_xilinx_init - Cisco HDLC protocol initialization routine. - * - * @card: Wanpipe card pointer - * @conf: User hardware/firmware/general protocol configuration - * pointer. - * - * This routine is called by the main WANPIPE module - * during setup: ROUTER_SETUP ioctl(). - * - * At this point adapter is completely initialized - * and firmware is running. - * o read firmware version (to make sure it's alive) - * o configure adapter - * o initialize protocol-specific fields of the adapter data space. - * - * Return: 0 o.k. - * < 0 failure. - */ +* wp_xilinx_init - Cisco HDLC protocol initialization routine. +* +* @card: Wanpipe card pointer +* @conf: User hardware/firmware/general protocol configuration +* pointer. +* +* This routine is called by the main WANPIPE module +* during setup: ROUTER_SETUP ioctl(). +* +* At this point adapter is completely initialized +* and firmware is running. +* o read firmware version (to make sure it's alive) +* o configure adapter +* o initialize protocol-specific fields of the adapter data space. +* +* Return: 0 o.k. +* < 0 failure. +*/ int wp_xilinx_init (sdla_t* card, wandev_conf_t* conf) { @@ -462,13 +506,13 @@ int wp_xilinx_init (sdla_t* card, wandev_conf_t* conf) wan_clear_bit(CARD_DOWN,&card->wandev.critical); if (card->wandev.config_id != WANCONFIG_AFT) { DEBUG_EVENT( "%s: invalid configuration ID %u!\n", - card->devname, card->wandev.config_id); + card->devname, card->wandev.config_id); return -EINVAL; } if (conf == NULL){ DEBUG_EVENT("%s: Bad configuration structre!\n", - card->devname); + card->devname); return -EINVAL; } @@ -487,11 +531,11 @@ int wp_xilinx_init (sdla_t* card, wandev_conf_t* conf) card->hw_iface.getcfg(card->hw, SDLA_COREREV, &card->u.aft.firm_ver); if (card->u.aft.firm_ver < AFT_MIN_FRMW_VER){ DEBUG_EVENT( "%s: Invalid/Obselete AFT A101/2 firmware ver %i (not >= %d)!\n", - card->devname, card->u.aft.firm_ver,AFT_MIN_FRMW_VER); + card->devname, card->u.aft.firm_ver,AFT_MIN_FRMW_VER); DEBUG_EVENT( "%s Refer to /usr/share/doc/wanpipe/README.aft_firm_update\n", - card->devname); + card->devname); DEBUG_EVENT( "%s: Please contact Sangoma Technologies for more info.\n", - card->devname); + card->devname); return -EINVAL; } @@ -502,11 +546,11 @@ int wp_xilinx_init (sdla_t* card, wandev_conf_t* conf) if (conf->u.aft.dma_per_ch){ card->u.aft.cfg.dma_per_ch=conf->u.aft.dma_per_ch; if (card->u.aft.cfg.dma_per_ch > MAX_DMA_PER_CH || - card->u.aft.cfg.dma_per_ch < MIN_DMA_PER_CH){ - DEBUG_EVENT("%s: Error invalid DMA Per Ch %d (Min=%d Max=%d)\n", + card->u.aft.cfg.dma_per_ch < MIN_DMA_PER_CH){ + DEBUG_EVENT("%s: Error invalid DMA Per Ch %d (Min=%d Max=%d)\n", card->devname,card->u.aft.cfg.dma_per_ch, MIN_DMA_PER_CH,MAX_DMA_PER_CH); - return -EINVAL; + return -EINVAL; } } @@ -530,7 +574,7 @@ int wp_xilinx_init (sdla_t* card, wandev_conf_t* conf) if (card->fe.fe_cfg.cfg.te_cfg.te_clock == WAN_NORMAL_CLK){ /* If using normal clocking disable - * reference clock configuration */ + * reference clock configuration */ card->fe.fe_cfg.cfg.te_cfg.te_ref_clock = WAN_TE1_REFCLK_OSC; } @@ -543,11 +587,11 @@ int wp_xilinx_init (sdla_t* card, wandev_conf_t* conf) }else{ DEBUG_EVENT("%s: Error: Unknown Front end cfg 0x%X (T1/E1)\n", - card->devname,conf->fe_cfg.media); + card->devname,conf->fe_cfg.media); return -EINVAL; } - card->u.aft.tdmv_dchan = 0; + card->u.aft.tdmv_dchan = 0; if (IS_E1_CARD(card)) { card->tdmv_conf.dchan = card->tdmv_conf.dchan << 1; wan_clear_bit(0,&card->tdmv_conf.dchan); @@ -555,24 +599,24 @@ int wp_xilinx_init (sdla_t* card, wandev_conf_t* conf) if (card->wandev.ignore_front_end_status == WANOPT_NO){ DEBUG_EVENT( - "%s: Enabling front end link monitor\n", - card->devname); + "%s: Enabling front end link monitor\n", + card->devname); }else{ DEBUG_EVENT( - "%s: Disabling front end link monitor\n", - card->devname); + "%s: Disabling front end link monitor\n", + card->devname); } /* WARNING: After this point the init function - * must return with 0. The following bind - * functions will cause problems if structures - * below are not initialized */ + * must return with 0. The following bind + * functions will cause problems if structures + * below are not initialized */ - card->wandev.update = &update; - card->wandev.new_if = &new_if; - card->wandev.del_if = &del_if; + card->wandev.update = &update; + card->wandev.new_if = &new_if; + card->wandev.del_if = &del_if; - card->disable_comm = NULL; + card->disable_comm = NULL; #ifdef WANPIPE_ENABLE_PROC_FILE_HOOKS /* Proc fs functions hooks */ @@ -589,22 +633,22 @@ int wp_xilinx_init (sdla_t* card, wandev_conf_t* conf) if(card->wandev.clocking) { card->wandev.bps = conf->bps; }else{ - card->wandev.bps = 0; - } + card->wandev.bps = 0; + } #ifdef CONFIG_PRODUCT_WANPIPE_TDM_VOICE - card->wandev.mtu = conf->mtu; + card->wandev.mtu = conf->mtu; card->wan_tdmv.sc = NULL; #else card->wandev.mtu=conf->mtu; if (card->wandev.mtu > MAX_WP_PRI_MTU || - card->wandev.mtu < MIN_WP_PRI_MTU){ - DEBUG_EVENT("%s: Error Invalid Global MTU %d (Min=%d, Max=%d)\n", + card->wandev.mtu < MIN_WP_PRI_MTU){ + DEBUG_EVENT("%s: Error Invalid Global MTU %d (Min=%d, Max=%d)\n", card->devname,card->wandev.mtu, MIN_WP_PRI_MTU,MAX_WP_PRI_MTU); - return -EINVAL; + return -EINVAL; } #endif @@ -614,12 +658,12 @@ int wp_xilinx_init (sdla_t* card, wandev_conf_t* conf) } if (card->u.aft.cfg.mru > MAX_WP_PRI_MTU || - card->u.aft.cfg.mru < MIN_WP_PRI_MTU){ - DEBUG_EVENT("%s: Error Invalid Global MRU %d (Min=%d, Max=%d)\n", + card->u.aft.cfg.mru < MIN_WP_PRI_MTU){ + DEBUG_EVENT("%s: Error Invalid Global MRU %d (Min=%d, Max=%d)\n", card->devname,card->u.aft.cfg.mru, MIN_WP_PRI_MTU,MAX_WP_PRI_MTU); - return -EINVAL; + return -EINVAL; } write_cpld(card,LED_CONTROL_REG,0x0E); @@ -627,16 +671,16 @@ int wp_xilinx_init (sdla_t* card, wandev_conf_t* conf) card->hw_iface.getcfg(card->hw, SDLA_BASEADDR, &card->u.aft.bar); - WAN_TASKQ_INIT((&card->u.aft.port_task),0,aft_port_task,card); + WAN_TASKQ_INIT((&card->u.aft.port_task),0,xilinx_aft_port_task,card); /* Set protocol link state to disconnected, - * After seting the state to DISCONNECTED this - * function must return 0 i.e. success */ + * After seting the state to DISCONNECTED this + * function must return 0 i.e. success */ port_set_state(card,WAN_CONNECTING); xilinx_delay(1); #if !defined(CONFIG_PRODUCT_WANPIPE_GENERIC) - card->isr = &wp_xilinx_isr; + card->isr = &wp_xilinx_isr; err=xilinx_chip_configure(card); if (err){ xilinx_chip_unconfigure(card); @@ -651,7 +695,7 @@ int wp_xilinx_init (sdla_t* card, wandev_conf_t* conf) if (wan_test_bit(AFT_FRONT_END_UP,&card->u.aft.chip_cfg_status)){ wan_smp_flag_t smp_flags; DEBUG_TEST("%s: Front end up, retrying enable front end!\n", - card->devname); + card->devname); wan_spin_lock_irq(&card->wandev.lock,&smp_flags); handle_front_end_state(card); wan_spin_unlock_irq(&card->wandev.lock,&smp_flags); @@ -660,30 +704,30 @@ int wp_xilinx_init (sdla_t* card, wandev_conf_t* conf) } DEBUG_EVENT("%s: Configuring Device :%s FrmVr=%02X\n", - card->devname,card->devname,card->u.aft.firm_ver); + card->devname,card->devname,card->u.aft.firm_ver); DEBUG_EVENT("%s: Global MTU = %d\n", - card->devname, - card->wandev.mtu); + card->devname, + card->wandev.mtu); DEBUG_EVENT("%s: Global MRU = %d\n", - card->devname, - card->u.aft.cfg.mru); + card->devname, + card->u.aft.cfg.mru); DEBUG_EVENT("%s: RBS Signal = %s\n", - card->devname, - card->u.aft.cfg.rbs?"On":"Off"); + card->devname, + card->u.aft.cfg.rbs?"On":"Off"); DEBUG_EVENT("%s: FE Ref Clock = %s\n", - card->devname, - WAN_TE1_REFCLK(&card->fe) == WAN_TE1_REFCLK_OSC?"Osc":"Line"); + card->devname, + WAN_TE1_REFCLK(&card->fe) == WAN_TE1_REFCLK_OSC?"Osc":"Line"); DEBUG_EVENT("\n"); - err=aft_tdmv_init(card,conf); + err=aft_tdmv_init(card,conf); if (err){ disable_comm(card); return err; } - card->disable_comm = &disable_comm; + card->disable_comm = &disable_comm; return 0; } @@ -692,40 +736,40 @@ int wp_xilinx_init (sdla_t* card, wandev_conf_t* conf) /**SECTION************************************************************** - * - * WANPIPE Device Driver Entry Points - * - * *********************************************************************/ +* +* WANPIPE Device Driver Entry Points +* +* *********************************************************************/ /*============================================================================ - * update - Update wanpipe device status & statistics - * - * @wandev: Wanpipe device pointer - * - * This procedure is called when updating the PROC file system. - * It returns various communications statistics. - * - * cat /proc/net/wanrouter/wanpipe# (where #=1,2,3...) - * - * These statistics are accumulated from 3 - * different locations: - * 1) The 'if_stats' recorded for the device. - * 2) Communication error statistics on the adapter. - * 3) Operational statistics on the adapter. - * - * The board level statistics are read during a timer interrupt. - * Note that we read the error and operational statistics - * during consecitive timer ticks so as to minimize the time - * that we are inside the interrupt handler. - * - */ +* update - Update wanpipe device status & statistics +* +* @wandev: Wanpipe device pointer +* +* This procedure is called when updating the PROC file system. +* It returns various communications statistics. +* +* cat /proc/net/wanrouter/wanpipe# (where #=1,2,3...) +* +* These statistics are accumulated from 3 +* different locations: +* 1) The 'if_stats' recorded for the device. +* 2) Communication error statistics on the adapter. +* 3) Operational statistics on the adapter. +* +* The board level statistics are read during a timer interrupt. +* Note that we read the error and operational statistics +* during consecitive timer ticks so as to minimize the time +* that we are inside the interrupt handler. +* +*/ static int update (wan_device_t* wandev) { sdla_t *card = wandev->priv; - netdevice_t *dev; - volatile private_area_t *chan; + netdevice_t *dev; + volatile private_area_t *chan; wan_smp_flag_t smp_flags; /* sanity checks */ @@ -736,7 +780,7 @@ static int update (wan_device_t* wandev) return -ENODEV; if(wan_test_bit(PERI_CRIT, (void*)&card->wandev.critical)) - return -EAGAIN; + return -EAGAIN; dev = WAN_DEVLE2DEV(WAN_LIST_FIRST(&card->wandev.dev_head)); if (!dev || !wan_netif_priv(dev)) @@ -744,21 +788,21 @@ static int update (wan_device_t* wandev) chan=wan_netif_priv(dev); - if(card->update_comms_stats){ + if(card->update_comms_stats){ return -EAGAIN; } #if 0 { - private_area_t* chan; + private_area_t* chan; DEBUG_EVENT("%s: Starting up Interfaces\n",card->devname); for (dev=card->wandev.dev;dev;dev=wan_next_dev(dev)){ chan=dev->priv; if (WAN_NETIF_QUEUE_STOPPED(dev)){ DEBUG_EVENT("%s: Waking up device! Q=%d\n", - wan_netif_name(dev), - wan_skb_queue_len(&chan->wp_tx_pending_list)); - WAN_NETIF_START_QUEUE(dev); /*start_net_queue(dev);*/ + wan_netif_name(dev), + wan_skb_queue_len(&chan->wp_tx_pending_list)); + WAN_NETIF_START_QUEUE(dev); /*start_net_queue(dev);*/ } } } @@ -772,27 +816,224 @@ static int update (wan_device_t* wandev) return 0; } -#if defined(__LINUX__) +/* can not include aft_core_private.h because private_area_t is defined there */ +static __inline void wan_aft_skb_defered_dealloc(private_area_t *chan, netskb_t *skb) +{ + wan_skb_queue_tail(&chan->wp_dealloc_list,skb); + WAN_TASKLET_SCHEDULE((&chan->common.bh_task)); +} + +#if defined(__LINUX__) || defined(__WINDOWS__) + +int xilinx_wan_user_process_udp_mgmt_pkt(void* card_ptr, void* chan_ptr, void *udata) +{ + sdla_t *card = (sdla_t *)card_ptr; + private_area_t *chan = (private_area_t*)chan_ptr; + wan_udp_pkt_t *wan_udp_pkt; + + DEBUG_UDP("%s(): line: %d\n", __FUNCTION__, __LINE__); + + if (wan_atomic_read(&chan->udp_pkt_len) != 0){ + return -EBUSY; + } + + wan_atomic_set(&chan->udp_pkt_len, MAX_LGTH_UDP_MGNT_PKT); + + wan_udp_pkt=(wan_udp_pkt_t*)chan->udp_pkt_data; + +#if defined (__WINDOWS__) + /* udata IS a pointer to wan_udp_hdr_t. copy data from user's buffer */ + wpabs_memcpy(&wan_udp_pkt->wan_udp_hdr, udata, sizeof(wan_udp_hdr_t)); +#else + if (WAN_COPY_FROM_USER( + &wan_udp_pkt->wan_udp_hdr, + udata, + sizeof(wan_udp_hdr_t))){ + wan_atomic_set(&chan->udp_pkt_len,0); + return -EFAULT; + } +#endif + + process_udp_mgmt_pkt(card,chan->common.dev,chan,1); + + /* This area will still be critical to other + * PIPEMON commands due to udp_pkt_len + * thus we can release the irq */ + + if (wan_atomic_read(&chan->udp_pkt_len) > sizeof(wan_udp_pkt_t)){ + DEBUG_EVENT( "%s: Error: Pipemon buf too bit on the way up! %d\n", + card->devname,wan_atomic_read(&chan->udp_pkt_len)); + wan_atomic_set(&chan->udp_pkt_len,0); + return -EINVAL; + } + +#if defined (__WINDOWS__) + /* udata IS a pointer to wan_udp_hdr_t. copy data into user's buffer */ + wpabs_memcpy(udata, &wan_udp_pkt->wan_udp_hdr, sizeof(wan_udp_hdr_t)); +#else + if (WAN_COPY_TO_USER( + udata, + &wan_udp_pkt->wan_udp_hdr, + sizeof(wan_udp_hdr_t))){ + wan_atomic_set(&chan->udp_pkt_len,0); + return -EFAULT; + } +#endif + + wan_atomic_set(&chan->udp_pkt_len,0); + + return 0; +} + +static int xilinx_driver_ctrl(void *chan_ptr, int cmd, wanpipe_api_cmd_t *api_cmd) +{ + private_area_t *chan = (private_area_t *)chan_ptr; + int err = 0; + + if (!chan) { + return -ENODEV; + } + + api_cmd->result=0; + + switch (cmd) + { + + case WP_API_CMD_SET_TX_Q_SIZE: + if (api_cmd->tx_queue_sz) { + chan->max_tx_bufs = (u16)api_cmd->tx_queue_sz; + } else { + err=-EINVAL; + } + break; + case WP_API_CMD_GET_TX_Q_SIZE: + api_cmd->tx_queue_sz = chan->max_tx_bufs; + break; + + case WP_API_CMD_GET_STATS: + { + wanpipe_chan_stats_t *wanpipe_chan_stats = (wanpipe_chan_stats_t*)&api_cmd->stats; +/* chan->chan_stats.max_tx_queue_length = (u8)chan->max_tx_bufs; + chan->chan_stats.max_rx_queue_length = (u8)chan->dma_per_ch;*/ + + memset(wanpipe_chan_stats, 0x00, sizeof(wanpipe_chan_stats_t)); + + wanpipe_chan_stats->rx_bytes = chan->opstats.Data_bytes_Rx_count; + wanpipe_chan_stats->rx_packets = chan->opstats.Data_frames_Rx_count; + + wanpipe_chan_stats->tx_bytes = chan->opstats.Data_bytes_Tx_count;; + wanpipe_chan_stats->tx_packets = chan->opstats.Data_frames_Tx_count; + } + break; + + case WP_API_CMD_RESET_STATS: + memset(&chan->opstats,0,sizeof(chan->opstats)); + memset(&chan->common.if_stats,0,sizeof(struct net_device_stats)); + break; + + case WP_API_CMD_DRIVER_VERSION: + { + wan_driver_version_t *drv_ver=(wan_driver_version_t*)&api_cmd->data[0]; + drv_ver->major=WANPIPE_VERSION_MAJOR; + drv_ver->minor=WANPIPE_VERSION_MINOR; + drv_ver->minor1=WANPIPE_VERSION_MINOR1; + drv_ver->minor2=WANPIPE_VERSION_MINOR2; + api_cmd->data_len=sizeof(wan_driver_version_t); + } + break; + + case WP_API_CMD_FIRMWARE_VERSION: +/* api_cmd->data[0] = card->u.aft.firm_ver;*/ + api_cmd->data_len=1; + break; + + case WP_API_CMD_CPLD_VERSION: + api_cmd->data[0] = 0; + api_cmd->data_len=1; + break; + + case WP_API_CMD_GEN_FIFO_ERR_RX: + case WP_API_CMD_GEN_FIFO_ERR_TX: +/* card->wp_debug_gen_fifo_err=1; */ + break; + +/* case WP_API_CMD_START_CHAN_SEQ_DEBUG: + DEBUG_EVENT("%s: Span %i channel sequence deugging enabled !\n", + card->devname, card->tdmv_conf.span_no); + card->wp_debug_gen_fifo_err=1;*/ + break; + +/* case WP_API_CMD_STOP_CHAN_SEQ_DEBUG: + DEBUG_EVENT("%s: Span %i channel sequence deugging disabled !\n", + card->devname, card->tdmv_conf.span_no); + card->wp_debug_gen_fifo_err=0;*/ + break; + + case WP_API_CMD_SET_IDLE_FLAG: + if(chan->tx_idle_skb){ + + chan->idle_flag = (unsigned char)api_cmd->idle_flag; + + memset(wan_skb_data(chan->tx_idle_skb), chan->idle_flag, wan_skb_len(chan->tx_idle_skb)); + + }else{ + DEBUG_EVENT("%s: Error: WP_API_CMD_SET_IDLE_FLAG: tx_idle_skb is NULL!\n", + chan->if_name); + } + break; + + default: + api_cmd->result=SANG_STATUS_OPTION_NOT_SUPPORTED; + err=-EOPNOTSUPP; + break; + } + + return err; +} static int aft_event_ctrl(void *chan_ptr, wan_event_ctrl_t *event_ctrl) { /* There are no events for A101/2 cards */ - return -EINVAL; + return -EINVAL; +} + +int xilinx_tdmapi_event_init(wanpipe_tdm_api_dev_t *wp_tdm_api_dev) +{ +#if defined(AFT_TDM_API_SUPPORT) + wp_tdm_api_dev->read_rbs_bits = aft_read_rbs_bits; + wp_tdm_api_dev->write_rbs_bits = aft_write_rbs_bits; + wp_tdm_api_dev->event_ctrl = aft_event_ctrl; + wp_tdm_api_dev->write_hdlc_frame = aft_write_hdlc_frame; + wp_tdm_api_dev->pipemon = xilinx_wan_user_process_udp_mgmt_pkt; + wp_tdm_api_dev->driver_ctrl = xilinx_driver_ctrl; +#endif + return 0; } static int aft_tdm_api_init(sdla_t *card, private_area_t *chan, int logic_ch, wanif_conf_t *conf) { - #ifdef AFT_TDM_API_SUPPORT int err=0; wanpipe_tdm_api_dev_t *wp_tdm_api_dev = &chan->wp_tdm_api_dev_idx[logic_ch]; + DEBUG_TEST("%s(): logic_ch: %d\n", __FUNCTION__, logic_ch); + +# ifdef __WINDOWS__ + chan->tdmv_chan = logic_ch; +# endif + if (chan->common.usedby != TDM_VOICE_API && - chan->common.usedby != TDM_VOICE_DCHAN) { - return 0; + chan->common.usedby != TDM_VOICE_DCHAN +# ifdef __WINDOWS__ + && chan->common.usedby != API +# endif + ) { + DEBUG_TEST("%s()\n", __FUNCTION__); + return 0; } if (chan->tdmv_zaptel_cfg) { + DEBUG_TEST("%s()\n", __FUNCTION__); return 0; } @@ -803,7 +1044,7 @@ static int aft_tdm_api_init(sdla_t *card, private_area_t *chan, int logic_ch, wa strncpy(wp_tdm_api_dev->name,chan->if_name,WAN_IFNAME_SZ); if (conf->hdlc_streaming) { - wp_tdm_api_dev->hdlc_framing=1; + wp_tdm_api_dev->hdlc_framing=1; } wp_tdm_api_dev->event_ctrl = aft_event_ctrl; @@ -811,6 +1052,8 @@ static int aft_tdm_api_init(sdla_t *card, private_area_t *chan, int logic_ch, wa wp_tdm_api_dev->write_rbs_bits = aft_write_rbs_bits; wp_tdm_api_dev->write_hdlc_frame = aft_write_hdlc_frame; + xilinx_tdmapi_event_init(wp_tdm_api_dev); + wp_tdm_api_dev->cfg.rx_disable = 0; wp_tdm_api_dev->cfg.tx_disable = 0; @@ -823,27 +1066,39 @@ static int aft_tdm_api_init(sdla_t *card, private_area_t *chan, int logic_ch, wa wp_tdm_api_dev->cfg.idle_flag = conf->u.aft.idle_flag; wp_tdm_api_dev->cfg.rbs_tx_bits = conf->u.aft.rbs_cas_idle; - wp_tdm_api_dev->tdm_span = card->tdmv_conf.span_no; + wp_tdm_api_dev->tdm_span = card->tdmv_conf.span_no; wp_tdm_api_dev->tdm_chan = logic_ch+1; - if (IS_T1_CARD(card)){ + if (IS_T1_CARD(card)){ /* Convert active_ch bit map to user */ wp_tdm_api_dev->active_ch = conf->active_ch << 1; }else{ wp_tdm_api_dev->active_ch = conf->active_ch; } + wp_tdm_api_dev->cfg.tx_queue_sz = 20; + + wp_tdm_api_dev->operation_mode = WP_TDM_OPMODE_SPAN; + wp_tdm_api_dev->cfg.hw_mtu_mru = chan->mtu; + wp_tdm_api_dev->cfg.usr_period = 20; //chan->tdm_api_period; + wp_tdm_api_dev->cfg.usr_mtu_mru = chan->mtu; + + /* Overwite the chan number based on group number */ + /*wp_tdm_api_dev->tdm_chan = (u8)chan->if_cnt;*/ + err=wanpipe_tdm_api_reg(wp_tdm_api_dev); if (err){ + DEBUG_TEST("%s()\n", __FUNCTION__); return err; } wan_set_bit(0,&wp_tdm_api_dev->init); + DEBUG_TEST("%s(): returning err: %d\n", __FUNCTION__, err); return err; #else DEBUG_EVENT("%s: TDM API support not compiled in\n", - card->devname); - return -EINVAL; + card->devname); + return -EINVAL; #endif } @@ -860,7 +1115,6 @@ static int aft_tdm_api_free(sdla_t *card, private_area_t *chan, int logic_ch) wan_set_bit(0,&wp_tdm_api_dev->init); return err; } - } #endif return 0; @@ -891,8 +1145,8 @@ static int aft_tdm_api_init_channelized(sdla_t *card, private_area_t *chan, wani return err; #else DEBUG_EVENT("%s: TDM API support not compiled in\n", - card->devname); - return -EINVAL; + card->devname); + return -EINVAL; #endif } @@ -912,60 +1166,60 @@ static int aft_tdm_api_free_channelized(sdla_t *card, private_area_t *chan) return 0; } #endif - -#endif - - +#endif/* #if defined(__LINUX__) || defined(__WINDOWS__) */ static void wanpipe_wake_stack(private_area_t* chan) { - WAN_NETIF_WAKE_QUEUE(chan->common.dev); + DEBUG_TEST("%s()\n", __FUNCTION__); + + WAN_NETIF_WAKE_QUEUE(chan->common.dev); + #if !defined(CONFIG_PRODUCT_WANPIPE_GENERIC) - if (chan->common.usedby == API){ -# if defined(__LINUX__) - wan_wakeup_api(chan); + if (chan->common.usedby == API){ +# if defined(__LINUX__) || defined(__WINDOWS__) + wan_wakeup_api(chan); # endif - }else if (chan->common.usedby == STACK){ - wanpipe_lip_kick(chan,0); - } + }else if (chan->common.usedby == STACK){ + wanpipe_lip_kick(chan,0); + } #endif } /*============================================================================ - * new_if - Create new logical channel. - * - * &wandev: Wanpipe device pointer - * &dev: Network device pointer - * &conf: User configuration options pointer - * - * This routine is called by the ROUTER_IFNEW ioctl, - * in wanmain.c. The ioctl passes us the user configuration - * options which we use to configure the driver and - * firmware. - * - * This functions main purpose is to allocate the - * private structure for protocol and bind it - * to dev->priv pointer. - * - * Also the dev->init pointer should also be initialized - * to the if_init() function. - * - * Any allocation necessary for the private strucutre - * should be done here, as well as proc/ file initializetion - * for the network interface. - * - * o parse media- and hardware-specific configuration - * o make sure that a new channel can be created - * o allocate resources, if necessary - * o prepare network device structure for registaration. - * o add network interface to the /proc/net/wanrouter - * - * The opposite of this function is del_if() - * - * Return: 0 o.k. - * < 0 failure (channel will not be created) - */ +* new_if - Create new logical channel. +* +* &wandev: Wanpipe device pointer +* &dev: Network device pointer +* &conf: User configuration options pointer +* +* This routine is called by the ROUTER_IFNEW ioctl, +* in wanmain.c. The ioctl passes us the user configuration +* options which we use to configure the driver and +* firmware. +* +* This functions main purpose is to allocate the +* private structure for protocol and bind it +* to dev->priv pointer. +* +* Also the dev->init pointer should also be initialized +* to the if_init() function. +* +* Any allocation necessary for the private strucutre +* should be done here, as well as proc/ file initializetion +* for the network interface. +* +* o parse media- and hardware-specific configuration +* o make sure that a new channel can be created +* o allocate resources, if necessary +* o prepare network device structure for registaration. +* o add network interface to the /proc/net/wanrouter +* +* The opposite of this function is del_if() +* +* Return: 0 o.k. +* < 0 failure (channel will not be created) +*/ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf, int channelized) { sdla_t* card = wandev->priv; @@ -973,7 +1227,7 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* int err = 0; DEBUG_EVENT( "%s: Configuring Interface: %s\n", - card->devname, wan_netif_name(dev)); + card->devname, wan_netif_name(dev)); if ((conf->name[0] == '\0') || (strlen(conf->name) > WAN_IFNAME_SZ)){ DEBUG_EVENT( "%s: Invalid interface name!\n", @@ -989,6 +1243,9 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* } memset(chan, 0, sizeof(private_area_t)); + DEBUG_UDP("%s(): dev->name: %s, chan ptr: 0x%p\n", + __FUNCTION__, dev->name, chan); + chan->first_time_slot=-1; strncpy(chan->if_name, wan_netif_name(dev), WAN_IFNAME_SZ); @@ -1025,11 +1282,13 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* WAN_IFQ_INIT(&chan->wp_rx_free_list,0); WAN_IFQ_INIT(&chan->wp_rx_complete_list,0); + WAN_IFQ_INIT(&chan->wp_dealloc_list,0); + wan_trace_info_init(&chan->trace_info,MAX_TRACE_QUEUE); /* Initialize the socket binding information - * These hooks are used by the API sockets to - * bind into the network interface */ + * These hooks are used by the API sockets to + * bind into the network interface */ WAN_TASKLET_INIT((&chan->common.bh_task), 0, wp_bh, chan); chan->common.dev = dev; @@ -1039,11 +1298,11 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* /* Setup interface as: - * WANPIPE = IP over Protocol (Firmware) - * API = Raw Socket access to Protocol (Firmware) - * BRIDGE = Ethernet over Protocol, no ip info - * BRIDGE_NODE = Ethernet over Protocol, with ip info - */ + * WANPIPE = IP over Protocol (Firmware) + * API = Raw Socket access to Protocol (Firmware) + * BRIDGE = Ethernet over Protocol, no ip info + * BRIDGE_NODE = Ethernet over Protocol, with ip info + */ chan->mtu = card->wandev.mtu; if (conf->u.aft.mtu){ @@ -1051,12 +1310,12 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* } if (chan->mtu > MAX_WP_PRI_MTU || - chan->mtu < MIN_WP_PRI_MTU){ - DEBUG_EVENT("%s: Error Invalid %s MTU %d (Min=%d, Max=%d)\n", - card->devname,chan->if_name,chan->mtu, - MIN_WP_PRI_MTU,MAX_WP_PRI_MTU); - err= -EINVAL; - goto new_if_error; + chan->mtu < MIN_WP_PRI_MTU){ + DEBUG_EVENT("%s: Error Invalid %s MTU %d (Min=%d, Max=%d)\n", + card->devname,chan->if_name,chan->mtu, + MIN_WP_PRI_MTU,MAX_WP_PRI_MTU); + err= -EINVAL; + goto new_if_error; } chan->mru = card->u.aft.cfg.mru; @@ -1065,18 +1324,18 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* } if (chan->mru > MAX_WP_PRI_MTU || - chan->mru < MIN_WP_PRI_MTU){ - DEBUG_EVENT("%s: Error Invalid %s MRU %d (Min=%d, Max=%d)\n", - card->devname,chan->if_name,chan->mru, - MIN_WP_PRI_MTU,MAX_WP_PRI_MTU); + chan->mru < MIN_WP_PRI_MTU){ + DEBUG_EVENT("%s: Error Invalid %s MRU %d (Min=%d, Max=%d)\n", + card->devname,chan->if_name,chan->mru, + MIN_WP_PRI_MTU,MAX_WP_PRI_MTU); - err= -EINVAL; - goto new_if_error; + err= -EINVAL; + goto new_if_error; } DEBUG_EVENT("%s: UsedBy :%s\n", - card->devname, - conf->usedby); + card->devname, + conf->usedby); if(strcmp(conf->usedby, "WANPIPE") == 0) { @@ -1084,12 +1343,12 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* chan->common.usedby = WANPIPE; /* Option to bring down the interface when - * the link goes down */ + * the link goes down */ if (conf->if_down){ wan_set_bit(DYN_OPT_ON,&chan->interface_down); DEBUG_EVENT( - "%s:%s: Dynamic interface configuration enabled\n", - card->devname,chan->if_name); + "%s:%s: Dynamic interface configuration enabled\n", + card->devname,chan->if_name); } if (conf->protocol != WANOPT_NO){ @@ -1101,20 +1360,32 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* if (conf->ignore_dcd == WANOPT_YES || conf->ignore_cts == WANOPT_YES){ DEBUG_EVENT("%s: Ignore modem changes DCD/CTS\n", - card->devname); + card->devname); chan->ignore_modem=1; }else{ DEBUG_EVENT("%s: Restart protocol on modem changes DCD/CTS\n", - card->devname); + card->devname); } DEBUG_EVENT("\n"); } -#if defined(__LINUX__) +#if defined(__LINUX__) || defined(__WINDOWS__) }else if( strcmp(conf->usedby, "API") == 0) { + chan->common.usedby = API; + DEBUG_EVENT( "%s:%s: Running in %s mode\n", wandev->name,chan->if_name, conf->usedby); + +# ifdef __WINDOWS__ + chan->tdmv_zaptel_cfg=0; + + err=aft_tdm_api_init_channelized(card,chan,conf);/* this will call CDEV code to create the file descriptor */ + if (err){ + goto new_if_error; + } +# else wan_reg_api(chan, dev, card->devname); +# endif #endif #if defined(__LINUX__) @@ -1135,8 +1406,8 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* int dchan=card->u.aft.tdmv_dchan; /* DCHAN must be decremented for both - * T1 and E1, since from tdmv driver's - * perspective all timeslots start from ZERO*/ + * T1 and E1, since from tdmv driver's + * perspective all timeslots start from ZERO*/ dchan--; chan->tdmv_chan=dchan; @@ -1147,16 +1418,16 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* card->u.aft.tdmv_zaptel_cfg=1; # else DEBUG_EVENT("%s: Error: TDMV_DCHAN Option not compiled into the driver!\n", - card->devname); + card->devname); err=-EINVAL; goto new_if_error; # endif # else DEBUG_EVENT("\n"); DEBUG_EVENT("%s:%s: Error: TDM VOICE/DCHAN prot not compiled\n", - card->devname,chan->if_name); + card->devname,chan->if_name); DEBUG_EVENT("%s:%s: during installation process!\n", - card->devname,chan->if_name); + card->devname,chan->if_name); err=-EINVAL; goto new_if_error; # endif @@ -1171,9 +1442,9 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* #else DEBUG_EVENT("\n"); DEBUG_EVENT("%s:%s: Error: TDM VOICE prot not compiled\n", - card->devname,chan->if_name); + card->devname,chan->if_name); DEBUG_EVENT("%s:%s: during installation process!\n", - card->devname,chan->if_name); + card->devname,chan->if_name); err=-EINVAL; goto new_if_error; #endif @@ -1184,8 +1455,8 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* int dchan=card->u.aft.tdmv_dchan; /* DCHAN must be decremented for both - * T1 and E1, since from tdmv driver's - * perspective all timeslots start from ZERO*/ + * T1 and E1, since from tdmv driver's + * perspective all timeslots start from ZERO*/ dchan--; chan->tdmv_chan=dchan; @@ -1206,8 +1477,8 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* int dchan=card->u.aft.tdmv_dchan; /* DCHAN must be decremented for both - * T1 and E1, since from tdmv driver's - * perspective all timeslots start from ZERO*/ + * T1 and E1, since from tdmv driver's + * perspective all timeslots start from ZERO*/ dchan--; chan->tdmv_chan=dchan; @@ -1230,7 +1501,7 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* }else{ DEBUG_EVENT( "%s:%s: Error: Invalid operation mode [%s]\n", - card->devname,chan->if_name, conf->usedby); + card->devname,chan->if_name, conf->usedby); err=-EINVAL; goto new_if_error; } @@ -1246,53 +1517,53 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* DEBUG_EVENT("%s: MRU :%d\n", - card->devname, - chan->mru); + card->devname, + chan->mru); DEBUG_EVENT("%s: MTU :%d\n", - card->devname, - chan->mtu); + card->devname, + chan->mtu); xilinx_delay(1); chan->hdlc_eng = conf->hdlc_streaming; DEBUG_EVENT("%s: HDLC Eng :%s\n", - card->devname, - chan->hdlc_eng?"On":"Off (Transparent)"); + card->devname, + chan->hdlc_eng?"On":"Off (Transparent)"); if (!chan->hdlc_eng){ if (!wan_test_bit(0,&card->u.aft.tdmv_sync)){ DEBUG_EVENT("%s: Slot Sync :Enabled\n", - card->devname); + card->devname); wan_set_bit(0,&card->u.aft.tdmv_sync); wan_set_bit(0,&chan->tdmv_sync); }else{ DEBUG_EVENT("%s: Slot Sync :Disabled\n", - card->devname); + card->devname); wan_clear_bit(0,&chan->tdmv_sync); } if(conf->protocol == WANCONFIG_LIP_ATM || - conf->protocol == WANCONFIG_LIP_KATM){ - /* if ATM NO sync needed!! */ - DEBUG_EVENT("%s: Disabling Time Slot Sync for ATM.\n", chan->if_name); - card->u.aft.tdmv_sync = 0; - chan->tdmv_sync = 0; + conf->protocol == WANCONFIG_LIP_KATM){ + /* if ATM NO sync needed!! */ + DEBUG_EVENT("%s: Disabling Time Slot Sync for ATM.\n", chan->if_name); + card->u.aft.tdmv_sync = 0; + chan->tdmv_sync = 0; } if (chan->mtu&0x03){ DEBUG_EVENT("%s:%s: Error, Transparent MTU must be word aligned!\n", - card->devname,chan->if_name); + card->devname,chan->if_name); err = -EINVAL; goto new_if_error; } } DEBUG_EVENT("%s: Timeslot Map :0x%08X\n", - card->devname, - chan->time_slot_map); + card->devname, + chan->time_slot_map); #ifndef CONFIG_PRODUCT_WANPIPE_GENERIC err=xilinx_dev_configure(card,chan); @@ -1301,11 +1572,11 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* } /*Set the actual logic ch number of this chan - *as the dchan. Due to HDLC security issue, the - *HDLC channels are mapped on first TWO logic channels */ - if (chan->common.usedby == TDM_VOICE_DCHAN){ - card->u.aft.tdmv_dchan=chan->logic_ch_num+1; - } + *as the dchan. Due to HDLC security issue, the + *HDLC channels are mapped on first TWO logic channels */ + if (chan->common.usedby == TDM_VOICE_DCHAN){ + card->u.aft.tdmv_dchan=chan->logic_ch_num+1; + } xilinx_delay(1); #endif @@ -1329,15 +1600,15 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* chan->idle_flag = conf->u.aft.idle_flag; DEBUG_EVENT("%s: Idle Flag :0x%02X\n", - card->devname, - chan->idle_flag); + card->devname, + chan->idle_flag); DEBUG_EVENT("%s: Idle Buf Len :%d\n", - card->devname, - chan->max_idle_size); + card->devname, + chan->max_idle_size); DEBUG_EVENT("%s: Data Mux :%s\n", - card->devname, - chan->cfg.data_mux?"On":"Off"); + card->devname, + chan->cfg.data_mux?"On":"Off"); #ifdef CONFIG_PRODUCT_WANPIPE_TDM_VOICE if (chan->common.usedby == TDM_VOICE){ @@ -1353,23 +1624,23 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* if(conf->protocol != WANCONFIG_LIP_ATM && - conf->protocol != WANCONFIG_LIP_KATM){ - buf=wan_skb_put(chan->tx_idle_skb,chan->dma_mru); - memset(buf,chan->idle_flag,chan->dma_mru); - wan_skb_trim(chan->tx_idle_skb,0); - wan_skb_put(chan->tx_idle_skb,chan->max_idle_size); + conf->protocol != WANCONFIG_LIP_KATM){ + buf=wan_skb_put(chan->tx_idle_skb,chan->dma_mru); + memset(buf,chan->idle_flag,chan->dma_mru); + wan_skb_trim(chan->tx_idle_skb,0); + wan_skb_put(chan->tx_idle_skb,chan->max_idle_size); }else{ buf=wan_skb_put(chan->tx_idle_skb,chan->max_idle_size); chan->lip_atm = 1; /* if running below LIP ATM, transmit idle cells */ if(init_atm_idle_buffer((unsigned char*)buf, - wan_skb_len(chan->tx_idle_skb), - chan->if_name, - chan->cfg.data_mux)){ + wan_skb_len(chan->tx_idle_skb), + chan->if_name, + chan->cfg.data_mux)){ - wan_skb_free(chan->tx_idle_skb); - chan->tx_idle_skb = NULL; - return -EINVAL; + wan_skb_free(chan->tx_idle_skb); + chan->tx_idle_skb = NULL; + return -EINVAL; } wan_skb_reverse(chan->tx_idle_skb); @@ -1379,23 +1650,23 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* DEBUG_EVENT("\n"); DEBUG_EVENT("%s: DMA MRU :%d\n", - card->devname, - chan->dma_mru); + card->devname, + chan->dma_mru); if (wan_test_bit(0,&chan->tdmv_sync)){ if (chan->dma_mru%4){ DEBUG_EVENT("%s:%s: Error invalid TDM_VOICE MTU %d MRU %d\n", - card->devname, - chan->if_name, - chan->dma_mru,card->u.aft.cfg.mru); - err= -EINVAL; - goto new_if_error; + card->devname, + chan->if_name, + chan->dma_mru,card->u.aft.cfg.mru); + err= -EINVAL; + goto new_if_error; } } DEBUG_EVENT("%s: RX DMA Per Ch :%d\n", - card->devname, - card->u.aft.cfg.dma_per_ch); + card->devname, + card->u.aft.cfg.dma_per_ch); @@ -1405,59 +1676,59 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* } /* If gateway option is set, then this interface is the - * default gateway on this system. We must know that information - * in case DYNAMIC interface configuration is enabled. - * - * I.E. If the interface is brought down by the driver, the - * default route will also be removed. Once the interface - * is brought back up, we must know to re-astablish the - * default route. - */ + * default gateway on this system. We must know that information + * in case DYNAMIC interface configuration is enabled. + * + * I.E. If the interface is brought down by the driver, the + * default route will also be removed. Once the interface + * is brought back up, we must know to re-astablish the + * default route. + */ DEBUG_EVENT( "%s: Net Gateway :%s\n", - card->devname, - conf->gateway?"Yes":"No"); + card->devname, + conf->gateway?"Yes":"No"); chan->gateway = conf->gateway; /* Get Multicast Information from the user - * FIXME: This option is not clearly defined - */ + * FIXME: This option is not clearly defined + */ chan->mc = conf->mc; chan->max_tx_bufs = MAX_TX_BUF; /* The network interface "dev" has been passed as - * an argument from the above layer. We must initialize - * it so it can be registered into the kernel. - * - * The "dev" structure is the link between the kernel - * stack and the wanpipe driver. It contains all - * access hooks that kernel uses to communicate to - * the our driver. - * - * For now, just set the "dev" name to the user - * defined name and initialize: - * dev->if_init : function that will be called - * to further initialize - * dev structure on "ifconfig up" - * - * dev->priv : private structure allocated above - * - */ + * an argument from the above layer. We must initialize + * it so it can be registered into the kernel. + * + * The "dev" structure is the link between the kernel + * stack and the wanpipe driver. It contains all + * access hooks that kernel uses to communicate to + * the our driver. + * + * For now, just set the "dev" name to the user + * defined name and initialize: + * dev->if_init : function that will be called + * to further initialize + * dev structure on "ifconfig up" + * + * dev->priv : private structure allocated above + * + */ #if 0 /* Create interface file in proc fs. - * Once the proc file system is created, the new_if() function - * should exit successfuly. - * - * DO NOT place code under this function that can return - * anything else but 0. - */ + * Once the proc file system is created, the new_if() function + * should exit successfuly. + * + * DO NOT place code under this function that can return + * anything else but 0. + */ err = wanrouter_proc_add_interface(wandev, - &chan->dent, - chan->if_name, - dev); + &chan->dent, + chan->if_name, + dev); if (err){ DEBUG_EVENT( "%s: can't create /proc/net/router/frmw/%s entry!\n", @@ -1467,8 +1738,17 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* #endif -#if defined(__LINUX__) - dev->init = &if_init; +#if defined(__LINUX__) || defined(__WINDOWS__) + WAN_NETDEV_OPS_BIND(dev,wan_netdev_ops); + WAN_NETDEV_OPS_INIT(dev,wan_netdev_ops,&if_init); + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&wanpipe_xilinx_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&wanpipe_xilinx_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&wanpipe_xilinx_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&wanpipe_xilinx_ifstats); + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&wanpipe_xilinx_tx_timeout); + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,wanpipe_xilinx_ioctl); + + # ifdef CONFIG_PRODUCT_WANPIPE_GENERIC if_init(dev); # endif @@ -1479,15 +1759,15 @@ static int new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) chan->common.is_netdev = 1; chan->common.iface.open = &wanpipe_xilinx_open; - chan->common.iface.close = &wanpipe_xilinx_close; - chan->common.iface.output = &wan_aft_output; - chan->common.iface.ioctl = &wanpipe_xilinx_ioctl; - chan->common.iface.tx_timeout= &wanpipe_xilinx_tx_timeout; + chan->common.iface.close = &wanpipe_xilinx_close; + chan->common.iface.output = &wan_aft_output; + chan->common.iface.ioctl = &wanpipe_xilinx_ioctl; + chan->common.iface.tx_timeout= &wanpipe_xilinx_tx_timeout; if (wan_iface.attach){ wan_iface.attach(dev, NULL, chan->common.is_netdev); }else{ DEBUG_EVENT("%s: Failed to attach interface %s!\n", - card->devname, wan_netif_name(dev)); + card->devname, wan_netif_name(dev)); wan_netif_set_priv(dev, NULL); err = -EINVAL; goto new_if_error; @@ -1533,68 +1813,68 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) err = wp_tdmv_te1_init(&card->tdmv_iface); if (err){ DEBUG_EVENT("%s: Error: Failed to initialize tdmv functions!\n", - card->devname); + card->devname); return -EINVAL; } WAN_TDMV_CALL(create, (card, &card->tdmv_conf), err); if (err){ DEBUG_EVENT("%s: Error: Failed to create tdmv span!\n", - card->devname); + card->devname); return err; } } #else DEBUG_EVENT("\n"); DEBUG_EVENT("%s: Error: TDM VOICE prot not compiled\n", - card->devname); + card->devname); DEBUG_EVENT("%s: during installation process!\n", - card->devname); + card->devname); return -EINVAL; #endif } if (strcmp(conf->usedby, "TDM_VOICE") == 0 || - strcmp(conf->usedby, "TDM_VOICE_API") == 0){ + strcmp(conf->usedby, "TDM_VOICE_API") == 0){ - int dchan=0; - int dchan_found=0; - int i; + int dchan=0; + int dchan_found=0; + int i; - for (i=card->u.aft.num_of_time_slots-1;i>=0;i--){ - if (wan_test_bit(i,&card->tdmv_conf.dchan)){ - dchan_found=1; - card->u.aft.tdmv_dchan=i; - dchan=i; - break; - } - } - - if (dchan_found){ - if (IS_T1_CARD(card)) { - card->u.aft.tdmv_dchan++; - } - wan_clear_bit(dchan,&conf->active_ch); - } - - err=new_if_private(wandev,dev,conf,1); - if (!err){ - if (card->tdmv_conf.dchan){ - - conf->active_ch=0; - if (strcmp(conf->usedby, "TDM_VOICE") == 0) { - sprintf(conf->usedby,"TDM_VOICE_DCHAN"); - } else { - sprintf(conf->usedby,"TDM_VOICE_DCHAN_API"); - } - wan_set_bit(dchan,&conf->active_ch); - - err=new_if_private(wandev,dev,conf,1); - if (err){ - return err; + for (i=card->u.aft.num_of_time_slots-1;i>=0;i--){ + if (wan_test_bit(i,&card->tdmv_conf.dchan)){ + dchan_found=1; + card->u.aft.tdmv_dchan=i; + dchan=i; + break; + } + } + + if (dchan_found){ + if (IS_T1_CARD(card)) { + card->u.aft.tdmv_dchan++; + } + wan_clear_bit(dchan,&conf->active_ch); + } + + err=new_if_private(wandev,dev,conf,1); + if (!err){ + if (card->tdmv_conf.dchan){ + + conf->active_ch=0; + if (strcmp(conf->usedby, "TDM_VOICE") == 0) { + sprintf(conf->usedby,"TDM_VOICE_DCHAN"); + } else { + sprintf(conf->usedby,"TDM_VOICE_DCHAN_API"); + } + wan_set_bit(dchan,&conf->active_ch); + + err=new_if_private(wandev,dev,conf,1); + if (err){ + return err; + } } } - } }else{ err=new_if_private(wandev,dev,conf,0); @@ -1616,28 +1896,30 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) /*============================================================================ - * del_if - Delete logical channel. - * - * @wandev: Wanpipe private device pointer - * @dev: Netowrk interface pointer - * - * This function is called by ROUTER_DELIF ioctl call - * to deallocate the network interface. - * - * The network interface and the private structure are - * about to be deallocated by the upper layer. - * We have to clean and deallocate any allocated memory. - * - * NOTE: DO NOT deallocate dev->priv here! It will be - * done by the upper layer. - * - */ +* del_if - Delete logical channel. +* +* @wandev: Wanpipe private device pointer +* @dev: Netowrk interface pointer +* +* This function is called by ROUTER_DELIF ioctl call +* to deallocate the network interface. +* +* The network interface and the private structure are +* about to be deallocated by the upper layer. +* We have to clean and deallocate any allocated memory. +* +* NOTE: DO NOT deallocate dev->priv here! It will be +* done by the upper layer. +* +*/ static int del_if_private (wan_device_t* wandev, netdevice_t* dev) { private_area_t* chan = wan_netif_priv(dev); sdla_t* card = (sdla_t*)chan->common.card; netskb_t *skb; - wan_smp_flag_t flags; + wan_smp_flag_t flags; + + DEBUG_TEST("%s()\n", __FUNCTION__); if (wan_test_bit(0,&chan->tdmv_sync)){ wan_clear_bit(0,&card->u.aft.tdmv_sync); @@ -1648,15 +1930,19 @@ static int del_if_private (wan_device_t* wandev, netdevice_t* dev) #ifdef AFT_TDM_API_SUPPORT if (aft_tdm_api_free_channelized(card,chan)){ DEBUG_EVENT( - "%s: Error: Failed to del iface: TDM API Device in use!\n", - chan->if_name); + "%s: Error: Failed to del iface: TDM API Device in use!\n", + chan->if_name); return -EBUSY; } #endif + + DEBUG_CFG("%s(): line: %d\n", __FUNCTION__, __LINE__); #ifndef CONFIG_PRODUCT_WANPIPE_GENERIC xilinx_dev_unconfigure(card,chan); #endif + DEBUG_CFG("%s(): line: %d\n", __FUNCTION__, __LINE__); + WAN_TASKLET_KILL((&chan->common.bh_task)); if (chan->common.usedby == API){ @@ -1677,8 +1963,11 @@ static int del_if_private (wan_device_t* wandev, netdevice_t* dev) } #endif - wan_spin_lock_irq(&card->wandev.lock,&flags); - +#ifndef __WINDOWS__ + /* The communications must be already disabled, before interfaces are deleted, + * there is no need to use IRQ lock. */ + wan_spin_lock_irq(&card->wandev.lock,&flags); +#endif while ((skb=wan_skb_dequeue(&chan->wp_rx_free_list)) != NULL){ wan_skb_free(skb); } @@ -1694,10 +1983,12 @@ static int del_if_private (wan_device_t* wandev, netdevice_t* dev) wan_skb_free(skb); } + WAN_IFQ_PURGE(&chan->wp_dealloc_list); + WAN_IFQ_DESTROY(&chan->wp_dealloc_list); - if (chan->tx_dma_addr && chan->tx_dma_len){ + if (chan->tx_dma_addr && chan->tx_dma_len){ aft_unmap_tx_dma(card,chan); - } + } if (chan->tx_dma_skb){ DEBUG_TEST("freeing tx dma skb\n"); @@ -1719,12 +2010,12 @@ static int del_if_private (wan_device_t* wandev, netdevice_t* dev) rx_el=(wp_rx_element_t *)wan_skb_data(skb); card->hw_iface.pci_unmap_dma(card->hw, - rx_el->dma_addr, - chan->dma_mru, - PCI_DMA_FROMDEVICE); + rx_el->dma_addr, + chan->dma_mru, + PCI_DMA_FROMDEVICE); - wan_skb_free(skb); - } + wan_skb_free(skb); + } if (chan->tx_realign_buf){ wan_free(chan->tx_realign_buf); @@ -1732,17 +2023,17 @@ static int del_if_private (wan_device_t* wandev, netdevice_t* dev) } chan->logic_ch_num=-1; - +#ifndef __WINDOWS__ wan_spin_unlock_irq(&card->wandev.lock,&flags); - +#endif /* Delete interface name from proc fs. */ #if 0 wanrouter_proc_delete_interface(wandev, chan->if_name); #endif /* Decrement the number of network interfaces - * configured on this card. - */ + * configured on this card. + */ wan_atomic_dec(&card->wandev.if_cnt); DEBUG_SUB_MEM(sizeof(private_area_t)); @@ -1752,6 +2043,9 @@ static int del_if_private (wan_device_t* wandev, netdevice_t* dev) static int del_if (wan_device_t* wandev, netdevice_t* dev) { private_area_t* chan=wan_netif_priv(dev); + int err; + + DEBUG_TEST("%s()\n", __FUNCTION__); if (!chan){ return 0; @@ -1759,11 +2053,10 @@ static int del_if (wan_device_t* wandev, netdevice_t* dev) if (chan->channelized_cfg){ sdla_t *card=chan->common.card; - int err; #ifdef CONFIG_PRODUCT_WANPIPE_TDM_VOICE if (chan->tdmv_zaptel_cfg) { - WAN_TDMV_CALL(running, (card), err); + WAN_TDMV_CALL(running, (card), err); if (err){ return -EBUSY; } @@ -1780,90 +2073,103 @@ static int del_if (wan_device_t* wandev, netdevice_t* dev) wan_netif_set_priv(dev, chan); }else{ /* Leave the last chan dev - * in dev->priv. It will get - * deallocated normally */ + * in dev->priv. It will get + * deallocated normally */ break; } } aft_tdmv_free(card); - return 0; + }else{ - return del_if_private(wandev,dev); + err = del_if_private(wandev,dev); + if (err){ + return err; + } +#if defined(__WINDOWS__)/* FIXME: make the same on Linux? */ + if (chan){ + wan_free(chan); + wan_netif_set_priv(dev, NULL); + chan = NULL; + } +#endif } + return 0; } - /**SECTION*********************************************************** - * - * KERNEL Device Entry Interfaces - * - ********************************************************************/ +* +* KERNEL Device Entry Interfaces +* +********************************************************************/ /*============================================================================ - * if_init - Initialize Linux network interface. - * - * @dev: Network interface pointer - * - * During "ifconfig up" the upper layer calls this function - * to initialize dev access pointers. Such as transmit, - * stats and header. - * - * It is called only once for each interface, - * during Linux network interface registration. - * - * Returning anything but zero will fail interface - * registration. - */ +* if_init - Initialize Linux network interface. +* +* @dev: Network interface pointer +* +* During "ifconfig up" the upper layer calls this function +* to initialize dev access pointers. Such as transmit, +* stats and header. +* +* It is called only once for each interface, +* during Linux network interface registration. +* +* Returning anything but zero will fail interface +* registration. +*/ static int if_init (netdevice_t* dev) { private_area_t* chan = wan_netif_priv(dev); -#if defined(__LINUX__) +#if defined(__LINUX__) || defined(__WINDOWS__) sdla_t* card = (sdla_t*)chan->common.card; wan_device_t* wandev = &card->wandev; #endif /* Initialize device driver entry points */ -#if defined(__LINUX__) +#if defined(__LINUX__) || defined(__WINDOWS__) # ifndef CONFIG_PRODUCT_WANPIPE_GENERIC - dev->open = &wanpipe_xilinx_open; - dev->stop = &wanpipe_xilinx_close; - dev->hard_start_xmit = &wanpipe_xilinx_send; - dev->get_stats = &wanpipe_xilinx_ifstats; + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&wanpipe_xilinx_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&wanpipe_xilinx_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&wanpipe_xilinx_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&wanpipe_xilinx_ifstats); + # if defined(LINUX_2_4)||defined(LINUX_2_6) if (chan->common.usedby == TDM_VOICE || - chan->common.usedby == TDM_VOICE_DCHAN || - chan->common.usedby == TDM_VOICE_API) { - dev->tx_timeout = NULL; + chan->common.usedby == TDM_VOICE_DCHAN || + chan->common.usedby == TDM_VOICE_API) { + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,NULL); } else { - dev->tx_timeout = &wanpipe_xilinx_tx_timeout; + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&wanpipe_xilinx_tx_timeout); } - dev->watchdog_timeo = 2*HZ; + dev->watchdog_timeo = 2*HZ; # endif - dev->do_ioctl = wanpipe_xilinx_ioctl; + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,wanpipe_xilinx_ioctl); # endif if (chan->common.usedby == BRIDGE || - chan->common.usedby == BRIDGE_NODE){ - - /* Setup the interface for Bridging */ - int hw_addr=0; - ether_setup(dev); - - /* Use a random number to generate the MAC address */ - memcpy(dev->dev_addr, "\xFE\xFC\x00\x00\x00\x00", 6); - get_random_bytes(&hw_addr, sizeof(hw_addr)); - *(int *)(dev->dev_addr + 2) += hw_addr; + chan->common.usedby == BRIDGE_NODE){ +#ifndef __WINDOWS__ + /* Setup the interface for Bridging */ + int hw_addr=0; + ether_setup(dev); + /* Use a random number to generate the MAC address */ + memcpy(dev->dev_addr, "\xFE\xFC\x00\x00\x00\x00", 6); + get_random_bytes(&hw_addr, sizeof(hw_addr)); + *(int *)(dev->dev_addr + 2) += hw_addr; +#endif }else{ if (chan->common.protocol != WANCONFIG_GENERIC){ +#ifndef __WINDOWS__ dev->flags |= IFF_POINTOPOINT; dev->flags |= IFF_NOARP; dev->type = ARPHRD_PPP; +#endif dev->mtu = chan->mtu; if (chan->common.usedby == API){ @@ -1871,7 +2177,7 @@ static int if_init (netdevice_t* dev) } dev->hard_header_len = 0; - +#ifndef __WINDOWS__ /* Enable Mulitcasting if user selected */ if (chan->mc == WANOPT_YES){ dev->flags |= IFF_MULTICAST; @@ -1885,6 +2191,7 @@ static int if_init (netdevice_t* dev) }else{ dev->type = ARPHRD_PPP; } +#endif } } @@ -1896,15 +2203,15 @@ static int if_init (netdevice_t* dev) card->hw_iface.getcfg(card->hw, SDLA_MEMEND, &dev->mem_end); /* Set transmit buffer queue length - * If too low packets will not be retransmitted - * by stack. - */ - dev->tx_queue_len = 100; + * If too low packets will not be retransmitted + * by stack. + */ + dev->tx_queue_len = 100; #else dev->if_mtu = chan->mtu; #if 0 DEBUG_EVENT("%s: Initialize network interface...\n", - wan_netif_name(dev)); + wan_netif_name(dev)); dev->if_output = NULL; dev->if_start = NULL; /*&wanpipe_xilinx_start;*/ dev->if_ioctl = NULL; /* &wplip_ioctl; */ @@ -1923,21 +2230,21 @@ static int if_init (netdevice_t* dev) } /*============================================================================ - * if_open - Open network interface. - * - * @dev: Network device pointer - * - * On ifconfig up, this function gets called in order - * to initialize and configure the private area. - * Driver should be configured to send and receive data. - * - * This functions starts a timer that will call - * frmw_config() function. This function must be called - * because the IP addresses could have been changed - * for this interface. - * - * Return 0 if O.k. or errno. - */ +* if_open - Open network interface. +* +* @dev: Network device pointer +* +* On ifconfig up, this function gets called in order +* to initialize and configure the private area. +* Driver should be configured to send and receive data. +* +* This functions starts a timer that will call +* frmw_config() function. This function must be called +* because the IP addresses could have been changed +* for this interface. +* +* Return 0 if O.k. or errno. +*/ static int wanpipe_xilinx_open (netdevice_t* dev) { private_area_t* chan = wan_netif_priv(dev); @@ -1946,7 +2253,7 @@ static int wanpipe_xilinx_open (netdevice_t* dev) int err = 0; #ifdef CONFIG_PRODUCT_WANPIPE_GENERIC - card->isr = &wp_xilinx_isr; + card->isr = &wp_xilinx_isr; err=xilinx_chip_configure(card); if (err){ xilinx_chip_unconfigure(card); @@ -1974,51 +2281,51 @@ static int wanpipe_xilinx_open (netdevice_t* dev) err=aft_dev_open(card,chan); if (err) { wan_spin_unlock_irq(&card->wandev.lock,&flags); - DEBUG_EVENT("%s: Error failed to configure interface!\n", - chan->if_name); + DEBUG_EVENT("%s: Error failed to configure interface!\n", + chan->if_name); return err; } if (wan_test_bit(0,&chan->tdmv_sync) && - (card->wandev.state == WAN_CONNECTED || - card->tdmv_conf.span_no)){ - /* At this point we are out of sync. The - * DMA was enabled while interface was down. - * We must do a FULL recovery */ - DEBUG_EVENT("%s: Interface resynching!\n", + (card->wandev.state == WAN_CONNECTED || + card->tdmv_conf.span_no)){ + /* At this point we are out of sync. The + * DMA was enabled while interface was down. + * We must do a FULL recovery */ + DEBUG_EVENT("%s: Interface resynching!\n", chan->if_name); - if (card->wandev.state == WAN_CONNECTED){ + if (card->wandev.state == WAN_CONNECTED){ + disable_data_error_intr(card,LINK_DOWN); + enable_data_error_intr(card); + + }else if (card->tdmv_conf.span_no) { + /* The A101/2 Card must supply clock to + * zaptel regardless of state. Thus fake + * the front end connected state */ + disable_data_error_intr(card,LINK_DOWN); + card->fe.fe_status = FE_CONNECTED; + handle_front_end_state(card); + + /* This will set the LEDs to RED and + * update the card state */ + card->fe.fe_status = FE_DISCONNECTED; + handle_front_end_state(card); + } + }else if (!chan->hdlc_eng && chan->common.usedby == API + && card->wandev.state == WAN_CONNECTED){ disable_data_error_intr(card,LINK_DOWN); enable_data_error_intr(card); - - }else if (card->tdmv_conf.span_no) { - /* The A101/2 Card must supply clock to - * zaptel regardless of state. Thus fake - * the front end connected state */ - disable_data_error_intr(card,LINK_DOWN); - card->fe.fe_status = FE_CONNECTED; - handle_front_end_state(card); - - /* This will set the LEDs to RED and - * update the card state */ - card->fe.fe_status = FE_DISCONNECTED; - handle_front_end_state(card); - } - }else if (!chan->hdlc_eng && chan->common.usedby == API - && card->wandev.state == WAN_CONNECTED){ - disable_data_error_intr(card,LINK_DOWN); - enable_data_error_intr(card); } - if (card->wandev.state == WAN_CONNECTED){ - /* If Front End is connected already set interface - * state to Connected too */ - set_chan_state(card, dev, WAN_CONNECTED); + if (card->wandev.state == WAN_CONNECTED){ + /* If Front End is connected already set interface + * state to Connected too */ + set_chan_state(card, dev, WAN_CONNECTED); WAN_NETIF_WAKE_QUEUE(dev); WAN_NETIF_CARRIER_ON(dev); wanpipe_wake_stack(chan); - } + } wan_spin_unlock_irq(&card->wandev.lock,&flags); @@ -2034,29 +2341,29 @@ static int wanpipe_xilinx_open (netdevice_t* dev) /*============================================================================ - * if_close - Close network interface. - * - * @dev: Network device pointer - * - * On ifconfig down, this function gets called in order - * to cleanup interace private area. - * - * IMPORTANT: - * - * No deallocation or unconfiguration should ever occur in this - * function, because the interface can come back up - * (via ifconfig up). - * - * Furthermore, in dynamic interfacace configuration mode, the - * interface will come up and down to reflect the protocol state. - * - * Any deallocation and cleanup can occur in del_if() - * function. That function is called before the dev interface - * itself is deallocated. - * - * Thus, we should only stop the net queue and decrement - * the wanpipe usage counter via wanpipe_close() function. - */ +* if_close - Close network interface. +* +* @dev: Network device pointer +* +* On ifconfig down, this function gets called in order +* to cleanup interace private area. +* +* IMPORTANT: +* +* No deallocation or unconfiguration should ever occur in this +* function, because the interface can come back up +* (via ifconfig up). +* +* Furthermore, in dynamic interfacace configuration mode, the +* interface will come up and down to reflect the protocol state. +* +* Any deallocation and cleanup can occur in del_if() +* function. That function is called before the dev interface +* itself is deallocated. +* +* Thus, we should only stop the net queue and decrement +* the wanpipe usage counter via wanpipe_close() function. +*/ static int wanpipe_xilinx_close (netdevice_t* dev) { private_area_t* chan = wan_netif_priv(dev); @@ -2082,25 +2389,27 @@ static int wanpipe_xilinx_close (netdevice_t* dev) /*============================================================= - * disable_comm - Main shutdown function - * - * @card: Wanpipe device pointer - * - * The command 'wanrouter stop' has been called - * and the whole wanpipe device is going down. - * This is the last function called to disable - * all comunications and deallocate any memory - * that is still allocated. - * - * o Disable communications, turn off interrupts - * o Deallocate memory used, if any - * o Unconfigure TE1 card - */ +* disable_comm - Main shutdown function +* +* @card: Wanpipe device pointer +* +* The command 'wanrouter stop' has been called +* and the whole wanpipe device is going down. +* This is the last function called to disable +* all comunications and deallocate any memory +* that is still allocated. +* +* o Disable communications, turn off interrupts +* o Deallocate memory used, if any +* o Unconfigure TE1 card +*/ static void disable_comm (sdla_t *card) { wan_smp_flag_t flags; + DEBUG_TEST("%s()\n", __FUNCTION__); + /* TE1 - Unconfiging, only on shutdown */ if (IS_TE1_CARD(card)) { wan_smp_flag_t smp_flags,smp_flags1; @@ -2119,8 +2428,8 @@ static void disable_comm (sdla_t *card) wan_spin_lock_irq(&card->wandev.lock,&flags); /* Disable DMA ENGINE before we perform - * core reset. Otherwise, we will receive - * rx fifo errors on subsequent resetart. */ + * core reset. Otherwise, we will receive + * rx fifo errors on subsequent resetart. */ disable_data_error_intr(card,DEVICE_DOWN); wan_set_bit(CARD_DOWN,&card->wandev.critical); @@ -2137,49 +2446,49 @@ static void disable_comm (sdla_t *card) /*============================================================================ - * if_tx_timeout - * - * Kernel networking stack calls this function in case - * the interface has been stopped for TX_TIMEOUT seconds. - * - * This would occur if we lost TX interrupts or the - * card has stopped working for some reason. - * - * Handle transmit timeout event from netif watchdog - */ +* if_tx_timeout +* +* Kernel networking stack calls this function in case +* the interface has been stopped for TX_TIMEOUT seconds. +* +* This would occur if we lost TX interrupts or the +* card has stopped working for some reason. +* +* Handle transmit timeout event from netif watchdog +*/ static void wanpipe_xilinx_tx_timeout (netdevice_t* dev) { - private_area_t* chan = wan_netif_priv(dev); + private_area_t* chan = wan_netif_priv(dev); sdla_t *card = (sdla_t*)chan->common.card; unsigned long dma_descr; u32 reg_lo, reg_hi; /* If our device stays busy for at least 5 seconds then we will - * kick start the device by making dev->tbusy = 0. We expect - * that our device never stays busy more than 5 seconds. So this - * is only used as a last resort. - */ + * kick start the device by making dev->tbusy = 0. We expect + * that our device never stays busy more than 5 seconds. So this + * is only used as a last resort. + */ ++chan->if_stats.collisions; DEBUG_EVENT( "%s: Transmit timed out on %s\n", card->devname,wan_netif_name(dev)); DEBUG_EVENT("%s: TxStatus=0x%X DMAADDR=0x%X DMALEN=%d \n", - chan->if_name, - chan->dma_status, - chan->tx_dma_addr, - chan->tx_dma_len); + chan->if_name, + chan->dma_status, + chan->tx_dma_addr, + chan->tx_dma_len); dma_descr=(chan->logic_ch_num<<4) + XILINX_TxDMA_DESCRIPTOR_LO; - card->hw_iface.bus_read_4(card->hw,dma_descr, ®_lo); + card->hw_iface.bus_read_4(card->hw,dma_descr, ®_lo); - dma_descr=(chan->logic_ch_num<<4) + XILINX_TxDMA_DESCRIPTOR_HI; - card->hw_iface.bus_read_4(card->hw,dma_descr, ®_hi); + dma_descr=(chan->logic_ch_num<<4) + XILINX_TxDMA_DESCRIPTOR_HI; + card->hw_iface.bus_read_4(card->hw,dma_descr, ®_hi); - DEBUG_EVENT("%s:%s: TX Error: Lch=%li DmaLO: 0x%08X DmaHI: 0x%08X\n", - card->devname,chan->if_name,chan->logic_ch_num, - reg_lo, reg_hi); + DEBUG_EVENT("%s:%s: TX Error: Lch=%li DmaLO: 0x%08X DmaHI: 0x%08X\n", + card->devname,chan->if_name,chan->logic_ch_num, + reg_lo, reg_hi); - wan_clear_bit(TX_BUSY,&chan->dma_status); + wan_clear_bit(TX_BUSY,&chan->dma_status); wan_netif_set_ticks(dev, SYSTEM_TICKS); xilinx_tx_fifo_under_recover(card,chan); @@ -2190,38 +2499,38 @@ static void wanpipe_xilinx_tx_timeout (netdevice_t* dev) /*============================================================================ - * if_send - Send a packet on a network interface. - * - * @dev: Network interface pointer - * @skb: Packet obtained from the stack or API - * that should be sent out the port. - * - * o Mark interface as stopped - * (marks start of the transmission) to indicate - * to the stack that the interface is busy. - * - * o Check link state. - * If link is not up, then drop the packet. - * - * o Copy the tx packet into the protocol tx buffers on - * the adapter. - * - * o If tx successful: - * Free the skb buffer and mark interface as running - * and return 0. - * - * o If tx failed, busy: - * Keep interface marked as busy - * Do not free skb buffer - * Enable Tx interrupt (which will tell the stack - * that interace is not busy) - * Return a non-zero value to tell the stack - * that the tx should be retried. - * - * Return: 0 complete (socket buffer must be freed) - * non-0 packet may be re-transmitted - * - */ +* if_send - Send a packet on a network interface. +* +* @dev: Network interface pointer +* @skb: Packet obtained from the stack or API +* that should be sent out the port. +* +* o Mark interface as stopped +* (marks start of the transmission) to indicate +* to the stack that the interface is busy. +* +* o Check link state. +* If link is not up, then drop the packet. +* +* o Copy the tx packet into the protocol tx buffers on +* the adapter. +* +* o If tx successful: +* Free the skb buffer and mark interface as running +* and return 0. +* +* o If tx failed, busy: +* Keep interface marked as busy +* Do not free skb buffer +* Enable Tx interrupt (which will tell the stack +* that interace is not busy) +* Return a non-zero value to tell the stack +* that the tx should be retried. +* +* Return: 0 complete (socket buffer must be freed) +* non-0 packet may be re-transmitted +* +*/ #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) static int wan_aft_output(netdevice_t *dev, netskb_t *skb, struct sockaddr *dst, struct rtentry *rt0) @@ -2235,9 +2544,15 @@ static int wanpipe_xilinx_send (netskb_t* skb, netdevice_t* dev) WAN_ASSERT(chan == NULL); WAN_ASSERT(chan->common.card == NULL); card = (sdla_t*)chan->common.card; + +#if defined(__WINDOWS__) + DEBUG_TEST("%s(): IRQL: %s, card->fe.fe_status: %s\n", __FUNCTION__, + decode_irql(KeGetCurrentIrql()), FE_STATUS_DECODE(card->fe.fe_status)); +#endif + if (skb == NULL){ /* This should never happen. Just a sanity check. - */ + */ DEBUG_EVENT( "%s: interface %s got kicked!\n", card->devname, wan_netif_name(dev)); @@ -2246,17 +2561,17 @@ static int wanpipe_xilinx_send (netskb_t* skb, netdevice_t* dev) } /* Non 2.4 kernels used to call if_send() - * after TX_TIMEOUT seconds have passed of interface - * being busy. Same as if_tx_timeout() in 2.4 kernels */ + * after TX_TIMEOUT seconds have passed of interface + * being busy. Same as if_tx_timeout() in 2.4 kernels */ #if defined(LINUX_2_1) if (dev->tbusy){ /* If our device stays busy for at least 5 seconds then we will - * kick start the device by making dev->tbusy = 0. We expect - * that our device never stays busy more than 5 seconds. So this - * is only used as a last resort. - */ - ++chan->if_stats.collisions; + * kick start the device by making dev->tbusy = 0. We expect + * that our device never stays busy more than 5 seconds. So this + * is only used as a last resort. + */ + ++chan->if_stats.collisions; if((SYSTEM_TICKS - chan->tick_counter) < (5 * HZ)) { return 1; } @@ -2266,39 +2581,41 @@ static int wanpipe_xilinx_send (netskb_t* skb, netdevice_t* dev) #endif if (chan->common.state != WAN_CONNECTED){ + DEBUG_TEST("%s(): line: %d: chan ptr: 0x%p, chan->common.state: %d\n", __FUNCTION__, __LINE__, chan, chan->common.state); ++chan->if_stats.tx_carrier_errors; - WAN_NETIF_STOP_QUEUE(dev); + WAN_NETIF_STOP_QUEUE(dev); wan_netif_set_ticks(dev, SYSTEM_TICKS); return 1; } else if (!WAN_NETIF_UP(dev)) { + DEBUG_TEST("%s(): line: %d\n", __FUNCTION__, __LINE__); ++chan->if_stats.tx_carrier_errors; - WAN_NETIF_START_QUEUE(dev); + WAN_NETIF_START_QUEUE(dev); wan_skb_free(skb); wan_netif_set_ticks(dev, SYSTEM_TICKS); return 0; }else { if (chan->common.usedby == TDM_VOICE || - chan->common.usedby == TDM_VOICE_DCHAN){ + chan->common.usedby == TDM_VOICE_DCHAN){ - if (!card->u.aft.tdmv_dchan || card->u.aft.tdmv_dchan>32){ - wan_skb_free(skb); - WAN_NETIF_START_QUEUE(dev); - goto if_send_exit_crit; - } + if (!card->u.aft.tdmv_dchan || card->u.aft.tdmv_dchan>32){ + wan_skb_free(skb); + WAN_NETIF_START_QUEUE(dev); + goto if_send_exit_crit; + } - chan=(private_area_t*)card->u.aft.dev_to_ch_map[card->u.aft.tdmv_dchan-1]; - if (!chan){ - wan_skb_free(skb); - WAN_NETIF_START_QUEUE(dev); - goto if_send_exit_crit; - } + chan=(private_area_t*)card->u.aft.dev_to_ch_map[card->u.aft.tdmv_dchan-1]; + if (!chan){ + wan_skb_free(skb); + WAN_NETIF_START_QUEUE(dev); + goto if_send_exit_crit; + } - if (!chan->hdlc_eng){ - wan_skb_free(skb); - WAN_NETIF_START_QUEUE(dev); - goto if_send_exit_crit; - } + if (!chan->hdlc_eng){ + wan_skb_free(skb); + WAN_NETIF_START_QUEUE(dev); + goto if_send_exit_crit; + } }else if (chan->common.usedby == API){ @@ -2312,18 +2629,22 @@ static int wanpipe_xilinx_send (netskb_t* skb, netdevice_t* dev) } if (chan->max_tx_bufs == 1) { - wan_smp_flag_t smp_flags; + wan_smp_flag_t smp_flags; + DEBUG_TEST("%s(): line: %d\n", __FUNCTION__, __LINE__); + wan_spin_lock_irq(&card->wandev.lock, &smp_flags); - if (wan_test_bit(TX_BUSY,&chan->dma_status)){ + if (wan_test_bit(TX_BUSY,&chan->dma_status)){ WAN_NETIF_STOP_QUEUE(dev); wan_spin_unlock_irq(&card->wandev.lock, &smp_flags); return 1; } - wan_spin_unlock_irq(&card->wandev.lock, &smp_flags); + wan_spin_unlock_irq(&card->wandev.lock, &smp_flags); goto xilinx_tx_dma; } else if (wan_skb_queue_len(&chan->wp_tx_pending_list) > chan->max_tx_bufs){ wan_smp_flag_t smp_flags; + DEBUG_TEST("%s(): line: %d\n", __FUNCTION__, __LINE__); + wan_spin_lock_irq(&card->wandev.lock, &smp_flags); WAN_NETIF_STOP_QUEUE(dev); /*stop_net_queue(dev);*/ wan_spin_unlock_irq(&card->wandev.lock, &smp_flags); @@ -2339,9 +2660,9 @@ xilinx_tx_dma: #if 0 if (is_tdm_api(chan,&chan->wp_tdm_api_cfg)){ /* We must do this here, since it guarantees us that - * the packet will be queued up. However, we should - * disable the lock since this process could be computer - * intensive */ + * the packet will be queued up. However, we should + * disable the lock since this process could be computer + * intensive */ int err=wanpipe_tdm_api_tx(&chan->wp_tdm_api_cfg,&skb); if (err){ ++chan->if_stats.tx_errors; @@ -2358,27 +2679,26 @@ xilinx_tx_dma: } wan_skb_queue_tail(&chan->wp_tx_pending_list,skb); - if (chan->hdlc_eng) { + if (chan->hdlc_eng) { xilinx_dma_tx(card,chan); } wan_netif_set_ticks(dev, SYSTEM_TICKS); } } - #ifdef __LINUX__ - if (dev->tx_queue_len < chan->max_tx_bufs && - dev->tx_queue_len > 0) { - DEBUG_EVENT("%s: Resizing Tx Queue Len to %li\n", + if (dev->tx_queue_len < chan->max_tx_bufs && + dev->tx_queue_len > 0) { + DEBUG_EVENT("%s: Resizing Tx Queue Len to %li\n", chan->if_name,dev->tx_queue_len); - chan->max_tx_bufs = dev->tx_queue_len; + chan->max_tx_bufs = dev->tx_queue_len; } if (dev->tx_queue_len > chan->max_tx_bufs && - chan->max_tx_bufs != MAX_TX_BUF) { - DEBUG_EVENT("%s: Resizing Tx Queue Len to %i\n", + chan->max_tx_bufs != MAX_TX_BUF) { + DEBUG_EVENT("%s: Resizing Tx Queue Len to %i\n", chan->if_name,MAX_TX_BUF); - chan->max_tx_bufs = MAX_TX_BUF; + chan->max_tx_bufs = MAX_TX_BUF; } #endif @@ -2390,15 +2710,15 @@ if_send_exit_crit: } -#if defined(__LINUX__) +#if defined(__LINUX__) || defined(__WINDOWS__) /*============================================================================ - * if_stats - * - * Used by /proc/net/dev and ifconfig to obtain interface - * statistics. - * - * Return a pointer to struct net_device_stats. - */ +* if_stats +* +* Used by /proc/net/dev and ifconfig to obtain interface +* statistics. +* +* Return a pointer to struct net_device_stats. +*/ static struct net_device_stats gstats; static struct net_device_stats* wanpipe_xilinx_ifstats (netdevice_t* dev) { @@ -2416,24 +2736,24 @@ static struct net_device_stats* wanpipe_xilinx_ifstats (netdevice_t* dev) #endif /*======================================================================== - * - * if_do_ioctl - Ioctl handler for fr - * - * @dev: Device subject to ioctl - * @ifr: Interface request block from the user - * @cmd: Command that is being issued - * - * This function handles the ioctls that may be issued by the user - * to control or debug the protocol or hardware . - * - * It does both busy and security checks. - * This function is intended to be wrapped by callers who wish to - * add additional ioctl calls of their own. - * - * Used by: SNMP Mibs - * wanpipemon debugger - * - */ +* +* if_do_ioctl - Ioctl handler for fr +* +* @dev: Device subject to ioctl +* @ifr: Interface request block from the user +* @cmd: Command that is being issued +* +* This function handles the ioctls that may be issued by the user +* to control or debug the protocol or hardware . +* +* It does both busy and security checks. +* This function is intended to be wrapped by callers who wish to +* add additional ioctl calls of their own. +* +* Used by: SNMP Mibs +* wanpipemon debugger +* +*/ static int wanpipe_xilinx_ioctl(netdevice_t *dev, struct ifreq *ifr, wan_ioctl_cmd_t cmd) { @@ -2445,155 +2765,159 @@ wanpipe_xilinx_ioctl(netdevice_t *dev, struct ifreq *ifr, wan_ioctl_cmd_t cmd) #endif int err=0; + DEBUG_UDP("%s(): line: %d\n", __FUNCTION__, __LINE__); + if (!dev || !WAN_NETIF_UP(dev)){ + DEBUG_UDP("%s(): line: %d\n", __FUNCTION__, __LINE__); return -ENODEV; } if (!(chan=(private_area_t*)wan_netif_priv(dev))){ + DEBUG_UDP("%s(): line: %d\n", __FUNCTION__, __LINE__); return -ENODEV; } card=(sdla_t*)chan->common.card; + DEBUG_UDP("%s(): line: %d\n", __FUNCTION__, __LINE__); switch(cmd) { #if defined(__LINUX__) - case SIOC_WANPIPE_BIND_SK: - if (!ifr){ - err= -EINVAL; - break; - } +case SIOC_WANPIPE_BIND_SK: + if (!ifr){ + err= -EINVAL; + break; + } #if !defined(CONFIG_PRODUCT_WANPIPE_GENERIC) - wan_spin_lock_irq(&card->wandev.lock, &smp_flags); - err=wan_bind_api_to_svc(chan,ifr->ifr_data); - wan_spin_unlock_irq(&card->wandev.lock, &smp_flags); + wan_spin_lock_irq(&card->wandev.lock, &smp_flags); + err=wan_bind_api_to_svc(chan,ifr->ifr_data); + wan_spin_unlock_irq(&card->wandev.lock, &smp_flags); #endif - chan->if_stats.tx_carrier_errors=0; + chan->if_stats.tx_carrier_errors=0; - break; + break; - case SIOC_WANPIPE_UNBIND_SK: - if (!ifr){ - err= -EINVAL; - break; - } +case SIOC_WANPIPE_UNBIND_SK: + if (!ifr){ + err= -EINVAL; + break; + } #if !defined(CONFIG_PRODUCT_WANPIPE_GENERIC) - wan_spin_lock_irq(&card->wandev.lock, &smp_flags); - err=wan_unbind_api_from_svc(chan,ifr->ifr_data); - wan_spin_unlock_irq(&card->wandev.lock, &smp_flags); + wan_spin_lock_irq(&card->wandev.lock, &smp_flags); + err=wan_unbind_api_from_svc(chan,ifr->ifr_data); + wan_spin_unlock_irq(&card->wandev.lock, &smp_flags); #endif - break; + break; - case SIOC_WANPIPE_CHECK_TX: - case SIOC_ANNEXG_CHECK_TX: - err=0; - break; +case SIOC_WANPIPE_CHECK_TX: +case SIOC_ANNEXG_CHECK_TX: + err=0; + break; - case SIOC_WANPIPE_DEV_STATE: - err = chan->common.state; - break; +case SIOC_WANPIPE_DEV_STATE: + err = chan->common.state; + break; - case SIOC_ANNEXG_KICK: - err=0; - break; +case SIOC_ANNEXG_KICK: + err=0; + break; #endif - case SIOC_WAN_DEVEL_IOCTL: - err = aft_devel_ioctl(card, ifr); - break; +case SIOC_WAN_DEVEL_IOCTL: + err = aft_devel_ioctl(card, ifr); + break; #if defined (__LINUX__) - case SIOC_WANPIPE_GET_DEVICE_CONFIG_ID: - err=card->wandev.config_id; - break; +case SIOC_WANPIPE_GET_DEVICE_CONFIG_ID: + err=card->wandev.config_id; + break; #endif - case SIOC_WANPIPE_PIPEMON: +case SIOC_WANPIPE_PIPEMON: + NET_ADMIN_CHECK(); - NET_ADMIN_CHECK(); + if (wan_atomic_read(&chan->udp_pkt_len) != 0){ + return -EBUSY; + } - if (wan_atomic_read(&chan->udp_pkt_len) != 0){ - return -EBUSY; - } + wan_atomic_set(&chan->udp_pkt_len,MAX_LGTH_UDP_MGNT_PKT); - wan_atomic_set(&chan->udp_pkt_len,MAX_LGTH_UDP_MGNT_PKT); - - /* For performance reasons test the critical - * here before spin lock */ - if (wan_test_bit(0,&card->in_isr)){ - wan_atomic_set(&chan->udp_pkt_len,0); - return -EBUSY; - } + /* For performance reasons test the critical + * here before spin lock */ + if (wan_test_bit(0,&card->in_isr)){ + wan_atomic_set(&chan->udp_pkt_len,0); + return -EBUSY; + } - wan_udp_pkt=(wan_udp_pkt_t*)chan->udp_pkt_data; - if (WAN_COPY_FROM_USER(&wan_udp_pkt->wan_udp_hdr,ifr->ifr_data,sizeof(wan_udp_hdr_t))){ - wan_atomic_set(&chan->udp_pkt_len,0); - return -EFAULT; - } + wan_udp_pkt=(wan_udp_pkt_t*)chan->udp_pkt_data; + if (WAN_COPY_FROM_USER(&wan_udp_pkt->wan_udp_hdr,ifr->ifr_data,sizeof(wan_udp_hdr_t))){ + wan_atomic_set(&chan->udp_pkt_len,0); + return -EFAULT; + } - /* We have to check here again because we don't know - * what happened during spin_lock */ - if (wan_test_bit(0,&card->in_isr)) { - DEBUG_TEST( "%s:%s Pipemon command failed, Driver busy: try again.\n", - card->devname,wan_netif_name(dev)); - wan_atomic_set(&chan->udp_pkt_len,0); - return -EBUSY; - } + /* We have to check here again because we don't know + * what happened during spin_lock */ + if (wan_test_bit(0,&card->in_isr)) { + DEBUG_TEST( "%s:%s Pipemon command failed, Driver busy: try again.\n", + card->devname,wan_netif_name(dev)); + wan_atomic_set(&chan->udp_pkt_len,0); + return -EBUSY; + } - process_udp_mgmt_pkt(card,dev,chan,1); + process_udp_mgmt_pkt(card,dev,chan,1); - /* This area will still be critical to other - * PIPEMON commands due to udp_pkt_len - * thus we can release the irq */ + /* This area will still be critical to other + * PIPEMON commands due to udp_pkt_len + * thus we can release the irq */ - if (wan_atomic_read(&chan->udp_pkt_len) > sizeof(wan_udp_pkt_t)){ - DEBUG_EVENT( "%s: Error: Pipemon buf too bit on the way up! %d\n", - card->devname,wan_atomic_read(&chan->udp_pkt_len)); - wan_atomic_set(&chan->udp_pkt_len,0); - return -EINVAL; - } + if (wan_atomic_read(&chan->udp_pkt_len) > sizeof(wan_udp_pkt_t)){ + DEBUG_EVENT( "%s: Error: Pipemon buf too bit on the way up! %d\n", + card->devname,wan_atomic_read(&chan->udp_pkt_len)); + wan_atomic_set(&chan->udp_pkt_len,0); + return -EINVAL; + } - if (WAN_COPY_TO_USER(ifr->ifr_data,&wan_udp_pkt->wan_udp_hdr,sizeof(wan_udp_hdr_t))){ - wan_atomic_set(&chan->udp_pkt_len,0); - return -EFAULT; - } + if (WAN_COPY_TO_USER(ifr->ifr_data,&wan_udp_pkt->wan_udp_hdr,sizeof(wan_udp_hdr_t))){ + wan_atomic_set(&chan->udp_pkt_len,0); + return -EFAULT; + } - wan_atomic_set(&chan->udp_pkt_len,0); - return 0; + wan_atomic_set(&chan->udp_pkt_len,0); + return 0; - case SIOC_AFT_CUSTOMER_ID: +case SIOC_AFT_CUSTOMER_ID: - if (!ifr){ - return -EINVAL; - }else{ - unsigned char cid; - wan_spin_lock_irq(&card->wandev.lock, &smp_flags); - cid=read_cpld(card,CUSTOMER_CPLD_ID_REG); - wan_spin_unlock_irq(&card->wandev.lock, &smp_flags); - return WAN_COPY_TO_USER(ifr->ifr_data,&cid,sizeof(unsigned char)); - } - break; + if (!ifr){ + return -EINVAL; + }else{ + unsigned char cid; + wan_spin_lock_irq(&card->wandev.lock, &smp_flags); + cid=read_cpld(card,CUSTOMER_CPLD_ID_REG); + wan_spin_unlock_irq(&card->wandev.lock, &smp_flags); + return WAN_COPY_TO_USER(ifr->ifr_data,&cid,sizeof(unsigned char)); + } + break; - default: +default: #if defined(CONFIG_PRODUCT_WANPIPE_GENERIC) - if (card->wandev.wanpipe_ioctl){ - err = card->wandev.wanpipe_ioctl(dev, ifr, cmd); - } + if (card->wandev.wanpipe_ioctl){ + err = card->wandev.wanpipe_ioctl(dev, ifr, cmd); + } #else # if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) - return 1; + return 1; # else - DEBUG_TEST("%s: Command %x not supported!\n", - card->devname,cmd); - return -EOPNOTSUPP; + DEBUG_TEST("%s: Command %x not supported!\n", + card->devname,cmd); + return -EOPNOTSUPP; # endif #endif - break; + break; } return err; @@ -2601,17 +2925,17 @@ wanpipe_xilinx_ioctl(netdevice_t *dev, struct ifreq *ifr, wan_ioctl_cmd_t cmd) /**SECTION********************************************************** - * - * FIRMWARE Specific Interface Functions - * - *******************************************************************/ +* +* FIRMWARE Specific Interface Functions +* +*******************************************************************/ /*============================================================================ - * xilinx_chip_configure - * - * - */ +* xilinx_chip_configure +* +* +*/ static int xilinx_chip_configure(sdla_t *card) { @@ -2620,7 +2944,7 @@ static int xilinx_chip_configure(sdla_t *card) u16 adapter_type,adptr_security; wan_smp_flag_t smp_flags; - DEBUG_CFG("Xilinx Chip Configuration. -- \n"); + DEBUG_CFG("Xilinx Chip Configuration. -- \n"); xilinx_delay(1); @@ -2637,14 +2961,14 @@ static int xilinx_chip_configure(sdla_t *card) wan_set_bit(FRONT_END_FRAME_FLAG_ENABLE_BIT,®); }else{ DEBUG_EVENT("%s: Error: Xilinx doesn't support non T1/E1 interface!\n", - card->devname); + card->devname); return -EINVAL; } /* Front end reference clock configuration. - * TE1 front end can use either Oscillator to - * generate clock, or use the clock from the - * other line. Supported in Ver:24 */ + * TE1 front end can use either Oscillator to + * generate clock, or use the clock from the + * other line. Supported in Ver:24 */ if (WAN_TE1_REFCLK(&card->fe) == WAN_TE1_REFCLK_OSC){ wan_clear_bit(AFT_TE1_FE_REF_CLOCK_BIT,®); }else{ @@ -2655,15 +2979,15 @@ static int xilinx_chip_configure(sdla_t *card) WP_DELAY(10000); - /* Reset PMC */ - card->hw_iface.bus_read_4(card->hw,XILINX_CHIP_CFG_REG,®); - wan_clear_bit(FRONT_END_RESET_BIT,®); - card->hw_iface.bus_write_4(card->hw,XILINX_CHIP_CFG_REG,reg); - WP_DELAY(1000); + /* Reset PMC */ + card->hw_iface.bus_read_4(card->hw,XILINX_CHIP_CFG_REG,®); + wan_clear_bit(FRONT_END_RESET_BIT,®); + card->hw_iface.bus_write_4(card->hw,XILINX_CHIP_CFG_REG,reg); + WP_DELAY(1000); - wan_set_bit(FRONT_END_RESET_BIT,®); - card->hw_iface.bus_write_4(card->hw,XILINX_CHIP_CFG_REG,reg); - WP_DELAY(100); + wan_set_bit(FRONT_END_RESET_BIT,®); + card->hw_iface.bus_write_4(card->hw,XILINX_CHIP_CFG_REG,reg); + WP_DELAY(100); DEBUG_CFG("--- Chip Reset. -- \n"); @@ -2693,13 +3017,13 @@ static int xilinx_chip_configure(sdla_t *card) /* Sanity check, where we make sure that A101 - * adapter gracefully fails on non existing - * secondary port */ + * adapter gracefully fails on non existing + * secondary port */ if (adapter_type == A101_1TE1_SUBSYS_VENDOR && - card->wandev.S514_cpu_no[0] == SDLA_CPU_B){ - DEBUG_EVENT("%s: Hardware Config Mismatch: A103 Adapter not found!\n", - card->devname); - /*return -ENODEV;*/ + card->wandev.S514_cpu_no[0] == SDLA_CPU_B){ + DEBUG_EVENT("%s: Hardware Config Mismatch: A103 Adapter not found!\n", + card->devname); + /*return -ENODEV;*/ } @@ -2708,30 +3032,30 @@ static int xilinx_chip_configure(sdla_t *card) adptr_security = adptr_security & SECURITY_CPLD_MASK; DEBUG_EVENT("%s: Hardware Adapter Type 0x%X Scurity 0x%02X\n", - card->devname,adapter_type, adptr_security); + card->devname,adapter_type, adptr_security); switch(adptr_security){ - case SECURITY_1LINE_UNCH: - DEBUG_EVENT("%s: Security 1 Line UnCh\n", - card->devname); - break; - case SECURITY_1LINE_CH: - DEBUG_EVENT("%s: Security 1 Line Ch\n", - card->devname); - break; - case SECURITY_2LINE_UNCH: - DEBUG_EVENT("%s: Security 2 Line UnCh\n", - card->devname); - break; - case SECURITY_2LINE_CH: - DEBUG_EVENT("%s: Security 2 Line Ch\n", - card->devname); - break; - default: - DEBUG_EVENT("%s: Error Invalid Security ID=0x%X\n", - card->devname,adptr_security); - return -EINVAL; +case SECURITY_1LINE_UNCH: + DEBUG_EVENT("%s: Security 1 Line UnCh\n", + card->devname); + break; +case SECURITY_1LINE_CH: + DEBUG_EVENT("%s: Security 1 Line Ch\n", + card->devname); + break; +case SECURITY_2LINE_UNCH: + DEBUG_EVENT("%s: Security 2 Line UnCh\n", + card->devname); + break; +case SECURITY_2LINE_CH: + DEBUG_EVENT("%s: Security 2 Line Ch\n", + card->devname); + break; +default: + DEBUG_EVENT("%s: Error Invalid Security ID=0x%X\n", + card->devname,adptr_security); + return -EINVAL; } #endif @@ -2745,7 +3069,7 @@ static int xilinx_chip_configure(sdla_t *card) err=aft_core_ready(card); if (err != 0){ DEBUG_EVENT("%s: WARNING: HDLC Core Not Ready: B4 TE CFG!\n", - card->devname); + card->devname); } @@ -2761,11 +3085,11 @@ static int xilinx_chip_configure(sdla_t *card) card->hw_iface.hw_unlock(card->hw,&smp_flags); if (err){ - DEBUG_EVENT("%s: Failed %s configuratoin!\n", - card->devname, - (IS_T1_CARD(card))?"T1":"E1"); - return -EINVAL; - } + DEBUG_EVENT("%s: Failed %s configuratoin!\n", + card->devname, + (IS_T1_CARD(card))?"T1":"E1"); + return -EINVAL; + } /* Run rest of initialization not from lock */ if (card->wandev.fe_iface.post_init){ err=card->wandev.fe_iface.post_init(&card->fe); @@ -2776,7 +3100,7 @@ static int xilinx_chip_configure(sdla_t *card) err=aft_core_ready(card); if (err != 0){ DEBUG_EVENT("%s: Error: HDLC Core Not Ready!\n", - card->devname); + card->devname); card->hw_iface.bus_read_4(card->hw,XILINX_CHIP_CFG_REG, ®); @@ -2785,10 +3109,10 @@ static int xilinx_chip_configure(sdla_t *card) card->hw_iface.bus_write_4(card->hw,XILINX_CHIP_CFG_REG,reg); return err; - } else{ + } else{ DEBUG_CFG("%s: HDLC Core Ready 0x%08X\n", - card->devname,reg); - } + card->devname,reg); + } xilinx_delay(1); @@ -2799,12 +3123,12 @@ static int xilinx_chip_configure(sdla_t *card) reg|=(XILINX_DMA_FIFO_LO << DMA_FIFO_LO_MARK_BIT_SHIFT); /* Enable global DMA engine and set to default - * number of active channels. Note: this value will - * change in dev configuration */ + * number of active channels. Note: this value will + * change in dev configuration */ reg|=(XILINX_DEFLT_ACTIVE_CH << DMA_ACTIVE_CHANNEL_BIT_SHIFT); wan_set_bit(DMA_ENGINE_ENABLE_BIT,®); - DEBUG_CFG("--- Setup DMA control Reg. -- \n"); + DEBUG_CFG("--- Setup DMA control Reg. -- \n"); card->hw_iface.bus_write_4(card->hw,XILINX_DMA_CONTROL_REG,reg); DEBUG_CFG("--- Tx/Rx global enable. -- \n"); @@ -2814,25 +3138,25 @@ static int xilinx_chip_configure(sdla_t *card) reg=0; card->hw_iface.bus_write_4(card->hw,XILINX_TIMESLOT_HDLC_CHAN_REG,reg); - /* Clear interrupt pending registers befor first interrupt enable */ + /* Clear interrupt pending registers befor first interrupt enable */ card->hw_iface.bus_read_4(card->hw, XILINX_DMA_RX_INTR_PENDING_REG, &tmp); card->hw_iface.bus_read_4(card->hw, XILINX_DMA_TX_INTR_PENDING_REG, &tmp); card->hw_iface.bus_read_4(card->hw,XILINX_HDLC_RX_INTR_PENDING_REG, &tmp); card->hw_iface.bus_read_4(card->hw,XILINX_HDLC_TX_INTR_PENDING_REG, &tmp); card->hw_iface.bus_read_4(card->hw,XILINX_CHIP_CFG_REG, (u32*)®); if (wan_test_bit(DMA_INTR_FLAG,®)){ - DEBUG_EVENT("%s: Error: Active DMA Interrupt Pending. !\n", - card->devname); + DEBUG_EVENT("%s: Error: Active DMA Interrupt Pending. !\n", + card->devname); - reg = 0; + reg = 0; /* Disable the chip/hdlc reset condition */ wan_set_bit(CHIP_RESET_BIT,®); card->hw_iface.bus_write_4(card->hw,XILINX_CHIP_CFG_REG,reg); - return err; + return err; } if (wan_test_bit(ERROR_INTR_FLAG,®)){ - DEBUG_EVENT("%s: Error: Active Error Interrupt Pending. !\n", - card->devname); + DEBUG_EVENT("%s: Error: Active Error Interrupt Pending. !\n", + card->devname); reg = 0; /* Disable the chip/hdlc reset condition */ @@ -2843,15 +3167,15 @@ static int xilinx_chip_configure(sdla_t *card) /* Alawys disable global data and error - * interrupts */ - wan_clear_bit(GLOBAL_INTR_ENABLE_BIT,®); + * interrupts */ + wan_clear_bit(GLOBAL_INTR_ENABLE_BIT,®); wan_clear_bit(ERROR_INTR_ENABLE_BIT,®); /* Always enable the front end interrupt */ wan_set_bit(FRONT_END_INTR_ENABLE_BIT,®); - DEBUG_CFG("%s: Enable Front End Interrupts\n", - card->devname); + DEBUG_CFG("%s: Enable Front End Interrupts\n", + card->devname); xilinx_delay(1); @@ -2862,16 +3186,16 @@ static int xilinx_chip_configure(sdla_t *card) } /*============================================================================ - * xilinx_chip_unconfigure - * - * - */ +* xilinx_chip_unconfigure +* +* +*/ static int xilinx_chip_unconfigure(sdla_t *card) { u32 reg = 0; - card->hw_iface.bus_write_4(card->hw,XILINX_TIMESLOT_HDLC_CHAN_REG,reg); + card->hw_iface.bus_write_4(card->hw,XILINX_TIMESLOT_HDLC_CHAN_REG,reg); card->hw_iface.bus_read_4(card->hw,XILINX_CHIP_CFG_REG, ®); /* Enable the chip/hdlc reset condition */ reg=0; @@ -2882,17 +3206,17 @@ static int xilinx_chip_unconfigure(sdla_t *card) /*============================================================================ - * xilinx_dev_configure - * - * - */ +* xilinx_dev_configure +* +* +*/ static int xilinx_dev_configure(sdla_t *card, private_area_t *chan) { u32 reg; long free_logic_ch,i; - DEBUG_TEST("-- Configure Xilinx. --\n"); + DEBUG_TEST("-- Configure Xilinx. --\n"); chan->logic_ch_num=-1; @@ -2901,10 +3225,10 @@ static int xilinx_dev_configure(sdla_t *card, private_area_t *chan) } /* Channel definition section. If not channels defined - * return error */ + * return error */ if (chan->time_slot_map == 0){ DEBUG_EVENT("%s: Invalid Channel Selection 0x%X\n", - card->devname,chan->time_slot_map); + card->devname,chan->time_slot_map); return -EINVAL; } @@ -2914,34 +3238,34 @@ static int xilinx_dev_configure(sdla_t *card, private_area_t *chan) xilinx_delay(1); /* Check that the time slot is not being used. If it is - * stop the interface setup. Notice, though we proceed - * to check for all timeslots before we start binding - * the channels in. This way, we don't have to go back - * and clean the time_slot_map */ + * stop the interface setup. Notice, though we proceed + * to check for all timeslots before we start binding + * the channels in. This way, we don't have to go back + * and clean the time_slot_map */ for (i=0;iu.aft.num_of_time_slots;i++){ if (wan_test_bit(i,&chan->time_slot_map)){ if (chan->first_time_slot == -1){ DEBUG_TEST("%s:%s: Setting first time slot to %ld\n", - card->devname,chan->if_name,i); + card->devname,chan->if_name,i); chan->first_time_slot=i; } DEBUG_CFG("%s: Configuring %s for timeslot %ld\n", - card->devname, chan->if_name, - IS_E1_CARD(card)?i:i+1); + card->devname, chan->if_name, + IS_E1_CARD(card)?i:i+1); if (wan_test_bit(i,&card->u.aft.time_slot_map)){ DEBUG_EVENT("%s: Channel/Time Slot resource conflict!\n", - card->devname); + card->devname); DEBUG_EVENT("%s: %s: Channel/Time Slot %ld, aready in use!\n", - card->devname,chan->if_name,(i+1)); + card->devname,chan->if_name,(i+1)); return -EEXIST; } /* Calculate the number of timeslots for this - * interface */ + * interface */ ++chan->num_of_time_slots; } } @@ -2969,13 +3293,13 @@ static int xilinx_dev_configure(sdla_t *card, private_area_t *chan) reg&=~TIMESLOT_BIT_MASK; /*FIXME do not hardcode !*/ - reg&= HDLC_LCH_TIMESLOT_MASK; /* mask not valid bits */ + reg&= HDLC_LCH_TIMESLOT_MASK; /* mask not valid bits */ /* Select a Timeslot for configuration */ card->hw_iface.bus_write_4(card->hw, - XILINX_TIMESLOT_HDLC_CHAN_REG, - (reg|(i<logic_ch_num&CONTROL_RAM_DATA_MASK; @@ -2988,19 +3312,19 @@ static int xilinx_dev_configure(sdla_t *card, private_area_t *chan) #endif reg|=(chan->fifo_base_addr&HDLC_FIFO_BASE_ADDR_MASK)<< - HDLC_FIFO_BASE_ADDR_SHIFT; + HDLC_FIFO_BASE_ADDR_SHIFT; if (!chan->hdlc_eng){ wan_set_bit(TRANSPARENT_MODE_BIT,®); } DEBUG_TEST("Setting Timeslot %ld to logic ch %ld Reg=0x%X\n", - i, chan->logic_ch_num,reg); + i, chan->logic_ch_num,reg); xilinx_write_ctrl_hdlc(card, - i, - XILINX_CONTROL_RAM_ACCESS_BUF, - reg); + i, + XILINX_CONTROL_RAM_ACCESS_BUF, + reg); } } @@ -3017,41 +3341,41 @@ static int xilinx_dev_configure(sdla_t *card, private_area_t *chan) if (!wan_test_bit(i,&card->u.aft.time_slot_map)){ card->hw_iface.bus_read_4(card->hw, - XILINX_TIMESLOT_HDLC_CHAN_REG, - ®); + XILINX_TIMESLOT_HDLC_CHAN_REG, + ®); - reg&=~TIMESLOT_BIT_MASK; - reg&= HDLC_LCH_TIMESLOT_MASK; /* mask not valid bits */ + reg&=~TIMESLOT_BIT_MASK; + reg&= HDLC_LCH_TIMESLOT_MASK; /* mask not valid bits */ /* Select a Timeslot for configuration */ card->hw_iface.bus_write_4(card->hw, - XILINX_TIMESLOT_HDLC_CHAN_REG, - (reg|(i<if_name,free_logic_ch); + chan->if_name,free_logic_ch); xilinx_delay(1); @@ -3072,22 +3396,22 @@ static int xilinx_dev_configure(sdla_t *card, private_area_t *chan) card->hw_iface.bus_read_4(card->hw, XILINX_TIMESLOT_HDLC_CHAN_REG, ®); reg&=~HDLC_LOGIC_CH_BIT_MASK; - reg&=HDLC_LCH_TIMESLOT_MASK; /* mask not valid bits */ + reg&=HDLC_LCH_TIMESLOT_MASK; /* mask not valid bits */ card->hw_iface.bus_write_4(card->hw, - XILINX_TIMESLOT_HDLC_CHAN_REG, - (reg|(free_logic_ch&HDLC_LOGIC_CH_BIT_MASK))); + XILINX_TIMESLOT_HDLC_CHAN_REG, + (reg|(free_logic_ch&HDLC_LOGIC_CH_BIT_MASK))); reg=0; wan_clear_bit(HDLC_RX_PROT_DISABLE_BIT,®); - wan_clear_bit(HDLC_TX_PROT_DISABLE_BIT,®); + wan_clear_bit(HDLC_TX_PROT_DISABLE_BIT,®); - wan_set_bit(HDLC_RX_ADDR_RECOGN_DIS_BIT,®); + wan_set_bit(HDLC_RX_ADDR_RECOGN_DIS_BIT,®); xilinx_write_ctrl_hdlc(card, - chan->first_time_slot, - XILINX_HDLC_CONTROL_REG, - reg); + chan->first_time_slot, + XILINX_HDLC_CONTROL_REG, + reg); } } @@ -3095,11 +3419,11 @@ static int xilinx_dev_configure(sdla_t *card, private_area_t *chan) card->hw_iface.bus_read_4(card->hw, XILINX_TIMESLOT_HDLC_CHAN_REG, ®); reg&=~HDLC_LOGIC_CH_BIT_MASK; - reg&= HDLC_LCH_TIMESLOT_MASK; /* mask not valid bits */ + reg&= HDLC_LCH_TIMESLOT_MASK; /* mask not valid bits */ card->hw_iface.bus_write_4(card->hw, - XILINX_TIMESLOT_HDLC_CHAN_REG, - (reg|(chan->logic_ch_num&HDLC_LOGIC_CH_BIT_MASK))); + XILINX_TIMESLOT_HDLC_CHAN_REG, + (reg|(chan->logic_ch_num&HDLC_LOGIC_CH_BIT_MASK))); reg=0; @@ -3108,21 +3432,21 @@ static int xilinx_dev_configure(sdla_t *card, private_area_t *chan) wan_clear_bit(HDLC_RX_PROT_DISABLE_BIT,®); wan_clear_bit(HDLC_TX_PROT_DISABLE_BIT,®); DEBUG_TEST("%s:%s: Config for HDLC mode\n", - card->devname,chan->if_name); + card->devname,chan->if_name); }else{ /* Transprent Mode */ /* Do not start HDLC Core here, because - * we have to setup Tx/Rx DMA buffers first - * The transparent mode, will start - * comms as soon as the HDLC is enabled */ + * we have to setup Tx/Rx DMA buffers first + * The transparent mode, will start + * comms as soon as the HDLC is enabled */ xilinx_write_ctrl_hdlc(card, - chan->first_time_slot, - XILINX_HDLC_CONTROL_REG, - 0); + chan->first_time_slot, + XILINX_HDLC_CONTROL_REG, + 0); return 0; } @@ -3130,9 +3454,9 @@ static int xilinx_dev_configure(sdla_t *card, private_area_t *chan) wan_set_bit(HDLC_RX_ADDR_RECOGN_DIS_BIT,®); xilinx_write_ctrl_hdlc(card, - chan->first_time_slot, - XILINX_HDLC_CONTROL_REG, - reg); + chan->first_time_slot, + XILINX_HDLC_CONTROL_REG, + reg); return 0; } @@ -3147,7 +3471,6 @@ static void xilinx_dev_unconfigure(sdla_t *card, private_area_t *chan) DEBUG_CFG("\n-- Unconfigure Xilinx. --\n"); - if (wan_test_bit(0,&chan->tdmv_sync)){ channel_timeslot_sync_ctrl(card,chan,0); rx_chan_timeslot_sync_ctrl(card,0); @@ -3155,63 +3478,67 @@ static void xilinx_dev_unconfigure(sdla_t *card, private_area_t *chan) /* Select an HDLC logic channel for configuration */ if (chan->logic_ch_num != -1){ - + DEBUG_CFG("%s(): line: %d\n", __FUNCTION__, __LINE__); card->hw_iface.bus_read_4(card->hw,XILINX_TIMESLOT_HDLC_CHAN_REG, ®); reg&=~HDLC_LOGIC_CH_BIT_MASK; reg&= HDLC_LCH_TIMESLOT_MASK; /* mask not valid bits */ card->hw_iface.bus_write_4(card->hw, - XILINX_TIMESLOT_HDLC_CHAN_REG, - (reg|(chan->logic_ch_num&HDLC_LOGIC_CH_BIT_MASK))); + XILINX_TIMESLOT_HDLC_CHAN_REG, + (reg|(chan->logic_ch_num&HDLC_LOGIC_CH_BIT_MASK))); reg=0x00020000; - xilinx_write_ctrl_hdlc(card, - chan->first_time_slot, - XILINX_HDLC_CONTROL_REG, - reg); + xilinx_write_ctrl_hdlc(card, + chan->first_time_slot, + XILINX_HDLC_CONTROL_REG, + reg); - for (i=0;iu.aft.num_of_time_slots;i++){ - if (wan_test_bit(i,&chan->time_slot_map)){ + for (i=0;iu.aft.num_of_time_slots;i++){ + if (wan_test_bit(i,&chan->time_slot_map)){ - card->hw_iface.bus_read_4(card->hw, XILINX_TIMESLOT_HDLC_CHAN_REG, ®); - reg&=~TIMESLOT_BIT_MASK; - reg&= HDLC_LCH_TIMESLOT_MASK; /* mask not valid bits */ + card->hw_iface.bus_read_4(card->hw, XILINX_TIMESLOT_HDLC_CHAN_REG, ®); + reg&=~TIMESLOT_BIT_MASK; + reg&= HDLC_LCH_TIMESLOT_MASK; /* mask not valid bits */ - /* Select a Timeslot for configuration */ - card->hw_iface.bus_write_4(card->hw, - XILINX_TIMESLOT_HDLC_CHAN_REG, - (reg|(i<hw_iface.bus_write_4(card->hw, + XILINX_TIMESLOT_HDLC_CHAN_REG, + (reg|(i<wandev.lock,&smp_flags); free_xilinx_logical_channel_num(card,chan->logic_ch_num); + DEBUG_CFG("%s(): line: %d\n", __FUNCTION__, __LINE__); free_fifo_baddr_and_size(card,chan); + DEBUG_CFG("%s(): line: %d\n", __FUNCTION__, __LINE__); wan_spin_unlock_irq(&card->wandev.lock,&smp_flags); + DEBUG_CFG("%s(): line: %d\n", __FUNCTION__, __LINE__); for (i=0;iu.aft.num_of_time_slots;i++){ if (wan_test_bit(i,&chan->time_slot_map)){ @@ -3226,21 +3553,21 @@ static void xilinx_dev_unconfigure(sdla_t *card, private_area_t *chan) static int xilinx_init_rx_dev_fifo(sdla_t *card, private_area_t *chan, unsigned char wait) { - u32 reg; - u32 dma_descr; + u32 reg; + u32 dma_descr; u8 timeout=1; u16 i; - /* Clean RX DMA fifo */ - dma_descr=(unsigned long)(chan->logic_ch_num<<4) + XILINX_RxDMA_DESCRIPTOR_HI; - reg=0; - wan_set_bit(INIT_DMA_FIFO_CMD_BIT,®); + /* Clean RX DMA fifo */ + dma_descr=(unsigned long)(chan->logic_ch_num<<4) + XILINX_RxDMA_DESCRIPTOR_HI; + reg=0; + wan_set_bit(INIT_DMA_FIFO_CMD_BIT,®); - DEBUG_TEST("%s: Clearing RX Fifo %s DmaDescr=(0x%X) Reg=(0x%X)\n", - __FUNCTION__,chan->if_name, - dma_descr,reg); + DEBUG_TEST("%s: Clearing RX Fifo %s DmaDescr=(0x%X) Reg=(0x%X)\n", + __FUNCTION__,chan->if_name, + dma_descr,reg); - card->hw_iface.bus_write_4(card->hw,dma_descr,reg); + card->hw_iface.bus_write_4(card->hw,dma_descr,reg); if (wait == WP_WAIT){ for(i=0;ilogic_ch_num<<4) + XILINX_TxDMA_DESCRIPTOR_HI; - reg=0; - wan_set_bit(INIT_DMA_FIFO_CMD_BIT,®); + /* Clean TX DMA fifo */ + dma_descr=(unsigned long)(chan->logic_ch_num<<4) + XILINX_TxDMA_DESCRIPTOR_HI; + reg=0; + wan_set_bit(INIT_DMA_FIFO_CMD_BIT,®); - DEBUG_TEST("%s: Clearing TX Fifo %s DmaDescr=(0x%X) Reg=(0x%X)\n", - __FUNCTION__,chan->if_name, - dma_descr,reg); + DEBUG_TEST("%s: Clearing TX Fifo %s DmaDescr=(0x%X) Reg=(0x%X)\n", + __FUNCTION__,chan->if_name, + dma_descr,reg); - card->hw_iface.bus_write_4(card->hw,dma_descr,reg); + card->hw_iface.bus_write_4(card->hw,dma_descr,reg); if (wait == WP_WAIT){ - for(i=0;ihw_iface.bus_read_4(card->hw,dma_descr,®); - if (wan_test_bit(INIT_DMA_FIFO_CMD_BIT,®)){ - WP_DELAY(FIFO_RESET_TIMEOUT_US); - continue; - } - timeout=0; - break; - } + for(i=0;ihw_iface.bus_read_4(card->hw,dma_descr,®); + if (wan_test_bit(INIT_DMA_FIFO_CMD_BIT,®)){ + WP_DELAY(FIFO_RESET_TIMEOUT_US); + continue; + } + timeout=0; + break; + } - if (timeout){ - DEBUG_EVENT("%s:%s: Error: Tx fifo reset timedout %u us\n", - card->devname,chan->if_name,i*FIFO_RESET_TIMEOUT_US); - }else{ - DEBUG_TEST("%s:%s: Tx Fifo reset successful %u us\n", - card->devname,chan->if_name,i*FIFO_RESET_TIMEOUT_US); - } + if (timeout){ + DEBUG_EVENT("%s:%s: Error: Tx fifo reset timedout %u us\n", + card->devname,chan->if_name,i*FIFO_RESET_TIMEOUT_US); + }else{ + DEBUG_TEST("%s:%s: Tx Fifo reset successful %u us\n", + card->devname,chan->if_name,i*FIFO_RESET_TIMEOUT_US); + } }else{ timeout=0; } @@ -3313,16 +3640,16 @@ static int xilinx_init_tx_dev_fifo(sdla_t *card, private_area_t *chan, unsigned static void xilinx_dev_enable(sdla_t *card, private_area_t *chan) { - u32 reg; + u32 reg; DEBUG_TEST("%s: Enabling Global Inter Mask !\n",chan->if_name); /* Enable Logic Channel Interrupts for DMA and fifo */ card->hw_iface.bus_read_4(card->hw, - XILINX_GLOBAL_INTER_MASK, ®); + XILINX_GLOBAL_INTER_MASK, ®); wan_set_bit(chan->logic_ch_num,®); card->hw_iface.bus_write_4(card->hw, - XILINX_GLOBAL_INTER_MASK, reg); + XILINX_GLOBAL_INTER_MASK, reg); wan_set_bit(chan->logic_ch_num,&card->u.aft.active_ch_map); } @@ -3338,16 +3665,16 @@ static int xilinx_dma_rx(sdla_t *card, private_area_t *chan) if (chan->rx_dma_skb){ wp_rx_element_t *rx_el; netskb_t *skb=chan->rx_dma_skb; - DEBUG_TEST("%s: Clearing RX DMA Pending buffer \n", - __FUNCTION__); + DEBUG_TEST("%s: Clearing RX DMA Pending buffer \n", + __FUNCTION__); chan->rx_dma_skb=NULL; rx_el=(wp_rx_element_t *)wan_skb_data(skb); card->hw_iface.pci_unmap_dma(card->hw, - rx_el->dma_addr, - chan->dma_mru, - PCI_DMA_FROMDEVICE); + rx_el->dma_addr, + chan->dma_mru, + PCI_DMA_FROMDEVICE); aft_init_requeue_free_skb(chan, skb); } @@ -3357,8 +3684,8 @@ static int xilinx_dma_rx(sdla_t *card, private_area_t *chan) if (!chan->rx_dma_skb){ netskb_t *skb; DEBUG_TEST("%s: Critical Error no rx dma buf Free=%d Comp=%d!\n", - chan->if_name,wan_skb_queue_len(&chan->wp_rx_free_list), - wan_skb_queue_len(&chan->wp_rx_complete_list)); + chan->if_name,wan_skb_queue_len(&chan->wp_rx_free_list), + wan_skb_queue_len(&chan->wp_rx_complete_list)); while((skb=wan_skb_dequeue(&chan->wp_rx_complete_list)) != NULL){ aft_init_requeue_free_skb(chan, skb); @@ -3376,9 +3703,9 @@ static int xilinx_dma_rx(sdla_t *card, private_area_t *chan) chan->rx_dma_skb = wan_skb_dequeue(&chan->wp_rx_free_list); if (!chan->rx_dma_skb){ DEBUG_EVENT("%s:%ld:%d Critical Error no STILL NO MEM: RxFree=%i, RxComp=%i!\n", - chan->if_name,chan->logic_ch_num,chan->hdlc_eng, - wan_skb_queue_len(&chan->wp_rx_free_list), - wan_skb_queue_len(&chan->wp_rx_complete_list)); + chan->if_name,chan->logic_ch_num,chan->hdlc_eng, + wan_skb_queue_len(&chan->wp_rx_free_list), + wan_skb_queue_len(&chan->wp_rx_complete_list)); aft_critical_shutdown(card); return -ENOMEM; @@ -3390,38 +3717,38 @@ static int xilinx_dma_rx(sdla_t *card, private_area_t *chan) memset(rx_el,0,sizeof(wp_rx_element_t)); bus_addr = card->hw_iface.pci_map_dma(card->hw, - wan_skb_tail(chan->rx_dma_skb), - chan->dma_mru, - PCI_DMA_FROMDEVICE); + wan_skb_tail(chan->rx_dma_skb), + chan->dma_mru, + PCI_DMA_FROMDEVICE); if (!bus_addr){ DEBUG_EVENT("%s: %s Critical error pci_map_dma() failed!\n", - chan->if_name,__FUNCTION__); + chan->if_name,__FUNCTION__); return -EINVAL; } rx_el->dma_addr=bus_addr; /* Write the pointer of the data packet to the - * DMA address register */ + * DMA address register */ reg=bus_addr; /* Set the 32bit alignment of the data length. - * Since we are setting up for rx, set this value - * to Zero */ + * Since we are setting up for rx, set this value + * to Zero */ reg&=~(RxDMA_LO_ALIGNMENT_BIT_MASK); - dma_descr=(chan->logic_ch_num<<4) + XILINX_RxDMA_DESCRIPTOR_LO; + dma_descr=(chan->logic_ch_num<<4) + XILINX_RxDMA_DESCRIPTOR_LO; #if 0 DEBUG_RX("%s: RxDMA_LO = 0x%X, BusAddr=0x%X DmaDescr=0x%X\n", __FUNCTION__,reg,bus_addr,dma_descr);*/ #endif - card->hw_iface.bus_write_4(card->hw,dma_descr,reg); + card->hw_iface.bus_write_4(card->hw,dma_descr,reg); dma_descr=(unsigned long)(chan->logic_ch_num<<4) + XILINX_RxDMA_DESCRIPTOR_HI; - reg =0; + reg =0; if (chan->hdlc_eng){ reg|=(chan->dma_mru>>2)&RxDMA_HI_DMA_DATA_LENGTH_MASK; @@ -3435,13 +3762,13 @@ static int xilinx_dma_rx(sdla_t *card, private_area_t *chan) reg|=(HARD_FIFO_CODE&DMA_FIFO_SIZE_MASK)<fifo_base_addr&DMA_FIFO_BASE_ADDR_MASK)<< - DMA_FIFO_BASE_ADDR_SHIFT; + reg|=(chan->fifo_base_addr&DMA_FIFO_BASE_ADDR_MASK)<< + DMA_FIFO_BASE_ADDR_SHIFT; wan_set_bit(RxDMA_HI_DMA_GO_READY_BIT,®); DEBUG_RX("%s: RXDMA_HI = 0x%X, BusAddr=0x%lX DmaDescr=0x%lX\n", - __FUNCTION__,reg,bus_addr,dma_descr); + __FUNCTION__,reg,bus_addr,dma_descr); card->hw_iface.bus_write_4(card->hw,dma_descr,reg); @@ -3457,25 +3784,25 @@ static void xilinx_dev_close(sdla_t *card, private_area_t *chan) unsigned long dma_descr; wan_smp_flag_t smp_flags; - DEBUG_CFG("-- Close Xilinx device. --\n"); + DEBUG_CFG("-- Close Xilinx device. --\n"); - /* Disable Logic Channel Interrupts for DMA and fifo */ - card->hw_iface.bus_read_4(card->hw, - XILINX_GLOBAL_INTER_MASK, ®); + /* Disable Logic Channel Interrupts for DMA and fifo */ + card->hw_iface.bus_read_4(card->hw, + XILINX_GLOBAL_INTER_MASK, ®); - wan_clear_bit(chan->logic_ch_num,®); + wan_clear_bit(chan->logic_ch_num,®); wan_clear_bit(chan->logic_ch_num,&card->u.aft.active_ch_map); /* We are masking the chan interrupt. - * Lock to make sure that the interrupt is - * not running */ + * Lock to make sure that the interrupt is + * not running */ wan_spin_lock_irq(&card->wandev.lock,&smp_flags); - card->hw_iface.bus_write_4(card->hw, - XILINX_GLOBAL_INTER_MASK, reg); + card->hw_iface.bus_write_4(card->hw, + XILINX_GLOBAL_INTER_MASK, reg); wan_spin_unlock_irq(&card->wandev.lock,&smp_flags); - /* Clear descriptors */ + /* Clear descriptors */ reg=0; dma_descr=(chan->logic_ch_num<<4) + XILINX_RxDMA_DESCRIPTOR_HI; card->hw_iface.bus_write_4(card->hw,dma_descr,reg); @@ -3495,7 +3822,7 @@ static int xilinx_dma_tx (sdla_t *card, private_area_t *chan) int len=0; - DEBUG_TX(" ------ Setup Tx DMA descriptor. --\n"); + DEBUG_TX(" ------ Setup Tx DMA descriptor. --\n"); if (wan_test_and_set_bit(TX_BUSY,&chan->dma_status)){ DEBUG_TX("%s:%d: TX_BUSY set!\n", @@ -3505,29 +3832,29 @@ static int xilinx_dma_tx (sdla_t *card, private_area_t *chan) /* Free the previously skb dma mapping. - * In this case the tx interrupt didn't finish - * and we must re-transmit.*/ + * In this case the tx interrupt didn't finish + * and we must re-transmit.*/ - if (chan->tx_dma_addr && chan->tx_dma_len){ + if (chan->tx_dma_addr && chan->tx_dma_len){ if (WAN_NET_RATELIMIT()){ DEBUG_EVENT("%s: Unmaping tx_dma_addr in %s\n", - chan->if_name,__FUNCTION__); + chan->if_name,__FUNCTION__); } aft_unmap_tx_dma(card,chan); - } + } /* Free the previously sent tx packet. To - * minimize tx isr, the previously transmitted - * packet is deallocated here */ + * minimize tx isr, the previously transmitted + * packet is deallocated here */ if (chan->tx_dma_skb){ if (WAN_NET_RATELIMIT()){ DEBUG_EVENT("%s: Deallocating tx_dma_skb in %s\n", chan->if_name,__FUNCTION__); } - wan_skb_free(chan->tx_dma_skb); + wan_aft_skb_defered_dealloc(chan, chan->tx_dma_skb); /* instead of wan_skb_free(chan->tx_dma_skb);*/ chan->tx_dma_skb=NULL; } @@ -3538,13 +3865,13 @@ static int xilinx_dma_tx (sdla_t *card, private_area_t *chan) dma_descr=(chan->logic_ch_num<<4) + XILINX_TxDMA_DESCRIPTOR_HI; DEBUG_TX("%s:%d: chan logic ch=%ld dma_descr=0x%lx set!\n", - __FUNCTION__,__LINE__,chan->logic_ch_num,dma_descr); + __FUNCTION__,__LINE__,chan->logic_ch_num,dma_descr); card->hw_iface.bus_read_4(card->hw,dma_descr, ®); if (wan_test_bit(TxDMA_HI_DMA_GO_READY_BIT,®)){ DEBUG_TEST("%s: Error: TxDMA GO Ready bit set on dma Tx 0x%X\n", - card->devname,reg); + card->devname,reg); wan_clear_bit(TX_BUSY,&chan->dma_status); return -EFAULT; } @@ -3557,7 +3884,6 @@ static int xilinx_dma_tx (sdla_t *card, private_area_t *chan) break; default: - if(!chan->lip_atm){ skb=wan_skb_dequeue(&chan->wp_tx_pending_list); }else{ @@ -3566,23 +3892,22 @@ static int xilinx_dma_tx (sdla_t *card, private_area_t *chan) break; } - if (!skb){ if (chan->hdlc_eng){ DEBUG_TEST("%s:%d: Tx pending list empty \n", - __FUNCTION__,__LINE__); + __FUNCTION__,__LINE__); wan_clear_bit(TX_BUSY,&chan->dma_status); return -ENOBUFS; } /* Transparent HDLC Mode - * Transmit Idle Flag */ + * Transmit Idle Flag */ len=wan_skb_len(chan->tx_idle_skb); chan->tx_dma_addr = card->hw_iface.pci_map_dma(card->hw, - wan_skb_data(chan->tx_idle_skb), - wan_skb_len(chan->tx_idle_skb), - PCI_DMA_TODEVICE); + wan_skb_data(chan->tx_idle_skb), + wan_skb_len(chan->tx_idle_skb), + PCI_DMA_TODEVICE); chan->tx_dma_len = wan_skb_len(chan->tx_idle_skb); @@ -3591,15 +3916,15 @@ static int xilinx_dma_tx (sdla_t *card, private_area_t *chan) len = wan_skb_len(skb); if (wan_skb_len(skb) > MAX_XILINX_TX_DMA_SIZE){ /* FIXME: We need to split this frame into - * multiple parts. For now thought - * just drop it :) */ + * multiple parts. For now thought + * just drop it :) */ DEBUG_EVENT("%s:%d: Tx pkt len > Max Len (%d)!\n", - __FUNCTION__,__LINE__,MAX_XILINX_TX_DMA_SIZE); + __FUNCTION__,__LINE__,MAX_XILINX_TX_DMA_SIZE); if (chan->common.usedby == TDM_VOICE || chan->common.usedby == TDM_VOICE_API){ aft_init_requeue_free_skb(chan, skb); }else{ - wan_skb_free(skb); + wan_aft_skb_defered_dealloc(chan, skb); /* instead of wan_skb_free(skb); */ } wan_clear_bit(TX_BUSY,&chan->dma_status); return -EINVAL; @@ -3609,12 +3934,12 @@ static int xilinx_dma_tx (sdla_t *card, private_area_t *chan) int err=aft_realign_skb_pkt(chan, skb); if (err){ DEBUG_EVENT("%s: Error: Failed to allocate memory in %s()\n", - card->devname,__FUNCTION__); + card->devname,__FUNCTION__); if (chan->common.usedby == TDM_VOICE || chan->common.usedby == TDM_VOICE_API){ aft_init_requeue_free_skb(chan, skb); }else{ - wan_skb_free(skb); + wan_aft_skb_defered_dealloc(chan, skb); /* instead of wan_skb_free(skb); */ } wan_clear_bit(TX_BUSY,&chan->dma_status); return -EINVAL; @@ -3624,10 +3949,10 @@ static int xilinx_dma_tx (sdla_t *card, private_area_t *chan) chan->tx_dma_addr = - card->hw_iface.pci_map_dma(card->hw, - wan_skb_data(skb), - wan_skb_len(skb), - PCI_DMA_TODEVICE); + card->hw_iface.pci_map_dma(card->hw, + wan_skb_data(skb), + wan_skb_len(skb), + PCI_DMA_TODEVICE); chan->tx_dma_len = wan_skb_len(skb); } @@ -3635,13 +3960,13 @@ static int xilinx_dma_tx (sdla_t *card, private_area_t *chan) if (chan->tx_dma_addr & 0x03){ DEBUG_EVENT("%s: Critcal Error: Tx Ptr not aligned to 32bit boudary!\n", - card->devname); + card->devname); if (skb){ if (chan->common.usedby == TDM_VOICE || chan->common.usedby == TDM_VOICE_API){ aft_init_requeue_free_skb(chan, skb); }else{ - wan_skb_free(skb); + wan_aft_skb_defered_dealloc(chan, skb); /* instead of wan_skb_free(skb); */ } } @@ -3657,11 +3982,14 @@ static int xilinx_dma_tx (sdla_t *card, private_area_t *chan) #endif chan->tx_dma_skb=skb; +#if defined(WAN_DEBUG_TX) DEBUG_TX("TX SKB DATA 0x%08X 0x%08X 0x%08X 0x%08X \n", *(unsigned int*)&data[0], *(unsigned int*)&data[4], *(unsigned int*)&data[8], *(unsigned int*)&data[12]); +#endif + #if defined(__LINUX__) DEBUG_TX("Tx dma skb bound %p List=%p Data=%p BusPtr=%lx\n", skb,skb->list,wan_skb_data(skb),chan->tx_dma_addr); @@ -3672,18 +4000,18 @@ static int xilinx_dma_tx (sdla_t *card, private_area_t *chan) /* WARNING: Do ont use the "skb" pointer from - * here on. The skb pointer might not exist if - * we are in transparent mode */ + * here on. The skb pointer might not exist if + * we are in transparent mode */ dma_descr=(chan->logic_ch_num<<4) + XILINX_TxDMA_DESCRIPTOR_LO; /* Write the pointer of the data packet to the - * DMA address register */ + * DMA address register */ reg=chan->tx_dma_addr; /* Set the 32bit alignment of the data length. - * Used to pad the tx packet to the 32 bit - * boundary */ + * Used to pad the tx packet to the 32 bit + * boundary */ reg&=~(TxDMA_LO_ALIGNMENT_BIT_MASK); reg|=(len&0x03); @@ -3692,7 +4020,7 @@ static int xilinx_dma_tx (sdla_t *card, private_area_t *chan) } DEBUG_TX("%s: TXDMA_LO=0x%X PhyAddr=0x%X DmaDescr=0x%lX\n", - __FUNCTION__,reg,chan->tx_dma_addr,dma_descr); + __FUNCTION__,reg,chan->tx_dma_addr,dma_descr); card->hw_iface.bus_write_4(card->hw,dma_descr,reg); @@ -3702,24 +4030,24 @@ static int xilinx_dma_tx (sdla_t *card, private_area_t *chan) reg|=(((len>>2)+len_align)&TxDMA_HI_DMA_DATA_LENGTH_MASK); #ifdef TRUE_FIFO_SIZE - reg|=(chan->fifo_size_code&DMA_FIFO_SIZE_MASK)<fifo_size_code&DMA_FIFO_SIZE_MASK)<fifo_base_addr&DMA_FIFO_BASE_ADDR_MASK)<< - DMA_FIFO_BASE_ADDR_SHIFT; + reg|=(chan->fifo_base_addr&DMA_FIFO_BASE_ADDR_MASK)<< + DMA_FIFO_BASE_ADDR_SHIFT; if (chan->hdlc_eng){ /* Only enable the Frame Start/Stop on - * non-transparent hdlc configuration */ + * non-transparent hdlc configuration */ wan_set_bit(TxDMA_HI_DMA_FRAME_START_BIT,®); wan_set_bit(TxDMA_HI_DMA_FRAME_END_BIT,®); } /* For TDM VOICE Timeslot Synchronization, - * we must use FRAME START bit as a first - * slot sync character */ + * we must use FRAME START bit as a first + * slot sync character */ if (wan_test_bit(0,&chan->tdmv_sync)){ wan_set_bit(TxDMA_HI_DMA_FRAME_START_BIT,®); } @@ -3727,7 +4055,7 @@ static int xilinx_dma_tx (sdla_t *card, private_area_t *chan) wan_set_bit(TxDMA_HI_DMA_GO_READY_BIT,®); DEBUG_TEST("%s: TXDMA_HI=0x%X DmaDescr=0x%lX\n", - __FUNCTION__,reg,dma_descr); + __FUNCTION__,reg,dma_descr); card->hw_iface.bus_write_4(card->hw,dma_descr,reg); @@ -3744,7 +4072,7 @@ static void xilinx_dma_tx_complete (sdla_t *card, private_area_t *chan) ++chan->if_stats.tx_carrier_errors; #endif /* DEBUGTX */ -/* card->hw_iface.bus_read_4(card->hw,0x78, &tmp1); */ + /* card->hw_iface.bus_read_4(card->hw,0x78, &tmp1); */ dma_descr=(chan->logic_ch_num<<4) + XILINX_TxDMA_DESCRIPTOR_HI; card->hw_iface.bus_read_4(card->hw,dma_descr, ®); @@ -3753,11 +4081,11 @@ static void xilinx_dma_tx_complete (sdla_t *card, private_area_t *chan) chan->errstats.Tx_dma_len_nonzero++; } - if (!chan->tx_dma_skb){ + if (!chan->tx_dma_skb){ if (chan->hdlc_eng){ DEBUG_EVENT("%s: Critical Error: Tx DMA intr: no tx skb !\n", - card->devname); + card->devname); wan_clear_bit(TX_BUSY,&chan->dma_status); return; }else{ @@ -3765,10 +4093,10 @@ static void xilinx_dma_tx_complete (sdla_t *card, private_area_t *chan) aft_unmap_tx_dma(card,chan); /* For Transparent mode, if no user - * tx frames available, send an idle - * frame as soon as possible. - */ - wan_set_bit(0,&chan->idle_start); + * tx frames available, send an idle + * frame as soon as possible. + */ + wan_set_bit(0,&chan->idle_start); wan_clear_bit(TX_BUSY,&chan->dma_status); xilinx_dma_tx(card,chan); @@ -3782,23 +4110,23 @@ static void xilinx_dma_tx_complete (sdla_t *card, private_area_t *chan) if (chan->hdlc_eng){ /* Do not free the packet here, - * copy the packet dma info into csum - * field and let the bh handler analyze - * the transmitted packet. - */ + * copy the packet dma info into csum + * field and let the bh handler analyze + * the transmitted packet. + */ if (reg & TxDMA_HI_DMA_PCI_ERROR_RETRY_TOUT){ DEBUG_EVENT("%s:%s: PCI Error: 'Retry' exceeds maximum (64k): Reg=0x%X!\n", - card->devname,chan->if_name,reg); + card->devname,chan->if_name,reg); if (++chan->pci_retry < 3){ wan_set_bit(TxDMA_HI_DMA_GO_READY_BIT,®); - DEBUG_EVENT("%s: Retry: TXDMA_HI=0x%X DmaDescr=0x%lX\n", - __FUNCTION__,reg,dma_descr); + DEBUG_EVENT("%s: Retry: TXDMA_HI=0x%X DmaDescr=0x%lX\n", + __FUNCTION__,reg,dma_descr); - card->hw_iface.bus_write_4(card->hw,dma_descr,reg); + card->hw_iface.bus_write_4(card->hw,dma_descr,reg); return; } } @@ -3810,19 +4138,19 @@ static void xilinx_dma_tx_complete (sdla_t *card, private_area_t *chan) wan_clear_bit(TX_BUSY,&chan->dma_status); - WAN_TASKLET_SCHEDULE(&chan->common.bh_task); + WAN_TASKLET_SCHEDULE((&chan->common.bh_task)); }else{ netskb_t *skb = chan->tx_dma_skb; chan->tx_dma_skb=NULL; /* For transparend mode, handle the - * transmitted packet directly from - * interrupt, to avoid tx overrun. - * - * We must clear TX_BUSY before post - * function, because the post function - * will restart tx dma via xilinx_dma_tx() - */ + * transmitted packet directly from + * interrupt, to avoid tx overrun. + * + * We must clear TX_BUSY before post + * function, because the post function + * will restart tx dma via xilinx_dma_tx() + */ wan_skb_set_csum(skb, reg); wan_clear_bit(TX_BUSY,&chan->dma_status); @@ -3830,17 +4158,17 @@ static void xilinx_dma_tx_complete (sdla_t *card, private_area_t *chan) if (chan->common.usedby == TDM_VOICE || chan->common.usedby == TDM_VOICE_API){ /* Voice code uses the rx buffer to - * transmit! So put the rx buffer back - * into the rx queue */ + * transmit! So put the rx buffer back + * into the rx queue */ aft_init_requeue_free_skb(chan, skb); }else{ - wan_skb_free(skb); + wan_aft_skb_defered_dealloc(chan, skb); /* instead of wan_skb_free(skb); */ } } } /* DEBUGTX */ -/* card->hw_iface.bus_read_4(card->hw,0x78, &tmp1); */ + /* card->hw_iface.bus_read_4(card->hw,0x78, &tmp1); */ } @@ -3859,54 +4187,54 @@ static void xilinx_tx_post_complete (sdla_t *card, private_area_t *chan, netskb_ xilinx_dma_tx(card,chan); if ((wan_test_bit(TxDMA_HI_DMA_GO_READY_BIT,®)) || - (reg & TxDMA_HI_DMA_DATA_LENGTH_MASK) || - (reg & TxDMA_HI_DMA_PCI_ERROR_MASK)){ + (reg & TxDMA_HI_DMA_DATA_LENGTH_MASK) || + (reg & TxDMA_HI_DMA_PCI_ERROR_MASK)){ - /* Checking Tx DMA Go bit. Has to be '0' */ - if (wan_test_bit(TxDMA_HI_DMA_GO_READY_BIT,®)){ - DEBUG_TEST("%s:%s: Error: TxDMA Intr: GO bit set on Tx intr (0x%lX)\n", - card->devname,chan->if_name,reg); - chan->errstats.Tx_dma_errors++; - } + /* Checking Tx DMA Go bit. Has to be '0' */ + if (wan_test_bit(TxDMA_HI_DMA_GO_READY_BIT,®)){ + DEBUG_TEST("%s:%s: Error: TxDMA Intr: GO bit set on Tx intr (0x%lX)\n", + card->devname,chan->if_name,reg); + chan->errstats.Tx_dma_errors++; + } - if (reg & TxDMA_HI_DMA_DATA_LENGTH_MASK){ - DEBUG_TEST("%s:%s: Error: TxDMA Length not equal 0 (0x%lX)\n", - card->devname,chan->if_name,reg); - chan->errstats.Tx_dma_errors++; - } + if (reg & TxDMA_HI_DMA_DATA_LENGTH_MASK){ + DEBUG_TEST("%s:%s: Error: TxDMA Length not equal 0 (0x%lX)\n", + card->devname,chan->if_name,reg); + chan->errstats.Tx_dma_errors++; + } - /* Checking Tx DMA PCI error status. Has to be '0's */ - if (reg&TxDMA_HI_DMA_PCI_ERROR_MASK){ + /* Checking Tx DMA PCI error status. Has to be '0's */ + if (reg&TxDMA_HI_DMA_PCI_ERROR_MASK){ - if (reg & TxDMA_HI_DMA_PCI_ERROR_M_ABRT){ - if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s:%s: Tx Error: Abort from Master: pci fatal error! (0x%lX)\n", - card->devname,chan->if_name,reg); + if (reg & TxDMA_HI_DMA_PCI_ERROR_M_ABRT){ + if (WAN_NET_RATELIMIT()){ + DEBUG_EVENT("%s:%s: Tx Error: Abort from Master: pci fatal error! (0x%lX)\n", + card->devname,chan->if_name,reg); + } } - } - if (reg & TxDMA_HI_DMA_PCI_ERROR_T_ABRT){ - if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s:%s: Tx Error: Abort from Target: pci fatal error! (0x%lX)\n", - card->devname,chan->if_name,reg); + if (reg & TxDMA_HI_DMA_PCI_ERROR_T_ABRT){ + if (WAN_NET_RATELIMIT()){ + DEBUG_EVENT("%s:%s: Tx Error: Abort from Target: pci fatal error! (0x%lX)\n", + card->devname,chan->if_name,reg); + } } - } - if (reg & TxDMA_HI_DMA_PCI_ERROR_DS_TOUT){ - DEBUG_TEST("%s:%s: Tx Warning: PCI Latency Timeout! (0x%lX)\n", - card->devname,chan->if_name,reg); - chan->errstats.Tx_pci_latency++; - goto tx_post_ok; - } - if (reg & TxDMA_HI_DMA_PCI_ERROR_RETRY_TOUT){ - if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s:%s: Tx Error: 'Retry' exceeds maximum (64k): pci fatal error! (0x%lX)\n", - card->devname,chan->if_name,reg); + if (reg & TxDMA_HI_DMA_PCI_ERROR_DS_TOUT){ + DEBUG_TEST("%s:%s: Tx Warning: PCI Latency Timeout! (0x%lX)\n", + card->devname,chan->if_name,reg); + chan->errstats.Tx_pci_latency++; + goto tx_post_ok; } + if (reg & TxDMA_HI_DMA_PCI_ERROR_RETRY_TOUT){ + if (WAN_NET_RATELIMIT()){ + DEBUG_EVENT("%s:%s: Tx Error: 'Retry' exceeds maximum (64k): pci fatal error! (0x%lX)\n", + card->devname,chan->if_name,reg); + } + } + chan->errstats.Tx_pci_errors++; } - chan->errstats.Tx_pci_errors++; - } - chan->if_stats.tx_dropped++; - goto tx_post_exit; + chan->if_stats.tx_dropped++; + goto tx_post_exit; } tx_post_ok: @@ -3916,23 +4244,26 @@ tx_post_ok: chan->if_stats.tx_packets++; chan->if_stats.tx_bytes+=wan_skb_len(skb); - /* Indicate that the first tx frame went - * out on the transparent link */ - wan_set_bit(0,&chan->idle_start); - + /* Indicate that the first tx frame went + * out on the transparent link */ + wan_set_bit(0,&chan->idle_start); +#if defined(__WINDOWS__) + wan_capture_trace_packet(card, &top_chan->trace_info, skb, TRC_OUTGOING_FRM); +#else if (wan_tracing_enabled(&top_chan->trace_info) >= 1){ if (card->u.aft.tdmv_dchan){ if (chan->common.usedby == TDM_VOICE_DCHAN){ - wan_capture_trace_packet(card, &top_chan->trace_info, skb, TRC_OUTGOING_FRM); + wan_capture_trace_packet(card, &top_chan->trace_info, skb, TRC_OUTGOING_FRM); } }else{ wan_capture_trace_packet_offset(card, &top_chan->trace_info, skb, - IS_T1_CARD(card)?24:16, TRC_OUTGOING_FRM); + IS_T1_CARD(card)?24:16, TRC_OUTGOING_FRM); } }else{ wan_capture_trace_packet(card, &top_chan->trace_info, skb, TRC_OUTGOING_FRM); } +#endif tx_post_exit: @@ -3943,7 +4274,7 @@ tx_post_exit: #ifdef AFT_TDM_API_SUPPORT if (chan->common.usedby == TDM_VOICE_DCHAN){ wanpipe_tdm_api_dev_t *wp_tdm_api_dev = - &chan->wp_tdm_api_dev_idx[chan->tdmv_chan]; + &chan->wp_tdm_api_dev_idx[chan->tdmv_chan]; if (is_tdm_api(chan,wp_tdm_api_dev)){ wanpipe_tdm_api_kick(wp_tdm_api_dev); @@ -3983,26 +4314,26 @@ static void xilinx_dma_rx_complete (sdla_t *card, private_area_t *chan) chan->if_stats.rx_frame_errors++; #endif -/* card->hw_iface.bus_read_4(card->hw,0x80, &rx_empty); */ + /* card->hw_iface.bus_read_4(card->hw,0x80, &rx_empty); */ - /* Reading Rx DMA descriptor information */ + /* Reading Rx DMA descriptor information */ dma_descr=(chan->logic_ch_num<<4) + XILINX_RxDMA_DESCRIPTOR_LO; card->hw_iface.bus_read_4(card->hw,dma_descr, &rx_el->align); rx_el->align&=RxDMA_LO_ALIGNMENT_BIT_MASK; - dma_descr=(chan->logic_ch_num<<4) + XILINX_RxDMA_DESCRIPTOR_HI; + dma_descr=(chan->logic_ch_num<<4) + XILINX_RxDMA_DESCRIPTOR_HI; card->hw_iface.bus_read_4(card->hw,dma_descr, &rx_el->reg); rx_el->pkt_error = chan->pkt_error; -/* DEBUG_RX("%s: DmaDescrLo=0x%X DmaHi=0x%X \n",*/ -/* __FUNCTION__,rx_el.align,reg);*/ + /* DEBUG_RX("%s: DmaDescrLo=0x%X DmaHi=0x%X \n",*/ + /* __FUNCTION__,rx_el.align,reg);*/ card->hw_iface.pci_unmap_dma(card->hw, - rx_el->dma_addr, - chan->dma_mru, - PCI_DMA_FROMDEVICE); + rx_el->dma_addr, + chan->dma_mru, + PCI_DMA_FROMDEVICE); DEBUG_RX("%s:%s: RX HI=0x%X LO=0x%X DMA=0x%lX\n", __FUNCTION__,chan->if_name,rx_el->reg,rx_el->align,rx_el->dma_addr); @@ -4039,22 +4370,22 @@ static void xilinx_dma_rx_complete (sdla_t *card, private_area_t *chan) } }else{ wan_skb_queue_tail(&chan->wp_rx_complete_list,skb); - WAN_TASKLET_SCHEDULE(&chan->common.bh_task); + WAN_TASKLET_SCHEDULE((&chan->common.bh_task)); } chan->pkt_error=0; -/* card->hw_iface.bus_read_4(card->hw,0x80, &rx_empty); */ + /* card->hw_iface.bus_read_4(card->hw,0x80, &rx_empty); */ } static void xilinx_rx_post_complete (sdla_t *card, private_area_t *chan, - netskb_t *skb, - netskb_t **new_skb, - unsigned char *pkt_error) + netskb_t *skb, + netskb_t **new_skb, + unsigned char *pkt_error) { - unsigned int len,data_error = 0; + unsigned int len,data_error = 0; unsigned char *buf; wp_rx_element_t *rx_el=(wp_rx_element_t *)wan_skb_data(skb); @@ -4070,10 +4401,10 @@ static void xilinx_rx_post_complete (sdla_t *card, private_area_t *chan, *new_skb=NULL; - /* Checking Rx DMA Go bit. Has to be '0' */ + /* Checking Rx DMA Go bit. Has to be '0' */ if (wan_test_bit(RxDMA_HI_DMA_GO_READY_BIT,&rx_el->reg)){ - DEBUG_TEST("%s:%s: Error: RxDMA Intr: GO bit set on Rx intr\n", - card->devname,chan->if_name); + DEBUG_TEST("%s:%s: Error: RxDMA Intr: GO bit set on Rx intr\n", + card->devname,chan->if_name); chan->if_stats.rx_errors++; chan->errstats.Rx_dma_descr_err++; goto rx_comp_error; @@ -4083,21 +4414,21 @@ static void xilinx_rx_post_complete (sdla_t *card, private_area_t *chan, if (rx_el->reg&RxDMA_HI_DMA_PCI_ERROR_MASK){ if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_M_ABRT){ - DEBUG_EVENT("%s:%s: Rx Error: Abort from Master: pci fatal error! (0x%08X)\n", - card->devname,chan->if_name,rx_el->reg); - } - if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_T_ABRT){ - DEBUG_EVENT("%s:%s: Rx Error: Abort from Target: pci fatal error! (0x%08X)\n", - card->devname,chan->if_name,rx_el->reg); - } - if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_DS_TOUT){ - DEBUG_EVENT("%s:%s: Rx Error: No 'DeviceSelect' from target: pci fatal error! (0x%08X)\n", - card->devname,chan->if_name,rx_el->reg); - } - if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_RETRY_TOUT){ - DEBUG_EVENT("%s:%s: Rx Error: 'Retry' exceeds maximum (64k): pci fatal error! (0x%08X)\n", - card->devname,chan->if_name,rx_el->reg); - } + DEBUG_EVENT("%s:%s: Rx Error: Abort from Master: pci fatal error! (0x%08X)\n", + card->devname,chan->if_name,rx_el->reg); + } + if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_T_ABRT){ + DEBUG_EVENT("%s:%s: Rx Error: Abort from Target: pci fatal error! (0x%08X)\n", + card->devname,chan->if_name,rx_el->reg); + } + if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_DS_TOUT){ + DEBUG_EVENT("%s:%s: Rx Error: No 'DeviceSelect' from target: pci fatal error! (0x%08X)\n", + card->devname,chan->if_name,rx_el->reg); + } + if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_RETRY_TOUT){ + DEBUG_EVENT("%s:%s: Rx Error: 'Retry' exceeds maximum (64k): pci fatal error! (0x%08X)\n", + card->devname,chan->if_name,rx_el->reg); + } chan->if_stats.rx_errors++; chan->errstats.Rx_pci_errors++; @@ -4109,7 +4440,7 @@ static void xilinx_rx_post_complete (sdla_t *card, private_area_t *chan, /* Checking Rx DMA Frame start bit. (information for api) */ if (!wan_test_bit(RxDMA_HI_DMA_FRAME_START_BIT,&rx_el->reg)){ DEBUG_TEST("%s:%s RxDMA Intr: Start flag missing: MTU Mismatch! Reg=0x%X\n", - card->devname,chan->if_name,rx_el->reg); + card->devname,chan->if_name,rx_el->reg); chan->if_stats.rx_frame_errors++; chan->opstats.Rx_Data_discard_long_count++; chan->errstats.Rx_hdlc_corrupiton++; @@ -4119,28 +4450,28 @@ static void xilinx_rx_post_complete (sdla_t *card, private_area_t *chan, /* Checking Rx DMA Frame end bit. (information for api) */ if (!wan_test_bit(RxDMA_HI_DMA_FRAME_END_BIT,&rx_el->reg)){ DEBUG_TEST("%s:%s: RxDMA Intr: End flag missing: MTU Mismatch! Reg=0x%X\n", - card->devname,chan->if_name,rx_el->reg); + card->devname,chan->if_name,rx_el->reg); chan->if_stats.rx_frame_errors++; chan->opstats.Rx_Data_discard_long_count++; chan->errstats.Rx_hdlc_corrupiton++; goto rx_comp_error; - } else { /* Check CRC error flag only if this is the end of Frame */ + } else { /* Check CRC error flag only if this is the end of Frame */ if (wan_test_bit(RxDMA_HI_DMA_CRC_ERROR_BIT,&rx_el->reg)){ - DEBUG_TEST("%s:%s: RxDMA Intr: CRC Error! Reg=0x%X\n", - card->devname,chan->if_name,rx_el->reg); + DEBUG_TEST("%s:%s: RxDMA Intr: CRC Error! Reg=0x%X\n", + card->devname,chan->if_name,rx_el->reg); chan->if_stats.rx_errors++; chan->errstats.Rx_crc_err_count++; wan_set_bit(WP_CRC_ERROR_BIT,&rx_el->pkt_error); - data_error = 1; - } + data_error = 1; + } /* Check if this frame is an abort, if it is - * drop it and continue receiving */ + * drop it and continue receiving */ if (wan_test_bit(RxDMA_HI_DMA_FRAME_ABORT_BIT,&rx_el->reg)){ DEBUG_TEST("%s:%s: RxDMA Intr: Abort! Reg=0x%X\n", - card->devname,chan->if_name,rx_el->reg); + card->devname,chan->if_name,rx_el->reg); chan->if_stats.rx_frame_errors++; chan->errstats.Rx_hdlc_corrupiton++; wan_set_bit(WP_ABORT_ERROR_BIT,&rx_el->pkt_error); @@ -4150,14 +4481,14 @@ static void xilinx_rx_post_complete (sdla_t *card, private_area_t *chan, if (chan->common.usedby != API && data_error){ goto rx_comp_error; } - } + } } len=rx_el->reg&RxDMA_HI_DMA_DATA_LENGTH_MASK; if (chan->hdlc_eng){ /* In HDLC mode, calculate rx length based - * on alignment value, received from DMA */ + * on alignment value, received from DMA */ len=(((chan->dma_mru>>2)-len)<<2) - (~(rx_el->align)&RxDMA_LO_ALIGNMENT_BIT_MASK); if (len < 1 || len > chan->dma_mru){ @@ -4168,8 +4499,8 @@ static void xilinx_rx_post_complete (sdla_t *card, private_area_t *chan, }else{ /* In Transparent mode, our RX buffer will always be - * aligned to the 32bit (word) boundary, because - * the RX buffers are all of equal length */ + * aligned to the 32bit (word) boundary, because + * the RX buffers are all of equal length */ len=(((chan->mru>>2)-len)<<2) - (~(0x03)&RxDMA_LO_ALIGNMENT_BIT_MASK); if (len < 1 || len > chan->mru){ @@ -4182,8 +4513,8 @@ static void xilinx_rx_post_complete (sdla_t *card, private_area_t *chan, *pkt_error=rx_el->pkt_error; /* After a RX FIFO overflow, we must mark max 7 - * subsequent frames since firmware, cannot - * guarantee the contents of the fifo */ + * subsequent frames since firmware, cannot + * guarantee the contents of the fifo */ if (wan_test_bit(WP_FIFO_ERROR_BIT,&rx_el->pkt_error)){ if (++chan->rx_fifo_err_cnt >= WP_MAX_FIFO_FRAMES){ @@ -4193,7 +4524,7 @@ static void xilinx_rx_post_complete (sdla_t *card, private_area_t *chan, }else{ if (chan->rx_fifo_err_cnt){ if (++chan->rx_fifo_err_cnt >= WP_MAX_FIFO_FRAMES){ - chan->rx_fifo_err_cnt=0; + chan->rx_fifo_err_cnt=0; } wan_set_bit(WP_FIFO_ERROR_BIT,pkt_error); } @@ -4220,8 +4551,8 @@ static void xilinx_rx_post_complete (sdla_t *card, private_area_t *chan, aft_init_requeue_free_skb(chan, skb); #else /* The rx size is big enough, thus - * send this buffer up the stack - * and allocate another one */ + * send this buffer up the stack + * and allocate another one */ wan_skb_put(skb,len); wan_skb_pull(skb, sizeof(wp_rx_element_t)); *new_skb=skb; @@ -4231,9 +4562,9 @@ static void xilinx_rx_post_complete (sdla_t *card, private_area_t *chan, } else { /* The rx packet is very - * small thus, allocate a new - * buffer and pass it up - */ + * small thus, allocate a new + * buffer and pass it up + */ *new_skb=wan_skb_alloc(len + 20); if (!*new_skb){ DEBUG_EVENT("%s:%s: Failed to allocate rx skb pkt (len=%d)!\n", @@ -4258,7 +4589,7 @@ static void xilinx_rx_post_complete (sdla_t *card, private_area_t *chan, rx_comp_error: aft_init_requeue_free_skb(chan, skb); - return; + return; } @@ -4273,8 +4604,8 @@ static char request_xilinx_logical_channel_num (sdla_t *card, private_area_t *ch DEBUG_TEST("%s:%d Global Num Timeslots=%d Global Logic ch Map 0x%lX \n", __FUNCTION__,__LINE__, - card->u.aft.num_of_time_slots, - card->u.aft.logic_ch_map); + card->u.aft.num_of_time_slots, + card->u.aft.logic_ch_map); err=request_fifo_baddr_and_size(card,chan); @@ -4303,7 +4634,7 @@ static char request_xilinx_logical_channel_num (sdla_t *card, private_area_t *ch if (card->u.aft.dev_to_ch_map[(unsigned char)logic_ch]){ DEBUG_EVENT("%s: Error, request logical ch=%d map busy\n", - card->devname,logic_ch); + card->devname,logic_ch); return -1; } @@ -4333,8 +4664,8 @@ static void free_xilinx_logical_channel_num (sdla_t *card, int logic_ch) for (i=0;iu.aft.num_of_time_slots;i++){ /*NC: Bug fix: Apr 28 2005 - * Used logic_ch instead of i as the - * index into the dev_to_ch_map */ + * Used logic_ch instead of i as the + * index into the dev_to_ch_map */ if (card->u.aft.dev_to_ch_map[i]){ card->u.aft.top_logic_ch=i; } @@ -4354,12 +4685,12 @@ static void xilinx_dma_max_logic_ch(sdla_t *card) card->hw_iface.bus_read_4(card->hw,XILINX_DMA_CONTROL_REG, ®); - /* Set up the current highest active logic channel */ + /* Set up the current highest active logic channel */ reg&=DMA_ACTIVE_CHANNEL_BIT_MASK; - reg|=(card->u.aft.top_logic_ch << DMA_ACTIVE_CHANNEL_BIT_SHIFT); + reg|=(card->u.aft.top_logic_ch << DMA_ACTIVE_CHANNEL_BIT_SHIFT); - card->hw_iface.bus_write_4(card->hw,XILINX_DMA_CONTROL_REG,reg); + card->hw_iface.bus_write_4(card->hw,XILINX_DMA_CONTROL_REG,reg); } static int aft_init_requeue_free_skb(private_area_t *chan, netskb_t *skb) @@ -4397,11 +4728,11 @@ static int aft_alloc_rx_dma_buff(sdla_t *card, private_area_t *chan, int num, in if (irq) { skb=wan_skb_alloc(chan->dma_mru); } else { - skb=wan_skb_kalloc(chan->dma_mru); + skb=wan_skb_kalloc(chan->dma_mru); } if (!skb){ DEBUG_EVENT("%s: %s no memory\n", - chan->if_name,__FUNCTION__); + chan->if_name,__FUNCTION__); return -ENOMEM; } wan_skb_queue_tail(&chan->wp_rx_free_list,skb); @@ -4414,13 +4745,13 @@ static int aft_alloc_rx_dma_buff(sdla_t *card, private_area_t *chan, int num, in /*============================================================================ - * Enable timer interrupt - */ +* Enable timer interrupt +*/ static void enable_timer (void* card_id) { sdla_t *card = (sdla_t*)card_id; -#if defined(__LINUX__) +#if defined(__LINUX__) || defined(__WINDOWS__) wan_set_bit(AFT_FE_POLL,&card->u.aft.port_task_cmd); WAN_TASKQ_SCHEDULE((&card->u.aft.port_task)); #else @@ -4435,42 +4766,50 @@ static void enable_timer (void* card_id) #endif DEBUG_TEST("%s: %s Sdla Polling End!\n", - __FUNCTION__,card->devname); + __FUNCTION__,card->devname); return; } /**SECTION************************************************** - * - * API Bottom Half Handlers - * - **********************************************************/ +* +* API Bottom Half Handlers +* +**********************************************************/ #if defined(__LINUX__) static void wp_bh (unsigned long data) +#elif defined(__WINDOWS__) +static void wp_bh (IN PKDPC Dpc, IN PVOID data, IN PVOID SystemArgument1, IN PVOID SystemArgument2) #else static void wp_bh(void *data, int pending) #endif { private_area_t *chan = (private_area_t *)data; sdla_t *card = chan->common.card; - netskb_t *new_skb, *skb; + netskb_t *new_skb, *skb, *dealloc_skb; unsigned char pkt_error; int len; private_area_t *top_chan; + wan_smp_flag_t smp_flags; + DEBUG_TEST("%s()\n", __FUNCTION__); if (!wan_test_bit(0,&chan->up)){ DEBUG_EVENT("%s: wp_bh() chan not up!\n", - chan->if_name); - WAN_TASKLET_END(&chan->common.bh_task); + chan->if_name); + WAN_TASKLET_END((&chan->common.bh_task)); return; } +#if defined(__WINDOWS__) + top_chan=chan; +#else if (card->u.aft.tdmv_dchan){ top_chan=wan_netif_priv(chan->common.dev); }else{ top_chan=chan; } +#endif while((skb=wan_skb_dequeue(&chan->wp_rx_complete_list)) != NULL){ int len; @@ -4483,13 +4822,13 @@ static void wp_bh(void *data, int pending) pkt_error=0; /* The post function will take care - * of the skb and new_skb buffer. - * If new_skb buffer exists, driver - * must pass it up the stack, or free it */ + * of the skb and new_skb buffer. + * If new_skb buffer exists, driver + * must pass it up the stack, or free it */ xilinx_rx_post_complete (chan->common.card, chan, - skb, - &new_skb, - &pkt_error); + skb, + &new_skb, + &pkt_error); if (new_skb == NULL){ continue; } @@ -4498,25 +4837,69 @@ static void wp_bh(void *data, int pending) if (top_chan){ wan_capture_trace_packet(chan->common.card, &top_chan->trace_info, - new_skb,TRC_INCOMING_FRM); + new_skb,TRC_INCOMING_FRM); } if (chan->common.usedby == API){ +#if defined(__WINDOWS__) + int err; + wanpipe_tdm_api_dev_t *wp_tdm_api_dev = + &chan->wp_tdm_api_dev_idx[chan->tdmv_chan]; + if (is_tdm_api(chan,wp_tdm_api_dev)){ + + DEBUG_TEST("%s()\n", __FUNCTION__); + + if (wan_skb_headroom(new_skb) >= sizeof(wp_api_hdr_t)){ + wp_api_hdr_t *rx_hdr = + (wp_api_hdr_t*)skb_push(new_skb,sizeof(wp_api_hdr_t)); + memset(rx_hdr,0,sizeof(wp_api_hdr_t)); + DEBUG_TEST("%s()\n", __FUNCTION__); + //rx_hdr->error_flag=pkt_error; + } else { + if (WAN_NET_RATELIMIT()){ + DEBUG_EVENT("%s: Error Rx pkt headroom %u < %u\n", + chan->if_name, + (u32)wan_skb_headroom(new_skb), + (u32)sizeof(wp_api_hdr_t)); + } + ++chan->if_stats.rx_dropped; + wan_skb_free(new_skb); + continue; + } + + //wan_skb_print(new_skb);//FIXME: comment it out + + err=wanpipe_tdm_api_span_rx(wp_tdm_api_dev,new_skb); + if (err){ + ++chan->if_stats.rx_dropped; + wan_skb_free(new_skb); + continue; + } + + } else { + DEBUG_EVENT("%s: DCHAN Rx Packet critical error op not supported ch=%i\n",card->devname,chan->tdmv_chan); + ++chan->if_stats.rx_dropped; + wan_skb_free(new_skb); + continue; + } +#else if (chan->common.sk == NULL){ DEBUG_TEST("%s: No sock bound to channel rx dropping!\n", chan->if_name); chan->if_stats.rx_dropped++; + wan_skb_print(new_skb); wan_skb_free(new_skb); continue; } +#endif #if defined(__LINUX__) # ifndef CONFIG_PRODUCT_WANPIPE_GENERIC /* Only for API, we insert packet status - * byte to indicate a packet error. Take - * this byte and put it in the api header */ + * byte to indicate a packet error. Take + * this byte and put it in the api header */ if (wan_skb_headroom(new_skb) >= sizeof(wp_api_hdr_t)){ wp_api_hdr_t *rx_hdr= (wp_api_hdr_t*)wan_skb_push(new_skb,sizeof(wp_api_hdr_t)); @@ -4526,9 +4909,9 @@ static void wp_bh(void *data, int pending) int hroom=wan_skb_headroom(new_skb); int rx_sz=sizeof(wp_api_hdr_t); DEBUG_EVENT("%s: Error Rx pkt headroom %d < %d\n", - chan->if_name, - hroom, - rx_sz); + chan->if_name, + hroom, + rx_sz); ++chan->if_stats.rx_dropped; wan_skb_free(new_skb); continue; @@ -4555,38 +4938,36 @@ static void wp_bh(void *data, int pending) # endif #endif - - }else if (chan->common.usedby == TDM_VOICE_DCHAN){ - if (chan->tdmv_zaptel_cfg) { + if (chan->tdmv_zaptel_cfg) { #if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE) && defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN) sdla_t *card=chan->common.card; int err; WAN_TDMV_CALL(rx_dchan, - (&card->wan_tdmv, - chan->tdmv_chan, - wan_skb_data(new_skb), - wan_skb_len(new_skb)), err); + (&card->wan_tdmv, + chan->tdmv_chan, + wan_skb_data(new_skb), + wan_skb_len(new_skb)), err); # else DEBUG_EVENT("%s: DCHAN Rx Packet critical error TDMV not compiled!\n",card->devname); # endif DEBUG_TEST("%s:%ld TDM DCHAN VOICE Rx Pkt Len=%d Chan=%d\n", - card->devname,chan->logic_ch_num,wan_skb_len(new_skb), - chan->tdmv_chan); + card->devname,chan->logic_ch_num,wan_skb_len(new_skb), + chan->tdmv_chan); wan_skb_free(new_skb); #ifdef AFT_TDM_API_SUPPORT -/* TDM API SUPPORT COMPILED IN */ + /* TDM API SUPPORT COMPILED IN */ } else { int err; wanpipe_tdm_api_dev_t *wp_tdm_api_dev = - &chan->wp_tdm_api_dev_idx[chan->tdmv_chan]; + &chan->wp_tdm_api_dev_idx[chan->tdmv_chan]; if (is_tdm_api(chan,wp_tdm_api_dev)){ @@ -4597,7 +4978,7 @@ static void wp_bh(void *data, int pending) //rx_hdr->error_flag=pkt_error; } else { if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: Error Rx pkt headroom %u < %u\n", + DEBUG_EVENT("%s: Error Rx pkt headroom %u < %u\n", chan->if_name, (u32)wan_skb_headroom(new_skb), (u32)sizeof(wp_api_hdr_t)); @@ -4607,6 +4988,8 @@ static void wp_bh(void *data, int pending) continue; } + //wan_skb_print(new_skb);//FIXME: comment it out + err=wanpipe_tdm_api_span_rx(wp_tdm_api_dev,new_skb); if (err){ ++chan->if_stats.rx_dropped; @@ -4615,14 +4998,14 @@ static void wp_bh(void *data, int pending) } } else { - DEBUG_EVENT("%s: DCHAN Rx Packet critical error op not supported ch=%i\n",card->devname,chan->tdmv_chan); - ++chan->if_stats.rx_dropped; + DEBUG_EVENT("%s: DCHAN Rx Packet critical error op not supported ch=%i\n",card->devname,chan->tdmv_chan); + ++chan->if_stats.rx_dropped; wan_skb_free(new_skb); - continue; + continue; } } #else -/* TDM API SUPPORT NOT COMPILED IN */ + /* TDM API SUPPORT NOT COMPILED IN */ } else { @@ -4630,29 +5013,28 @@ static void wp_bh(void *data, int pending) ++chan->if_stats.rx_dropped; wan_skb_free(new_skb); continue; - } + } #endif - } else if (chan->common.usedby == TDM_VOICE){ + } else if (chan->common.usedby == TDM_VOICE){ /* TDM VOICE doesn't operate here */ if (WAN_NET_RATELIMIT()){ DEBUG_EVENT("%s:%ld Critical Error: TDM VOICE Rx Pkt in BH\n", - chan->if_name, - chan->logic_ch_num); + chan->if_name, + chan->logic_ch_num); } ++chan->if_stats.rx_dropped; wan_skb_free(new_skb); continue; }else if (chan->common.usedby == STACK){ - if (wanpipe_lip_rx(chan,new_skb) != 0){ ++chan->if_stats.rx_dropped; wan_skb_free(new_skb); continue; } - }else{ + DEBUG_TEST("%s(): line: %d\n", __FUNCTION__, __LINE__); protocol_recv(chan->common.card,chan,new_skb); } @@ -4660,22 +5042,33 @@ static void wp_bh(void *data, int pending) chan->opstats.Data_bytes_Rx_count+=len; chan->if_stats.rx_packets++; chan->if_stats.rx_bytes+=len; - } + }/* while((skb=wan_skb_dequeue(&chan->wp_rx_complete_list)) != NULL) */ while(chan->hdlc_eng && (skb=wan_skb_dequeue(&chan->wp_tx_complete_list)) != NULL){ xilinx_tx_post_complete (chan->common.card,chan,skb); wan_skb_free(skb); } - WAN_TASKLET_END(&chan->common.bh_task); + do { + wan_spin_lock_irq(&card->wandev.lock,&smp_flags); + dealloc_skb = wan_skb_dequeue(&chan->wp_dealloc_list); + wan_spin_unlock_irq(&card->wandev.lock,&smp_flags); + if (!dealloc_skb) { + break; + } + DEBUG_TEST("%s(): from 'wp_dealloc_list' freeing: 0x%p\n", __FUNCTION__, dealloc_skb); + wan_skb_free(dealloc_skb); + }while(dealloc_skb); + + WAN_TASKLET_END((&chan->common.bh_task)); if ((len=wan_skb_queue_len(&chan->wp_rx_complete_list))){ DEBUG_TEST("%s: Triggering from bh rx=%d\n",chan->if_name,len); - WAN_TASKLET_SCHEDULE(&chan->common.bh_task); + WAN_TASKLET_SCHEDULE((&chan->common.bh_task)); }else if ((len=wan_skb_queue_len(&chan->wp_tx_complete_list))){ - DEBUG_TEST("%s: Triggering from bh tx=%d\n",chan->if_name,len); - WAN_TASKLET_SCHEDULE(&chan->common.bh_task); - } + DEBUG_TEST("%s: Triggering from bh tx=%d\n",chan->if_name,len); + WAN_TASKLET_SCHEDULE((&chan->common.bh_task)); + } return; } @@ -4686,14 +5079,14 @@ static int fifo_error_interrupt(sdla_t *card, u32 reg,u32 tx_status,u32 rx_statu u32 i; private_area_t *chan; - /* Clear HDLC pending registers */ + /* Clear HDLC pending registers */ if (card->wandev.state != WAN_CONNECTED){ - DEBUG_TEST("%s: Warning: Ignoring Error Intr: link disc!\n", - card->devname); - return 0; - } + DEBUG_TEST("%s: Warning: Ignoring Error Intr: link disc!\n", + card->devname); + return 0; + } - if (tx_status != 0){ + if (tx_status != 0){ for (i=0;iu.aft.num_of_time_slots;i++){ if (wan_test_bit(i,&tx_status) && wan_test_bit(i,&card->u.aft.logic_ch_map)){ @@ -4705,31 +5098,31 @@ static int fifo_error_interrupt(sdla_t *card, u32 reg,u32 tx_status,u32 rx_statu if (!wan_test_bit(0,&chan->up)){ DEBUG_TEST("%s: Warning: ignoring tx error intr: dev down 0x%X UP=0x%X!\n", - wan_netif_name(chan->common.dev), - chan->common.state, - chan->ignore_modem); + wan_netif_name(chan->common.dev), + chan->common.state, + chan->ignore_modem); continue; } if (chan->common.state != WAN_CONNECTED){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: Warning: ignoring tx error intr: dev disc!\n", - wan_netif_name(chan->common.dev)); + DEBUG_EVENT("%s: Warning: ignoring tx error intr: dev disc!\n", + wan_netif_name(chan->common.dev)); } continue; } if (!chan->hdlc_eng && !wan_test_bit(0,&chan->idle_start)){ DEBUG_TEST("%s: Warning: ignoring tx error intr: dev init error!\n", - wan_netif_name(chan->common.dev)); + wan_netif_name(chan->common.dev)); if (chan->hdlc_eng){ xilinx_tx_fifo_under_recover(card,chan); } - continue; + continue; } #if 0 if (chan->hdlc_eng && WAN_NET_RATELIMIT()){ - u32 reg_lo, reg_hi,dma_descr; + u32 reg_lo, reg_hi,dma_descr; dma_descr=(chan->logic_ch_num<<4) + XILINX_TxDMA_DESCRIPTOR_LO; card->hw_iface.bus_read_4(card->hw,dma_descr, ®_lo); @@ -4751,10 +5144,10 @@ static int fifo_error_interrupt(sdla_t *card, u32 reg,u32 tx_status,u32 rx_statu err=-EINVAL; } } - } + } - if (rx_status != 0){ + if (rx_status != 0){ for (i=0;iu.aft.num_of_time_slots;i++){ if (wan_test_bit(i,&rx_status) && wan_test_bit(i,&card->u.aft.logic_ch_map)){ chan=(private_area_t*)card->u.aft.dev_to_ch_map[i]; @@ -4772,16 +5165,16 @@ static int fifo_error_interrupt(sdla_t *card, u32 reg,u32 tx_status,u32 rx_statu if (chan->common.state != WAN_CONNECTED){ DEBUG_TEST("%s: Warning: ignoring rx error intr: dev disc!\n", - wan_netif_name(chan->common.dev)); - continue; - } + wan_netif_name(chan->common.dev)); + continue; + } chan->if_stats.rx_fifo_errors++; chan->errstats.Rx_overrun_err_count++; #if 0 if (chan->hdlc_eng && WAN_NET_RATELIMIT()){ - u32 reg_lo, reg_hi,dma_descr; + u32 reg_lo, reg_hi,dma_descr; dma_descr=(chan->logic_ch_num<<4) + XILINX_RxDMA_DESCRIPTOR_LO; card->hw_iface.bus_read_4(card->hw,dma_descr, ®_lo); @@ -4794,7 +5187,7 @@ static int fifo_error_interrupt(sdla_t *card, u32 reg,u32 tx_status,u32 rx_statu wan_skb_queue_len(&chan->wp_rx_complete_list), wan_skb_queue_len(&chan->wp_rx_free_list), chan->rx_dma, - reg,reg_lo, reg_hi); + reg,reg_lo, reg_hi); } #endif wan_set_bit(WP_FIFO_ERROR_BIT, &chan->pkt_error); @@ -4802,7 +5195,7 @@ static int fifo_error_interrupt(sdla_t *card, u32 reg,u32 tx_status,u32 rx_statu err=-EINVAL; } } - } + } return err; } @@ -4825,22 +5218,22 @@ static void front_end_interrupt(sdla_t *card, unsigned long reg,int lock) } /**SECTION*************************************************************** - * - * HARDWARE Interrupt Handlers - * - ***********************************************************************/ +* +* HARDWARE Interrupt Handlers +* +***********************************************************************/ /*============================================================================ - * wpfw_isr - * - * Main interrupt service routine. - * Determin the interrupt received and handle it. - * - */ +* wpfw_isr +* +* Main interrupt service routine. +* Determin the interrupt received and handle it. +* +*/ static WAN_IRQ_RETVAL wp_xilinx_isr (sdla_t* card) { - int i; + int i; u32 reg; u32 dma_tx_reg,dma_rx_reg,tx_fifo_status=0,rx_fifo_status=0; private_area_t *chan; @@ -4850,20 +5243,20 @@ static WAN_IRQ_RETVAL wp_xilinx_isr (sdla_t* card) WAN_IRQ_RETURN(irq_ret); } - wan_set_bit(0,&card->in_isr); + wan_set_bit(0,&card->in_isr); -/* write_cpld(card,LED_CONTROL_REG,0x0F);*/ + /* write_cpld(card,LED_CONTROL_REG,0x0F);*/ - /* -----------------2/6/2003 9:02AM------------------ - * Disable all chip Interrupts (offset 0x040) - * -- "Transmit/Receive DMA Engine" interrupt disable - * -- "FiFo/Line Abort Error" interrupt disable - * --------------------------------------------------*/ - card->hw_iface.bus_read_4(card->hw,XILINX_CHIP_CFG_REG, ®); + /* -----------------2/6/2003 9:02AM------------------ + * Disable all chip Interrupts (offset 0x040) + * -- "Transmit/Receive DMA Engine" interrupt disable + * -- "FiFo/Line Abort Error" interrupt disable + * --------------------------------------------------*/ + card->hw_iface.bus_read_4(card->hw,XILINX_CHIP_CFG_REG, ®); DEBUG_ISR("\n"); DEBUG_ISR("%s: ISR (0x%X) = 0x%08X \n", - card->devname,XILINX_CHIP_CFG_REG,reg); + card->devname,XILINX_CHIP_CFG_REG,reg); if (wan_test_bit(SECURITY_STATUS_FLAG,®)){ if (++card->u.aft.chip_security_cnt > AFT_MAX_CHIP_SECURITY_CNT){ @@ -4881,37 +5274,37 @@ static WAN_IRQ_RETVAL wp_xilinx_isr (sdla_t* card) } /* Note: If interrupts are received without pending - * flags, it usually indicates that the interrupt - * is being shared. (Check 'cat /proc/interrupts') - */ + * flags, it usually indicates that the interrupt + * is being shared. (Check 'cat /proc/interrupts') + */ - if (wan_test_bit(FRONT_END_INTR_ENABLE_BIT,®)){ + if (wan_test_bit(FRONT_END_INTR_ENABLE_BIT,®)){ if (wan_test_bit(FRONT_END_INTR_FLAG,®)){ -#if defined(__LINUX__) +#if defined(__LINUX__) || defined(__WINDOWS__) if (card->wandev.fe_iface.check_isr && - card->wandev.fe_iface.check_isr(&card->fe)){ - wan_set_bit(AFT_FE_INTR,&card->u.aft.port_task_cmd); - WAN_TASKQ_SCHEDULE((&card->u.aft.port_task)); + card->wandev.fe_iface.check_isr(&card->fe)){ + wan_set_bit(AFT_FE_INTR,&card->u.aft.port_task_cmd); + WAN_TASKQ_SCHEDULE((&card->u.aft.port_task)); - __aft_fe_intr_ctrl(card,0); + __aft_fe_intr_ctrl(card,0); } #else - front_end_interrupt(card,reg,0); + front_end_interrupt(card,reg,0); #endif WAN_IRQ_RETVAL_SET(irq_ret, WAN_IRQ_HANDLED); } - } + } /* Test Fifo Error Interrupt, - * If set shutdown all interfaces and - * reconfigure */ + * If set shutdown all interfaces and + * reconfigure */ if (wan_test_bit(ERROR_INTR_ENABLE_BIT,®)){ - if (wan_test_bit(ERROR_INTR_FLAG,®)){ + if (wan_test_bit(ERROR_INTR_FLAG,®)){ #if 0 DEBUG_EVENT("%s: ERR INTR (0x%X)\n",card->devname,reg); #endif - card->hw_iface.bus_read_4(card->hw,XILINX_HDLC_TX_INTR_PENDING_REG,&tx_fifo_status); - card->hw_iface.bus_read_4(card->hw,XILINX_HDLC_RX_INTR_PENDING_REG,&rx_fifo_status); + card->hw_iface.bus_read_4(card->hw,XILINX_HDLC_TX_INTR_PENDING_REG,&tx_fifo_status); + card->hw_iface.bus_read_4(card->hw,XILINX_HDLC_RX_INTR_PENDING_REG,&rx_fifo_status); rx_fifo_status&=card->u.aft.active_ch_map; tx_fifo_status&=card->u.aft.active_ch_map; @@ -4921,146 +5314,146 @@ static WAN_IRQ_RETVAL wp_xilinx_isr (sdla_t* card) } } - /* -----------------2/6/2003 9:37AM------------------ - * Checking for Interrupt source: - * 1. Receive DMA Engine - * 2. Transmit DMA Engine - * 3. Error conditions. - * --------------------------------------------------*/ - if (wan_test_bit(GLOBAL_INTR_ENABLE_BIT,®) && - (wan_test_bit(DMA_INTR_FLAG,®) || rx_fifo_status) ){ + /* -----------------2/6/2003 9:37AM------------------ + * Checking for Interrupt source: + * 1. Receive DMA Engine + * 2. Transmit DMA Engine + * 3. Error conditions. + * --------------------------------------------------*/ + if (wan_test_bit(GLOBAL_INTR_ENABLE_BIT,®) && + (wan_test_bit(DMA_INTR_FLAG,®) || rx_fifo_status) ){ - WAN_IRQ_RETVAL_SET(irq_ret, WAN_IRQ_HANDLED); + WAN_IRQ_RETVAL_SET(irq_ret, WAN_IRQ_HANDLED); - /* Receive DMA Engine */ - card->hw_iface.bus_read_4(card->hw, - XILINX_DMA_RX_INTR_PENDING_REG, - &dma_rx_reg); + /* Receive DMA Engine */ + card->hw_iface.bus_read_4(card->hw, + XILINX_DMA_RX_INTR_PENDING_REG, + &dma_rx_reg); #if 0 - card->hw_iface.bus_read_4(card->hw,XILINX_HDLC_TX_INTR_PENDING_REG,&tx_fifo_status); - card->hw_iface.bus_read_4(card->hw,XILINX_HDLC_RX_INTR_PENDING_REG,&rx_fifo_status); + card->hw_iface.bus_read_4(card->hw,XILINX_HDLC_TX_INTR_PENDING_REG,&tx_fifo_status); + card->hw_iface.bus_read_4(card->hw,XILINX_HDLC_RX_INTR_PENDING_REG,&rx_fifo_status); - rx_fifo_status&=card->u.aft.active_ch_map; - tx_fifo_status&=card->u.aft.active_ch_map; + rx_fifo_status&=card->u.aft.active_ch_map; + tx_fifo_status&=card->u.aft.active_ch_map; #endif - DEBUG_ISR("%s: DMA_RX_INTR_REG(0x%X) = 0x%X\n", + DEBUG_ISR("%s: DMA_RX_INTR_REG(0x%X) = 0x%X\n", card->devname, XILINX_DMA_RX_INTR_PENDING_REG,dma_rx_reg); - dma_rx_reg&=card->u.aft.active_ch_map; + dma_rx_reg&=card->u.aft.active_ch_map; - if (dma_rx_reg == 0 && rx_fifo_status == 0){ - goto isr_skb_rx; - } + if (dma_rx_reg == 0 && rx_fifo_status == 0){ + goto isr_skb_rx; + } - for (i=0; iu.aft.num_of_time_slots ;i++){ - if ((wan_test_bit(i,&dma_rx_reg) || wan_test_bit(i,&rx_fifo_status)) && - wan_test_bit(i,&card->u.aft.logic_ch_map)){ + for (i=0; iu.aft.num_of_time_slots ;i++){ + if ((wan_test_bit(i,&dma_rx_reg) || wan_test_bit(i,&rx_fifo_status)) && + wan_test_bit(i,&card->u.aft.logic_ch_map)){ - chan=(private_area_t*)card->u.aft.dev_to_ch_map[i]; - if (!chan){ - DEBUG_EVENT("%s: Error: No Dev for Rx logical ch=%d\n", - card->devname,i); - continue; - } + chan=(private_area_t*)card->u.aft.dev_to_ch_map[i]; + if (!chan){ + DEBUG_EVENT("%s: Error: No Dev for Rx logical ch=%d\n", + card->devname,i); + continue; + } - if (!wan_test_bit(0,&chan->up)){ - DEBUG_EVENT("%s: Error: Dev not up for Rx logical ch=%d\n", - card->devname,i); - continue; - } + if (!wan_test_bit(0,&chan->up)){ + DEBUG_EVENT("%s: Error: Dev not up for Rx logical ch=%d\n", + card->devname,i); + continue; + } #if 0 - chan->if_stats.rx_frame_errors++; + chan->if_stats.rx_frame_errors++; #endif - if (wan_test_bit(i, &rx_fifo_status)){ - /* If fifo error occured, the rx descriptor - * was already handled, thus the rx pending - * should just be disregarded */ - chan->if_stats.rx_frame_errors++; - wan_set_bit(WP_FIFO_ERROR_BIT, &chan->pkt_error); - } + if (wan_test_bit(i, &rx_fifo_status)){ + /* If fifo error occured, the rx descriptor + * was already handled, thus the rx pending + * should just be disregarded */ + chan->if_stats.rx_frame_errors++; + wan_set_bit(WP_FIFO_ERROR_BIT, &chan->pkt_error); + } - DEBUG_RX("%s: RX Interrupt pend. \n", - card->devname); + DEBUG_RX("%s: RX Interrupt pend. \n", + card->devname); #if 0 - /* NCDEBUG */ - chan->if_stats.tx_fifo_errors=wan_skb_queue_len(&chan->wp_tx_pending_list); - chan->if_stats.rx_fifo_errors=wan_skb_queue_len(&chan->wp_rx_free_list); + /* NCDEBUG */ + chan->if_stats.tx_fifo_errors=wan_skb_queue_len(&chan->wp_tx_pending_list); + chan->if_stats.rx_fifo_errors=wan_skb_queue_len(&chan->wp_rx_free_list); #endif - xilinx_dma_rx_complete(card,chan); + xilinx_dma_rx_complete(card,chan); - if (chan->common.usedby == TDM_VOICE || chan->common.usedby == TDM_VOICE_API){ + if (chan->common.usedby == TDM_VOICE || chan->common.usedby == TDM_VOICE_API){ #if 0 - /* Never check interrupt here - * since it breaks channelization */ - card->hw_iface.bus_read_4(card->hw, - XILINX_DMA_TX_INTR_PENDING_REG, - &dma_tx_reg); + /* Never check interrupt here + * since it breaks channelization */ + card->hw_iface.bus_read_4(card->hw, + XILINX_DMA_TX_INTR_PENDING_REG, + &dma_tx_reg); #endif - xilinx_dma_tx_complete(card,chan); + xilinx_dma_tx_complete(card,chan); + } } } - } isr_skb_rx: - /* Transmit DMA Engine */ + /* Transmit DMA Engine */ - card->hw_iface.bus_read_4(card->hw, - XILINX_DMA_TX_INTR_PENDING_REG, - &dma_tx_reg); + card->hw_iface.bus_read_4(card->hw, + XILINX_DMA_TX_INTR_PENDING_REG, + &dma_tx_reg); - dma_tx_reg&=card->u.aft.active_ch_map; + dma_tx_reg&=card->u.aft.active_ch_map; - DEBUG_ISR("%s: DMA_TX_INTR_REG(0x%X) = 0x%X\n", + DEBUG_ISR("%s: DMA_TX_INTR_REG(0x%X) = 0x%X\n", card->devname, XILINX_DMA_RX_INTR_PENDING_REG,dma_tx_reg); - if (dma_tx_reg == 0){ - goto isr_skb_tx; - } - - for (i=0; iu.aft.num_of_time_slots ;i++){ - if (wan_test_bit(i,&dma_tx_reg) && wan_test_bit(i,&card->u.aft.logic_ch_map)){ - chan=(private_area_t*)card->u.aft.dev_to_ch_map[i]; - if (!chan){ - DEBUG_EVENT("%s: Error: No Dev for Tx logical ch=%d\n", - card->devname,i); - continue; - } - - if (!wan_test_bit(0,&chan->up)){ - DEBUG_EVENT("%s: Error: Dev not up for Tx logical ch=%d\n", - card->devname,i); - continue; - } - - if (chan->common.usedby == TDM_VOICE ||chan->common.usedby == TDM_VOICE_API) { - continue; - } - - DEBUG_TX(" ---- TX Interrupt pend. --\n"); - xilinx_dma_tx_complete(card,chan); + if (dma_tx_reg == 0){ + goto isr_skb_tx; } - } - } + + for (i=0; iu.aft.num_of_time_slots ;i++){ + if (wan_test_bit(i,&dma_tx_reg) && wan_test_bit(i,&card->u.aft.logic_ch_map)){ + chan=(private_area_t*)card->u.aft.dev_to_ch_map[i]; + if (!chan){ + DEBUG_EVENT("%s: Error: No Dev for Tx logical ch=%d\n", + card->devname,i); + continue; + } + + if (!wan_test_bit(0,&chan->up)){ + DEBUG_EVENT("%s: Error: Dev not up for Tx logical ch=%d\n", + card->devname,i); + continue; + } + + if (chan->common.usedby == TDM_VOICE ||chan->common.usedby == TDM_VOICE_API) { + continue; + } + + DEBUG_TX(" ---- TX Interrupt pend. --\n"); + xilinx_dma_tx_complete(card,chan); + } + } + } isr_skb_tx: /* -----------------2/6/2003 10:36AM----------------- - * Finish of the interupt handler - * --------------------------------------------------*/ + * Finish of the interupt handler + * --------------------------------------------------*/ isr_end: -/* write_cpld(card,LED_CONTROL_REG,0x0E); */ + /* write_cpld(card,LED_CONTROL_REG,0x0E); */ - DEBUG_ISR("---- ISR end.-------------------\n"); - wan_clear_bit(0,&card->in_isr); + DEBUG_ISR("---- ISR end.-------------------\n"); + wan_clear_bit(0,&card->in_isr); WAN_IRQ_RETURN(irq_ret); } @@ -5068,34 +5461,34 @@ isr_end: /**SECTION*********************************************************** - * - * WANPIPE Debugging Interfaces - * - ********************************************************************/ +* +* WANPIPE Debugging Interfaces +* +********************************************************************/ /*============================================================================= - * process_udp_mgmt_pkt - * - * Process all "wanpipemon" debugger commands. This function - * performs all debugging tasks: - * - * Line Tracing - * Line/Hardware Statistics - * Protocol Statistics - * - * "wanpipemon" utility is a user-space program that - * is used to debug the WANPIPE product. - * - */ -#if 1 +* process_udp_mgmt_pkt +* +* Process all "wanpipemon" debugger commands. This function +* performs all debugging tasks: +* +* Line Tracing +* Line/Hardware Statistics +* Protocol Statistics +* +* "wanpipemon" utility is a user-space program that +* is used to debug the WANPIPE product. +* +*/ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, - private_area_t* chan, int local_dev ) + private_area_t* chan, int local_dev ) { unsigned short buffer_length; wan_udp_pkt_t *wan_udp_pkt; wan_trace_t *trace_info=NULL; + wp_tdm_chan_stats_t *wp_tdm_chan_stats=NULL; wan_udp_pkt = (wan_udp_pkt_t *)chan->udp_pkt_data; @@ -5105,9 +5498,9 @@ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, trace_info=&chan->trace_info; wan_udp_pkt = (wan_udp_pkt_t *)chan->udp_pkt_data; + wp_tdm_chan_stats = (wp_tdm_chan_stats_t*)wan_udp_pkt->wan_udp_data; - { - + { netskb_t *skb; wan_udp_pkt->wan_udp_opp_flag = 0; @@ -5115,34 +5508,29 @@ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, switch(wan_udp_pkt->wan_udp_command) { case WANPIPEMON_READ_CONFIGURATION: - wan_udp_pkt->wan_udp_return_code = 0; + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; wan_udp_pkt->wan_udp_data_len=0; break; case WANPIPEMON_READ_CODE_VERSION: - wan_udp_pkt->wan_udp_return_code = 0; + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; card->hw_iface.getcfg(card->hw, SDLA_COREREV, &wan_udp_pkt->wan_udp_data[0]); wan_udp_pkt->wan_udp_data_len=1; break; case WANPIPEMON_AFT_LINK_STATUS: - wan_udp_pkt->wan_udp_return_code = 0; - if (card->wandev.state == WAN_CONNECTED){ - wan_udp_pkt->wan_udp_data[0]=1; - }else{ - wan_udp_pkt->wan_udp_data[0]=0; - } - wan_udp_pkt->wan_udp_data_len=1; + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; + wan_udp_pkt->wan_udp_data[0] = card->wandev.state;/* WAN_CONNECTED/WAN_DISCONNECTED */ + wan_udp_pkt->wan_udp_data_len = 1; break; case WANPIPEMON_DIGITAL_LOOPTEST: - wan_udp_pkt->wan_udp_return_code = 0; + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; DEBUG_EVENT("Ready to send some data!!!\n"); break; - case WANPIPEMON_AFT_MODEM_STATUS: - wan_udp_pkt->wan_udp_return_code = 0; + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; if (card->wandev.state == WAN_CONNECTED){ wan_udp_pkt->wan_udp_data[0]=0x28; }else{ @@ -5152,24 +5540,38 @@ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, break; case WANPIPEMON_READ_OPERATIONAL_STATS: - wan_udp_pkt->wan_udp_return_code = 0; +#if defined(__WINDOWS__) + memset(wp_tdm_chan_stats, 0x00, sizeof(wp_tdm_chan_stats_t)); + + wp_tdm_chan_stats->rx_bytes = chan->opstats.Data_bytes_Rx_count; + wp_tdm_chan_stats->rx_packets = chan->opstats.Data_frames_Rx_count; + + wp_tdm_chan_stats->tx_bytes = chan->opstats.Data_bytes_Tx_count;; + wp_tdm_chan_stats->tx_packets = chan->opstats.Data_frames_Tx_count; + + wan_udp_pkt->wan_udp_data_len=sizeof(wp_tdm_chan_stats_t); + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; +#else + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; memcpy(wan_udp_pkt->wan_udp_data,&chan->opstats,sizeof(aft_op_stats_t)); wan_udp_pkt->wan_udp_data_len=sizeof(aft_op_stats_t); +#endif break; case WANPIPEMON_FLUSH_OPERATIONAL_STATS: - wan_udp_pkt->wan_udp_return_code = 0; + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; memset(&chan->opstats,0,sizeof(aft_op_stats_t)); wan_udp_pkt->wan_udp_data_len=0; break; case WANPIPEMON_READ_COMMS_ERROR_STATS: - wan_udp_pkt->wan_udp_return_code = 0; + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; memcpy(wan_udp_pkt->wan_udp_data,&chan->errstats,sizeof(aft_comm_err_stats_t)); wan_udp_pkt->wan_udp_data_len=sizeof(aft_comm_err_stats_t); break; + case WANPIPEMON_FLUSH_COMMS_ERROR_STATS: - wan_udp_pkt->wan_udp_return_code = 0; + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; memset(&chan->errstats,0,sizeof(aft_comm_err_stats_t)); wan_udp_pkt->wan_udp_data_len=0; break; @@ -5192,18 +5594,18 @@ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, wan_clear_bit(2,&trace_info->tracing_enabled); wan_set_bit(1,&trace_info->tracing_enabled); DEBUG_UDP("%s: ADSL L2 trace enabled!\n", - card->devname); + card->devname); }else{ wan_clear_bit(1,&trace_info->tracing_enabled); wan_set_bit(2,&trace_info->tracing_enabled); DEBUG_UDP("%s: ADSL L1 trace enabled!\n", - card->devname); + card->devname); } wan_set_bit (0,&trace_info->tracing_enabled); }else{ DEBUG_EVENT("%s: Error: AFT trace running!\n", - card->devname); + card->devname); wan_udp_pkt->wan_udp_return_code = 2; } @@ -5222,23 +5624,22 @@ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, wan_trace_purge(trace_info); DEBUG_UDP("%s: Disabling AFT trace\n", - card->devname); + card->devname); }else{ /* set return code to line trace already - disabled */ + disabled */ wan_udp_pkt->wan_udp_return_code = 1; } - break; - case WANPIPEMON_GET_TRACE_INFO: + case WANPIPEMON_GET_TRACE_INFO: if(wan_test_bit(0,&trace_info->tracing_enabled)){ trace_info->trace_timeout = SYSTEM_TICKS; }else{ DEBUG_EVENT("%s: Error AFT trace not enabled\n", - card->devname); + card->devname); /* set return code */ wan_udp_pkt->wan_udp_return_code = 1; break; @@ -5253,7 +5654,7 @@ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, WAN_IFQ_POLL(&trace_info->trace_queue, skb); if (skb == NULL){ DEBUG_EVENT("%s: No more trace packets in trace queue!\n", - card->devname); + card->devname); break; } if ((WAN_MAX_DATA_SIZE - buffer_length) < skb->m_pkthdr.len){ @@ -5263,9 +5664,9 @@ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, } m_copydata(skb, - 0, - skb->m_pkthdr.len, - &wan_udp_pkt->wan_udp_data[buffer_length]); + 0, + skb->m_pkthdr.len, + &wan_udp_pkt->wan_udp_data[buffer_length]); buffer_length += skb->m_pkthdr.len; WAN_IFQ_DEQUEUE(&trace_info->trace_queue, skb); if (skb){ @@ -5283,8 +5684,8 @@ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, wan_skb_queue_head(&trace_info->trace_queue, skb); }else{ /* If rx buffer length is greater than the - * whole udp buffer copy only the trace - * header and drop the trace packet */ + * whole udp buffer copy only the trace + * header and drop the trace packet */ memcpy(&wan_udp_pkt->wan_udp_atm_data[buffer_length], wan_skb_data(skb), @@ -5298,13 +5699,41 @@ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, } memcpy(&wan_udp_pkt->wan_udp_atm_data[buffer_length], - wan_skb_data(skb), - wan_skb_len(skb)); + wan_skb_data(skb), + wan_skb_len(skb)); buffer_length += wan_skb_len(skb); wan_skb_free(skb); wan_udp_pkt->wan_udp_atm_num_frames++; } +#elif defined(__WINDOWS__) + { + wan_smp_flag_t smp_flags; + wan_spin_lock_irq(&card->wandev.lock,&smp_flags); + skb=skb_dequeue(&trace_info->trace_queue); + wan_spin_unlock_irq(&card->wandev.lock,&smp_flags); + + if(skb){ + memcpy(&wan_udp_pkt->wan_udp_data[buffer_length], + wan_skb_data(skb), + wan_skb_len(skb)); + + buffer_length += (unsigned short)wan_skb_len(skb); + wan_skb_free(skb); + wan_udp_pkt->wan_udp_aft_num_frames++; + + /* NOT locking check of queue length intentionally! */ + if(wan_skb_queue_len(&trace_info->trace_queue) > 0){ + + if(wan_skb_queue_len(&trace_info->trace_queue)){ + /* indicate there are more frames on board & exit */ + wan_udp_pkt->wan_udp_aft_ismoredata = 0x01; + }else{ + wan_udp_pkt->wan_udp_aft_ismoredata = 0x00; + } + } + }/* if(skb) */ + } #endif /* set the data length and return code */ wan_udp_pkt->wan_udp_data_len = buffer_length; @@ -5314,16 +5743,16 @@ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, case WANPIPEMON_ROUTER_UP_TIME: wan_getcurrenttime(&chan->router_up_time, NULL); chan->router_up_time -= chan->router_start_time; - *(unsigned long *)&wan_udp_pkt->wan_udp_data = - chan->router_up_time; + *(wan_time_t *)&wan_udp_pkt->wan_udp_data = + chan->router_up_time; wan_udp_pkt->wan_udp_data_len = sizeof(unsigned long); - wan_udp_pkt->wan_udp_return_code = 0; + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; break; case WAN_GET_MEDIA_TYPE: case WAN_FE_GET_STAT: case WAN_FE_LB_MODE: - case WAN_FE_FLUSH_PMON: + case WAN_FE_FLUSH_PMON: case WAN_FE_GET_CFG: case WAN_FE_SET_DEBUG_MODE: case WAN_FE_TX_MODE: @@ -5331,13 +5760,13 @@ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, wan_smp_flag_t smp_flags; card->hw_iface.hw_lock(card->hw,&smp_flags); card->wandev.fe_iface.process_udp( - &card->fe, - &wan_udp_pkt->wan_udp_cmd, - &wan_udp_pkt->wan_udp_data[0]); + &card->fe, + &wan_udp_pkt->wan_udp_cmd, + &wan_udp_pkt->wan_udp_data[0]); card->hw_iface.hw_unlock(card->hw,&smp_flags); }else{ if (wan_udp_pkt->wan_udp_command == WAN_GET_MEDIA_TYPE){ - wan_udp_pkt->wan_udp_data_len = sizeof(wan_femedia_t); + wan_udp_pkt->wan_udp_data_len = sizeof(wan_femedia_t); wan_udp_pkt->wan_udp_return_code = CMD_OK; }else{ wan_udp_pkt->wan_udp_return_code = WAN_UDP_INVALID_CMD; @@ -5364,123 +5793,154 @@ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, wan_udp_pkt->wan_udp_return_code = 0xCD; break; +#if defined(__WINDOWS__) + case WAN_GET_HW_MAC_ADDR: + /* There is no MAC address on AFT hardware, simply create a random MAC. + Only S518 ADSL provides built-in MAC addrr (sdla_adsl.c). */ + wan_get_random_mac_address(wan_udp_pkt->wan_udp_data); + wan_udp_pkt->wan_udp_return_code = WAN_CMD_OK; + wan_udp_pkt->wan_udp_data_len = ETHER_ADDR_LEN; + break; + + case WANPIPEMON_GET_BIOS_ENCLOSURE3_SERIAL_NUMBER: + wan_udp_pkt->wan_udp_return_code = + wp_get_motherboard_enclosure_serial_number(wan_udp_pkt->wan_udp_data, + sizeof(wan_udp_pkt->wan_udp_data)); + + wan_udp_pkt->wan_udp_data_len = sizeof(wan_udp_pkt->wan_udp_data); + break; +#endif default: wan_udp_pkt->wan_udp_data_len = 0; - wan_udp_pkt->wan_udp_return_code = 0xCD; + wan_udp_pkt->wan_udp_return_code = WAN_UDP_INVALID_NET_CMD; if (WAN_NET_RATELIMIT()){ DEBUG_EVENT( - "%s: Warning, Illegal UDP command attempted from network: %x\n", - card->devname,wan_udp_pkt->wan_udp_command); + "%s: Warning, Illegal UDP command attempted from network: %x\n", + card->devname,wan_udp_pkt->wan_udp_command); } break; } /* end of switch */ - } /* end of else */ + } /* end of else */ - /* Fill UDP TTL */ + /* Fill UDP TTL */ wan_udp_pkt->wan_ip_ttl= card->wandev.ttl; wan_udp_pkt->wan_udp_request_reply = UDPMGMT_REPLY; return 1; } -#endif - - /**SECTION************************************************************* - * - * TASK Functions and Triggers - * - **********************************************************************/ +* +* TASK Functions and Triggers +* +**********************************************************************/ /*============================================================================ - * port_set_state - * - * Set PORT state. - * - */ +* port_set_state +* +* Set PORT state. +* +*/ static void port_set_state (sdla_t *card, int state) { struct wan_dev_le *devle; netdevice_t *dev; - if (card->wandev.state != state) - { - switch (state) - { - case WAN_CONNECTED: - DEBUG_TEST( "%s: Link connected!\n", - card->devname); - break; + if (card->wandev.state == state){ + return; + } - case WAN_CONNECTING: - DEBUG_TEST( "%s: Link connecting...\n", - card->devname); - break; + switch (state) + { + case WAN_CONNECTED: + DEBUG_TEST( "%s: Link connected!\n", + card->devname); + break; - case WAN_DISCONNECTED: - DEBUG_TEST( "%s: Link disconnected!\n", - card->devname); - break; - } + case WAN_CONNECTING: + DEBUG_TEST( "%s: Link connecting...\n", + card->devname); + break; - card->wandev.state = state; - WAN_LIST_FOREACH(devle, &card->wandev.dev_head, dev_link){ - dev = WAN_DEVLE2DEV(devle); - if (dev){ - set_chan_state(card, dev, state); + case WAN_DISCONNECTED: + DEBUG_TEST( "%s: Link disconnected!\n", + card->devname); + break; + } + + card->wandev.state = state; +#if defined(__WINDOWS__) + { + int i; + for (i = 0; i < NUM_OF_E1_CHANNELS; i++){ + if(card->sdla_net_device[i] != NULL && wan_test_bit(0,&card->up[i]) ){ + set_chan_state(card, card->sdla_net_device[i], state); } } - } + } +#else + WAN_LIST_FOREACH(devle, &card->wandev.dev_head, dev_link){ + dev = WAN_DEVLE2DEV(devle); + if (dev){ + set_chan_state(card, dev, state); + } + } +#endif } /*============================================================ - * handle_front_end_state - * - * Front end state indicates the physical medium that - * the Z80 backend connects to. - * - * S514-1/2/3: V32/RS232/FT1 Front End - * Front end state is determined via - * Modem/Status. - * S514-4/5/7/8: 56K/T1/E1 Front End - * Front end state is determined via - * link status interrupt received - * from the front end hardware. - * - * If the front end state handler is enabed by the - * user. The interface state will follow the - * front end state. I.E. If the front end goes down - * the protocol and interface will be declared down. - * - * If the front end state is UP, then the interface - * and protocol will be up ONLY if the protocol is - * also UP. - * - * Therefore, we must have three state variables - * 1. Front End State (card->wandev.front_end_status) - * 2. Protocol State (card->wandev.state) - * 3. Interface State (dev->flags & IFF_UP) - * - */ +* handle_front_end_state +* +* Front end state indicates the physical medium that +* the Z80 backend connects to. +* +* S514-1/2/3: V32/RS232/FT1 Front End +* Front end state is determined via +* Modem/Status. +* S514-4/5/7/8: 56K/T1/E1 Front End +* Front end state is determined via +* link status interrupt received +* from the front end hardware. +* +* If the front end state handler is enabed by the +* user. The interface state will follow the +* front end state. I.E. If the front end goes down +* the protocol and interface will be declared down. +* +* If the front end state is UP, then the interface +* and protocol will be up ONLY if the protocol is +* also UP. +* +* Therefore, we must have three state variables +* 1. Front End State (card->wandev.front_end_status) +* 2. Protocol State (card->wandev.state) +* 3. Interface State (dev->flags & IFF_UP) +* +*/ static void handle_front_end_state(void *card_id) { sdla_t* card = (sdla_t*)card_id; +#if defined(__WINDOWS__) + DBG_LIP_OOB("%s(): IRQL: %s, card->fe.fe_status: %s\n", __FUNCTION__, + decode_irql(KeGetCurrentIrql()), FE_STATUS_DECODE(card->fe.fe_status)); +#endif + if (card->wandev.ignore_front_end_status == WANOPT_YES){ return; } if (!wan_test_bit(AFT_CHIP_CONFIGURED,&card->u.aft.chip_cfg_status)&& - card->fe.fe_status == FE_CONNECTED){ - DEBUG_TEST("%s: Skipping Front Front End State = %x\n", + card->fe.fe_status == FE_CONNECTED){ + DEBUG_TEST("%s: Skipping Front Front End State = %x\n", card->devname,card->fe.fe_status); - wan_set_bit(AFT_FRONT_END_UP,&card->u.aft.chip_cfg_status); - return; + wan_set_bit(AFT_FRONT_END_UP,&card->u.aft.chip_cfg_status); + return; } if (card->fe.fe_status == FE_CONNECTED){ @@ -5501,39 +5961,43 @@ static void handle_front_end_state(void *card_id) if (card->u.aft.cfg.rbs){ if (card->wandev.fe_iface.set_fe_sigctrl){ card->wandev.fe_iface.set_fe_sigctrl( - &card->fe, - WAN_TE_SIG_INTR, - ENABLE_ALL_CHANNELS, - WAN_ENABLE); + &card->fe, + WAN_TE_SIG_INTR, + ENABLE_ALL_CHANNELS, + WAN_ENABLE); } } } else if (card->wandev.state != WAN_CONNECTED && - wan_test_bit(0,&card->u.aft.comm_enabled)){ + wan_test_bit(0,&card->u.aft.comm_enabled)){ - disable_data_error_intr(card,LINK_DOWN); + disable_data_error_intr(card,LINK_DOWN); #if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE) - if (card->wan_tdmv.sc){ - int err; - WAN_TDMV_CALL(state, (card, WAN_CONNECTED), err); - } + if (card->wan_tdmv.sc){ + int err; + WAN_TDMV_CALL(state, (card, WAN_CONNECTED), err); + } #endif - enable_data_error_intr(card); - card->wandev.state = WAN_CONNECTED; - aft_red_led_ctrl(card, AFT_LED_OFF); - card->wandev.fe_iface.led_ctrl(&card->fe, AFT_LED_ON); + enable_data_error_intr(card); + card->wandev.state = WAN_CONNECTED; + aft_red_led_ctrl(card, AFT_LED_OFF); + card->wandev.fe_iface.led_ctrl(&card->fe, AFT_LED_ON); - DEBUG_EVENT("%s: AFT Front End Restart!\n", - card->devname); + DEBUG_EVENT("%s: AFT Front End Restart!\n", + card->devname); + }else{ + DEBUG_TEST("%s(): line: %d: unhandled case!\n", __FUNCTION__, __LINE__); } }else{ +#ifndef __WINDOWS__ if (card->tdmv_conf.span_no){ + /* If running in TDMV voice mode, just note - * that state went down, but keep the - * connection up */ + * that state went down, but keep the + * connection up */ card->wandev.state = WAN_DISCONNECTED; aft_red_led_ctrl(card, AFT_LED_ON); card->wandev.fe_iface.led_ctrl(&card->fe, AFT_LED_OFF); @@ -5545,20 +6009,22 @@ static void handle_front_end_state(void *card_id) #endif return; } - - if (wan_test_bit(0,&card->u.aft.comm_enabled) || - card->wandev.state != WAN_DISCONNECTED){ - - port_set_state(card,WAN_DISCONNECTED); - disable_data_error_intr(card,LINK_DOWN); -#if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE) - if (card->wan_tdmv.sc){ - int err; - WAN_TDMV_CALL(state, (card, WAN_DISCONNECTED), err); - } #endif - aft_red_led_ctrl(card, AFT_LED_ON); - card->wandev.fe_iface.led_ctrl(&card->fe, AFT_LED_OFF); + if (wan_test_bit(0,&card->u.aft.comm_enabled) || + card->wandev.state != WAN_DISCONNECTED){ + + port_set_state(card,WAN_DISCONNECTED); + disable_data_error_intr(card,LINK_DOWN); +#if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE) + if (card->wan_tdmv.sc){ + int err; + WAN_TDMV_CALL(state, (card, WAN_DISCONNECTED), err); + } +#endif + aft_red_led_ctrl(card, AFT_LED_ON); + card->wandev.fe_iface.led_ctrl(&card->fe, AFT_LED_OFF); + }else{ + DEBUG_TEST("%s(): line: %d: unhandled case!\n", __FUNCTION__, __LINE__); } } return; @@ -5567,28 +6033,28 @@ static void handle_front_end_state(void *card_id) static unsigned char read_cpld(sdla_t *card, unsigned short cpld_off) { - u16 org_off; - u8 tmp; + u16 org_off; + u8 tmp; - cpld_off &= ~BIT_DEV_ADDR_CLEAR; - cpld_off |= BIT_DEV_ADDR_CPLD; + cpld_off &= ~BIT_DEV_ADDR_CLEAR; + cpld_off |= BIT_DEV_ADDR_CPLD; - /*Save the current address. */ - card->hw_iface.bus_read_2(card->hw, - XILINX_MCPU_INTERFACE_ADDR, - &org_off); + /*Save the current address. */ + card->hw_iface.bus_read_2(card->hw, + XILINX_MCPU_INTERFACE_ADDR, + &org_off); - card->hw_iface.bus_write_2(card->hw, - XILINX_MCPU_INTERFACE_ADDR, - cpld_off); + card->hw_iface.bus_write_2(card->hw, + XILINX_MCPU_INTERFACE_ADDR, + cpld_off); - card->hw_iface.bus_read_1(card->hw,XILINX_MCPU_INTERFACE, &tmp); + card->hw_iface.bus_read_1(card->hw,XILINX_MCPU_INTERFACE, &tmp); - /*Restore original address */ - card->hw_iface.bus_write_2(card->hw, - XILINX_MCPU_INTERFACE_ADDR, - org_off); - return tmp; + /*Restore original address */ + card->hw_iface.bus_write_2(card->hw, + XILINX_MCPU_INTERFACE_ADDR, + org_off); + return tmp; } @@ -5597,44 +6063,44 @@ static unsigned char write_cpld(sdla_t *card, unsigned short off,unsigned char d { u16 org_off; - off &= ~BIT_DEV_ADDR_CLEAR; - off |= BIT_DEV_ADDR_CPLD; + off &= ~BIT_DEV_ADDR_CLEAR; + off |= BIT_DEV_ADDR_CPLD; - /* Save the current original address */ - card->hw_iface.bus_read_2(card->hw, - XILINX_MCPU_INTERFACE_ADDR, - &org_off); + /* Save the current original address */ + card->hw_iface.bus_read_2(card->hw, + XILINX_MCPU_INTERFACE_ADDR, + &org_off); card->hw_iface.bus_write_2(card->hw, - XILINX_MCPU_INTERFACE_ADDR, - off); + XILINX_MCPU_INTERFACE_ADDR, + off); /* This delay is required to avoid bridge optimization - * (combining two writes together)*/ + * (combining two writes together)*/ WP_DELAY(5); card->hw_iface.bus_write_1(card->hw, - XILINX_MCPU_INTERFACE, - data); + XILINX_MCPU_INTERFACE, + data); /* This delay is required to avoid bridge optimization - * (combining two writes together)*/ + * (combining two writes together)*/ WP_DELAY(5); - /* Restore the original address */ - card->hw_iface.bus_write_2(card->hw, - XILINX_MCPU_INTERFACE_ADDR, - org_off); - return 0; + /* Restore the original address */ + card->hw_iface.bus_write_2(card->hw, + XILINX_MCPU_INTERFACE_ADDR, + org_off); + return 0; } static int xilinx_read(sdla_t *card, wan_cmd_api_t *api_cmd) { if (api_cmd->offset <= 0x3C){ - card->hw_iface.pci_read_config_dword(card->hw, - api_cmd->offset, - (u32*)&api_cmd->data[0]); - api_cmd->len=4; + card->hw_iface.pci_read_config_dword(card->hw, + api_cmd->offset, + (u32*)&api_cmd->data[0]); + api_cmd->len=4; }else{ card->hw_iface.peek(card->hw, api_cmd->offset, &api_cmd->data[0], api_cmd->len); @@ -5642,7 +6108,7 @@ static int xilinx_read(sdla_t *card, wan_cmd_api_t *api_cmd) #ifdef DEB_XILINX DEBUG_EVENT("%s: Reading Bar%d Offset=0x%X Len=%d\n", - card->devname,api_cmd->bar,api_cmd->offset,api_cmd->len); + card->devname,api_cmd->bar,api_cmd->offset,api_cmd->len); #endif return 0; @@ -5659,7 +6125,7 @@ static int xilinx_api_fe_read(sdla_t *card, wan_cmd_api_t *api_cmd) #ifdef DEB_XILINX DEBUG_EVENT("%s: Reading Bar%d Offset=0x%X Len=%d\n", - card->devname,api_cmd->bar,api_cmd->offset,api_cmd->len); + card->devname,api_cmd->bar,api_cmd->offset,api_cmd->len); #endif return 0; } @@ -5669,8 +6135,8 @@ static int xilinx_write(sdla_t *card, wan_cmd_api_t *api_cmd) { #ifdef DEB_XILINX DEBUG_EVENT("%s: Writting Bar%d Offset=0x%X Len=%d\n", - card->devname, - api_cmd->bar,api_cmd->offset,api_cmd->len); + card->devname, + api_cmd->bar,api_cmd->offset,api_cmd->len); #endif if (api_cmd->len == 1){ @@ -5708,8 +6174,8 @@ static int xilinx_api_fe_write(sdla_t *card, wan_cmd_api_t *api_cmd) qaccess = card->wandev.state == WAN_CONNECTED ? 1 : 0; #ifdef DEB_XILINX DEBUG_EVENT("%s: Writting Bar%d Offset=0x%X Len=%d\n", - card->devname, - api_cmd->bar,api_cmd->offset,api_cmd->len); + card->devname, + api_cmd->bar,api_cmd->offset,api_cmd->len); #endif card->hw_iface.hw_lock(card->hw,&smp_flags); @@ -5724,34 +6190,40 @@ static int xilinx_write_bios(sdla_t *card, wan_cmd_api_t *api_cmd) { #ifdef DEB_XILINX DEBUG_EVENT("Setting PCI 0xX=0x%08lX 0x3C=0x%08X\n", - (card->wandev.S514_cpu_no[0] == SDLA_CPU_A) ? 0x10 : 0x14, - card->u.aft.bar,card->wandev.irq); + (card->wandev.S514_cpu_no[0] == SDLA_CPU_A) ? 0x10 : 0x14, + card->u.aft.bar,card->wandev.irq); #endif card->hw_iface.pci_write_config_dword(card->hw, - (card->wandev.S514_cpu_no[0] == SDLA_CPU_A) ? 0x10 : 0x14, - card->u.aft.bar); + (card->wandev.S514_cpu_no[0] == SDLA_CPU_A) ? 0x10 : 0x14, + card->u.aft.bar); card->hw_iface.pci_write_config_dword(card->hw, 0x3C, card->wandev.irq); card->hw_iface.pci_write_config_dword(card->hw, 0x0C, 0x0000ff00); return 0; } - + static int aft_devel_ioctl(sdla_t *card, struct ifreq *ifr) { wan_cmd_api_t *api_cmd; wan_cmd_api_t api_cmd_struct; int err = -EINVAL; + void *ifr_data_ptr; - api_cmd=&api_cmd_struct; - +#if defined (__WINDOWS__) + ifr_data_ptr = ifr; +#else if (!ifr || !ifr->ifr_data){ DEBUG_EVENT("%s: Error: No ifr or ifr_data\n",__FUNCTION__); return -EINVAL; } + ifr_data_ptr = ifr->ifr_data; +#endif + + api_cmd = &api_cmd_struct; memset(api_cmd, 0, sizeof(wan_cmd_api_t)); - if (WAN_COPY_FROM_USER(api_cmd,ifr->ifr_data,sizeof(wan_cmd_api_t))){ + if (WAN_COPY_FROM_USER(api_cmd, ifr_data_ptr, sizeof(wan_cmd_api_t))){ return -EFAULT; } @@ -5759,60 +6231,103 @@ static int aft_devel_ioctl(sdla_t *card, struct ifreq *ifr) case SIOC_WAN_READ_REG: err=xilinx_read(card,api_cmd); break; - + case SIOC_WAN_WRITE_REG: err=xilinx_write(card,api_cmd); break; - + case SIOC_WAN_FE_READ_REG: err=xilinx_api_fe_read(card,api_cmd); break; - + case SIOC_WAN_FE_WRITE_REG: err=xilinx_api_fe_write(card,api_cmd); break; - + case SIOC_WAN_SET_PCI_BIOS: +#if defined(__WINDOWS__) + /* Restore PCI config space to what it was + * before the firmware update, so access to card + * is possible again. */ + err=sdla_restore_pci_config_space(card); +#else err=xilinx_write_bios(card,api_cmd); +#endif break; + +#if defined(__WINDOWS__) + case SIOC_WAN_GET_CARD_TYPE: + api_cmd->len = 0; + + err = card->hw_iface.getcfg(card->hw, SDLA_ADAPTERTYPE, (u16*)&api_cmd->data[0]); + if(err){ + break; + } + + err = card->hw_iface.getcfg(card->hw, SDLA_ADAPTERSUBTYPE, (u16*)&api_cmd->data[sizeof(u16)]); + if(err){ + break; + } + + api_cmd->len = 2*sizeof(u16); + break; +#endif + + case SIOC_WAN_COREREV: + api_cmd->len = 0; + + err = card->hw_iface.getcfg(card->hw, SDLA_COREREV, (u8*)&api_cmd->data[0]); + if(err){ + break; + } + + api_cmd->len = sizeof(u8); + break; + + default: + DEBUG_ERROR("%s(): Error: unsupported cmd: %d\n", __FUNCTION__, api_cmd->cmd); + err = -EINVAL; + break; + } - if (WAN_COPY_TO_USER(ifr->ifr_data,api_cmd,sizeof(wan_cmd_api_t))){ + + api_cmd->ret = err; + + if (WAN_COPY_TO_USER(ifr_data_ptr, api_cmd, sizeof(wan_cmd_api_t))){ return -EFAULT; } - return err; } - /*========================================= - * enable_data_error_intr - * - * Description: - * - * Run only after the front end comes - * up from down state. - * - * Clean the DMA Tx/Rx pending interrupts. - * (Ignore since we will reconfigure - * all dma descriptors. DMA controler - * was already disabled on link down) - * - * For all channels clean Tx/Rx Fifo - * - * Enable DMA controler - * (This starts the fifo cleaning - * process) - * - * For all channels reprogram Tx/Rx DMA - * descriptors. - * - * Clean the Tx/Rx Error pending interrupts. - * (Since dma fifo's are now empty) - * - * Enable global DMA and Error interrutps. - * - */ +* enable_data_error_intr +* +* Description: +* +* Run only after the front end comes +* up from down state. +* +* Clean the DMA Tx/Rx pending interrupts. +* (Ignore since we will reconfigure +* all dma descriptors. DMA controler +* was already disabled on link down) +* +* For all channels clean Tx/Rx Fifo +* +* Enable DMA controler +* (This starts the fifo cleaning +* process) +* +* For all channels reprogram Tx/Rx DMA +* descriptors. +* +* Clean the Tx/Rx Error pending interrupts. +* (Since dma fifo's are now empty) +* +* Enable global DMA and Error interrutps. +* +*/ static void enable_data_error_intr(sdla_t *card) { @@ -5824,9 +6339,9 @@ static void enable_data_error_intr(sdla_t *card) /* Clean Tx/Rx DMA interrupts */ card->hw_iface.bus_read_4(card->hw, - XILINX_DMA_RX_INTR_PENDING_REG, ®); - card->hw_iface.bus_read_4(card->hw, - XILINX_DMA_TX_INTR_PENDING_REG, ®); + XILINX_DMA_RX_INTR_PENDING_REG, ®); + card->hw_iface.bus_read_4(card->hw, + XILINX_DMA_TX_INTR_PENDING_REG, ®); for (i=0; iu.aft.num_of_time_slots;i++){ private_area_t *chan; @@ -5845,22 +6360,22 @@ static void enable_data_error_intr(sdla_t *card) } DEBUG_TEST("%s: Init interface fifo no wait %s\n", - __FUNCTION__,chan->if_name); + __FUNCTION__,chan->if_name); - xilinx_init_rx_dev_fifo(card, chan, WP_NO_WAIT); - xilinx_init_tx_dev_fifo(card, chan, WP_NO_WAIT); + xilinx_init_rx_dev_fifo(card, chan, WP_NO_WAIT); + xilinx_init_tx_dev_fifo(card, chan, WP_NO_WAIT); if (wan_test_bit(0,&chan->tdmv_sync)){ - channel_timeslot_sync_ctrl(card,chan,1); - } - } + channel_timeslot_sync_ctrl(card,chan,1); + } + } - /* Enable DMA controler, in order to start the - * fifo cleaning */ - card->hw_iface.bus_read_4(card->hw,XILINX_DMA_CONTROL_REG,®); - wan_set_bit(DMA_ENGINE_ENABLE_BIT,®); - card->hw_iface.bus_write_4(card->hw,XILINX_DMA_CONTROL_REG,reg); + /* Enable DMA controler, in order to start the + * fifo cleaning */ + card->hw_iface.bus_read_4(card->hw,XILINX_DMA_CONTROL_REG,®); + wan_set_bit(DMA_ENGINE_ENABLE_BIT,®); + card->hw_iface.bus_write_4(card->hw,XILINX_DMA_CONTROL_REG,reg); /* For all channels clean Tx/Rx fifos */ @@ -5886,13 +6401,13 @@ static void enable_data_error_intr(sdla_t *card) xilinx_init_tx_dev_fifo(card, chan, WP_WAIT); DEBUG_TEST("%s: Clearing Fifo and idle_flag %s\n", - card->devname,chan->if_name); + card->devname,chan->if_name); wan_clear_bit(0,&chan->idle_start); } /* For all channels, reprogram Tx/Rx DMA descriptors. - * For Tx also make sure that the BUSY flag is clear - * and previoulsy Tx packet is deallocated */ + * For Tx also make sure that the BUSY flag is clear + * and previoulsy Tx packet is deallocated */ for (i=0; iu.aft.num_of_time_slots;i++){ private_area_t *chan; @@ -5916,15 +6431,15 @@ static void enable_data_error_intr(sdla_t *card) if (chan->rx_dma_skb){ wp_rx_element_t *rx_el; netskb_t *skb=chan->rx_dma_skb; - DEBUG_TEST("%s: Clearing RX DMA Pending buffer \n",__FUNCTION__); + DEBUG_TEST("%s: Clearing RX DMA Pending buffer \n",__FUNCTION__); chan->rx_dma_skb=NULL; rx_el=(wp_rx_element_t *)wan_skb_data(skb); card->hw_iface.pci_unmap_dma(card->hw, - rx_el->dma_addr, - chan->dma_mru, - PCI_DMA_FROMDEVICE); + rx_el->dma_addr, + chan->dma_mru, + PCI_DMA_FROMDEVICE); aft_init_requeue_free_skb(chan, skb); } @@ -5941,19 +6456,19 @@ static void enable_data_error_intr(sdla_t *card) } } - xilinx_dma_rx(card,chan); + xilinx_dma_rx(card,chan); - if (chan->tx_dma_addr && chan->tx_dma_len){ - DEBUG_TEST("%s: Clearing TX DMA Pending buffer \n",__FUNCTION__); + if (chan->tx_dma_addr && chan->tx_dma_len){ + DEBUG_TEST("%s: Clearing TX DMA Pending buffer \n",__FUNCTION__); aft_unmap_tx_dma(card,chan); - } + } if (chan->tx_dma_skb){ if (chan->common.usedby == TDM_VOICE || chan->common.usedby == TDM_VOICE_API){ aft_init_requeue_free_skb(chan, chan->tx_dma_skb); }else{ - wan_skb_free(chan->tx_dma_skb); + wan_aft_skb_defered_dealloc(chan,chan->tx_dma_skb);/* instead of wan_skb_free(chan->tx_dma_skb); */ } chan->tx_dma_skb=NULL; } @@ -5965,30 +6480,30 @@ static void enable_data_error_intr(sdla_t *card) int err=xilinx_dma_tx(card,chan); if (err){ DEBUG_EVENT("%s: DMA Tx failed err=%i\n", - chan->if_name,err); + chan->if_name,err); } } - DEBUG_TEST("%s: Clearing Fifo and idle_flag %s\n", - card->devname,chan->if_name); + DEBUG_TEST("%s: Clearing Fifo and idle_flag %s\n", + card->devname,chan->if_name); - } + } /* Clean Tx/Rx Error interrupts, since fifos are now - * empty, and Tx fifo may generate an underrun which - * we want to ignore :) */ + * empty, and Tx fifo may generate an underrun which + * we want to ignore :) */ - card->hw_iface.bus_read_4(card->hw, - XILINX_HDLC_RX_INTR_PENDING_REG, ®); - card->hw_iface.bus_read_4(card->hw, - XILINX_HDLC_TX_INTR_PENDING_REG, ®); + card->hw_iface.bus_read_4(card->hw, + XILINX_HDLC_RX_INTR_PENDING_REG, ®); + card->hw_iface.bus_read_4(card->hw, + XILINX_HDLC_TX_INTR_PENDING_REG, ®); /* Enable Global DMA and Error Interrupts */ reg=0; card->hw_iface.bus_read_4(card->hw,XILINX_CHIP_CFG_REG,®); - wan_set_bit(GLOBAL_INTR_ENABLE_BIT,®); + wan_set_bit(GLOBAL_INTR_ENABLE_BIT,®); wan_set_bit(ERROR_INTR_ENABLE_BIT,®); card->hw_iface.bus_write_4(card->hw,XILINX_CHIP_CFG_REG,reg); @@ -6007,10 +6522,10 @@ static void disable_data_error_intr(sdla_t *card, enum wp_device_down_states eve { u32 reg; - DEBUG_TEST("%s: !!!!!!!\n",__FUNCTION__); + DEBUG_TEST("%s(): !!!!!!!\n",__FUNCTION__); card->hw_iface.bus_read_4(card->hw,XILINX_CHIP_CFG_REG,®); - wan_clear_bit(GLOBAL_INTR_ENABLE_BIT,®); + wan_clear_bit(GLOBAL_INTR_ENABLE_BIT,®); wan_clear_bit(ERROR_INTR_ENABLE_BIT,®); if (event==DEVICE_DOWN){ wan_clear_bit(FRONT_END_INTR_ENABLE_BIT,®); @@ -6019,7 +6534,7 @@ static void disable_data_error_intr(sdla_t *card, enum wp_device_down_states eve card->hw_iface.bus_read_4(card->hw,XILINX_DMA_CONTROL_REG,®); wan_clear_bit(DMA_ENGINE_ENABLE_BIT,®); - card->hw_iface.bus_write_4(card->hw,XILINX_DMA_CONTROL_REG,reg); + card->hw_iface.bus_write_4(card->hw,XILINX_DMA_CONTROL_REG,reg); if (event==DEVICE_DOWN){ wan_set_bit(CARD_DOWN,&card->wandev.critical); @@ -6040,25 +6555,25 @@ static void xilinx_init_tx_dma_descr(sdla_t *card, private_area_t *chan) /*============================================================================ - * Update communications error and general packet statistics. - */ +* Update communications error and general packet statistics. +*/ static int update_comms_stats(sdla_t* card) { /* 1. On the first timer interrupt, update T1/E1 alarms - * and PMON counters (only for T1/E1 card) (TE1) - */ + * and PMON counters (only for T1/E1 card) (TE1) + */ - /* TE1 Update T1/E1 alarms */ - if (IS_TE1_CARD(card)) { + /* TE1 Update T1/E1 alarms */ + if (IS_TE1_CARD(card)) { wan_smp_flag_t smp_flags; card->hw_iface.hw_lock(card->hw,&smp_flags); - card->wandev.fe_iface.read_alarm(&card->fe, WAN_FE_ALARM_READ|WAN_FE_ALARM_UPDATE); - /* TE1 Update T1/E1 perfomance counters */ + card->wandev.fe_iface.read_alarm(&card->fe, WAN_FE_ALARM_READ|WAN_FE_ALARM_UPDATE); + /* TE1 Update T1/E1 perfomance counters */ card->wandev.fe_iface.read_pmon(&card->fe, 0); card->hw_iface.hw_unlock(card->hw,&smp_flags); - } + } - return 0; + return 0; } #if 0 @@ -6066,12 +6581,12 @@ static void xilinx_rx_fifo_over_recover(sdla_t *card, private_area_t *chan) { u32 reg=0; - u32 dma_descr; + u32 dma_descr; netskb_t *skb; - /* Initialize Tx DMA descriptor: Stop DMA */ - dma_descr=(chan->logic_ch_num<<4) + XILINX_RxDMA_DESCRIPTOR_HI; - card->hw_iface.bus_write_4(card->hw,dma_descr, reg); + /* Initialize Tx DMA descriptor: Stop DMA */ + dma_descr=(chan->logic_ch_num<<4) + XILINX_RxDMA_DESCRIPTOR_HI; + card->hw_iface.bus_write_4(card->hw,dma_descr, reg); /* Clean the RX FIFO */ rx_chan_timeslot_sync_ctrl(card,0); @@ -6082,12 +6597,12 @@ static void xilinx_rx_fifo_over_recover(sdla_t *card, private_area_t *chan) rx_el=(wp_rx_element_t *)wan_skb_data(chan->rx_dma_skb); card->hw_iface.pci_unmap_dma(card->hw, - rx_el->dma_addr, - chan->dma_mru, - PCI_DMA_FROMDEVICE); + rx_el->dma_addr, + chan->dma_mru, + PCI_DMA_FROMDEVICE); DEBUG_RX("%s:%s: RX HI=0x%X LO=0x%X DMA=0x%lX\n", - __FUNCTION__,chan->if_name,rx_el->reg,rx_el->align,rx_el->dma_addr); + __FUNCTION__,chan->if_name,rx_el->reg,rx_el->align,rx_el->dma_addr); chan->rx_dma_skb=NULL; aft_init_requeue_free_skb(chan, skb); @@ -6109,35 +6624,35 @@ static void xilinx_tx_fifo_under_recover (sdla_t *card, private_area_t *chan) } #if 0 -/* FIFO Already empty no need to clean the fifo */ + /* FIFO Already empty no need to clean the fifo */ if (chan->hdlc_eng){ - /* Initialize Tx DMA descriptor: Stop DMA */ - dma_descr=(chan->logic_ch_num<<4) + XILINX_TxDMA_DESCRIPTOR_HI; - card->hw_iface.bus_write_4(card->hw,dma_descr, reg); + /* Initialize Tx DMA descriptor: Stop DMA */ + dma_descr=(chan->logic_ch_num<<4) + XILINX_TxDMA_DESCRIPTOR_HI; + card->hw_iface.bus_write_4(card->hw,dma_descr, reg); /* Clean the TX FIFO */ xilinx_init_tx_dev_fifo(card, chan, WP_WAIT); } #endif - if (chan->tx_dma_addr && chan->tx_dma_len){ + if (chan->tx_dma_addr && chan->tx_dma_len){ aft_unmap_tx_dma(card,chan); - } + } /* Requeue the current tx packet, for - * re-transmission */ + * re-transmission */ if (chan->tx_dma_skb){ wan_skb_queue_head(&chan->wp_tx_pending_list, chan->tx_dma_skb); chan->tx_dma_skb=NULL; - if (chan->lip_atm) { + if (chan->lip_atm) { netskb_t *tmpskb = chan->tx_dma_skb; chan->tx_dma_skb=NULL; - wan_skb_free(tmpskb); - } else { + wan_aft_skb_defered_dealloc(chan, tmpskb); /* instead of wan_skb_free(tmpskb); */ + } else { wan_skb_queue_head(&chan->wp_tx_pending_list, chan->tx_dma_skb); chan->tx_dma_skb=NULL; - } + } } /* Wake up the stack, because tx dma interrupt failed */ @@ -6152,12 +6667,12 @@ static void xilinx_tx_fifo_under_recover (sdla_t *card, private_area_t *chan) } DEBUG_TEST("%s:%s: Tx Fifo Recovery: Restarting Transmission \n", - card->devname,chan->if_name); + card->devname,chan->if_name); /* Re-start transmission */ wan_clear_bit(TX_BUSY,&chan->dma_status); - xilinx_dma_tx(card,chan); + xilinx_dma_tx(card,chan); } static int xilinx_write_ctrl_hdlc(sdla_t *card, u32 timeslot, u8 reg_off, u32 data) @@ -6205,30 +6720,35 @@ static int set_chan_state(sdla_t* card, netdevice_t* dev, int state) if (chan == NULL) { if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: %s:%d No chan ptr!\n", - card->devname,__FUNCTION__,__LINE__); + DEBUG_EVENT("%s: %s:%d No chan ptr!\n", + card->devname,__FUNCTION__,__LINE__); } /* This is case can happened for WANPIPE (LITE) */ return 0; } - chan->common.state = state; - for (ch_ptr=chan; ch_ptr != NULL; ch_ptr=ch_ptr->next){ +#if defined(__WINDOWS__) + set_netdev_state(card, dev, state); +#endif + + chan->common.state = state; + + for (ch_ptr=chan; ch_ptr != NULL; ch_ptr=ch_ptr->next){ ch_ptr->common.state=state; if (ch_ptr->tdmv_zaptel_cfg) { continue; } #ifdef AFT_TDM_API_SUPPORT if (ch_ptr->common.usedby == TDM_VOICE_API || - ch_ptr->common.usedby == TDM_VOICE_DCHAN) { - aft_tdm_api_update_state_channelized(card, ch_ptr, state); + ch_ptr->common.usedby == TDM_VOICE_DCHAN) { + aft_tdm_api_update_state_channelized(card, ch_ptr, state); } #endif } if (state == WAN_CONNECTED){ DEBUG_TEST("%s: Setting idle_start to 0\n", - chan->if_name); + chan->if_name); wan_clear_bit(0,&chan->idle_start); chan->opstats.link_active_count++; @@ -6243,10 +6763,11 @@ static int set_chan_state(sdla_t* card, netdevice_t* dev, int state) # if !defined(CONFIG_PRODUCT_WANPIPE_GENERIC) #if defined(__LINUX__) if (chan->common.usedby == API){ - wan_update_api_state(chan); + wan_update_api_state(chan); } #endif if (chan->common.usedby == STACK){ + if (state == WAN_CONNECTED){ wanpipe_lip_connect(chan,0); }else{ @@ -6267,7 +6788,7 @@ static int request_fifo_baddr_and_size(sdla_t *card, private_area_t *chan) int i; /* Calculate the optimal fifo size based - * on the number of time slots requested */ + * on the number of time slots requested */ if (IS_T1_CARD(card)){ @@ -6285,27 +6806,27 @@ static int request_fifo_baddr_and_size(sdla_t *card, private_area_t *chan) req_fifo_size=16; }else{ DEBUG_EVENT("%s:%s: Invalid number of timeslots %d\n", - card->devname,chan->if_name,chan->num_of_time_slots); + card->devname,chan->if_name,chan->num_of_time_slots); return -EINVAL; } }else{ if (chan->num_of_time_slots == (NUM_OF_E1_CHANNELS-1)){ req_fifo_size=32; - }else if (chan->num_of_time_slots == 1){ + }else if (chan->num_of_time_slots == 1){ req_fifo_size=1; - }else if (chan->num_of_time_slots == 2 || chan->num_of_time_slots == 3){ + }else if (chan->num_of_time_slots == 2 || chan->num_of_time_slots == 3){ req_fifo_size=2; - }else if (chan->num_of_time_slots >= 4 && chan->num_of_time_slots <= 7){ + }else if (chan->num_of_time_slots >= 4 && chan->num_of_time_slots <= 7){ req_fifo_size=4; - }else if (chan->num_of_time_slots >= 8 && chan->num_of_time_slots <= 15){ + }else if (chan->num_of_time_slots >= 8 && chan->num_of_time_slots <= 15){ req_fifo_size=8; - }else if (chan->num_of_time_slots >= 16 && chan->num_of_time_slots <= 31){ + }else if (chan->num_of_time_slots >= 16 && chan->num_of_time_slots <= 31){ req_fifo_size=16; - }else{ - DEBUG_EVENT("%s:%s: Invalid number of timeslots %d\n", - card->devname,chan->if_name,chan->num_of_time_slots); - return -EINVAL; - } + }else{ + DEBUG_EVENT("%s:%s: Invalid number of timeslots %d\n", + card->devname,chan->if_name,chan->num_of_time_slots); + return -EINVAL; + } } DEBUG_TEST("%s:%s: Optimal Fifo Size =%d Timeslots=%d \n", @@ -6314,12 +6835,12 @@ static int request_fifo_baddr_and_size(sdla_t *card, private_area_t *chan) fifo_size=map_fifo_baddr_and_size(card,req_fifo_size,&chan->fifo_base_addr); if (fifo_size == 0 || chan->fifo_base_addr == 31){ DEBUG_EVENT("%s:%s: Error: Failed to obtain fifo size %d or addr %d \n", - card->devname,chan->if_name,fifo_size,chan->fifo_base_addr); - return -EINVAL; - } + card->devname,chan->if_name,fifo_size,chan->fifo_base_addr); + return -EINVAL; + } DEBUG_TEST("%s:%s: Optimal Fifo Size =%d Timeslots=%d New Fifo Size=%d \n", - card->devname,chan->if_name,req_fifo_size,chan->num_of_time_slots,fifo_size); + card->devname,chan->if_name,req_fifo_size,chan->num_of_time_slots,fifo_size); for (i=0;idevname,chan->if_name,fifo_size, + card->devname,chan->if_name,fifo_size, chan->num_of_time_slots,chan->fifo_size_code, chan->fifo_base_addr); @@ -6355,7 +6876,7 @@ static int map_fifo_baddr_and_size(sdla_t *card, unsigned char fifo_size, unsign } DEBUG_TEST("%s: Trying to MAP 0x%X to 0x%lX\n", - card->devname,reg,card->u.aft.fifo_addr_map); + card->devname,reg,card->u.aft.fifo_addr_map); for (i=0;i<32;i+=fifo_size){ if (card->u.aft.fifo_addr_map & (reg<devname,card->u.aft.fifo_addr_map,i); + card->devname,card->u.aft.fifo_addr_map,i); return fifo_size; } @@ -6386,8 +6907,8 @@ static int free_fifo_baddr_and_size (sdla_t *card, private_area_t *chan) int i; for (i=0;ififo_size;i++){ - wan_set_bit(i,®); - } + wan_set_bit(i,®); + } DEBUG_TEST("%s: Unmapping 0x%X from 0x%lX\n", card->devname,reg<fifo_base_addr, card->u.aft.fifo_addr_map); @@ -6395,7 +6916,7 @@ static int free_fifo_baddr_and_size (sdla_t *card, private_area_t *chan) card->u.aft.fifo_addr_map &= ~(reg<fifo_base_addr); DEBUG_TEST("%s: New Map is 0x%lX\n", - card->devname, card->u.aft.fifo_addr_map); + card->devname, card->u.aft.fifo_addr_map); chan->fifo_size=0; @@ -6431,23 +6952,23 @@ int aft_core_ready(sdla_t *card) u32 reg; volatile unsigned char cnt=0; - for (;;){ - card->hw_iface.bus_read_4(card->hw,XILINX_CHIP_CFG_REG, ®); + for (;;){ + card->hw_iface.bus_read_4(card->hw,XILINX_CHIP_CFG_REG, ®); - if (!wan_test_bit(HDLC_CORE_READY_FLAG_BIT,®)){ - /* The HDLC Core is not ready! we have - * an error. */ - if (++cnt > 5){ - return -EINVAL; - }else{ - WP_DELAY(500); - /* WARNING: we cannot do this while in - * critical area */ - } - }else{ + if (!wan_test_bit(HDLC_CORE_READY_FLAG_BIT,®)){ + /* The HDLC Core is not ready! we have + * an error. */ + if (++cnt > 5){ + return -EINVAL; + }else{ + WP_DELAY(500); + /* WARNING: we cannot do this while in + * critical area */ + } + }else{ return 0; - } - } + } + } return -EINVAL; } @@ -6457,9 +6978,9 @@ static void aft_unmap_tx_dma(sdla_t *card, private_area_t *chan) { card->hw_iface.pci_unmap_dma(card->hw, - chan->tx_dma_addr, - chan->tx_dma_len, - PCI_DMA_TODEVICE); + chan->tx_dma_addr, + chan->tx_dma_len, + PCI_DMA_TODEVICE); chan->tx_dma_addr=0; chan->tx_dma_len=0; @@ -6467,8 +6988,8 @@ static void aft_unmap_tx_dma(sdla_t *card, private_area_t *chan) } static int protocol_init (sdla_t *card, netdevice_t *dev, - private_area_t *chan, - wanif_conf_t* conf) + private_area_t *chan, + wanif_conf_t* conf) { chan->common.protocol = conf->protocol; @@ -6476,53 +6997,53 @@ static int protocol_init (sdla_t *card, netdevice_t *dev, #ifndef CONFIG_PRODUCT_WANPIPE_GENERIC DEBUG_EVENT("%s: AFT Driver doesn't directly support any protocols!\n", - chan->if_name); + chan->if_name); return -EPROTONOSUPPORT; #else if (chan->common.protocol == WANCONFIG_PPP || - chan->common.protocol == WANCONFIG_CHDLC){ + chan->common.protocol == WANCONFIG_CHDLC){ - struct ifreq ifr; - struct if_settings ifsettings; + struct ifreq ifr; + struct if_settings ifsettings; - chan->common.is_netdev = 0; - if (wan_iface.attach){ - wan_iface.attach(dev, wan_netif_name(dev), chan->common.is_netdev); - }else{ - DEBUG_EVENT("%s: Failed to attach interface!\n", + chan->common.is_netdev = 0; + if (wan_iface.attach){ + wan_iface.attach(dev, wan_netif_name(dev), chan->common.is_netdev); + }else{ + DEBUG_EVENT("%s: Failed to attach interface!\n", chan->if_name); - return -EINVAL; - } - chan->common.prot_ptr = dev; - - if (chan->common.protocol == WANCONFIG_CHDLC){ - DEBUG_EVENT("%s: Starting Kernel CISCO HDLC protocol\n", - chan->if_name); - ifsettings.type = IF_PROTO_CISCO; - }else{ - DEBUG_EVENT("%s: Starting Kernel Sync PPP protocol\n", - chan->if_name); - ifsettings.type = IF_PROTO_PPP; - - } - ifr.ifr_data = (caddr_t)&ifsettings; - if (!wan_iface.set_proto || wan_iface.set_proto(dev, &ifr)){ - if (wan_iface.detach){ - wan_iface.detach(dev, chan->common.is_netdev); + return -EINVAL; } - if (wan_iface.free){ - wan_iface.free(dev); + chan->common.prot_ptr = dev; + + if (chan->common.protocol == WANCONFIG_CHDLC){ + DEBUG_EVENT("%s: Starting Kernel CISCO HDLC protocol\n", + chan->if_name); + ifsettings.type = IF_PROTO_CISCO; + }else{ + DEBUG_EVENT("%s: Starting Kernel Sync PPP protocol\n", + chan->if_name); + ifsettings.type = IF_PROTO_PPP; + + } + ifr.ifr_data = (caddr_t)&ifsettings; + if (!wan_iface.set_proto || wan_iface.set_proto(dev, &ifr)){ + if (wan_iface.detach){ + wan_iface.detach(dev, chan->common.is_netdev); + } + if (wan_iface.free){ + wan_iface.free(dev); + } + return -EINVAL; } - return -EINVAL; - } }else if (chan->common.protocol == WANCONFIG_GENERIC){ chan->common.prot_ptr = dev; }else{ DEBUG_EVENT("%s:%s: Unsupported protocol %d\n", - card->devname,chan->if_name,chan->common.protocol); + card->devname,chan->if_name,chan->common.protocol); return -EPROTONOSUPPORT; } #endif @@ -6563,16 +7084,16 @@ static int protocol_shutdown (sdla_t *card, netdevice_t *dev) #ifdef CONFIG_PRODUCT_WANPIPE_GENERIC if (chan->common.protocol == WANCONFIG_PPP || - chan->common.protocol == WANCONFIG_CHDLC){ + chan->common.protocol == WANCONFIG_CHDLC){ - chan->common.prot_ptr = NULL; - wanpipe_generic_unregister(dev); - if (wan_iface.detach){ - wan_iface.detach(dev, chan->common.is_netdev); - } - if (wan_iface.free){ - wan_iface.free(dev); - } + chan->common.prot_ptr = NULL; + wanpipe_generic_unregister(dev); + if (wan_iface.detach){ + wan_iface.detach(dev, chan->common.is_netdev); + } + if (wan_iface.free){ + wan_iface.free(dev); + } } #endif @@ -6584,9 +7105,9 @@ void protocol_recv(sdla_t *card, private_area_t *chan, netskb_t *skb) #ifdef CONFIG_PRODUCT_WANPIPE_GENERIC if (chan->common.protocol == WANCONFIG_PPP || - chan->common.protocol == WANCONFIG_CHDLC){ - wanpipe_generic_input(chan->common.dev, skb); - return 0; + chan->common.protocol == WANCONFIG_CHDLC){ + wanpipe_generic_input(chan->common.dev, skb); + return 0; } if (wan_iface.input){ @@ -6598,7 +7119,7 @@ void protocol_recv(sdla_t *card, private_area_t *chan, netskb_t *skb) if (chan->common.protocol == WANCONFIG_GENERIC){ skb->protocol = htons(ETH_P_HDLC); skb->dev = chan->common.dev; - wan_skb_reset_mac_header(skb); + wan_skb_reset_mac_header(skb); netif_rx(skb); return 0; } @@ -6607,7 +7128,7 @@ void protocol_recv(sdla_t *card, private_area_t *chan, netskb_t *skb) #if defined(__LINUX__) skb->protocol = htons(ETH_P_IP); skb->dev = chan->common.dev; - wan_skb_reset_mac_header(skb); + wan_skb_reset_mac_header(skb); netif_rx(skb); #else if (wan_iface.input){ @@ -6628,11 +7149,11 @@ static int channel_timeslot_sync_ctrl(sdla_t *card, private_area_t * chan, int e if (enable){ set_channel_timeslot_sync(®,chan->first_time_slot); - /* Make sure that Rx channel is disabled until - * we setup an rx dma descriptor */ + /* Make sure that Rx channel is disabled until + * we setup an rx dma descriptor */ wan_clear_bit(START_RX_CHANNEL_TSLOT_SYNC,®); - /* Enable Global Tx Rx timeslot sync */ + /* Enable Global Tx Rx timeslot sync */ wan_set_bit(ENABLE_CHANNEL_TSLOT_SYNC,®); DEBUG_TEST("%s:%s: Enabling Channel Timeslot Synch (Reg=0x%X)\n", @@ -6642,7 +7163,7 @@ static int channel_timeslot_sync_ctrl(sdla_t *card, private_area_t * chan, int e wan_clear_bit(ENABLE_CHANNEL_TSLOT_SYNC,®); DEBUG_TEST("%s:%s: Disabling Channel Timeslot Synch (Reg=0x%X)\n", - card->devname,chan->if_name,reg); + card->devname,chan->if_name,reg); } card->hw_iface.bus_write_4(card->hw,XILINX_CHIP_CFG_REG, reg); @@ -6653,16 +7174,16 @@ static int channel_timeslot_sync_ctrl(sdla_t *card, private_area_t * chan, int e static int rx_chan_timeslot_sync_ctrl(sdla_t *card, int start) { u32 reg; - card->hw_iface.bus_read_4(card->hw,XILINX_CHIP_CFG_REG, ®); + card->hw_iface.bus_read_4(card->hw,XILINX_CHIP_CFG_REG, ®); if (start){ DEBUG_TEST("%s: Enabling Rx Timeslot Synch (Reg=0x%X)\n", - card->devname,reg); + card->devname,reg); wan_set_bit(START_RX_CHANNEL_TSLOT_SYNC,®); }else{ DEBUG_TEST("%s: Disabling Rx Timeslot Synch (Reg=0x%X)\n", - card->devname,reg); + card->devname,reg); wan_clear_bit(START_RX_CHANNEL_TSLOT_SYNC,®); } @@ -6706,7 +7227,7 @@ static int send_rbs_oob_msg (sdla_t *card, private_area_t *chan, int channel, in skb->dev=chan->common.dev; DEBUG_TEST("%s: Sending OOB message len=%i\n", - chan->if_name, wan_skb_len(skb)); + chan->if_name, wan_skb_len(skb)); if (wan_api_rx(chan,skb)!=0){ err=-ENODEV; @@ -6716,7 +7237,7 @@ static int send_rbs_oob_msg (sdla_t *card, private_area_t *chan, int channel, in #else DEBUG_EVENT("%s: OOB messages not supported!\n", - chan->if_name); + chan->if_name); return -EINVAL; #endif } @@ -6764,10 +7285,10 @@ static void aft_report_rbsbits(void* pcard, int channel, unsigned char status) static int aft_rx_post_complete_voice (sdla_t *card, private_area_t *chan, - netskb_t *skb) + netskb_t *skb) { - unsigned int len = 0; + unsigned int len = 0; wp_rx_element_t *rx_el=(wp_rx_element_t *)wan_skb_data(skb); DEBUG_RX("%s:%s: RX HI=0x%X LO=0x%X DMA=0x%lX\n", @@ -6776,10 +7297,10 @@ static int aft_rx_post_complete_voice (sdla_t *card, private_area_t *chan, rx_el->align&=RxDMA_LO_ALIGNMENT_BIT_MASK; - /* Checking Rx DMA Go bit. Has to be '0' */ + /* Checking Rx DMA Go bit. Has to be '0' */ if (wan_test_bit(RxDMA_HI_DMA_GO_READY_BIT,&rx_el->reg)){ - DEBUG_TEST("%s:%s: Error: RxDMA Intr: GO bit set on Rx intr\n", - card->devname,chan->if_name); + DEBUG_TEST("%s:%s: Error: RxDMA Intr: GO bit set on Rx intr\n", + card->devname,chan->if_name); chan->if_stats.rx_errors++; chan->errstats.Rx_dma_descr_err++; goto rx_comp_error; @@ -6789,21 +7310,21 @@ static int aft_rx_post_complete_voice (sdla_t *card, private_area_t *chan, if (rx_el->reg&RxDMA_HI_DMA_PCI_ERROR_MASK){ if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_M_ABRT){ - DEBUG_EVENT("%s:%s: Rx Error: Abort from Master: pci fatal error! (0x%08X)\n", - card->devname,chan->if_name,rx_el->reg); - } - if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_T_ABRT){ - DEBUG_EVENT("%s:%s: Rx Error: Abort from Target: pci fatal error! (0x%08X)\n", - card->devname,chan->if_name,rx_el->reg); - } - if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_DS_TOUT){ - DEBUG_EVENT("%s:%s: Rx Error: No 'DeviceSelect' from target: pci fatal error! (0x%08X)\n", - card->devname,chan->if_name,rx_el->reg); - } - if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_RETRY_TOUT){ - DEBUG_EVENT("%s:%s: Rx Error: 'Retry' exceeds maximum (64k): pci fatal error! (0x%08X)\n", - card->devname,chan->if_name,rx_el->reg); - } + DEBUG_EVENT("%s:%s: Rx Error: Abort from Master: pci fatal error! (0x%08X)\n", + card->devname,chan->if_name,rx_el->reg); + } + if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_T_ABRT){ + DEBUG_EVENT("%s:%s: Rx Error: Abort from Target: pci fatal error! (0x%08X)\n", + card->devname,chan->if_name,rx_el->reg); + } + if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_DS_TOUT){ + DEBUG_EVENT("%s:%s: Rx Error: No 'DeviceSelect' from target: pci fatal error! (0x%08X)\n", + card->devname,chan->if_name,rx_el->reg); + } + if (rx_el->reg & RxDMA_HI_DMA_PCI_ERROR_RETRY_TOUT){ + DEBUG_EVENT("%s:%s: Rx Error: 'Retry' exceeds maximum (64k): pci fatal error! (0x%08X)\n", + card->devname,chan->if_name,rx_el->reg); + } chan->if_stats.rx_errors++; chan->errstats.Rx_pci_errors++; @@ -6813,16 +7334,16 @@ static int aft_rx_post_complete_voice (sdla_t *card, private_area_t *chan, len=rx_el->reg&RxDMA_HI_DMA_DATA_LENGTH_MASK; /* In Transparent mode, our RX buffer will always be - * aligned to the 32bit (word) boundary, because - * the RX buffers are all of equal length */ + * aligned to the 32bit (word) boundary, because + * the RX buffers are all of equal length */ len=(((chan->mru>>2)-len)<<2) - (~(0x03)&RxDMA_LO_ALIGNMENT_BIT_MASK); memset(wan_skb_data(skb),0,sizeof(wp_rx_element_t)); wan_skb_put(skb,len); /* The rx size is big enough, thus - * send this buffer up the stack - * and allocate another one */ + * send this buffer up the stack + * and allocate another one */ wan_skb_pull(skb, sizeof(wp_rx_element_t)); wan_skb_reverse(skb); @@ -6842,21 +7363,21 @@ static int aft_dma_rx_tdmv(sdla_t *card, private_area_t *chan, netskb_t *skb) if (err==0){ if (wan_tracing_enabled(&chan->trace_info) >= 1){ if (card->u.aft.tdmv_dchan == 0){ - wan_capture_trace_packet_offset(card, &chan->trace_info, skb, + wan_capture_trace_packet_offset(card, &chan->trace_info, skb, IS_T1_CARD(card)?24:16, TRC_INCOMING_FRM); } }else{ wan_capture_trace_packet(card, &chan->trace_info, - skb, TRC_INCOMING_FRM); + skb, TRC_INCOMING_FRM); } if (chan->tdmv_zaptel_cfg){ #ifdef CONFIG_PRODUCT_WANPIPE_TDM_VOICE WAN_TDMV_CALL(rx_tx, (card,skb), err); if (err == 0) { - chan->if_stats.rx_frame_errors++; - aft_init_requeue_free_skb(chan, skb); - return 0; + chan->if_stats.rx_frame_errors++; + aft_init_requeue_free_skb(chan, skb); + return 0; } if (card->wan_tdmv.sc){ @@ -6893,7 +7414,7 @@ static int aft_realign_skb_pkt(private_area_t *chan, netskb_t *skb) if (len > chan->dma_mru){ DEBUG_EVENT("%s: Critical error: Tx unalign pkt(%d) > MTU buf(%d)!\n", - chan->if_name,len,chan->dma_mru); + chan->if_name,len,chan->dma_mru); return -ENOMEM; } @@ -6901,11 +7422,11 @@ static int aft_realign_skb_pkt(private_area_t *chan, netskb_t *skb) chan->tx_realign_buf=wan_malloc(chan->dma_mru); if (!chan->tx_realign_buf){ DEBUG_EVENT("%s: Error: Failed to allocate tx memory buf\n", - chan->if_name); + chan->if_name); return -ENOMEM; }else{ DEBUG_EVENT("%s: AFT Realign buffer allocated Len=%d\n", - chan->if_name,chan->dma_mru); + chan->if_name,chan->dma_mru); } } @@ -6917,7 +7438,7 @@ static int aft_realign_skb_pkt(private_area_t *chan, netskb_t *skb) if (wan_skb_tailroom(skb) < len){ DEBUG_EVENT("%s: Critical error: Tx unalign pkt tail room(%i) < unalign len(%i)!\n", - chan->if_name,wan_skb_tailroom(skb),len); + chan->if_name,wan_skb_tailroom(skb),len); return -ENOMEM; } @@ -6926,7 +7447,7 @@ static int aft_realign_skb_pkt(private_area_t *chan, netskb_t *skb) if ((unsigned long)data & 0x03){ /* At this point pkt should be realigned. If not - * there is something really wrong! */ + * there is something really wrong! */ return -EINVAL; } @@ -6940,7 +7461,7 @@ static int aft_realign_skb_pkt(private_area_t *chan, netskb_t *skb) static void __aft_fe_intr_ctrl(sdla_t *card, int status) { u32 reg; - + card->hw_iface.bus_read_4(card->hw,XILINX_CHIP_CFG_REG,®); if (status){ wan_set_bit(FRONT_END_INTR_ENABLE_BIT,®); @@ -6961,17 +7482,19 @@ static void aft_fe_intr_ctrl(sdla_t *card, int status) #if defined(__LINUX__) # if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) -static void aft_port_task (void * card_ptr) +static void xilinx_aft_port_task (void * card_ptr) # else -static void aft_port_task (struct work_struct *work) +static void xilinx_aft_port_task (struct work_struct *work) # endif +#elif defined(__WINDOWS__) +static void xilinx_aft_port_task (IN PKDPC Dpc, IN PVOID card_ptr, IN PVOID SystemArgument1, IN PVOID SystemArgument2) #else -static void aft_port_task (void * card_ptr, int arg) +static void xilinx_aft_port_task (void * card_ptr, int arg) #endif { #if defined(__LINUX__) # if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)) - sdla_t *card = (sdla_t *)container_of(work, sdla_t, u.aft.port_task); + sdla_t *card = (sdla_t *)container_of(work, sdla_t, u.aft.port_task); # else sdla_t *card = (sdla_t *)card_ptr; # endif @@ -6980,12 +7503,15 @@ static void aft_port_task (void * card_ptr, int arg) #endif wan_smp_flag_t smp_flags; + DEBUG_TEST("%s()\n", __FUNCTION__); + if (wan_test_bit(CARD_DOWN,&card->wandev.critical)){ + DEBUG_TEST("%s(): CARD_DOWN bit set\n", __FUNCTION__); return; } DEBUG_TEST("%s: AFT PORT TASK CMD=0x%X!\n", - card->devname,card->u.aft.port_task_cmd); + card->devname,card->u.aft.port_task_cmd); card->hw_iface.hw_lock(card->hw,&smp_flags); @@ -7033,9 +7559,9 @@ static void aft_port_task (void * card_ptr, int arg) /* - * ****************************************************************** - * Proc FS function - */ +* ****************************************************************** +* Proc FS function +*/ static int wan_aft_get_info(void* pcard, struct seq_file *m, int *stop_cnt) { sdla_t *card = (sdla_t*)pcard; @@ -7043,16 +7569,16 @@ static int wan_aft_get_info(void* pcard, struct seq_file *m, int *stop_cnt) if (card->wandev.fe_iface.update_alarm_info){ m->count = WAN_FECALL( - &card->wandev, - update_alarm_info, - (&card->fe, m, stop_cnt)); + &card->wandev, + update_alarm_info, + (&card->fe, m, stop_cnt)); } if (card->wandev.fe_iface.update_pmon_info){ m->count = WAN_FECALL( - &card->wandev, - update_pmon_info, - (&card->fe, m, stop_cnt)); + &card->wandev, + update_pmon_info, + (&card->fe, m, stop_cnt)); } return m->count; @@ -7063,14 +7589,14 @@ static int aft_dev_open_private(sdla_t *card, private_area_t *chan) int err=0; /* Initialize the router start time. - * Used by wanpipemon debugger to indicate - * how long has the interface been up */ + * Used by wanpipemon debugger to indicate + * how long has the interface been up */ wan_getcurrenttime(&chan->router_start_time, NULL); - /* If FRONT End is down, it means that the DMA - * is disabled. In this case don't try to - * reset fifo. Let the enable_data_error_intr() - * function do this, after front end has come up */ + /* If FRONT End is down, it means that the DMA + * is disabled. In this case don't try to + * reset fifo. Let the enable_data_error_intr() + * function do this, after front end has come up */ if (card->wandev.state == WAN_CONNECTED){ @@ -7090,53 +7616,53 @@ static int aft_dev_open_private(sdla_t *card, private_area_t *chan) u32 reg=0,reg1; /* The Transparent HDLC engine is - * enabled. The Rx dma has already - * been setup above. Now setup - * TX DMA and enable the HDLC engine */ + * enabled. The Rx dma has already + * been setup above. Now setup + * TX DMA and enable the HDLC engine */ xilinx_dma_tx(card,chan); DEBUG_CFG("%s: Transparent Tx Enabled!\n", - chan->if_name); + chan->if_name); - /* Select an HDLC logic channel for configuration */ - card->hw_iface.bus_read_4( - card->hw, - XILINX_TIMESLOT_HDLC_CHAN_REG, - ®); + /* Select an HDLC logic channel for configuration */ + card->hw_iface.bus_read_4( + card->hw, + XILINX_TIMESLOT_HDLC_CHAN_REG, + ®); - reg&=~HDLC_LOGIC_CH_BIT_MASK; - reg&= HDLC_LCH_TIMESLOT_MASK; /* mask not valid bits */ + reg&=~HDLC_LOGIC_CH_BIT_MASK; + reg&= HDLC_LCH_TIMESLOT_MASK; /* mask not valid bits */ - card->hw_iface.bus_write_4(card->hw, - XILINX_TIMESLOT_HDLC_CHAN_REG, - (reg|(chan->logic_ch_num&HDLC_LOGIC_CH_BIT_MASK))); + card->hw_iface.bus_write_4(card->hw, + XILINX_TIMESLOT_HDLC_CHAN_REG, + (reg|(chan->logic_ch_num&HDLC_LOGIC_CH_BIT_MASK))); reg=0; /* Enable the transparend HDLC - * engine. */ - wan_set_bit(HDLC_RX_PROT_DISABLE_BIT,®); - wan_set_bit(HDLC_TX_PROT_DISABLE_BIT,®); + * engine. */ + wan_set_bit(HDLC_RX_PROT_DISABLE_BIT,®); + wan_set_bit(HDLC_TX_PROT_DISABLE_BIT,®); - wan_set_bit(HDLC_TX_CHAN_ENABLE_BIT,®); - wan_set_bit(HDLC_RX_ADDR_RECOGN_DIS_BIT,®); + wan_set_bit(HDLC_TX_CHAN_ENABLE_BIT,®); + wan_set_bit(HDLC_RX_ADDR_RECOGN_DIS_BIT,®); card->hw_iface.bus_read_4( - card->hw, - XILINX_TIMESLOT_HDLC_CHAN_REG, - ®1); + card->hw, + XILINX_TIMESLOT_HDLC_CHAN_REG, + ®1); DEBUG_CFG("%s: Writting to REG(0x64)=0x%X Reg(0x60)=0x%X\n", - chan->if_name,reg,reg1); + chan->if_name,reg,reg1); xilinx_write_ctrl_hdlc(card, - chan->first_time_slot, - XILINX_HDLC_CONTROL_REG, - reg); + chan->first_time_slot, + XILINX_HDLC_CONTROL_REG, + reg); if (err){ - DEBUG_EVENT("%s:%d wait for timeslot failed\n", - __FUNCTION__,__LINE__); + DEBUG_EVENT("%s:%d wait for timeslot failed\n", + __FUNCTION__,__LINE__); return err; } } @@ -7183,7 +7709,7 @@ static void aft_dev_close(sdla_t *card, private_area_t *gchan) aft_dev_close_private(card,chan); DEBUG_TEST("%s: Closing Ch=%ld\n", - chan->if_name,chan->logic_ch_num); + chan->if_name,chan->logic_ch_num); wan_clear_bit(0,&chan->up); } @@ -7196,18 +7722,18 @@ static void aft_dev_close(sdla_t *card, private_area_t *gchan) /* - * ****************************************************************** - * Init TDMV interface - */ +* ****************************************************************** +* Init TDMV interface +*/ static int aft_tdmv_init(sdla_t *card, wandev_conf_t *conf) { int err; DEBUG_EVENT("%s: TDMV Span = %d : %s\n", - card->devname, - card->tdmv_conf.span_no, - card->tdmv_conf.span_no?"Enabled":"Disabled"); + card->devname, + card->tdmv_conf.span_no, + card->tdmv_conf.span_no?"Enabled":"Disabled"); err=0; @@ -7218,9 +7744,9 @@ static int aft_tdmv_free(sdla_t *card) { #if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE) if (card->tdmv_conf.span_no && - card->wan_tdmv.sc) { - int err; - WAN_TDMV_CALL(remove, (card), err); + card->wan_tdmv.sc) { + int err; + WAN_TDMV_CALL(remove, (card), err); } #endif return 0; @@ -7254,7 +7780,7 @@ static int aft_tdmv_if_init(sdla_t *card, private_area_t *chan, wanif_conf_t *co if (chan->common.usedby == TDM_VOICE_API) { #ifdef AFT_TDM_API_SUPPORT - err=wp_tdmapi_check_mtu(card, conf->active_ch, 8, &card->wandev.mtu); + err=wp_tdmapi_check_mtu(card, conf->active_ch, 8, &card->wandev.mtu); #endif } @@ -7269,16 +7795,16 @@ static int aft_tdmv_if_init(sdla_t *card, private_area_t *chan, wanif_conf_t *co /* If DCHAN is enabled, set this timeslot, so zaptel - * configures it. However, the wp_tdmv_software_init() - * will remove it from the timeslot list. */ + * configures it. However, the wp_tdmv_software_init() + * will remove it from the timeslot list. */ if (card->u.aft.tdmv_dchan){ wan_set_bit(dchan,&conf->active_ch); } /* The TDMV drivers always starts from number - * ZERO. Wanpipe driver doesn't allow timeslot - * ZERO. Thus, the active_ch map must me adjusted - * before calling tdmv_reg */ + * ZERO. Wanpipe driver doesn't allow timeslot + * ZERO. Thus, the active_ch map must me adjusted + * before calling tdmv_reg */ if (IS_E1_CARD(card)){ conf->active_ch=conf->active_ch>>1; } @@ -7289,7 +7815,7 @@ static int aft_tdmv_if_init(sdla_t *card, private_area_t *chan, wanif_conf_t *co WAN_TDMV_CALL(reg, (card, &conf->tdmv, conf->active_ch, conf->hwec.enable, chan->common.dev), err); if (err < 0){ DEBUG_EVENT("%s: Error: Failed to register TDMV channel!\n", - chan->if_name); + chan->if_name); return -EINVAL; } @@ -7339,9 +7865,9 @@ static int aft_read_rbs_bits(void *chan_ptr, u32 ch, u8 *rbs_bits) card->hw_iface.hw_lock(card->hw,&flags); *rbs_bits = card->wandev.fe_iface.read_rbsbits( - &card->fe, - ch, - WAN_TE_RBS_UPDATE); + &card->fe, + ch, + WAN_TE_RBS_UPDATE); card->hw_iface.hw_unlock(card->hw,&flags); return 0; @@ -7362,8 +7888,8 @@ static int aft_write_rbs_bits(void *chan_ptr, u32 ch, u8 rbs_bits) card=(sdla_t*)chan->common.card; card->hw_iface.hw_lock(card->hw,&flags); err = card->wandev.fe_iface.set_rbsbits(&card->fe, - ch, - rbs_bits); + ch, + rbs_bits); card->hw_iface.hw_unlock(card->hw,&flags); return err; @@ -7376,13 +7902,19 @@ static int aft_write_hdlc_frame(void *chan_ptr, netskb_t *skb, wp_api_hdr_t *hd sdla_t *card=chan->common.card; wan_smp_flag_t smp_flags; int err=-EINVAL; + + DEBUG_TDMAPI("%s(): line: %d\n", __FUNCTION__, __LINE__); if (!chan_ptr || !chan->common.dev || !card){ WAN_ASSERT(1); return -EINVAL; } - if (chan->common.usedby != TDM_VOICE_DCHAN) { + if (chan->common.usedby != TDM_VOICE_DCHAN +#if defined(__WINDOWS__) + && chan->common.usedby != API +#endif + ) { return -EINVAL; } @@ -7396,7 +7928,6 @@ static int aft_write_hdlc_frame(void *chan_ptr, netskb_t *skb, wp_api_hdr_t *hd xilinx_dma_tx(card,chan); wan_spin_unlock_irq(&card->wandev.lock, &smp_flags); return -EBUSY; - } wan_skb_unlink(skb); @@ -7406,7 +7937,6 @@ static int aft_write_hdlc_frame(void *chan_ptr, netskb_t *skb, wp_api_hdr_t *hd err=0; wan_spin_unlock_irq(&card->wandev.lock, &smp_flags); - return err; } @@ -7460,13 +7990,13 @@ static int aft_tdm_api_rx_tx_channelized(sdla_t *card, private_area_t *chan, net } chan->wp_tdm_api_dev_idx[x].rx_data[y] = - rxbuf[offset++]; + rxbuf[offset++]; if (y == WP_TDM_API_CHUNK_SZ-1) { wanpipe_tdm_api_rx_tx(&chan->wp_tdm_api_dev_idx[x], - chan->wp_tdm_api_dev_idx[x].rx_data, - chan->wp_tdm_api_dev_idx[x].tx_data, - WP_TDM_API_CHUNK_SZ); + chan->wp_tdm_api_dev_idx[x].rx_data, + chan->wp_tdm_api_dev_idx[x].tx_data, + WP_TDM_API_CHUNK_SZ); } } } @@ -7491,13 +8021,13 @@ static void aft_critical_shutdown (sdla_t *card) #ifdef __LINUX__ printk(KERN_ERR "%s: Error: Card Critically Shutdown!\n", - card->devname); + card->devname); #else DEBUG_EVENT("%s: Error: Card Critically Shutdown!\n", - card->devname); + card->devname); #endif - /* TE1 - Unconfiging, only on shutdown */ + /* TE1 - Unconfiging, only on shutdown */ if (IS_TE1_CARD(card)) { if (card->wandev.fe_iface.pre_release){ card->wandev.fe_iface.pre_release(&card->fe); @@ -7507,7 +8037,7 @@ static void aft_critical_shutdown (sdla_t *card) } } - port_set_state(card,WAN_DISCONNECTED); + port_set_state(card,WAN_DISCONNECTED); disable_data_error_intr(card,DEVICE_DOWN); wan_set_bit(CARD_DOWN,&card->wandev.critical); diff --git a/patches/kdrivers/src/net/sdladrv.c b/patches/kdrivers/src/net/sdladrv.c index 837064f..12aedeb 100644 --- a/patches/kdrivers/src/net/sdladrv.c +++ b/patches/kdrivers/src/net/sdladrv.c @@ -129,6 +129,11 @@ #include "wanpipe.h" #include "wan_mem_debug.h" +#if defined(__LINUX__) +#include "wanpipe_cdev_iface.h" +#include "wanpipe_logger.h" +#endif + #include "sdlasfm.h" /* SDLA firmware module definitions */ #include "sdlapci.h" /* SDLA PCI hardware definitions */ #include "sdladrv.h" /* API definitions */ @@ -353,7 +358,7 @@ static int sdla_pcibridge_info(sdlahw_t* hw); static int sdla_hwdev_common_unregister(sdlahw_cpu_t*); static sdlahw_t* sdla_hwdev_common_register(sdlahw_cpu_t* hwcpu, int, int); -static sdlahw_t* sdla_hwdev_te1_register(sdlahw_cpu_t*, int); +static sdlahw_t* sdla_hwdev_te1_register(sdlahw_cpu_t*, int, int); static sdlahw_t* sdla_hwdev_Remora_register(sdlahw_cpu_t*, int*); static sdlahw_t* sdla_hwdev_a600_register(sdlahw_cpu_t*, int*); static sdlahw_t* sdla_hwdev_ISDN_register(sdlahw_cpu_t*, int*); @@ -469,6 +474,13 @@ extern int sdla_usb_restore_intrhand(void*, int); extern int sdla_usb_err_stats(void*,void*,int); extern int sdla_usb_flush_err_stats(void*); + +#if defined(CONFIG_PRODUCT_WANPIPE_AFT_B601) +extern int sdla_b601_te1_write_fe(void *phw, ...); +extern u_int8_t sdla_b601_te1_read_fe (void *phw, ...); +extern u_int8_t __sdla_b601_te1_read_fe (void *phw, ...); +#endif + #if defined(__LINUX__) static int sdla_pci_probe(sdlahw_t*); #endif @@ -663,6 +675,7 @@ WAN_MODULE_VERSION(sdladrv, SDLADRV_MAJOR_VER); int sdladrv_init(void* arg) { int volatile i=0; + int err; #if defined(WAN_DEBUG_MEM) sdla_memdbg_init(); @@ -683,6 +696,18 @@ int sdladrv_init(void* arg) memset(&sdla_adapter_cnt,0,sizeof(sdla_hw_type_cnt_t)); +#if defined(__LINUX__) + err=wanpipe_global_cdev_init(); + if (err) { + return err; + } + err=wp_logger_create(); + if (err) { + wanpipe_global_cdev_free(); + return err; + } +#endif + #if defined(CONFIG_PRODUCT_WANPIPE_USB) sdla_usb_init(); #endif @@ -761,6 +786,11 @@ int sdladrv_exit (void *arg) #if defined(WAN_DEBUG_MEM) sdla_memdbg_free(); #endif + +#if defined(__LINUX__) + wp_logger_delete(); + wanpipe_global_cdev_free(); +#endif return 0; } @@ -918,7 +948,6 @@ sdla_save_hw_probe (sdlahw_t* hw, int port) case A200_ADPTR_ANALOG: case A400_ADPTR_ANALOG: - case AFT_ADPTR_A600: /*sprintf(tmp_hw_probe->hw_info,*/ SDLA_PROBE_SPRINT(hwprobe->hw_info, sizeof(hwprobe->hw_info), @@ -932,7 +961,21 @@ sdla_save_hw_probe (sdlahw_t* hw, int port) hwcpu->hwcard->hwec_chan_no, hwcpu->hwcard->core_rev); break; - + case AFT_ADPTR_A600: +#if defined(CONFIG_PRODUCT_WANPIPE_AFT_B601) + case AFT_ADPTR_B601: +#endif + SDLA_PROBE_SPRINT(hwprobe->hw_info, + sizeof(hwprobe->hw_info), + SDLA_HWPROBE_A500_SH_FORMAT, + hwcpu->hwcard->adptr_name, + hwcpu->hwcard->u_pci.slot_no, + hwcpu->hwcard->u_pci.bus_no, + hwcpu->irq, + hw->line_no+1, /* Physical line number */ + hwcpu->hwcard->hwec_chan_no, + hwcpu->hwcard->core_rev); + break; case A300_ADPTR_U_1TE3: if (hwcpu->hwcard->adptr_subtype == AFT_SUBTYPE_SHARK){ SDLA_PROBE_SPRINT(hwprobe->hw_info, @@ -1003,7 +1046,7 @@ sdla_save_hw_probe (sdlahw_t* hw, int port) hwcpu->hwcard->core_rev); break; case AFT_ADPTR_FLEXBRI: - snprintf(hwprobe->hw_info, + SDLA_PROBE_SPRINT(hwprobe->hw_info, sizeof(hwprobe->hw_info), SDLA_HWPROBE_A500_SH_FORMAT, hwcpu->hwcard->adptr_name, @@ -1044,7 +1087,7 @@ sdla_save_hw_probe (sdlahw_t* hw, int port) } #if defined(CONFIG_PRODUCT_WANPIPE_USB) }else if (hwcpu->hwcard->hw_type == SDLA_USB_CARD){ - snprintf(hwprobe->hw_info, sizeof(hwprobe->hw_info), + SDLA_PROBE_SPRINT(hwprobe->hw_info, sizeof(hwprobe->hw_info), SDLA_HWPROBE_USB_FORMAT, "U100", WP_USB_BUSID(hwcpu->hwcard), @@ -1140,7 +1183,7 @@ static int sdla_hwdev_common_unregister(sdlahw_cpu_t* hwcpu) static sdlahw_t* -sdla_hwdev_te1_register(sdlahw_cpu_t* hwcpu, int max_line_no) +sdla_hwdev_te1_register(sdlahw_cpu_t* hwcpu, int first_line_no, int max_line_no) { sdlahw_t *hw = NULL, *first_hw = NULL; int line; @@ -1150,7 +1193,7 @@ sdla_hwdev_te1_register(sdlahw_cpu_t* hwcpu, int max_line_no) memset(id_str, 0x00, sizeof(id_str)); memset(id_dump_str, 0x00, sizeof(id_dump_str)); - for(line = 0; line < max_line_no; line++){ + for(line = first_line_no; line < max_line_no; line++){ if (hwcpu->hwcard->type == SDLA_S514){ strcpy(id_str, "PMC4351"); }else if (hwcpu->hwcard->type == SDLA_AFT){ @@ -1174,6 +1217,10 @@ sdla_hwdev_te1_register(sdlahw_cpu_t* hwcpu, int max_line_no) } }else if (hwcpu->hwcard->cfg_type == WANOPT_AFT108){ strcpy(id_str, "DS26528"); +#if defined (CONFIG_PRODUCT_WANPIPE_AFT_B601) + } else if (hwcpu->hwcard->adptr_type == AFT_ADPTR_B601){ + strcpy(id_str, "DS26521"); +#endif } }else{ continue; @@ -1521,7 +1568,7 @@ sdla_hwdev_ISDN_register(sdlahw_cpu_t* hwcpu, int *lines_no) max_analog_modules_per_remora = 2; break; default: - DEBUG_EVENT("Critical error: Not a valid BRI adapter type (0x%X)\n", hw->hwcpu->hwcard->adptr_type); + DEBUG_ERROR("Critical error: Not a valid BRI adapter type (0x%X)\n", hw->hwcpu->hwcard->adptr_type); return 0; } @@ -1596,7 +1643,7 @@ sdla_hwdev_ISDN_register(sdlahw_cpu_t* hwcpu, int *lines_no) } if (first_hw == NULL) { - DEBUG_EVENT("%s: Error: Failed to detect any Modules!\n",hw->devname); + DEBUG_ERROR("%s: Error: Failed to detect any Modules!\n",hw->devname); } /* Reset SPI bus */ @@ -1735,7 +1782,7 @@ static int sdla_scan_bri_modules(sdlahw_t* hw, int *rm_mod_type, u_int8_t rm_no hw->hwcpu->hwcard->cpld_rev=1; if((value >> 6) == 0x1){ - DEBUG_EVENT("%s: Error: Analog FXO/FXS module detected in BRI slot\n", + DEBUG_ERROR("%s: Error: Analog FXO/FXS module detected in BRI slot\n", hw->devname); DEBUG_EVENT("Analog module should only be inserted in FXO/FXS slot\n"); return 0; @@ -1743,7 +1790,7 @@ static int sdla_scan_bri_modules(sdlahw_t* hw, int *rm_mod_type, u_int8_t rm_no max_module_no = 4; break; default: - DEBUG_EVENT("Critical error: Not a valid BRI adapter type (0x%X)\n", hw->hwcpu->hwcard->adptr_type); + DEBUG_ERROR("Critical error: Not a valid BRI adapter type (0x%X)\n", hw->hwcpu->hwcard->adptr_type); return 0; } @@ -1763,7 +1810,7 @@ static int sdla_scan_bri_modules(sdlahw_t* hw, int *rm_mod_type, u_int8_t rm_no DEBUG_TEST("mod_no_index (line number) on CARD (should be 0-23): %d\n", mod_no_index); if(mod_no_index >= MAX_BRI_LINES){ - DEBUG_EVENT("%s: Error: Module %d/%d exceeds maximum (%d)\n", + DEBUG_ERROR("%s: Error: Module %d/%d exceeds maximum (%d)\n", hw->devname, mod_no_index, mod_no_index, MAX_BRI_LINES); return 0; } @@ -1853,8 +1900,13 @@ static int sdla_pcibridge_info(sdlahw_t* hw) #define AFT_CHIP_CFG_REG 0x40 #define AFT_CHIPCFG_SFR_IN_BIT 2 #define AFT_CHIPCFG_SFR_EX_BIT 1 +#define AFT_CHIPCFG_B600_EC_CHIP_PRESENT_BIT 20 +#define AFT_CHIPCFG_B600_EC_RESET_BIT 25 -static int sdla_get_hw_info(sdlahw_t* hw) +#if !defined(__WINDOWS__) +static +#endif +int sdla_get_hw_info(sdlahw_t* hw) { sdlahw_cpu_t *hwcpu; sdlahw_card_t *hwcard; @@ -1862,8 +1914,6 @@ static int sdla_get_hw_info(sdlahw_t* hw) unsigned short cpld_off; unsigned char status = 0, tmp = 0, adptr_sec = 0; - AFT_FUNC_DEBUG(); - WAN_ASSERT(hw == NULL); WAN_ASSERT(hw->hwcpu == NULL); WAN_ASSERT(hw->hwcpu->hwcard == NULL); @@ -1879,6 +1929,14 @@ static int sdla_get_hw_info(sdlahw_t* hw) return -EINVAL; } + if (hwcard->core_rev == WAN_INVALID_FIRMWARE) { + /* if PCI_SUBSYS_ID_WORD == WAN_INVALID_FIRMWARE, then read true firmware version + from PCI_CORE_REV_REG (0xFC) */ + u16 new_core_rev = 0x00; + sdla_bus_read_2(hw, SDLA_REG_OFF(hwcard, PCI_CORE_REV_REG), &new_core_rev); + hwcard->core_rev = new_core_rev; + } + if (hwcard->adptr_subtype == AFT_SUBTYPE_NORMAL){ switch(hwcard->adptr_type){ case A101_ADPTR_1TE1: @@ -1904,7 +1962,7 @@ static int sdla_get_hw_info(sdlahw_t* hw) break; case A104_ADPTR_4TE1: - /* Enable memory access */ + /* Take card out of reset read security and then put card back into reset */ sdla_bus_read_4(hw, SDLA_REG_OFF(hwcard, AFT_CHIP_CFG_REG), ®1); reg = reg1; wan_clear_bit(AFT_CHIPCFG_SFR_IN_BIT, ®); @@ -1936,7 +1994,7 @@ static int sdla_get_hw_info(sdlahw_t* hw) case AFT_PMC_FE_CORE_ID: switch(hwcard->adptr_type){ case A104_ADPTR_4TE1: - /* Enable memory access */ + /* Take card out of reset read CPLD and then put card back into reset */ sdla_bus_read_4(hw, SDLA_REG_OFF(hwcard, AFT_CHIP_CFG_REG), ®1); reg = reg1; wan_clear_bit(AFT_CHIPCFG_SFR_IN_BIT, ®); @@ -1972,7 +2030,7 @@ static int sdla_get_hw_info(sdlahw_t* hw) cpld_off = AFT_SH_CPLD_BOARD_STATUS_REG; sdla_hw_read_cpld(hw, cpld_off, &status); hwcard->hwec_chan_no = A108_ECCHAN(AFT_SH_SECURITY(status)); - + /* Restore original value */ sdla_bus_write_4(hw, SDLA_REG_OFF(hwcard, AFT_CHIP_CFG_REG), reg1); break; @@ -1994,7 +2052,6 @@ static int sdla_get_hw_info(sdlahw_t* hw) hwcard->hwec_chan_no = AFT_RM_ECCHAN(AFT_SH_SECURITY(status)); if (hwcard->hwec_chan_no){ - /* Check EC access */ /* Clear octasic reset */ cpld_off = 0x00; @@ -2009,21 +2066,43 @@ static int sdla_get_hw_info(sdlahw_t* hw) sdla_bus_write_4(hw, SDLA_REG_OFF(hwcard, AFT_CHIP_CFG_REG), reg1); break; case AFT_ADPTR_A600: +#if defined(CONFIG_PRODUCT_WANPIPE_AFT_B601) + case AFT_ADPTR_B601: +#endif /* Enable memory access */ sdla_bus_read_4(hw, SDLA_REG_OFF(hwcard, AFT_CHIP_CFG_REG), ®1); + wan_clear_bit(AFT_CHIPCFG_B600_EC_RESET_BIT, ®1); reg = reg1; wan_clear_bit(AFT_CHIPCFG_SFR_IN_BIT, ®); wan_clear_bit(AFT_CHIPCFG_SFR_EX_BIT, ®); + /* This will reset security chip so we can read proper value later in chip_cfg */ + wan_set_bit(AFT_CHIPCFG_B600_EC_RESET_BIT, ®); sdla_bus_write_4(hw, SDLA_REG_OFF(hwcard, AFT_CHIP_CFG_REG), reg); + WP_DELAY(1000); + wan_clear_bit(AFT_CHIPCFG_B600_EC_RESET_BIT, ®); + sdla_bus_write_4(hw, SDLA_REG_OFF(hwcard, AFT_CHIP_CFG_REG), reg); + WP_DELAY(50000); + /* Clearing AFT_CHIPCFG_SFR_IN_BIT also resets C2 security */ /* Dummy access to start C2 Security*/ sdla_bus_read_4(hw, SDLA_REG_OFF(hwcard, AFT_CHIP_CFG_REG), ®); /* Delay for C2 Security to be done */ WP_DELAY(1000); sdla_bus_read_4(hw, SDLA_REG_OFF(hwcard, AFT_CHIP_CFG_REG), ®); - hwcard->hwec_chan_no = A600_ECCHAN((reg >> 4) & 0x3); - + /* B600 cards with firmware v3 or higher use same format as B601 */ + + if (hwcard->core_rev < 3) { + hwcard->hwec_chan_no = A600_ECCHAN((reg >> 4) & 0x3); + } else { + if (wan_test_bit(AFT_CHIPCFG_B600_EC_CHIP_PRESENT_BIT, ®)) { + if (hwcard->adptr_type == AFT_ADPTR_B601) { + hwcard->hwec_chan_no = 64; + } else { + hwcard->hwec_chan_no = 5; + } + } + } if (hwcard->hwec_chan_no) { /* Clear octasic reset */ reg &= ~0x1000000; @@ -2147,7 +2226,7 @@ sdla_s514_hw_select (sdlahw_card_t* hwcard, int cpu_no, int irq, void* dev) if ((hwcpu = sdla_hwcpu_register(hwcard, cpu_no, irq, dev)) == NULL){ return -EINVAL; } - if ((hw = sdla_hwdev_te1_register(hwcpu, 2)) == NULL){ + if ((hw = sdla_hwdev_te1_register(hwcpu, 0, 2)) == NULL){ sdla_hwcpu_unregister(hwcpu); return 0; } @@ -2220,7 +2299,7 @@ sdla_s514_hw_select (sdlahw_card_t* hwcard, int cpu_no, int irq, void* dev) if ((hwcpu = sdla_hwcpu_register(hwcard, cpu_no, irq, dev)) == NULL){ return -EINVAL; } - if ((hw = sdla_hwdev_te1_register(hwcpu, 1)) == NULL){ + if ((hw = sdla_hwdev_te1_register(hwcpu, 0, 1)) == NULL){ sdla_hwcpu_unregister(hwcpu); return 0; } @@ -2236,7 +2315,7 @@ sdla_s514_hw_select (sdlahw_card_t* hwcard, int cpu_no, int irq, void* dev) if ((hwcpu = sdla_hwcpu_register(hwcard, cpu_no, irq, dev)) == NULL){ return -EINVAL; } - if ((hw = sdla_hwdev_te1_register(hwcpu, 1)) == NULL){ + if ((hw = sdla_hwdev_te1_register(hwcpu, 0, 1)) == NULL){ sdla_hwcpu_unregister(hwcpu); return 0; } @@ -2261,7 +2340,7 @@ sdla_s514_hw_select (sdlahw_card_t* hwcard, int cpu_no, int irq, void* dev) } /* Aug 10, 2007 ** Do not overwrite first hw for this Serial card */ - if (sdla_hwdev_te1_register(hwcpu, 1) == NULL){ + if (sdla_hwdev_te1_register(hwcpu, 0, 1) == NULL){ sdla_hwcpu_unregister(hwcpu); return 0; } @@ -2274,7 +2353,7 @@ sdla_s514_hw_select (sdlahw_card_t* hwcard, int cpu_no, int irq, void* dev) if ((hwcpu = sdla_hwcpu_register(hwcard, cpu_no, irq, dev)) == NULL){ return -EINVAL; } - if ((hw = sdla_hwdev_te1_register(hwcpu, 1)) == NULL){ + if ((hw = sdla_hwdev_te1_register(hwcpu, 0, 1)) == NULL){ sdla_hwcpu_unregister(hwcpu); return 0; } @@ -2361,7 +2440,7 @@ static int sdla_aft_hw_select (sdlahw_card_t* hwcard, int cpu_no, int irq, void* if ((hwcpu = sdla_hwcpu_register(hwcard, cpu_no, irq, dev)) == NULL){ return 0; } - if ((hw = sdla_hwdev_te1_register(hwcpu, 1)) == NULL){ + if ((hw = sdla_hwdev_te1_register(hwcpu, 0, 1)) == NULL){ sdla_hwcpu_unregister(hwcpu); return 0; } @@ -2383,7 +2462,7 @@ static int sdla_aft_hw_select (sdlahw_card_t* hwcard, int cpu_no, int irq, void* if ((hwcpu = sdla_hwcpu_register(hwcard, cpu_no, irq, dev)) == NULL){ return 0; } - if ((hw = sdla_hwdev_te1_register(hwcpu, 1)) == NULL){ + if ((hw = sdla_hwdev_te1_register(hwcpu, 0, 1)) == NULL){ sdla_hwcpu_unregister(hwcpu); return 0; } @@ -2393,7 +2472,7 @@ static int sdla_aft_hw_select (sdlahw_card_t* hwcard, int cpu_no, int irq, void* if ((hwcpu2 = sdla_hwcpu_register(hwcard, SDLA_CPU_B, irq, dev)) == NULL){ return 0; } - if ((hw2 = sdla_hwdev_te1_register(hwcpu2, 1)) == NULL){ + if ((hw2 = sdla_hwdev_te1_register(hwcpu2, 0, 1)) == NULL){ sdla_hwcpu_unregister(hwcpu2); sdla_hwcpu_unregister(hwcpu); return 0; @@ -2404,7 +2483,7 @@ static int sdla_aft_hw_select (sdlahw_card_t* hwcard, int cpu_no, int irq, void* if ((hwcpu2 = sdla_hwcpu_register(hwcard, SDLA_CPU_B, irq, dev)) == NULL){ return 0; } - if ((hw2 = sdla_hwdev_te1_register(hwcpu2, 1)) == NULL){ + if ((hw2 = sdla_hwdev_te1_register(hwcpu2, 0, 1)) == NULL){ sdla_hwcpu_unregister(hwcpu2); return 0; } @@ -2432,7 +2511,7 @@ static int sdla_aft_hw_select (sdlahw_card_t* hwcard, int cpu_no, int irq, void* if ((hwcpu = sdla_hwcpu_register(hwcard, cpu_no, irq, dev)) == NULL){ return 0; } - if ((hw = sdla_hwdev_te1_register(hwcpu, 2)) == NULL){ + if ((hw = sdla_hwdev_te1_register(hwcpu, 0, 2)) == NULL){ sdla_hwcpu_unregister(hwcpu); return 0; } @@ -2454,7 +2533,7 @@ static int sdla_aft_hw_select (sdlahw_card_t* hwcard, int cpu_no, int irq, void* if ((hwcpu = sdla_hwcpu_register(hwcard, cpu_no, irq, dev)) == NULL){ return 0; } - if ((hw = sdla_hwdev_te1_register(hwcpu, 4)) == NULL){ + if ((hw = sdla_hwdev_te1_register(hwcpu, 0, 4)) == NULL){ sdla_hwcpu_unregister(hwcpu); return 0; } @@ -2476,7 +2555,7 @@ static int sdla_aft_hw_select (sdlahw_card_t* hwcard, int cpu_no, int irq, void* if ((hwcpu = sdla_hwcpu_register(hwcard, cpu_no, irq, dev)) == NULL){ return 0; } - if ((hw = sdla_hwdev_te1_register(hwcpu, 8)) == NULL){ + if ((hw = sdla_hwdev_te1_register(hwcpu, 0, 8)) == NULL){ sdla_hwcpu_unregister(hwcpu); return 0; } @@ -2632,7 +2711,6 @@ static int sdla_aft_hw_select (sdlahw_card_t* hwcard, int cpu_no, int irq, void* hwcard->core_rev, lines_no, hwcard->u_pci.bus_no, hwcard->u_pci.slot_no, irq); break; - case AFT_ADPTR_A600: hwcard->cfg_type = WANOPT_AFT_ANALOG; sdla_adapter_cnt.aft_a600_adapters++; @@ -2656,6 +2734,36 @@ static int sdla_aft_hw_select (sdlahw_card_t* hwcard, int cpu_no, int irq, void* hwcard->core_rev, hwcard->u_pci.bus_no, hwcard->u_pci.slot_no, irq); break; +#if defined (CONFIG_PRODUCT_WANPIPE_AFT_B601) + case AFT_ADPTR_B601: + hwcard->cfg_type = WANOPT_AFT_ANALOG; + sdla_adapter_cnt.aft_b601_adapters++; + + if ((hwcpu = sdla_hwcpu_register(hwcard, cpu_no, irq, dev)) == NULL){ + return 0; + } + lines_no = 2; + if ((hw = sdla_hwdev_a600_register(hwcpu, &lines_no)) == NULL){ + sdla_hwcpu_unregister(hwcpu); + return 0; + } + + if ((hw = sdla_hwdev_te1_register(hwcpu, 1, 2)) == NULL){ + sdla_hwcpu_unregister(hwcpu); + return 0; + } + + number_of_cards+=2; + DEBUG_EVENT("%s: %s %s B601 card found (%s rev.%X), cpu(s) 1, bus #%d, slot #%d, irq #%d\n", + wan_drvname, + hwcard->adptr_name, + AFT_PCITYPE_DECODE(hwcard), + AFT_CORE_ID_DECODE(hwcard->core_id), + hwcard->core_rev, + hwcard->u_pci.bus_no, hwcard->u_pci.slot_no, irq); + break; +#endif + default: DEBUG_EVENT( "%s: Unknown adapter %04X (bus #%d, slot #%d, irq #%d)!\n", @@ -2753,7 +2861,7 @@ sdla_pci_probe_S(sdlahw_t *hw, int slot, int bus, int irq) hwcard->adptr_type = S5148_ADPTR_1_CPU_T1E1; break; default: - DEBUG_EVENT("sdladrv: Error: Invalid S514 PCI Subsystem ID 0x%X\n", + DEBUG_ERROR("sdladrv: Error: Invalid S514 PCI Subsystem ID 0x%X\n", pci_subsystem_id & 0xFF); return 0; } @@ -2938,6 +3046,12 @@ sdla_pci_probe_aft(sdlahw_t *hw, int bus_no, int slot_no, int irq) hwcard->adptr_type = AFT_ADPTR_A600; hwcard->adptr_subtype = AFT_SUBTYPE_SHARK; break; +#if defined (CONFIG_PRODUCT_WANPIPE_AFT_B601) + case AFT_B601_SUBSYS_VENDOR: + hwcard->adptr_type = AFT_ADPTR_B601; + hwcard->adptr_subtype = AFT_SUBTYPE_SHARK; + break; +#endif default: DEBUG_EVENT( "%s: Unsupported SubVendor ID:%04X (bus=%d, slot=%d)\n", @@ -2962,6 +3076,7 @@ sdla_pci_probe_aft(sdlahw_t *hw, int bus_no, int slot_no, int irq) case AFT_2SERIAL_RS232_SUBSYS_VENDOR: case AFT_4SERIAL_RS232_SUBSYS_VENDOR: case AFT_A600_SUBSYS_VENDOR: + case AFT_B601_SUBSYS_VENDOR: case A700_SHARK_SUBSYS_VENDOR: sdla_pcibridge_detect(hwcard); break; @@ -2969,6 +3084,7 @@ sdla_pci_probe_aft(sdlahw_t *hw, int bus_no, int slot_no, int irq) hwcard->core_id = AFT_CORE_ID(pci_subsystem_id); hwcard->core_rev= AFT_CORE_REV(pci_subsystem_id); + if (hwcard->adptr_subtype == AFT_SUBTYPE_SHARK){ switch(hwcard->core_id){ case AFT_ANALOG_FE_CORE_ID: @@ -3506,7 +3622,7 @@ unsigned int sdla_hw_probe(void) if (tmp_hwcpu) wan_free(tmp_hwcpu); if (tmp_hwcard) wan_free(tmp_hwcard); # else - DEBUG_EVENT( "Warning, Kernel not compiled for PCI support!\n"); + DEBUG_WARNING( "Warning, Kernel not compiled for PCI support!\n"); DEBUG_EVENT( "PCI Hardware Probe Failed!\n"); # endif return cardno; @@ -3648,7 +3764,7 @@ static int sdladrv_hw_probe_delete_pci_device(struct pci_dev *pci_dev, int slot, if(hwcard){ return sdla_card_unregister(hwcard); }else{ - DEBUG_EVENT("%s(): Error: failed to find 'hwcard' for deletion! (slot: %d, bus: %d)\n", + DEBUG_ERROR("%s(): Error: failed to find 'hwcard' for deletion! (slot: %d, bus: %d)\n", __FUNCTION__, slot, bus); return 1; } @@ -3993,7 +4109,7 @@ sdla_card_unregister(sdlahw_card_t* hwcard) WAN_ASSERT(hwcard == NULL); if (hwcard->internal_used){ - DEBUG_EVENT("%s: Error: This card is still in used (used=%d)!\n", + DEBUG_ERROR("%s: Error: This card is still in used (used=%d)!\n", __FUNCTION__, hwcard->internal_used); sdla_card_info(hwcard); @@ -4125,7 +4241,7 @@ static int sdla_hwcpu_unregister(sdlahw_cpu_t *hwcpu) WAN_ASSERT(hwcpu == NULL); if (hwcpu->internal_used){ - DEBUG_EVENT("%s: Error: HW Card Cpu is still in used (used=%d)\n", + DEBUG_ERROR("%s: Error: HW Card Cpu is still in used (used=%d)\n", __FUNCTION__, hwcpu->internal_used); sdla_hwcpu_info(hwcpu); @@ -4237,13 +4353,26 @@ static sdlahw_t* sdla_hw_register(sdlahw_cpu_t *hwcpu, int dev_no) new_hw->magic = SDLADRV_MAGIC; new_hw->line_no = dev_no; new_hw->cfg_type = hwcpu->hwcard->cfg_type; - if (hwcpu->hwcard->adptr_type == AFT_ADPTR_FLEXBRI) { - if (dev_no == 4) { - new_hw->cfg_type = WANOPT_AFT_ANALOG; - } else { - new_hw->cfg_type = WANOPT_AFT_ISDN; - } + + /* Hybrid cards */ + switch(hwcpu->hwcard->adptr_type) { + case AFT_ADPTR_FLEXBRI: + if (dev_no == 4) { + new_hw->cfg_type = WANOPT_AFT_ANALOG; + } else { + new_hw->cfg_type = WANOPT_AFT_ISDN; + } + break; + case AFT_ADPTR_B601: + if (dev_no == 1) { + new_hw->cfg_type = WANOPT_AFT601; + } else { + new_hw->cfg_type = WANOPT_AFT_ANALOG; + } + break; } + + hwcpu->internal_used++; hwcpu->max_lines_num++; @@ -4576,6 +4705,21 @@ void* sdla_register(sdlahw_iface_t* hw_iface, wandev_conf_t* conf, char* devname hw_iface->fe_write = sdla_a600_write_fe; hw_iface->reset_fe = sdla_a600_reset_fe; break; +#if defined(CONFIG_PRODUCT_WANPIPE_AFT_B601) + case AFT_ADPTR_B601: + if(hw->cfg_type == WANOPT_AFT_ANALOG) { + hw_iface->fe_read = sdla_a600_read_fe; + hw_iface->__fe_read = __sdla_a600_read_fe; + hw_iface->fe_write = sdla_a600_write_fe; + hw_iface->reset_fe = sdla_a600_reset_fe; + } else { + hw_iface->fe_read = sdla_b601_te1_read_fe; + hw_iface->__fe_read = __sdla_b601_te1_read_fe; + hw_iface->fe_write = sdla_b601_te1_write_fe; + } + break; +#endif + } switch(hwcard->adptr_type){ @@ -4593,7 +4737,9 @@ void* sdla_register(sdlahw_iface_t* hw_iface, wandev_conf_t* conf, char* devname case AFT_ADPTR_2SERIAL_RS232: case AFT_ADPTR_4SERIAL_RS232: case AFT_ADPTR_A600: - +#if defined(CONFIG_PRODUCT_WANPIPE_AFT_B601) + case AFT_ADPTR_B601: +#endif DEBUG_EVENT("%s: Found: %s card, CPU %c, PciBus=%d, PciSlot=%d, Port=%d\n", devname, SDLA_DECODE_CARDTYPE(hwcard->cfg_type), @@ -4640,7 +4786,7 @@ void* sdla_register(sdlahw_iface_t* hw_iface, wandev_conf_t* conf, char* devname break; #endif default: - DEBUG_EVENT("%s:%d: %s: (2) ERROR, invalid card type! 0x%X\n", + DEBUG_ERROR("%s:%d: %s: (2) ERROR, invalid card type! 0x%X\n", __FUNCTION__,__LINE__, devname, hwcard->cfg_type); @@ -4729,6 +4875,13 @@ int sdla_unregister(void** p_hw, char* devname) hw->hwport[1].used--; } } + if (hwcpu->hwcard->adptr_type == AFT_ADPTR_B601) { + if (hw->cfg_type == WANOPT_AFT601) { + /* T1/E1 port of B601 */ + hw->hwport[1].used--; + } + } + /* Clear bit for specific port */ wan_clear_bit(hw->line_no, &hw->hwcpu->reg_line_map); @@ -4739,7 +4892,6 @@ int sdla_unregister(void** p_hw, char* devname) if (!hw->used){ hw->devname = SDLA_HWPROBE_NAME; } - *p_hw = NULL; return 0; } @@ -4922,6 +5074,18 @@ static int sdla_register_check (wandev_conf_t* conf, char* devname) return -EINVAL; } break; +#if defined(CONFIG_PRODUCT_WANPIPE_AFT_B601) + case WANOPT_AFT601: + if (conf->auto_hw_detect && sdla_adapter_cnt.aft_b601_adapters > 1){ + DEBUG_EVENT( "%s: HW Auto PCI failed: Multiple AFT-A601 cards found! \n" + "%s: Disable the Autodetect feature and supply\n" + "%s: the PCI Slot and Bus numbers for each card.\n", + devname,devname,devname); + return -EINVAL; + } + break; +#endif + #if defined(CONFIG_PRODUCT_WANPIPE_USB) case WANOPT_USB_ANALOG: if (conf->auto_hw_detect && sdla_adapter_cnt.usb_adapters > 1){ @@ -4992,7 +5156,7 @@ static int sdla_setup (void* phw, wandev_conf_t* conf) case SDLA_S514: if (conf && !conf->S514_CPU_no[0]){ - DEBUG_EVENT("%s: ERROR, invalid S514 CPU [ A|B ]\n", + DEBUG_ERROR("%s: ERROR, invalid S514 CPU [ A|B ]\n", hw->devname); return -EINVAL; } @@ -5058,6 +5222,9 @@ static int sdla_setup (void* phw, wandev_conf_t* conf) case AFT_ADPTR_2SERIAL_RS232: case AFT_ADPTR_4SERIAL_RS232: case AFT_ADPTR_A600: +#if defined(CONFIG_PRODUCT_WANPIPE_AFT_B601) + case AFT_ADPTR_B601: +#endif if (hwcpu->used > 1){ if (conf) conf->irq = hwcpu->irq; return 0; @@ -5215,7 +5382,7 @@ static int sdla_setup (void* phw, wandev_conf_t* conf) } hwcpu->memory = sdla_test_memregion(hw, MAX_SIZEOF_S514_MEMORY); if(hwcpu->memory < (256 * 1024)) { - DEBUG_EVENT("%s: error in testing S514 memory (0x%lX)\n", + DEBUG_ERROR("%s: error in testing S514 memory (0x%lX)\n", hw->devname, hwcpu->memory); sdla_down(hw); return -EINVAL; @@ -5751,6 +5918,10 @@ static int sdla_down (void* phw) case AFT_ADPTR_2SERIAL_RS232: case AFT_ADPTR_4SERIAL_RS232: case AFT_ADPTR_A600: +#if defined(CONFIG_PRODUCT_WANPIPE_AFT_B601) + case AFT_ADPTR_B601: +#endif + if (hwcpu->used > 1){ break; } @@ -6075,7 +6246,7 @@ int sdla_cmd (void* phw, unsigned long offset, wan_mbox_t* mbox) sdla_peek(hw, offset+offsetof(wan_mbox_t, wan_data), mbox->wan_data, mbox->wan_data_len); }else{ - DEBUG_EVENT("%s: Error: sdla_cmd() data_len=%d\n", + DEBUG_ERROR("%s: Error: sdla_cmd() data_len=%d\n", hw->devname,mbox->wan_data_len); return WAN_CMD_TIMEOUT; } @@ -6437,7 +6608,7 @@ static int sdla_init_pci_slot(sdlahw_t *hw) sdla_read_int_stat(hw,&int_status); sdla_intack(hw,int_status); if (i == MAX_S514_CARDS){ - DEBUG_EVENT( "%s: Critical Error !!!\n",hw->devname); + DEBUG_ERROR( "%s: Critical Error !!!\n",hw->devname); DEBUG_EVENT( "%s: Number of Sangoma PCI cards exceeded maximum limit.\n", hw->devname); @@ -7373,7 +7544,7 @@ static int sdla_detect_s514 (sdlahw_t* hw) sdla_pci_write_config_dword(hw, PCI_INT_CONFIG, ut_u32); if (sdla_pci_enable_device(hw)){ - DEBUG_EVENT( "%s: Error: S514 PCI enable failed\n", + DEBUG_ERROR( "%s: Error: S514 PCI enable failed\n", hw->devname); return -EINVAL; } @@ -7471,7 +7642,7 @@ static int sdla_detect_pulsar(sdlahw_t* hw) #if 0 if (sdla_pci_enable_device(hw)){ - DEBUG_EVENT( "%s: Error: S518 ADSL PCI enable failed\n", + DEBUG_ERROR( "%s: Error: S518 ADSL PCI enable failed\n", hw->devname); return -EINVAL; } @@ -7484,7 +7655,7 @@ static int sdla_detect_pulsar(sdlahw_t* hw) if (sdla_request_mem_region(hw, hw->mem_base_addr, GSI_PCI_MEMORY_SIZE, "WANPIPE ADSL", &presource)){ - DEBUG_EVENT("%s: Error: Unable to reserve S518 ADSL pci bar0 memory\n", + DEBUG_ERROR("%s: Error: Unable to reserve S518 ADSL pci bar0 memory\n", hw->devname); return -EINVAL; } @@ -7496,7 +7667,7 @@ static int sdla_detect_pulsar(sdlahw_t* hw) /* map the physical PCI memory to virtual memory */ sdla_bus_space_map(hw, 0x0, GSI_PCI_MEMORY_SIZE, &hw->dpmbase); if(!hw->dpmbase) { - DEBUG_EVENT( "%s: Error: S518 ADSL PCI virtual memory ioremap failed\n", + DEBUG_ERROR( "%s: Error: S518 ADSL PCI virtual memory ioremap failed\n", hw->devname); return -EINVAL; } @@ -7553,6 +7724,9 @@ static int sdla_memory_map(sdlahw_t* hw) case A400_ADPTR_ANALOG: case AFT_ADPTR_ISDN: case AFT_ADPTR_A600: +#if defined(CONFIG_PRODUCT_WANPIPE_AFT_B601) + case AFT_ADPTR_B601: +#endif case AFT_ADPTR_FLEXBRI: hwcpu->memory = AFT4_PCI_MEM_SIZE; break; @@ -7579,7 +7753,7 @@ static int sdla_memory_map(sdlahw_t* hw) #endif default: - DEBUG_EVENT("%s:%d Error: Invalid hw adapter type (0x%X)\n", + DEBUG_ERROR("%s:%d Error: Invalid hw adapter type (0x%X)\n", __FUNCTION__,__LINE__,hwcard->type); return -EINVAL; } @@ -7593,10 +7767,10 @@ static int sdla_memory_map(sdlahw_t* hw) if (!hwcpu->mem_base_addr){ if(hwcpu->cpu_no == SDLA_CPU_B){ - DEBUG_EVENT( "%s: Error: CPU #B not present on adapter\n", + DEBUG_ERROR( "%s: Error: CPU #B not present on adapter\n", hw->devname); }else{ - DEBUG_EVENT( "%s: Error: Failed to allocate PCI memory on AFT/ADSL card\n", + DEBUG_ERROR( "%s: Error: Failed to allocate PCI memory on AFT/ADSL card\n", hw->devname); } err = -EINVAL; @@ -7604,7 +7778,7 @@ static int sdla_memory_map(sdlahw_t* hw) } if (sdla_pci_enable_device(hw)){ - DEBUG_EVENT( "%s: Error: AFT PCI enable failed\n", + DEBUG_ERROR( "%s: Error: AFT PCI enable failed\n", hw->devname); return -EINVAL; } @@ -7623,7 +7797,7 @@ static int sdla_memory_map(sdlahw_t* hw) #endif if (err){ - DEBUG_EVENT("%s: Error: Unable to reserve AFT pci bar%i memory\n", + DEBUG_ERROR("%s: Error: Unable to reserve AFT pci bar%i memory\n", hw->devname,(hwcpu->cpu_no == SDLA_CPU_A)?0:1); err = -EINVAL; @@ -7634,7 +7808,7 @@ static int sdla_memory_map(sdlahw_t* hw) #if defined(__LINUX__) && defined(DMA_32BIT_MASK) if((err = pci_set_dma_mask(hwcard->u_pci.pci_dev, DMA_32BIT_MASK))) { - DEBUG_EVENT("%s: Error: No usable DMA configuration, aborting.\n", + DEBUG_ERROR("%s: Error: No usable DMA configuration, aborting.\n", hw->devname); err = -EINVAL; goto aft_pci_error; @@ -7649,10 +7823,10 @@ static int sdla_memory_map(sdlahw_t* hw) &hwcpu->mem_base_addr); if (!hwcpu->mem_base_addr){ if(hwcpu->cpu_no == SDLA_CPU_B){ - DEBUG_EVENT( "%s: Error: CPU #B not present on adapter\n", + DEBUG_ERROR( "%s: Error: CPU #B not present on adapter\n", hw->devname); }else{ - DEBUG_EVENT( "%s: Error: Failed to allocate PCI memory on AFT/ADSL card\n", + DEBUG_ERROR( "%s: Error: Failed to allocate PCI memory on AFT/ADSL card\n", hw->devname); } err = -EINVAL; @@ -7664,7 +7838,7 @@ static int sdla_memory_map(sdlahw_t* hw) /* map the physical PCI memory to virtual memory */ sdla_bus_space_map(hw, 0x0, hwcpu->memory, &hwcpu->dpmbase); if (!hwcpu->dpmbase){ - DEBUG_EVENT( "%s: Error: AFT PCI virtual memory ioremap failed\n", + DEBUG_ERROR( "%s: Error: AFT PCI virtual memory ioremap failed\n", hw->devname); err = -EINVAL; goto aft_pci_error; @@ -7781,7 +7955,7 @@ static int sdla_detect_aft(sdlahw_t* hw) /* Latency important only for PCI. PCI Express ignores it. */ /* Set PCI Latency of 0xFF */ - sdla_pci_write_config_word(hw, XILINX_PCI_LATENCY_REG/* 0xD */, 0xFF); + sdla_pci_write_config_byte(hw, XILINX_PCI_LATENCY_REG/* 0xD */, 0xFF); return 0; } @@ -8080,10 +8254,22 @@ adapter_found: conf->comm_port = 0; conf->fe_cfg.line_no = 0; break; +#if defined (CONFIG_PRODUCT_WANPIPE_AFT_B601) + case AFT_ADPTR_B601: + if(conf->card_type == WANOPT_AFT_ANALOG) { + conf->comm_port = 0; + conf->fe_cfg.line_no = 0; + } else { + conf->comm_port = 1; + conf->fe_cfg.line_no = 1; + } + break; +#endif + #if defined(CONFIG_PRODUCT_WANPIPE_AFT_BRI) case AFT_ADPTR_ISDN: if (conf->fe_cfg.line_no < 1 || conf->fe_cfg.line_no > MAX_BRI_LINES){ - DEBUG_EVENT("%s: Error, Invalid ISDN port selected %d (Min=1 Max=%d)\n", + DEBUG_ERROR("%s: Error, Invalid ISDN port selected %d (Min=1 Max=%d)\n", devname, conf->fe_cfg.line_no, MAX_BRI_LINES); return NULL; } @@ -8096,7 +8282,7 @@ adapter_found: if (conf->card_type == WANOPT_AFT_ISDN) { /* This is for the BRI ports of the A700 */ if (conf->fe_cfg.line_no < 1 || conf->fe_cfg.line_no > A700_MAX_BRI_LINES){ - DEBUG_EVENT("%s: Error, Invalid port selected %d (Min=1 Max=%d)\n", + DEBUG_ERROR("%s: Error, Invalid port selected %d (Min=1 Max=%d)\n", devname, conf->fe_cfg.line_no, A700_MAX_BRI_LINES); return NULL; } @@ -8120,7 +8306,7 @@ adapter_found: break; } if (conf->fe_cfg.line_no < 1 || conf->fe_cfg.line_no > 2){ - DEBUG_EVENT("%s: Error, Invalid T1/E1 port selected %d (Min=1 Max=2)\n", + DEBUG_ERROR("%s: Error, Invalid T1/E1 port selected %d (Min=1 Max=2)\n", devname, conf->fe_cfg.line_no); return NULL; } @@ -8129,7 +8315,7 @@ adapter_found: break; case A104_ADPTR_4TE1: if (conf->fe_cfg.line_no < 1 || conf->fe_cfg.line_no > 4){ - DEBUG_EVENT("%s: Error, Invalid T1/E1 port selected %d (Min=1 Max=4)\n", + DEBUG_ERROR("%s: Error, Invalid T1/E1 port selected %d (Min=1 Max=4)\n", devname, conf->fe_cfg.line_no); return NULL; } @@ -8138,7 +8324,7 @@ adapter_found: break; case A108_ADPTR_8TE1: if (conf->fe_cfg.line_no < 1 || conf->fe_cfg.line_no > 8){ - DEBUG_EVENT("%s: Error, Invalid T1/E1 port selected %d (Min=1 Max=8)\n", + DEBUG_ERROR("%s: Error, Invalid T1/E1 port selected %d (Min=1 Max=8)\n", devname, conf->fe_cfg.line_no); return NULL; } @@ -8148,7 +8334,7 @@ adapter_found: case AFT_ADPTR_2SERIAL_V35X21: case AFT_ADPTR_2SERIAL_RS232: if (conf->fe_cfg.line_no < 1 || conf->fe_cfg.line_no > 2){ - DEBUG_EVENT("%s: Error, Invalid Serial port selected %d (Min=1 Max=2)\n", + DEBUG_ERROR("%s: Error, Invalid Serial port selected %d (Min=1 Max=2)\n", devname, conf->fe_cfg.line_no); return NULL; } @@ -8158,7 +8344,7 @@ adapter_found: case AFT_ADPTR_4SERIAL_V35X21: case AFT_ADPTR_4SERIAL_RS232: if (conf->fe_cfg.line_no < 1 || conf->fe_cfg.line_no > 4){ - DEBUG_EVENT("%s: Error, Invalid Serial port selected %d (Min=1 Max=4)\n", + DEBUG_ERROR("%s: Error, Invalid Serial port selected %d (Min=1 Max=4)\n", devname, conf->fe_cfg.line_no); return NULL; } @@ -8348,7 +8534,7 @@ adapter_found: break; } }else{ - DEBUG_EVENT("%s: Error, Failed to find new Sangoma card!\n", + DEBUG_ERROR("%s: Error, Failed to find new Sangoma card!\n", devname); }/* if (conf) */ @@ -8478,7 +8664,7 @@ static int sdla_check_mismatch(void* phw, unsigned char media) if (hwcard->adptr_type != S5144_ADPTR_1_CPU_T1E1 && hwcard->adptr_type != S5147_ADPTR_2_CPU_T1E1 && hwcard->adptr_type != S5148_ADPTR_1_CPU_T1E1){ - DEBUG_EVENT("%s: Error: Card type mismatch: User=T1/E1 Actual=%s\n", + DEBUG_ERROR("%s: Error: Card type mismatch: User=T1/E1 Actual=%s\n", hw->devname, hwcard->adptr_name); return -EIO; @@ -8487,7 +8673,7 @@ static int sdla_check_mismatch(void* phw, unsigned char media) }else if (media == WAN_MEDIA_56K){ if (hwcard->adptr_type != S5145_ADPTR_1_CPU_56K){ - DEBUG_EVENT("%s: Error: Card type mismatch: User=56K Actual=%s\n", + DEBUG_ERROR("%s: Error: Card type mismatch: User=56K Actual=%s\n", hw->devname, hwcard->adptr_name); return -EIO; @@ -8497,7 +8683,7 @@ static int sdla_check_mismatch(void* phw, unsigned char media) hwcard->adptr_type == S5144_ADPTR_1_CPU_T1E1 || hwcard->adptr_type == S5147_ADPTR_2_CPU_T1E1 || hwcard->adptr_type == S5148_ADPTR_1_CPU_T1E1){ - DEBUG_EVENT("%s: Error: Card type mismatch: User=S514(1/2/3) Actual=%s\n", + DEBUG_ERROR("%s: Error: Card type mismatch: User=S514(1/2/3) Actual=%s\n", hw->devname, hwcard->adptr_name); return -EIO; @@ -9906,7 +10092,7 @@ static int sdla_busdma_alloc(void *phw, wan_dma_descr_t *dma_descr) dma_descr->max_len, &virtual_addr, &physical_addr)){ - DEBUG_EVENT("%s(): %s: Error: failed to allocate DMA memory!\n", + DEBUG_ERROR("%s(): %s: Error: failed to allocate DMA memory!\n", __FUNCTION__, hw->devname); return -ENOMEM; } @@ -10328,7 +10514,7 @@ static int sdla_hw_read_cpld(void *phw, u16 off, u8 *data) sdla_bus_write_2(hw, AFT_MCPU_INTERFACE_ADDR, org_off); break; default: - DEBUG_EVENT("%s: ERROR: Invalid read access to cpld (Normal)!\n", + DEBUG_ERROR("%s: ERROR: Invalid read access to cpld (Normal)!\n", hw->devname); return -EINVAL; } @@ -10350,7 +10536,7 @@ static int sdla_hw_read_cpld(void *phw, u16 off, u8 *data) sdla_bus_write_2(hw, AFT_MCPU_INTERFACE_ADDR, org_off); break; default: - DEBUG_EVENT("%s: ERROR: Invalid read access to cpld (PMC)!\n", + DEBUG_ERROR("%s: ERROR: Invalid read access to cpld (PMC)!\n", hw->devname); return -EINVAL; } @@ -10374,7 +10560,7 @@ static int sdla_hw_read_cpld(void *phw, u16 off, u8 *data) sdla_bus_write_2(hw, AFT_MCPU_INTERFACE_ADDR, org_off); break; default: - DEBUG_EVENT("%s: ERROR: Invalid read access to cpld (DS)!\n", + DEBUG_ERROR("%s: ERROR: Invalid read access to cpld (DS)!\n", hw->devname); return -EINVAL; } @@ -10437,14 +10623,14 @@ static int sdla_hw_read_cpld(void *phw, u16 off, u8 *data) break; default: - DEBUG_EVENT("%s: ERROR: Invalid read access to cpld (SHARK)!\n", + DEBUG_ERROR("%s: ERROR: Invalid read access to cpld (SHARK)!\n", hw->devname); return -EINVAL; } break; } }else{ - DEBUG_EVENT("%s: ERROR: Invalid read access to cpld!\n", + DEBUG_ERROR("%s: ERROR: Invalid read access to cpld!\n", hw->devname); return -EINVAL; } @@ -10489,7 +10675,7 @@ static int sdla_hw_write_cpld(void *phw, u16 off, u8 data) sdla_bus_write_2(hw, AFT_MCPU_INTERFACE_ADDR, org_off); break; default: - DEBUG_EVENT("%s: (line: %d) ERROR: Invalid write access to cpld!\n", + DEBUG_ERROR("%s: (line: %d) ERROR: Invalid write access to cpld!\n", hw->devname, __LINE__); return -EINVAL; } @@ -10511,7 +10697,7 @@ static int sdla_hw_write_cpld(void *phw, u16 off, u8 data) sdla_bus_write_2(hw, AFT_MCPU_INTERFACE_ADDR, org_off); break; default: - DEBUG_EVENT("%s: (%d)ERROR: Invalid write access to cpld!\n", + DEBUG_ERROR("%s: (%d)ERROR: Invalid write access to cpld!\n", hw->devname, __LINE__); return -EINVAL; } @@ -10536,7 +10722,7 @@ static int sdla_hw_write_cpld(void *phw, u16 off, u8 data) break; default: - DEBUG_EVENT("%s: (1)ERROR: Invalid write access to cpld!\n", + DEBUG_ERROR("%s: (1)ERROR: Invalid write access to cpld!\n", hw->devname); return -EINVAL; } @@ -10583,14 +10769,14 @@ static int sdla_hw_write_cpld(void *phw, u16 off, u8 data) break; default: - DEBUG_EVENT("%s: (line: %d)ERROR: Invalid write access to cpld!\n", + DEBUG_ERROR("%s: (line: %d)ERROR: Invalid write access to cpld!\n", hw->devname, __LINE__); return -EINVAL; } break; } }else{ - DEBUG_EVENT("%s: (line: %d)ERROR: Invalid write access to cpld!\n", + DEBUG_ERROR("%s: (line: %d)ERROR: Invalid write access to cpld!\n", hw->devname, __LINE__); return -EINVAL; } @@ -10763,6 +10949,25 @@ int sdla_usb_remove(struct usb_interface *intf, int force) } #endif +void _sdla_copy_hwinfo(hardware_info_t *hwinfo, sdlahw_t *hw, sdlahw_cpu_t *hwcpu) +{ + hwinfo->card_model = hwcpu->hwcard->adptr_type; + hwinfo->firmware_version = hwcpu->hwcard->core_rev; + hwinfo->pci_bus_number = hwcpu->hwcard->u_pci.bus_no; + hwinfo->pci_slot_number = hwcpu->hwcard->u_pci.slot_no; + hwinfo->max_hw_ec_chans = hwcpu->hwcard->hwec_chan_no; + hwinfo->port_number = hw->line_no+1; + hwinfo->chans_map = hw->chans_map; /* available tdm slots map */ + + hwinfo->max_chans_num = hw->max_chans_num; /* max possible number of tdm slots */ + hwinfo->fxo_map = hw->fxo_map; + hwinfo->fxs_map = hw->fxs_map; + hwinfo->bri_modtype = hw->bri_modtype; + return; +} + +/* Note: Under Windows _sdla_copy_hwinfo() is called directly + * by external code. */ EXPORT_SYMBOL(sdla_get_hwinfo); int sdla_get_hwinfo(hardware_info_t *hwinfo, int card_no) { @@ -10789,12 +10994,9 @@ int sdla_get_hwinfo(hardware_info_t *hwinfo, int card_no) } } - hwinfo->card_model = hwcpu->hwcard->adptr_type; - hwinfo->firmware_version = hwcpu->hwcard->core_rev; - hwinfo->pci_bus_number = hwcpu->hwcard->u_pci.bus_no; - hwinfo->pci_slot_number = hwcpu->hwcard->u_pci.slot_no; - hwinfo->max_hw_ec_chans = hwcpu->hwcard->hwec_chan_no; - hwinfo->port_number = hw->line_no+1; + _sdla_copy_hwinfo(hwinfo, hw, hwcpu); + + /* S514 not supported under Windows --> 'sec_port' can be ignored. */ if (sec_port) { hwinfo->port_number++; } @@ -10877,9 +11079,10 @@ int sdla_hwdev_register_bri(sdlahw_cpu_t *hwcpu, sdlahw_t **first_hw_p, int *rm_ hw->adptr_type = AFT_ADPTR_ISDN; hw->chans_map = 0x03; /* 2 BRI bchans */ hw->max_chans_num = 2; + hw->bri_modtype = rm_mod_type_bri[mod_no]; hwcpu->lines_info[AFT_ADPTR_ISDN].total_line_no++; } - hwcpu->lines_info[AFT_ADPTR_ISDN].line_map = line_map; + hwcpu->lines_info[AFT_ADPTR_ISDN].line_map = line_map; return 0; } @@ -10889,7 +11092,7 @@ int sdla_hwdev_register_analog(sdlahw_cpu_t *hwcpu, sdlahw_t **first_hw_p, int * { int mod_no; sdlahw_t *hw; - u32 chans_map = 0x00; + u32 chans_map, fxo_map, fxs_map; unsigned char str[50]; unsigned char str_dump[50]; int off=0; @@ -10923,6 +11126,9 @@ int sdla_hwdev_register_analog(sdlahw_cpu_t *hwcpu, sdlahw_t **first_hw_p, int * } chans_map = 0x00; + fxo_map = 0x00; + fxs_map = 0x00; + for(mod_no = 0; mod_no < max_analog_lines; mod_no++) { if (rm_mod_type_analog[mod_no] == MOD_TYPE_FXS) { sprintf(str, "\n+%02d:FXS: %s: %s", @@ -10931,22 +11137,26 @@ int sdla_hwdev_register_analog(sdlahw_cpu_t *hwcpu, sdlahw_t **first_hw_p, int * sprintf(&hw->hwport[hw->max_port_no-1].hwprobe->hw_info_dump[strlen(hw->hwport[hw->max_port_no-1].hwprobe->hw_info_dump)], "|FE=%s","FXS"); chans_map |= (1 << (mod_no+1)); + fxs_map |= (1 << (mod_no+1)); } else if (rm_mod_type_analog[mod_no] == MOD_TYPE_FXO) { sprintf(str, "\n+%02d:FXO: %s: %s", mod_no+1, AFT_PCITYPE_DECODE(hwcpu->hwcard), AFT_PCIBRIDGE_DECODE(hwcpu->hwcard)); sprintf(&hw->hwport[hw->max_port_no-1].hwprobe->hw_info_dump[strlen(hw->hwport[hw->max_port_no-1].hwprobe->hw_info_dump)], "|FE=%s","FXO"); chans_map |= (1 << (mod_no+1)); + fxo_map |= (1 << (mod_no+1)); }else{ sprintf(str, "\n+%02d:EMPTY", mod_no+1); sprintf(&hw->hwport[hw->max_port_no-1].hwprobe->hw_info_dump[strlen(hw->hwport[hw->max_port_no-1].hwprobe->hw_info_dump)], "|FE=%s","EMPTY"); } - hw->max_chans_num++; + memcpy(&hw->hwport[hw->max_port_no-1].hwprobe->hw_info_verbose[off], str, strlen(str)); off += strlen(str); } hw->adptr_type = A200_ADPTR_ANALOG; hw->chans_map = chans_map; + hw->fxo_map = fxo_map; + hw->fxs_map = fxs_map; hw->max_chans_num = max_analog_lines; hwcpu->lines_info[A200_ADPTR_ANALOG].total_line_no = 1; return 0; diff --git a/patches/kdrivers/src/net/sdladrv_fe.c b/patches/kdrivers/src/net/sdladrv_fe.c index d4cae22..8e8c47a 100644 --- a/patches/kdrivers/src/net/sdladrv_fe.c +++ b/patches/kdrivers/src/net/sdladrv_fe.c @@ -46,10 +46,19 @@ #if defined(WAN_DEBUG_FE) -# warning "WAN_DEBUG_FE - Debugging Enabled" +# if defined(__WINDOWS__) +# pragma message("WAN_DEBUG_FE - Debugging Enabled") +# else +# warning "WAN_DEBUG_FE - Debugging Enabled" +# endif #endif + #if defined(WAN_DEBUG_REG) +# if defined(__WINDOWS__) +# pragma message("WAN_DEBUG_REG - Debugging Enabled") +# else # warning "WAN_DEBUG_REG - Debugging Enabled" +# endif #endif @@ -398,7 +407,7 @@ int sdla_shark_te1_write_fe (void *phw, ...) WAN_ASSERT(hw->magic != SDLADRV_MAGIC); if (sdla_hw_fe_test_and_set_bit(hw,0)){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", hw->devname, __FUNCTION__,__LINE__); } return -EINVAL; @@ -479,7 +488,7 @@ u_int8_t sdla_shark_te1_read_fe (void *phw, ...) WAN_ASSERT(hw->magic != SDLADRV_MAGIC); if (sdla_hw_fe_test_and_set_bit(hw,0)){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", hw->devname, __FUNCTION__,__LINE__); } return 0x00; @@ -598,7 +607,7 @@ u_int8_t sdla_shark_56k_read_fe (void *phw, ...) WAN_ASSERT(hw->magic != SDLADRV_MAGIC); if (sdla_hw_fe_test_and_set_bit(hw,0)){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", hw->devname, __FUNCTION__,__LINE__); } return 0x00; @@ -639,7 +648,7 @@ static int __sdla_a600_write_fe(void *phw, ...) value = va_arg(args, int); va_end(args); - if (chain) DEBUG_EVENT ("%s :%d Error: chain mode not supported on A600 (%s:%d)\n", + if (chain) DEBUG_ERROR ("%s :%d Error: chain mode not supported on A600 (%s:%d)\n", hw->devname, mod_no, __FUNCTION__,__LINE__); @@ -684,7 +693,7 @@ static int __sdla_a600_write_fe(void *phw, ...) } if (wan_test_bit(A600_SPI_REG_SPI_BUSY_BIT, &data)) { - DEBUG_EVENT("%s: ERROR:SPI Iface not ready\n", hw->devname); + DEBUG_ERROR("%s: ERROR:SPI Iface not ready\n", hw->devname); return -EINVAL; } @@ -712,7 +721,7 @@ int sdla_a600_write_fe(void *phw, ...) va_end(args); if (sdla_hw_fe_test_and_set_bit(hw,0)){ - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", hw->devname, __FUNCTION__,__LINE__); return -EINVAL; } @@ -745,7 +754,7 @@ u_int8_t __sdla_a600_read_fe (void *phw, ...) reg = va_arg(args, int); va_end(args); - if (chain) DEBUG_EVENT ("%s :%d Error: chain mode not supported on A600 (%s:%d)\n", + if (chain) DEBUG_ERROR ("%s :%d Error: chain mode not supported on A600 (%s:%d)\n", hw->devname, mod_no, __FUNCTION__,__LINE__); @@ -790,7 +799,7 @@ u_int8_t __sdla_a600_read_fe (void *phw, ...) spi_read_done: if (wan_test_bit(A600_SPI_REG_SPI_BUSY_BIT, &data)) { - DEBUG_EVENT("%s: ERROR:SPI Iface not ready\n", hw->devname); + DEBUG_ERROR("%s: ERROR:SPI Iface not ready\n", hw->devname); data = 0xFF; } @@ -814,7 +823,7 @@ u_int8_t sdla_a600_read_fe (void *phw, ...) va_end(args); if (sdla_hw_fe_test_and_set_bit(hw,0)){ - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", hw->devname, __FUNCTION__,__LINE__); return 0x00; } @@ -855,7 +864,7 @@ static int __sdla_a700_analog_write_fe (void* phw, ...) va_end(args); #if 0 if (!wan_test_bit(mod_no, card->fe.fe_param.remora.module_map)){ - DEBUG_EVENT("%s: %s:%d: Internal Error: Module %d\n", + DEBUG_ERROR("%s: %s:%d: Internal Error: Module %d\n", card->devname, __FUNCTION__,__LINE__,mod_no); return -EINVAL; } @@ -936,7 +945,7 @@ static int __sdla_a700_analog_write_fe (void* phw, ...) } if (data & MOD_SPI_BUSY) { - DEBUG_EVENT("%s: Module %d: Critical Error (%s:%d)!\n", + DEBUG_ERROR("%s: Module %d: Critical Error (%s:%d)!\n", hw->devname, mod_no, __FUNCTION__,__LINE__); return -EINVAL; @@ -969,10 +978,10 @@ int sdla_a700_analog_write_fe (void* phw, ...) if (sdla_hw_fe_test_and_set_bit(hw,0)){ #if defined(WAN_DEBUG_FE) - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE (%s:%d)!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE (%s:%d)!\n", hw->devname, __FUNCTION__,__LINE__, fname, fline); #else - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", hw->devname, __FUNCTION__,__LINE__); #endif return -EINVAL; @@ -1082,7 +1091,7 @@ u_int8_t __sdla_a700_analog_read_fe (void* phw, ...) } if (data & MOD_SPI_BUSY){ - DEBUG_EVENT("%s: Module %d: Critical Error (%s:%d)!\n", + DEBUG_ERROR("%s: Module %d: Critical Error (%s:%d)!\n", hw->devname, mod_no, __FUNCTION__,__LINE__); return 0xFF; @@ -1116,10 +1125,10 @@ u_int8_t sdla_a700_analog_read_fe (void* phw, ...) if (sdla_hw_fe_test_and_set_bit(hw,0)){ #if defined(WAN_DEBUG_FE) - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE (%s:%d)!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE (%s:%d)!\n", hw->devname, __FUNCTION__,__LINE__,fname,fline); #else - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", hw->devname, __FUNCTION__,__LINE__); #endif return 0x00; @@ -1253,7 +1262,7 @@ static int __sdla_shark_rm_write_fe (void* phw, ...) va_end(args); #if 0 if (!wan_test_bit(mod_no, card->fe.fe_param.remora.module_map)){ - DEBUG_EVENT("%s: %s:%d: Internal Error: Module %d\n", + DEBUG_ERROR("%s: %s:%d: Internal Error: Module %d\n", card->devname, __FUNCTION__,__LINE__,mod_no); return -EINVAL; } @@ -1372,7 +1381,7 @@ static int __sdla_shark_rm_write_fe (void* phw, ...) } if (data & MOD_SPI_BUSY) { - DEBUG_EVENT("%s: Module %d: Critical Error (%s:%d)!\n", + DEBUG_ERROR("%s: Module %d: Critical Error (%s:%d)!\n", hw->devname, mod_no, __FUNCTION__,__LINE__); return -EINVAL; @@ -1405,10 +1414,10 @@ int sdla_shark_rm_write_fe (void* phw, ...) if (sdla_hw_fe_test_and_set_bit(hw,0)){ #if defined(WAN_DEBUG_FE) - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE (%s:%d)!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE (%s:%d)!\n", hw->devname, __FUNCTION__,__LINE__, fname, fline); #else - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", hw->devname, __FUNCTION__,__LINE__); #endif return -EINVAL; @@ -1443,7 +1452,7 @@ u_int8_t __sdla_shark_rm_read_fe (void* phw, ...) va_end(args); #if 0 if (!wan_test_bit(mod_no, card->fe.fe_param.remora.module_map)){ - DEBUG_EVENT("%s: %s:%d: Internal Error: Module %d\n", + DEBUG_ERROR("%s: %s:%d: Internal Error: Module %d\n", card->devname, __FUNCTION__,__LINE__,mod_no); return 0x00; } @@ -1560,7 +1569,7 @@ u_int8_t __sdla_shark_rm_read_fe (void* phw, ...) } if (data & MOD_SPI_BUSY){ - DEBUG_EVENT("%s: Module %d: Critical Error (%s:%d)!\n", + DEBUG_ERROR("%s: Module %d: Critical Error (%s:%d)!\n", hw->devname, mod_no, __FUNCTION__,__LINE__); return 0xFF; @@ -1594,10 +1603,10 @@ u_int8_t sdla_shark_rm_read_fe (void* phw, ...) if (sdla_hw_fe_test_and_set_bit(hw,0)){ #if defined(WAN_DEBUG_FE) - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE (%s:%d)!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE (%s:%d)!\n", hw->devname, __FUNCTION__,__LINE__,fname,fline); #else - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", hw->devname, __FUNCTION__,__LINE__); #endif return 0x00; @@ -1750,10 +1759,10 @@ int sdla_shark_bri_write_fe (void* phw, ...) if (sdla_hw_fe_test_and_set_bit(hw,0)){ #if defined(WAN_DEBUG_FE) - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE (%s:%d)!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE (%s:%d)!\n", hw->devname, __FUNCTION__,__LINE__, fname, fline); #else - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", hw->devname, __FUNCTION__,__LINE__); #endif return -EINVAL; @@ -1955,10 +1964,10 @@ u_int8_t sdla_shark_bri_read_fe (void* phw, ...) if (sdla_hw_fe_test_and_set_bit(hw,0)){ #if defined(WAN_DEBUG_FE) - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE (%s:%d)!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE (%s:%d)!\n", hw->devname, __FUNCTION__,__LINE__,fname,fline); #else - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", hw->devname, __FUNCTION__,__LINE__); #endif return 0x00; @@ -2058,7 +2067,7 @@ u_int32_t sdla_shark_serial_read_fe(void *phw, ...) if (sdla_hw_fe_test_and_set_bit(hw,0)){ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + DEBUG_ERROR("%s: %s:%d: Critical Error: Re-entry in FE!\n", hw->devname, __FUNCTION__,__LINE__); } return 0x00; @@ -2074,3 +2083,144 @@ u_int32_t sdla_shark_serial_read_fe(void *phw, ...) sdla_hw_fe_clear_bit(hw,0); return value; } + +#if defined(CONFIG_PRODUCT_WANPIPE_AFT_B601) +static int __sdla_b601_te1_write_fe(void *phw, ...) +{ + sdlahw_t* hw = (sdlahw_t*)phw; + sdlahw_cpu_t *hwcpu; + sdlahw_card_t *hwcard; + va_list args; + int qaccess=0, line_no=0, off=0, value=0; + u16 data_hi, data_lo; + + WAN_ASSERT(hw == NULL); + WAN_ASSERT(hw->hwcpu == NULL); + WAN_ASSERT(hw->hwcpu->hwcard == NULL); + hwcpu = hw->hwcpu; + hwcard = hwcpu->hwcard; + va_start(args, phw); + qaccess = (u_int16_t)va_arg(args, int); + line_no = (u_int16_t)va_arg(args, int); + off = (u_int16_t)va_arg(args, int); + value = (u_int8_t)va_arg(args, int); + va_end(args); + + data_hi = 0x0000; + data_lo = 0x0000; + + if (off & 0x800) { + data_hi |= 0x2000; + } + if (off & 0x1000) { + data_hi |= 0x4000; + } + + data_hi |= (off & 0x7FF); + sdla_bus_write_2(hw, A600_MAXIM_INTERFACE_REG_ADD_HI, data_hi); + data_lo = (value & 0xFF); + sdla_bus_write_2(hw, A600_MAXIM_INTERFACE_REG_ADD_LO, data_lo); + + return 0; +} + +int sdla_b601_te1_write_fe(void *phw, ...) +{ + sdlahw_t *hw = (sdlahw_t*)phw; + va_list args; + int mod_no, type, chain, reg, value; +#if defined(WAN_DEBUG_FE) + char *fname; + int fline; +#endif + + WAN_ASSERT(hw->magic != SDLADRV_MAGIC); + va_start(args, phw); + mod_no = va_arg(args, int); + type = va_arg(args, int); + chain = va_arg(args, int); + reg = va_arg(args, int); + value = va_arg(args, int); + va_end(args); + + if (sdla_hw_fe_test_and_set_bit(hw,0)){ + DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + hw->devname, __FUNCTION__,__LINE__); + return -EINVAL; + } + + __sdla_b601_te1_write_fe(hw, mod_no, type, chain, reg, value); + + sdla_hw_fe_clear_bit(hw,0); + return 0; +} + +u_int8_t __sdla_b601_te1_read_fe (void *phw, ...) +{ + sdlahw_t* hw = (sdlahw_t*)phw; + sdlahw_cpu_t *hwcpu; + sdlahw_card_t *hwcard; + va_list args; + int qaccess=0, line_no=0, off=0; + u32 data_read; + u16 data_hi; + + WAN_ASSERT(hw == NULL); + WAN_ASSERT(hw->hwcpu == NULL); + WAN_ASSERT(hw->hwcpu->hwcard == NULL); + hwcpu = hw->hwcpu; + hwcard = hwcpu->hwcard; + va_start(args, phw); + qaccess = (u_int16_t)va_arg(args, int); + line_no = (u_int16_t)va_arg(args, int); + off = (u_int16_t)va_arg(args, int); + va_end(args); + WAN_ASSERT(qaccess != 0 && qaccess != 1); + + data_hi = 0x00; + if (off & 0x800) { + data_hi |= 0x2000; + } + if (off & 0x1000) { + data_hi |= 0x4000; + } + + data_hi |= (off & 0x7FF); + sdla_bus_write_2(hw, A600_MAXIM_INTERFACE_REG_ADD_HI, data_hi); + data_read = 0x00; + sdla_bus_read_4(hw, A600_MAXIM_INTERFACE_REG_ADD_LO, &data_read); + + return (data_read & 0xFF); +} + +u_int8_t sdla_b601_te1_read_fe (void *phw, ...) +{ + sdlahw_t *hw = (sdlahw_t*)phw; + va_list args; + int mod_no, type, chain, reg; + unsigned char data = 0; + + WAN_ASSERT(hw->magic != SDLADRV_MAGIC); + va_start(args, phw); + mod_no = va_arg(args, int); + type = va_arg(args, int); + chain = va_arg(args, int); + reg = va_arg(args, int); + va_end(args); + + if (sdla_hw_fe_test_and_set_bit(hw,0)){ + DEBUG_EVENT("%s: %s:%d: Critical Error: Re-entry in FE!\n", + hw->devname, __FUNCTION__,__LINE__); + return 0x00; + } + + data = __sdla_b601_te1_read_fe (hw, mod_no, type, chain, reg); + + sdla_hw_fe_clear_bit(hw,0); + return data; +} + +#endif + + + diff --git a/patches/kdrivers/src/net/sdladrv_usb.c b/patches/kdrivers/src/net/sdladrv_usb.c index edc97ff..32cbf36 100644 --- a/patches/kdrivers/src/net/sdladrv_usb.c +++ b/patches/kdrivers/src/net/sdladrv_usb.c @@ -31,7 +31,6 @@ # include # include # include -# include # include # include # include @@ -43,6 +42,12 @@ # include #endif +#if defined(CONFIG_PRODUCT_WANPIPE_USB) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) +# include +#else +# define MAX_NUM_PORTS 8 //This is normally defined in /linux/usb/serial.h +#endif /*************************************************************************** **** M A C R O S D E F I N E S **** ***************************************************************************/ @@ -210,7 +215,11 @@ extern int sdla_usb_remove(struct usb_interface*, int); static int sdla_usb_probe(struct usb_interface*, const struct usb_device_id*); static void sdla_usb_disconnect(struct usb_interface*); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) static int sdla_usb_suspend (struct usb_interface*, pm_message_t); +#else +static int sdla_usb_suspend (struct usb_interface*, u32 msg); +#endif static int sdla_usb_resume (struct usb_interface*); #if 0 static void sdla_usb_prereset (struct usb_interface*); @@ -361,7 +370,7 @@ static int sdla_usb_probe(struct usb_interface *intf, const struct usb_device_id SDLA_USB_NAME, desc->name, desc->adptr_type, udev->devnum); if (sdla_usb_create(intf, desc->adptr_type)){ - DEBUG_EVENT("ERROR: %s: Failed to creae hwcard structures\n", + DEBUG_ERROR("ERROR: %s: Failed to creae hwcard structures\n", SDLA_USB_NAME); return -ENODEV; } @@ -386,6 +395,7 @@ static void sdla_usb_disconnect(struct usb_interface *intf) return; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) static int sdla_usb_suspend (struct usb_interface *intf, pm_message_t message) { struct usb_device *dev = interface_to_usbdev(intf); @@ -393,6 +403,15 @@ static int sdla_usb_suspend (struct usb_interface *intf, pm_message_t message) SDLA_USB_NAME, dev->devnum); return 0; } +#else +static int sdla_usb_suspend (struct usb_interface *intf, u32 msg) +{ + struct usb_device *dev = interface_to_usbdev(intf); + DEBUG_EVENT("%s: Suspend USB device on %d (not implemented)!\n", + SDLA_USB_NAME, dev->devnum); + return 0; +} +#endif static int sdla_usb_resume (struct usb_interface *intf) { @@ -1798,7 +1817,7 @@ sdla_usb_set_config(sdlahw_t *hw, u8 request, unsigned int *data, int size) buf = kmalloc(length * sizeof(__le32), GFP_KERNEL); if (!buf) { - DEBUG_EVENT("ERROR [%s:%d]: Out of memory!\n", + DEBUG_ERROR("ERROR [%s:%d]: Out of memory!\n", __FUNCTION__,__LINE__); return -ENOMEM; } @@ -1822,7 +1841,7 @@ sdla_usb_set_config(sdlahw_t *hw, u8 request, unsigned int *data, int size) kfree(buf); if ((size > 2 && result != size) || result < 0) { - DEBUG_EVENT("ERROR [%s:%d]: Unable to send request: request=0x%x size=%d result=%d!\n", + DEBUG_ERROR("ERROR [%s:%d]: Unable to send request: request=0x%x size=%d result=%d!\n", __FUNCTION__,__LINE__, request, size, result); return -EPROTO; } @@ -1862,7 +1881,7 @@ sdla_usb_get_config(sdlahw_t *hw, u8 request, unsigned int *data, int size) buf = kcalloc(length, sizeof(__le32), GFP_KERNEL); if (!buf) { - DEBUG_EVENT("ERROR [%s:%d]: Out of memory.\n", __FUNCTION__,__LINE__); + DEBUG_ERROR("ERROR [%s:%d]: Out of memory.\n", __FUNCTION__,__LINE__); return -ENOMEM; } @@ -1927,7 +1946,7 @@ int sdla_usb_setup(sdlahw_t *hw) for(x = 0; x < WP_USB_MAX_RX_CMD_QLEN; x++){ skb = wan_skb_alloc(10); if (!skb){ - DEBUG_EVENT("%s: ERROR: Failed to allocate RX cmd buffer!\n", + DEBUG_ERROR("%s: ERROR: Failed to allocate RX cmd buffer!\n", hw->devname); goto cleanup; } @@ -1939,7 +1958,7 @@ int sdla_usb_setup(sdlahw_t *hw) for(x = 0; x < WP_USB_MAX_RX_CMD_QLEN; x++){ skb = wan_skb_alloc(10); if (!skb){ - DEBUG_EVENT("%s: ERROR: Failed to allocate TX cmd buffer!\n", + DEBUG_ERROR("%s: ERROR: Failed to allocate TX cmd buffer!\n", hw->devname); goto cleanup; } @@ -1958,7 +1977,7 @@ int sdla_usb_setup(sdlahw_t *hw) wan_spin_lock_irq_init(&hwcard->u_usb.lock,"usb_bh_lock"); hwcard->u_usb.ctrl_idle_pattern = WP_USB_CTRL_IDLE_PATTERN; if (sdla_usb_set_config_single(hw, CP2101_UART, UART_ENABLE)) { - DEBUG_EVENT("%s: ERROR: Unable to enable UART!\n", + DEBUG_ERROR("%s: ERROR: Unable to enable UART!\n", hw->devname); goto cleanup; } @@ -2136,3 +2155,5 @@ int sdla_usb_down(sdlahw_t *hw, int force) usb_set_intfdata(hwcard->u_usb.usb_intf, NULL); return 0; } + +#endif /* #if defined(CONFIG_PRODUCT_WANPIPE_USB) */ diff --git a/patches/kdrivers/src/net/sdladrv_utils.c b/patches/kdrivers/src/net/sdladrv_utils.c index 02201d5..0d85f34 100644 --- a/patches/kdrivers/src/net/sdladrv_utils.c +++ b/patches/kdrivers/src/net/sdladrv_utils.c @@ -134,7 +134,7 @@ static int sdla_plx_EE_waitidle(void *phw) } WP_DELAY(1000); } - DEBUG_EVENT("%s: ERROR: EEPROM Busy timeout!\n", + DEBUG_ERROR("%s: ERROR: EEPROM Busy timeout!\n", hw->devname); return SDLA_PLXE_MASK_BUSY; } @@ -172,7 +172,7 @@ static int sdla_plx_EE_readbyte(void *phw, unsigned char *data) } } if (eeCtl & SDLA_PLXE_MASK_BYTE_READ_START){ - DEBUG_EVENT("%s: ERROR: Timeout on PLX READ!\n", + DEBUG_ERROR("%s: ERROR: Timeout on PLX READ!\n", hw->devname); return -EINVAL; } @@ -206,7 +206,7 @@ static int sdla_plx_EE_writebyte(void *phw, unsigned char val) } } if (eeCtl & SDLA_PLXE_MASK_BYTE_WRITE_START){ - DEBUG_EVENT("%s: ERROR: Timeout on PLX write!\n", + DEBUG_ERROR("%s: ERROR: Timeout on PLX write!\n", hw->devname); return -EINVAL; } diff --git a/patches/kdrivers/src/net/sdlamain.c b/patches/kdrivers/src/net/sdlamain.c index f4a4d27..46fe7c2 100644 --- a/patches/kdrivers/src/net/sdlamain.c +++ b/patches/kdrivers/src/net/sdlamain.c @@ -951,6 +951,10 @@ static int setup (wan_device_t* wandev, wandev_conf_t* conf) DEBUG_EVENT("%s: Starting AFT B600 Hardware Init.\n", card->devname); err = wp_aft_a600_init(card,conf); + } else if (card->adptr_type == AFT_ADPTR_B601) { + DEBUG_EVENT("%s: Starting AFT B601 Hardware Init.\n", + card->devname); + err = wp_aft_a600_init(card,conf); } else { DEBUG_EVENT("%s: Starting AFT Analog Hardware Init.\n", card->devname); diff --git a/patches/kdrivers/src/net/wan_mem_debug.c b/patches/kdrivers/src/net/wan_mem_debug.c index 26e6583..9a30e9c 100644 --- a/patches/kdrivers/src/net/wan_mem_debug.c +++ b/patches/kdrivers/src/net/wan_mem_debug.c @@ -33,18 +33,30 @@ typedef struct sdla_memdbg_el WAN_LIST_ENTRY(sdla_memdbg_el) next; }sdla_memdbg_el_t; +#if defined(__WINDOWS__) +int __sdla_memdbg_init(void); +int __sdla_memdbg_free(void); +#else int sdla_memdbg_init(void); int sdla_memdbg_free(void); +#endif +#if defined(__WINDOWS__) +int __sdla_memdbg_init(void) +#else int sdla_memdbg_init(void) +#endif { wan_spin_lock_init(&wan_debug_mem_lock,"wan_debug_mem_lock"); WAN_LIST_INIT(&sdla_memdbg_head); return 0; } - +#if defined(__WINDOWS__) +int __sdla_memdbg_push(void *mem, const char *func_name, const int line, int len) +#else int sdla_memdbg_push(void *mem, const char *func_name, const int line, int len) +#endif { sdla_memdbg_el_t *sdla_mem_el = NULL; wan_smp_flag_t flags; @@ -90,7 +102,11 @@ int sdla_memdbg_push(void *mem, const char *func_name, const int line, int len) } EXPORT_SYMBOL(sdla_memdbg_push); +#if defined(__WINDOWS__) +int __sdla_memdbg_pull(void *mem, const char *func_name, const int line) +#else int sdla_memdbg_pull(void *mem, const char *func_name, const int line) +#endif { sdla_memdbg_el_t *sdla_mem_el; wan_smp_flag_t flags; @@ -138,7 +154,7 @@ int sdla_memdbg_pull(void *mem, const char *func_name, const int line) } if (err) { - DEBUG_EVENT("%s:%d: Critical Error: Unknown Memory 0x%p\n", + DEBUG_ERROR("%s:%d: Critical Error: Unknown Memory 0x%p\n", __FUNCTION__,__LINE__,mem); } @@ -146,10 +162,15 @@ int sdla_memdbg_pull(void *mem, const char *func_name, const int line) } EXPORT_SYMBOL(sdla_memdbg_pull); +#if defined(__WINDOWS__) +int __sdla_memdbg_free(void) +#else int sdla_memdbg_free(void) +#endif { sdla_memdbg_el_t *sdla_mem_el; int total=0; + int leaked_buffer_counter=0; DEBUG_EVENT("sdladrv: Memory Still Allocated=%i \n", wan_debug_mem); @@ -164,6 +185,7 @@ int sdla_memdbg_free(void) sdla_mem_el->cmd_func,sdla_mem_el->line, sdla_mem_el->mem, sdla_mem_el->len); total+=sdla_mem_el->len; + leaked_buffer_counter++; sdla_mem_el = WAN_LIST_NEXT(sdla_mem_el, next); WAN_LIST_REMOVE(tmp, next); @@ -175,8 +197,8 @@ int sdla_memdbg_free(void) } DEBUG_EVENT("=====================END==================================\n"); - DEBUG_EVENT("sdladrv: Memory Still Allocated=%i Leaks Found=%i Missing=%i\n", - wan_debug_mem,total,wan_debug_mem-total); + DEBUG_EVENT("sdladrv: Memory Still Allocated=%i Leaks Found=%i Missing=%i leaked_buffer_counter=%i\n", + wan_debug_mem, total, wan_debug_mem - total, leaked_buffer_counter); return 0; } diff --git a/patches/kdrivers/src/net/wanpipe_abstr.c b/patches/kdrivers/src/net/wanpipe_abstr.c index 09fea5a..b029f23 100644 --- a/patches/kdrivers/src/net/wanpipe_abstr.c +++ b/patches/kdrivers/src/net/wanpipe_abstr.c @@ -573,9 +573,9 @@ void wpabs_debug_event(const char * fmt, ...) { #ifdef WAN_DEBUG_EVENT va_list args; - char buf[1024]; + char buf[1020]; va_start(args, fmt); - vsnprintf(buf, sizeof(buf), fmt, args); + wp_vsnprintf(buf, sizeof(buf), fmt, args); DEBUG_EVENT("%s", buf); va_end(args); #endif @@ -586,9 +586,9 @@ void __wpabs_debug_event(const char * fmt, ...) { #ifdef WAN_DEBUG_EVENT va_list args; - char buf[1024]; + char buf[1020]; va_start(args, fmt); - vsnprintf(buf, sizeof(buf), fmt, args); + wp_vsnprintf(buf, sizeof(buf), fmt, args); DEBUG_EVENT("%s", buf); va_end(args); #endif @@ -604,7 +604,7 @@ void wpabs_debug_test(const char * fmt, ...) va_list args; char buf[1024]; va_start(args, fmt); - vsnprintf(buf, sizeof(buf), fmt, args); + wp_vsnprintf(buf, sizeof(buf), fmt, args); DEBUG_TEST("%s", buf); va_end(args); #endif @@ -620,7 +620,7 @@ void wpabs_debug_cfg(const char * fmt, ...) va_list args; char buf[1024]; va_start(args, fmt); - vsnprintf(buf, sizeof(buf), fmt, args); + wp_vsnprintf(buf, sizeof(buf), fmt, args); DEBUG_CFG("%s",buf); va_end(args); #endif @@ -635,7 +635,7 @@ void wpabs_debug_init(const char * fmt, ...) va_list args; char buf[1024]; va_start(args, fmt); - vsnprintf(buf, sizeof(buf), fmt, args); + wp_vsnprintf(buf, sizeof(buf), fmt, args); DEBUG_INIT("%s",buf); va_end(args); #endif @@ -650,7 +650,7 @@ void wpabs_debug_tx(const char * fmt, ...) va_list args; char buf[1024]; va_start(args, fmt); - vsnprintf(buf, sizeof(buf), fmt, args); + wp_vsnprintf(buf, sizeof(buf), fmt, args); DEBUG_TX("%s",buf); va_end(args); #endif @@ -665,7 +665,7 @@ void wpabs_debug_rx(const char * fmt, ...) va_list args; char buf[1024]; va_start(args, fmt); - vsnprintf(buf, sizeof(buf), fmt, args); + wp_vsnprintf(buf, sizeof(buf), fmt, args); DEBUG_RX("%s",buf); va_end(args); #endif @@ -680,7 +680,7 @@ void wpabs_debug_isr(const char * fmt, ...) va_list args; char buf[1024]; va_start(args, fmt); - vsnprintf(buf, sizeof(buf), fmt, args); + wp_vsnprintf(buf, sizeof(buf), fmt, args); DEBUG_ISR("%s",buf); va_end(args); #endif @@ -694,7 +694,7 @@ void wpabs_debug_timer(const char * fmt, ...) va_list args; char buf[1024]; va_start(args, fmt); - vsnprintf(buf, sizeof(buf), fmt, args); + wp_vsnprintf(buf, sizeof(buf), fmt, args); DEBUG_TIMER("%s",buf); va_end(args); #endif @@ -1102,7 +1102,7 @@ void wpabs_get_random_bytes(void *ptr, int len) #elif defined(__OpenBSD__) get_random_bytes(ptr,len); #elif defined(__WINDOWS__) - FUNC_NOT_IMPL(); + get_random_bytes(ptr, len); #else # error "wpabs_get_random_bytes not supported" #endif diff --git a/patches/kdrivers/src/wanrouter/wanpipe_cdev_linux.c b/patches/kdrivers/src/net/wanpipe_cdev_linux.c similarity index 86% rename from patches/kdrivers/src/wanrouter/wanpipe_cdev_linux.c rename to patches/kdrivers/src/net/wanpipe_cdev_linux.c index 8c2e82e..57a3f23 100644 --- a/patches/kdrivers/src/wanrouter/wanpipe_cdev_linux.c +++ b/patches/kdrivers/src/net/wanpipe_cdev_linux.c @@ -70,6 +70,9 @@ static struct class_simple *wp_cdev_class = NULL; /* CTRL Deivce is always 2 */ # define WP_CDEV_CTRL_DEV_MOFFSET (WP_CDEV_MAX_SPANS+2) +/* Logger Deivce is always 3 */ +# define WP_CDEV_LOGGER_DEV_MOFFSET (WP_CDEV_MAX_SPANS+3) + /* Timer devices can range from WP_CDEV_TIMER_DEV_MOFFSET to WP_CDEV_TIMER_DEV_MOFFSET + WP_MAX_TIMER_DEV_CNT */ # define WP_CDEV_TIMER_DEV_MOFFSET (WP_CDEV_MAX_SPANS+10) # define WP_MAX_TIMER_DEV_CNT 20 @@ -88,7 +91,9 @@ static int wp_cdev_release(struct inode *inode, struct file *file); static ssize_t wp_cdev_read(struct file *file, char *usrbuf, size_t count, loff_t *ppos); static ssize_t wp_cdev_write(struct file *file, const char *usrbuf, size_t count, loff_t *ppos); static int wp_cdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) static long wp_cdev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long data); +#endif static unsigned int wp_cdev_poll(struct file *file, struct poll_table_struct *wait_table); /*========================================================= @@ -120,7 +125,9 @@ static struct file_operations wp_cdev_fops = { read: wp_cdev_read, write: wp_cdev_write, poll: wp_cdev_poll, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) compat_ioctl: wp_cdev_compat_ioctl, +#endif mmap: NULL, flush: NULL, fsync: NULL, @@ -154,7 +161,7 @@ int wanpipe_global_cdev_init(void) int err; #ifdef LINUX_2_4 if ((err = register_chrdev(WP_CDEV_MAJOR, "wanpipe", &wp_cdev_fops))) { - DEBUG_EVENT("%s(): Error unable to register device!\n",__FUNCTION__); + DEBUG_ERROR("%s(): Error unable to register device!\n",__FUNCTION__); return err; } @@ -164,7 +171,7 @@ int wanpipe_global_cdev_init(void) dev_t dev = MKDEV(WP_CDEV_MAJOR, 0); if ((err=register_chrdev_region(dev, WP_CDEV_MAX_MINORS, "wanpipe"))) { - DEBUG_EVENT("%s(): Error unable to register device!\n",__FUNCTION__); + DEBUG_ERROR("%s(): Error unable to register device!\n",__FUNCTION__); return err; } @@ -172,13 +179,13 @@ int wanpipe_global_cdev_init(void) if (cdev_add(&wp_cdev_dev, dev, WP_CDEV_MAX_MINORS)) { kobject_put(&wp_cdev_dev.kobj); unregister_chrdev_region(dev, WP_CDEV_MAX_MINORS); - DEBUG_EVENT("%s(): Error cdev_add!\n",__FUNCTION__); + DEBUG_ERROR("%s(): Error cdev_add!\n",__FUNCTION__); return -EINVAL; } wp_cdev_class = class_create(THIS_MODULE, "wanpipe"); if (IS_ERR(wp_cdev_class)) { - DEBUG_EVENT("%s(): Error creating class!\n",__FUNCTION__); + DEBUG_ERROR("%s(): Error creating class!\n",__FUNCTION__); cdev_del(&wp_cdev_dev); unregister_chrdev_region(dev, WP_CDEV_MAX_MINORS); return -EINVAL; @@ -202,7 +209,7 @@ int wanpipe_global_cdev_init(void) int wanpipe_global_cdev_free(void) { if (wandev.dev_cnt) { - DEBUG_EVENT("%s: Error: Wanpipe CDEV Busy - failed to free!\n",__FUNCTION__); + DEBUG_ERROR("%s: Error: Wanpipe CDEV Busy - failed to free!\n",__FUNCTION__); return 1; } @@ -237,21 +244,21 @@ int wanpipe_cdev_tdm_create(wanpipe_cdev_t *cdev) int minor=-1; if (!wan_test_bit(0,&wandev.init)) { - DEBUG_EVENT("%s(): Error global device not initialized!\n",__FUNCTION__); + DEBUG_ERROR("%s(): Error global device not initialized!\n",__FUNCTION__); return -1; } if (!cdev->dev_ptr) { - DEBUG_EVENT("%s(): Error cdev->dev_ptr not initialized!\n",__FUNCTION__); + DEBUG_ERROR("%s(): Error cdev->dev_ptr not initialized!\n",__FUNCTION__); return -1; } if (cdev->span < 0 || cdev->span > WP_CDEV_MAX_SPANS) { - DEBUG_EVENT("%s(): Error span out of range %i!\n",__FUNCTION__,cdev->span); + DEBUG_ERROR("%s(): Error span out of range %i!\n",__FUNCTION__,cdev->span); return -1; } if (cdev->chan < 0 || cdev->chan > WP_CDEV_MAX_CHANS) { - DEBUG_EVENT("%s(): Error chan out of range %i!\n",__FUNCTION__,cdev->chan); + DEBUG_ERROR("%s(): Error chan out of range %i!\n",__FUNCTION__,cdev->chan); return -1; } minor=WP_CDEV_SET_MINOR(cdev->span,cdev->chan); @@ -277,12 +284,12 @@ int wanpipe_cdev_tdm_ctrl_create(wanpipe_cdev_t *cdev) int minor=-1; if (!wan_test_bit(0,&wandev.init)) { - DEBUG_EVENT("%s(): Error global device not initialized!\n",__FUNCTION__); + DEBUG_ERROR("%s(): Error global device not initialized!\n",__FUNCTION__); return -1; } if (!cdev->dev_ptr) { - DEBUG_EVENT("%s(): Error cdev->dev_ptr not initialized!\n",__FUNCTION__); + DEBUG_ERROR("%s(): Error cdev->dev_ptr not initialized!\n",__FUNCTION__); return -1; } @@ -306,12 +313,12 @@ int wanpipe_cdev_cfg_ctrl_create(wanpipe_cdev_t *cdev) int minor=-1; if (!wan_test_bit(0,&wandev.init)) { - DEBUG_EVENT("%s(): Error global device not initialized!\n",__FUNCTION__); + DEBUG_ERROR("%s(): Error global device not initialized!\n",__FUNCTION__); return -1; } if (!cdev->dev_ptr) { - DEBUG_EVENT("%s(): Error cdev->dev_ptr not initialized!\n",__FUNCTION__); + DEBUG_ERROR("%s(): Error cdev->dev_ptr not initialized!\n",__FUNCTION__); return -1; } @@ -324,6 +331,31 @@ int wanpipe_cdev_cfg_ctrl_create(wanpipe_cdev_t *cdev) } +/*========================================================= + * wanpipe_cdev_timer_create + *========================================================*/ +int wanpipe_cdev_logger_create(wanpipe_cdev_t *cdev) +{ + int minor=-1; + + if (!wan_test_bit(0,&wandev.init)) { + DEBUG_ERROR("%s(): Error global device not initialized!\n",__FUNCTION__); + return -1; + } + + if (!cdev->dev_ptr) { + DEBUG_ERROR("%s(): Error cdev->dev_ptr not initialized!\n",__FUNCTION__); + return -1; + } + + minor=WP_CDEV_SET_OFFSET_MINOR(WP_CDEV_LOGGER_DEV_MOFFSET); + + cdev->name[WAN_IFNAME_SZ]=0; + sprintf(cdev->name, "wanpipe_logger"); + + return wanpipe_create_cdev(cdev, minor, NULL); +} + /*========================================================= * wanpipe_cdev_timer_create @@ -335,17 +367,17 @@ int wanpipe_cdev_timer_create(wanpipe_cdev_t *cdev) int minor=-1; if (!wan_test_bit(0,&wandev.init)) { - DEBUG_EVENT("%s(): Error global device not initialized!\n",__FUNCTION__); + DEBUG_ERROR("%s(): Error global device not initialized!\n",__FUNCTION__); return -1; } if (!cdev->dev_ptr) { - DEBUG_EVENT("%s(): Error cdev->dev_ptr not initialized!\n",__FUNCTION__); + DEBUG_ERROR("%s(): Error cdev->dev_ptr not initialized!\n",__FUNCTION__); return -1; } if (wandev.timer_dev_cnt >= WP_MAX_TIMER_DEV_CNT) { - DEBUG_EVENT("%s(): Error timer dev cnt limit %i!\n",__FUNCTION__,wandev.timer_dev_cnt); + DEBUG_ERROR("%s(): Error timer dev cnt limit %i!\n",__FUNCTION__,wandev.timer_dev_cnt); return -1; } @@ -360,7 +392,6 @@ int wanpipe_cdev_timer_create(wanpipe_cdev_t *cdev) } - /*========================================================= * wanpipe_cdev_tdm_free *========================================================*/ @@ -373,24 +404,24 @@ int wanpipe_cdev_free(wanpipe_cdev_t *cdev) DEBUG_CDEV ("%s:%d\n",__FUNCTION__,__LINE__); if (!CPRIV(cdev)) { - DEBUG_EVENT("%s(): Error cdev priv not initialized!\n",__FUNCTION__); + DEBUG_ERROR("%s(): Error cdev priv not initialized!\n",__FUNCTION__); return -ENODEV; } minor=CPRIV(cdev)->dev_minor; if (!wan_test_bit(0,&wandev.init)) { - DEBUG_EVENT("%s(): Error global device not initialized!\n",__FUNCTION__); + DEBUG_ERROR("%s(): Error global device not initialized!\n",__FUNCTION__); return -ENODEV; } if (minor < 0 || minor > WP_CDEV_MAX_MINORS) { - DEBUG_EVENT("%s(): Error MINOR out of range %i!\n",__FUNCTION__,minor); + DEBUG_ERROR("%s(): Error MINOR out of range %i!\n",__FUNCTION__,minor); return -EINVAL; } if (!wan_test_bit(0,&cdev->init)) { - DEBUG_EVENT("%s(): Error cdev device not initialized!\n",__FUNCTION__); + DEBUG_ERROR("%s(): Error cdev device not initialized!\n",__FUNCTION__); return -ENODEV; } @@ -418,7 +449,7 @@ static int wanpipe_create_cdev(wanpipe_cdev_t *cdev, int minor, int *counter) cdev_priv = wan_kmalloc(sizeof(wanpipe_cdev_priv_t)); if (!cdev_priv) { - DEBUG_EVENT("%s(): Error unable to alloc cdev_priv mem!\n",__FUNCTION__); + DEBUG_ERROR("%s(): Error unable to alloc cdev_priv mem!\n",__FUNCTION__); return -ENOMEM; } @@ -432,7 +463,7 @@ static int wanpipe_create_cdev(wanpipe_cdev_t *cdev, int minor, int *counter) wan_spin_unlock_irq(&wandev.lock,&flags); wan_free(cdev_priv); /* Busy */ - DEBUG_EVENT("%s(): Error MINOR device busy %i!\n",__FUNCTION__,minor); + DEBUG_ERROR("%s(): Error MINOR device busy %i!\n",__FUNCTION__,minor); return 1; } @@ -476,7 +507,7 @@ static int wanpipe_free_cdev(wanpipe_cdev_t *cdev, int minor, int *counter) if (wandev.idx[minor] != cdev || !cdev_priv) { wan_spin_unlock_irq(&wandev.lock,&flags); /* Busy */ - DEBUG_EVENT("%s(): Error MINOR device busy 0x%X! cdev=%p idx=%p cdev_priv=%p\n", + DEBUG_ERROR("%s(): Error MINOR device busy 0x%X! cdev=%p idx=%p cdev_priv=%p\n", __FUNCTION__,minor,cdev,wandev.idx[minor],cdev_priv); return 1; } @@ -529,14 +560,14 @@ static int wp_cdev_open(struct inode *inode, struct file *file) if (cdev == NULL) { wan_spin_unlock_irq(&wandev.lock,&flags); /* No Dev */ - DEBUG_EVENT("%s(): Error cdev is null!\n",__FUNCTION__); + DEBUG_ERROR("%s(): Error cdev is null!\n",__FUNCTION__); return -ENODEV; } if (!wan_test_bit(0,&cdev->init)) { wan_spin_unlock_irq(&wandev.lock,&flags); /* Dev not initialized */ - DEBUG_EVENT("%s(): Error cdev is not initialized!\n",__FUNCTION__); + DEBUG_ERROR("%s(): Error cdev is not initialized!\n",__FUNCTION__); return -ENODEV; } @@ -574,12 +605,12 @@ static int wp_cdev_release(struct inode *inode, struct file *file) } minor=CPRIV(cdev)->dev_minor; if (minor > WP_CDEV_MAX_MINORS) { - DEBUG_EVENT("%s(): Error MINOR is out of range %i !\n",__FUNCTION__,minor); + DEBUG_ERROR("%s(): Error MINOR is out of range %i !\n",__FUNCTION__,minor); return -ENODEV; } if (wandev.idx[minor] != cdev) { - DEBUG_EVENT("%s(): Error cdev does not match minor index ptr!\n",__FUNCTION__); + DEBUG_ERROR("%s(): Error cdev does not match minor index ptr!\n",__FUNCTION__); return -ENODEV; } @@ -619,7 +650,7 @@ static ssize_t wp_cdev_read(struct file *file, char *usrbuf, size_t count, loff_ } if (count < sizeof(wan_msghdr_t)) { - DEBUG_EVENT("%s:%d Error: Invalid read buffer size %i\n",__FUNCTION__,__LINE__,count); + DEBUG_ERROR("%s:%d Error: Invalid read buffer size %i\n",__FUNCTION__,__LINE__,count); return -EINVAL; } @@ -627,7 +658,7 @@ static ssize_t wp_cdev_read(struct file *file, char *usrbuf, size_t count, loff_ return -EFAULT; if (msg_sys.msg_iovlen == 0 || msg_sys.msg_iovlen > WP_UIO_MAX_SZ) { - DEBUG_EVENT("%s:%d Error: Invalid read buffer msg_iovlen %i\n",__FUNCTION__,__LINE__,msg_sys.msg_iovlen); + DEBUG_ERROR("%s:%d Error: Invalid read buffer msg_iovlen %i\n",__FUNCTION__,__LINE__,msg_sys.msg_iovlen); return -EFAULT; } @@ -767,12 +798,13 @@ static int wp_cdev_ioctl(struct inode *inode, struct file *file, unsigned int cm return err; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) static long wp_cdev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long data) { long err = (long) wp_cdev_ioctl(NULL, file, cmd, data); return err; } - +#endif static unsigned int wp_cdev_poll(struct file *file, struct poll_table_struct *wait_table) { @@ -806,5 +838,6 @@ EXPORT_SYMBOL(wanpipe_cdev_tdm_create); EXPORT_SYMBOL(wanpipe_cdev_tdm_ctrl_create); EXPORT_SYMBOL(wanpipe_cdev_cfg_ctrl_create); EXPORT_SYMBOL(wanpipe_cdev_timer_create); +EXPORT_SYMBOL(wanpipe_cdev_logger_create); diff --git a/patches/kdrivers/src/net/wanpipe_codec_law.c b/patches/kdrivers/src/net/wanpipe_codec_law.c index dd5de2a..c1b4ef1 100644 --- a/patches/kdrivers/src/net/wanpipe_codec_law.c +++ b/patches/kdrivers/src/net/wanpipe_codec_law.c @@ -246,7 +246,7 @@ static int wanpipe_codec_convert_law2s(u8 *data, } if (usr) { #if defined(__WINDOWS__) - DEBUG_EVENT("%s(): Error: copy_to_user() is NOT supported!!\n"); + DEBUG_ERROR("%s(): Error: copy_to_user() is NOT supported!!\n"); #else int err; err=copy_to_user(&buf[i],&codec[ data[i] ],sizeof(u16)); @@ -288,7 +288,7 @@ static int wanpipe_codec_convert_s2law(u16 *data, for (i=0;iinit = &wan_iface_eth_init; + WAN_NETDEV_OPS_BIND(dev,wan_netdev_ops); + WAN_NETDEV_OPS_INIT(dev,wan_netdev_ops,&wan_iface_eth_init); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&wan_iface_get_stats); + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,&wan_iface_ioctl); + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&wan_iface_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&wan_iface_close); + + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&wan_iface_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&wan_iface_get_stats); + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&wan_iface_tx_timeout); + WAN_NETDEV_OPS_MTU(dev,wan_netdev_ops,&wan_iface_change_mtu); + err=register_netdev(dev); }else{ #ifdef CONFIG_PRODUCT_WANPIPE_GENERIC @@ -154,7 +167,7 @@ static int wan_iface_attach_eth (netdevice_t* dev, char *ifname, int is_netdev) if (err){ DEBUG_EVENT("%s: Failed to register interface (%d)\n", wan_netif_name(dev), err); - dev->init = NULL; + WAN_NETDEV_OPS_INIT(dev,wan_netdev_ops,NULL); *(dev->name) = 0; wan_netif_free(dev); return -EINVAL; @@ -170,7 +183,17 @@ static int wan_iface_attach (netdevice_t* dev, char *ifname, int is_netdev) if (ifname){ wan_netif_init(dev, ifname); } - dev->init = &wan_iface_init; + WAN_NETDEV_OPS_BIND(dev,wan_netdev_ops); + WAN_NETDEV_OPS_INIT(dev,wan_netdev_ops,&wan_iface_init); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&wan_iface_get_stats); + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,&wan_iface_ioctl); + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&wan_iface_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&wan_iface_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&wan_iface_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&wan_iface_get_stats); + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&wan_iface_tx_timeout); + WAN_NETDEV_OPS_MTU(dev,wan_netdev_ops,&wan_iface_change_mtu); + //dev->init = &wan_iface_init; err=register_netdev(dev); }else{ #ifdef CONFIG_PRODUCT_WANPIPE_GENERIC @@ -187,7 +210,8 @@ static int wan_iface_attach (netdevice_t* dev, char *ifname, int is_netdev) if (err){ DEBUG_EVENT("%s: Failed to register interface (%d)\n", wan_netif_name(dev), err); - dev->init = NULL; + WAN_NETDEV_OPS_INIT(dev,wan_netdev_ops,NULL); + *(dev->name) = 0; wan_netif_free(dev); return -EINVAL; @@ -216,20 +240,18 @@ static void wan_iface_detach (netdevice_t* dev, int is_netdev) static int wan_iface_init(netdevice_t* dev) { -// dev->priv = NULL; /* We need 'priv', hdlc doesn't */ - dev->get_stats = &wan_iface_get_stats; - dev->do_ioctl = &wan_iface_ioctl; - dev->open = &wan_iface_open; - dev->stop = &wan_iface_close; - - dev->hard_start_xmit = &wan_iface_send; - dev->get_stats = &wan_iface_get_stats; - dev->tx_timeout = &wan_iface_tx_timeout; - dev->change_mtu = &wan_iface_change_mtu; + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&wan_iface_get_stats); + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,&wan_iface_ioctl); + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&wan_iface_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&wan_iface_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&wan_iface_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&wan_iface_get_stats); + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&wan_iface_tx_timeout); + WAN_NETDEV_OPS_MTU(dev,wan_netdev_ops,&wan_iface_change_mtu); dev->watchdog_timeo = HZ*2; dev->hard_header_len = 32; - dev->set_config = NULL; + WAN_NETDEV_OPS_CONFIG(dev,wan_netdev_ops,NULL); /* Initialize media-specific parameters */ dev->flags |= IFF_POINTOPOINT; @@ -260,17 +282,18 @@ static int wan_iface_eth_init(netdevice_t* dev) { int hw_addr=0; -// dev->priv = NULL; /* We need 'priv', hdlc doesn't */ - dev->get_stats = &wan_iface_get_stats; - dev->do_ioctl = &wan_iface_ioctl; - dev->open = &wan_iface_open; - dev->stop = &wan_iface_close; - dev->hard_start_xmit = &wan_iface_send; - dev->get_stats = &wan_iface_get_stats; - dev->tx_timeout = &wan_iface_tx_timeout; + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&wan_iface_get_stats); + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,&wan_iface_ioctl); + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&wan_iface_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&wan_iface_close); + + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&wan_iface_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&wan_iface_get_stats); + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&wan_iface_tx_timeout); + dev->watchdog_timeo = HZ*2; dev->hard_header_len = 32; - dev->set_config = NULL; + WAN_NETDEV_OPS_CONFIG(dev,wan_netdev_ops,NULL); if (!dev->mtu) { dev->mtu = 1500; diff --git a/patches/kdrivers/src/net/wanpipe_logger.c b/patches/kdrivers/src/net/wanpipe_logger.c new file mode 100644 index 0000000..a49e6a1 --- /dev/null +++ b/patches/kdrivers/src/net/wanpipe_logger.c @@ -0,0 +1,778 @@ +/***************************************************************************** +* wanpipe_logger.c +* +* WANPIPE(tm) - Event Logger API Support +* +* Authors: David Rokhvarg +* +* Copyright: (c) 2003-2009 Sangoma Technologies Inc. +* +* ============================================================================ +* September 04, 2009 David Rokhvarg Initial version. +*****************************************************************************/ + +#include "wanpipe_includes.h" +#include "wanpipe_cdev_iface.h" +#include "wanpipe_logger.h" + +/*================================================================= + * Debugging Definitions + *================================================================*/ + +#ifdef __WINDOWS__ +# define DEBUG_LOGGER if(0)DbgPrint +extern void vOutputLogString(const char * pvFormat, va_list args); +#else +# define DEBUG_LOGGER if(0)printk +#endif + +#undef LOGGER_FUNC_DEBUG +#if 0 +# define LOGGER_FUNC_DEBUG() DEBUG_LOGGER("%s():Line:%d\n", __FUNCTION__, __LINE__) +#else +# define LOGGER_FUNC_DEBUG() +#endif + +#define LOGGER_QLEN_DBG(queue) \ +if(0){ \ + DEBUG_LOGGER("%s(): queue ptr: 0x%p (%s), qlen:%d\n", \ + __FUNCTION__, \ + queue, \ + (queue == &logger_api_dev.wp_event_list ? "wp_event_list":"wp_event_free_list"), \ + wan_skb_queue_len(queue)); \ +} + +/*================================================================= + * Macro Definitions + *================================================================*/ + + +#define WAN_LOGGER_SET_DATA(logger_event, logger_type, evt_type, format) \ +{ \ + va_list args; \ + \ + memset(logger_event, 0x00, sizeof(*logger_event)); \ + \ + logger_event->logger_type = logger_type; \ + logger_event->event_type = evt_type; \ + \ + va_start(args, format); \ + wp_vsnprintf(logger_event->data, sizeof(logger_event->data), format, args); \ + va_end(args); \ + \ + WAN_LOGGER_SET_TIME_STAMP(logger_event) \ +} + +#define WAN_LOGGER_SET_TIME_STAMP(logger_event) \ +{ \ + struct timeval tv; \ + \ + do_gettimeofday(&tv); \ + \ + logger_event->time_stamp_sec = tv.tv_sec; \ + logger_event->time_stamp_usec = tv.tv_usec; \ +} + + +/*================================================================= + * Static Defines + *================================================================*/ + +typedef struct wp_logger_api_dev { + + u32 init; + char name[WAN_IFNAME_SZ]; + + wan_spinlock_t lock; + + u32 used, open_cnt; + + wanpipe_api_dev_cfg_t cfg; + + wan_skb_queue_t wp_event_list; + wan_skb_queue_t wp_event_free_list; + wan_taskq_t wp_logger_task; + + void *cdev; + u32 magic_no; + +#if defined(__WINDOWS__) + LONG spin_flag; +#endif +} wp_logger_api_dev_t; + + +/*================================================================= + * Function Prototypes and Global Variables + *================================================================*/ + +u_int32_t wp_logger_level_default = SANG_LOGGER_INFORMATION | SANG_LOGGER_WARNING | SANG_LOGGER_ERROR; +u_int32_t wp_logger_level_te1 = 0; +u_int32_t wp_logger_level_hwec = 0; +u_int32_t wp_logger_level_tdmapi = 0; +u_int32_t wp_logger_level_fe = 0; +u_int32_t wp_logger_level_bri = 0; + + +static wp_logger_api_dev_t logger_api_dev; +static wanpipe_cdev_ops_t wp_logger_api_fops; + + +/*================================================================= + * Private Functions + *================================================================*/ + +static u_int32_t* wp_logger_type_to_variable_ptr(u_int32_t logger_type) +{ + switch(logger_type) + { + case WAN_LOGGER_DEFAULT: + return &wp_logger_level_default; + case WAN_LOGGER_TE1: + return &wp_logger_level_te1; + case WAN_LOGGER_HWEC: + return &wp_logger_level_hwec; + case WAN_LOGGER_TDMAPI: + return &wp_logger_level_tdmapi; + case WAN_LOGGER_FE: + return &wp_logger_level_fe; + case WAN_LOGGER_BRI: + return &wp_logger_level_bri; + default: + return NULL; + } +} + +static void wp_logger_spin_lock_irq(wan_smp_flag_t *flags) +{ +#if defined(__WINDOWS__) + wan_lock_global_irq_spin_flag(&logger_api_dev.spin_flag, flags); +#else + wan_spin_lock_irq(&logger_api_dev.lock, flags); +#endif +} + +static void wp_logger_spin_unlock_irq(wan_smp_flag_t *flags) +{ +#if defined(__WINDOWS__) + wan_unlock_global_irq_spin_flag(&logger_api_dev.spin_flag, flags); +#else + wan_spin_unlock_irq(&logger_api_dev.lock, flags); +#endif +} + +static int wp_logger_increment_open_cnt(void) +{ + logger_api_dev.open_cnt++; + return logger_api_dev.open_cnt; +} + +static int wp_logger_decrement_open_cnt(void) +{ + logger_api_dev.open_cnt--; + return logger_api_dev.open_cnt; +} + +static int wp_logger_get_open_cnt(void) +{ + return logger_api_dev.open_cnt; +} + +/* set logger level bitmap */ +static void wp_logger_set_level(u_int32_t logger_type, u_int32_t new_level) +{ + u_int32_t *logger_var_ptr = wp_logger_type_to_variable_ptr(logger_type); + + DEBUG_LOGGER("%s(): logger_type: %d, new_level: 0x%08X\n", + __FUNCTION__, logger_type, new_level); + + if(logger_var_ptr){ + *logger_var_ptr = new_level; + }else{ + DEBUG_WARNING("Warning: %s(): unknown logger_type: %d\n", + __FUNCTION__, logger_type); + } +} + + +/* return current logger level bitmap to the caller */ +static u_int32_t wp_logger_get_level(u_int32_t logger_type) +{ + u_int32_t *logger_var_ptr = wp_logger_type_to_variable_ptr(logger_type); + + if(logger_var_ptr){ + DEBUG_LOGGER("%s(): logger_type: %d, current level: 0x%08X\n", + __FUNCTION__, logger_type, *logger_var_ptr); + + return *logger_var_ptr; + }else{ + DEBUG_WARNING("Warning: %s(): unknown logger_type: %d\n", + __FUNCTION__, logger_type); + return 0; + } +} + +static void wp_logger_init_buffs(void) +{ + netskb_t *skb; + wan_smp_flag_t irq_flag; + + wp_logger_spin_lock_irq(&irq_flag); + + while((skb = wan_skb_dequeue(&logger_api_dev.wp_event_list))) { + wan_skb_init(skb,0); + wan_skb_trim(skb,0); + wan_skb_queue_tail(&logger_api_dev.wp_event_free_list, skb); + } + + wp_logger_spin_unlock_irq(&irq_flag); +} + +static int wp_logger_api_open(void *not_used) +{ + if (wp_logger_increment_open_cnt() == 1) { + wp_logger_init_buffs(); + } + + wan_set_bit(0,&logger_api_dev.used); + + DEBUG_LOGGER ("%s(): OpenCounter: %d\n", + __FUNCTION__, wp_logger_get_open_cnt()); + return 0; +} + +static void wp_logger_wakeup_api(void) +{ + if (logger_api_dev.cdev) { + wanpipe_cdev_event_wake(logger_api_dev.cdev); + } +} + +static int wp_logger_api_release(void *not_used) +{ + u32 cnt; + + if (!wan_test_bit(0, &logger_api_dev.init)) { + wan_clear_bit(0, &logger_api_dev.used); + return -ENODEV; + } + + cnt = wp_logger_decrement_open_cnt(); + + if (cnt == 0) { + + wan_clear_bit(0, &logger_api_dev.used); + + wp_logger_wakeup_api(); + } + + return 0; +} + +static int wp_logger_alloc_q(wan_skb_queue_t *queue) +{ + netskb_t *skb; + int i; + wan_smp_flag_t irq_flag; + + for (i = 0; i < WP_MAX_NO_LOGGER_EVENTS; i++) { + + skb = wan_skb_kalloc(sizeof(wp_logger_event_t)); + + if (!skb) { + if (WAN_NET_RATELIMIT()) { + DEBUG_ERROR("Error: %s(): %d: Failed to Allocate Memory!\n", + __FUNCTION__, __LINE__); + } + return -1; + } + + /*wan_skb_init(skb, sizeof(wp_logger_event_t));*/ + wan_skb_init(skb, 0); + wan_skb_trim(skb, 0); + + wp_logger_spin_lock_irq(&irq_flag); + wan_skb_queue_tail(queue, skb); + wp_logger_spin_unlock_irq(&irq_flag); + } + + LOGGER_QLEN_DBG(queue); + return 0; +} + +static void wp_logger_free_q(wan_skb_queue_t *queue) +{ + netskb_t *skb; + wan_smp_flag_t irq_flag; + + LOGGER_QLEN_DBG(queue); + + do { + wp_logger_spin_lock_irq(&irq_flag); + + skb = wan_skb_dequeue(queue); + + wp_logger_spin_unlock_irq(&irq_flag); + + if(skb){ + wan_skb_free(skb); + } + }while(skb); + +} + +/* Note that there is no need to check for queue length exceeding + * some maximum because it is done automatically, when wp_logger_get_free_skb() + * returns NULL. */ +static void wp_logger_enqueue_skb(wan_skb_queue_t *queue, netskb_t *skb) +{ + wan_smp_flag_t irq_flag; + + wp_logger_spin_lock_irq(&irq_flag); + wan_skb_queue_tail(queue, skb); + wp_logger_spin_unlock_irq(&irq_flag); +} + +static netskb_t* wp_logger_dequeue_skb(wan_skb_queue_t *queue) +{ + netskb_t *skb; + wan_smp_flag_t irq_flag; + + wp_logger_spin_lock_irq(&irq_flag); + skb = wan_skb_dequeue(queue); + wp_logger_spin_unlock_irq(&irq_flag); + + return skb; +} + +static unsigned int wp_logger_api_poll(void *not_used) +{ + int ret = 0, qlength; + wan_smp_flag_t irq_flag; + + + if (!wan_test_bit(0, &logger_api_dev.init) || !wan_test_bit(0, &logger_api_dev.used)){ + return -ENODEV; + } + + wp_logger_spin_lock_irq(&irq_flag); + + qlength = wan_skb_queue_len(&logger_api_dev.wp_event_list); + + wp_logger_spin_unlock_irq(&irq_flag); + + if (qlength) { + /* Indicate an exception */ + ret = POLLPRI; + } + + return ret; +} + +static int wp_logger_handle_api_cmd(void *user_data) +{ + wp_logger_cmd_t tmp_usr_logger_api, *user_logger_api_ptr; + int err; + netskb_t *skb; + wan_smp_flag_t irq_flag; + + user_logger_api_ptr = (wp_logger_cmd_t*)user_data; + +#if defined(__WINDOWS__) + /* user_data IS a kernel-space pointer to wp_logger_cmd_t */ + memcpy(&tmp_usr_logger_api, user_data, sizeof(wp_logger_cmd_t)); +#else + if(WAN_COPY_FROM_USER(&tmp_usr_logger_api, user_logger_api_ptr, sizeof(wp_logger_cmd_t))){ + return -EFAULT; + } +#endif + + err = tmp_usr_logger_api.result = SANG_STATUS_SUCCESS; + + DEBUG_LOGGER("%s: Logger CMD: %i, sizeof(wp_logger_cmd_t)=%i\n", + logger_api_dev.name, tmp_usr_logger_api.cmd, sizeof(wp_logger_cmd_t)); + + switch (tmp_usr_logger_api.cmd) + { + case WP_API_LOGGER_CMD_READ_EVENT: + + skb = wp_logger_dequeue_skb(&logger_api_dev.wp_event_list); + if (!skb){ + err = SANG_STATUS_NO_FREE_BUFFERS; + break; + } + + memcpy(&tmp_usr_logger_api.logger_event, wan_skb_data(skb), sizeof(wp_logger_event_t)); + + /* return the buffer into free skb list (do NOT deallocate it!) */ + wp_logger_enqueue_skb(&logger_api_dev.wp_event_free_list, skb); + break; + + case WP_API_LOGGER_CMD_GET_LOGGER_LEVEL: + tmp_usr_logger_api.logger_level_ctrl.logger_level = + wp_logger_get_level(tmp_usr_logger_api.logger_level_ctrl.logger_type); + break; + + case WP_API_LOGGER_CMD_SET_LOGGER_LEVEL: + wp_logger_set_level(tmp_usr_logger_api.logger_level_ctrl.logger_type, + tmp_usr_logger_api.logger_level_ctrl.logger_level); + break; + + case WP_API_LOGGER_CMD_OPEN_CNT: + tmp_usr_logger_api.open_cnt = wp_logger_get_open_cnt(); + break; + + case WP_API_LOGGER_CMD_GET_STATS: + + wp_logger_spin_lock_irq(&irq_flag); + + tmp_usr_logger_api.stats.max_event_queue_length = wan_skb_queue_len(&logger_api_dev.wp_event_list) + wan_skb_queue_len(&logger_api_dev.wp_event_free_list); + tmp_usr_logger_api.stats.current_number_of_events_in_event_queue = wan_skb_queue_len(&logger_api_dev.wp_event_list); + + wp_logger_spin_unlock_irq(&irq_flag); + + tmp_usr_logger_api.stats.rx_events_dropped = logger_api_dev.cfg.stats.rx_events_dropped; + tmp_usr_logger_api.stats.rx_events = logger_api_dev.cfg.stats.rx_events; + break; + + case WP_API_LOGGER_CMD_RESET_STATS: + memset(&logger_api_dev.cfg.stats, 0x00, sizeof(logger_api_dev.cfg.stats)); + break; + + case WP_API_LOGGER_CMD_FLUSH_BUFFERS: + wp_logger_init_buffs(); + break; + + default: + DEBUG_WARNING("%s: Warning: Invalid Wanpipe Logger API Command: %i\n", + logger_api_dev.name, tmp_usr_logger_api.cmd); + err = SANG_STATUS_OPTION_NOT_SUPPORTED; + break; + + }/* switch (tmp_usr_logger_api.cmd) */ + + tmp_usr_logger_api.result = err; + +#if defined(__WINDOWS__) + /* user_data IS a kernel-space pointer to wp_logger_cmd_t */ + memcpy(user_data, &tmp_usr_logger_api, sizeof(wp_logger_cmd_t)); +#else + if(WAN_COPY_TO_USER(user_data, &tmp_usr_logger_api, sizeof(wp_logger_cmd_t))){ + err = -EFAULT; + } +#endif + + return err; +} + +static int wp_logger_api_ioctl(void *not_used, int ioctl_cmd, void *user_data) +{ + int err = 0; + + if (!wan_test_bit(0,&logger_api_dev.init) || WANPIPE_MAGIC != logger_api_dev.magic_no){ + DEBUG_ERROR("Error: %s(): line: %d: Logger API Not initialized!\n", + __FUNCTION__, __LINE__); + return -EFAULT; + } + + if (!user_data){ + return -EINVAL; + } + + switch (ioctl_cmd) + { + case WANPIPE_IOCTL_LOGGER_CMD: + err = wp_logger_handle_api_cmd(user_data); + break; + + default: + DEBUG_ERROR("Error: %s(): line: %d: Invalid IOCTL CMD: %d!\n", + __FUNCTION__, __LINE__, ioctl_cmd); + err = -EINVAL; + break; + } + + return err; +} + + +#if defined(__LINUX__) +# if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) +static void wp_logger_task_func (void *not_used) +# else +static void wp_logger_task_func (struct work_struct *not_used) +# endif +#elif defined(__WINDOWS__) +static void wp_logger_task_func (IN PKDPC Dpc, IN PVOID not_used, IN PVOID SystemArgument1, IN PVOID SystemArgument2) +#else +static void wp_logger_task_func (void *not_used, int arg) +#endif +{ + wp_logger_wakeup_api(); +} + +static netskb_t* wp_logger_get_free_skb(void) +{ + netskb_t *skb; + + skb = wp_logger_dequeue_skb(&logger_api_dev.wp_event_free_list); + + if (skb == NULL) { + DEBUG_ERROR("Error: %s(): no free buffers. Dropping Logger Event!\n", + __FUNCTION__); + WP_AFT_CHAN_ERROR_STATS(logger_api_dev.cfg.stats, rx_events_dropped); + return NULL; + } + + return skb; +} + +static int wp_logger_push_event(netskb_t *skb) +{ + if (!wan_test_bit(0,&logger_api_dev.init) || WANPIPE_MAGIC != logger_api_dev.magic_no){ + DEBUG_TEST("%s(): Initialization incomplete. Dropping event.\n", __FUNCTION__); + return -ENODEV; + } + + if (!skb) { + DEBUG_ERROR("Error: Logger: Null skb argument in %s()!\n", __FUNCTION__); + return -ENODEV; + } + + if (!wan_test_bit(0,&logger_api_dev.used)) { + DEBUG_TEST("%s(): Not in use. Dropping event.\n", __FUNCTION__); + return -EBUSY; + } + + wp_logger_enqueue_skb(&logger_api_dev.wp_event_list, skb); + + logger_api_dev.cfg.stats.rx_events++; + + return 0; +} + +int wp_logger_repeating_message_filter(u_int32_t logger_type, u_int32_t evt_type, const char * fmt, ...) +{ + va_list va_arg_list; + + /* Filter out repeating messages. */ + static char previous_error_message[WP_MAX_NO_BYTES_IN_LOGGER_EVENT]; + static char current_error_message[WP_MAX_NO_BYTES_IN_LOGGER_EVENT]; + static char tmp_message_buf[WP_MAX_NO_BYTES_IN_LOGGER_EVENT]; + static u32 repeating_error_message_counter = 0; + u8 apply_filter = 0; + + /* Filter only ERROR message from Default logger. + * Other types of message can be filtered too. */ + if(WAN_LOGGER_DEFAULT == logger_type){ + if(SANG_LOGGER_ERROR == evt_type){ + apply_filter = 1; + } + } + + if(!apply_filter){ + /* consider it as NOT a repeating message */ + return 0; + } + + memset(current_error_message, 0x00, sizeof(current_error_message)); + + /* the paramter list must start at fmt, not at evt_type */ + va_start (va_arg_list, fmt); + + wp_vsnprintf(current_error_message, sizeof(current_error_message) - 1, + (const char *)fmt, va_arg_list); + + va_end (va_arg_list); + + if(!memcmp(previous_error_message, current_error_message, sizeof(current_error_message))){ + + ++repeating_error_message_counter; + /* every 100 messages print the repeating message counter */ + if((repeating_error_message_counter % 100 == 0)){ + wp_snprintf(tmp_message_buf, sizeof(tmp_message_buf), + "* Message repeated %d times.\n", repeating_error_message_counter); + wp_logger_input(logger_type, evt_type, tmp_message_buf); + } + /* is IS a repeating message */ + return 1; + + }else{ + + if(repeating_error_message_counter){ + /* this message broke a sequence of repeating messages */ + wp_snprintf(tmp_message_buf, sizeof(tmp_message_buf), + "* Message repeated %d times.\n", repeating_error_message_counter); + wp_logger_input(logger_type, evt_type, tmp_message_buf); + repeating_error_message_counter = 0; + } + /* store current message for comparison with a future message */ + memcpy(previous_error_message, current_error_message, sizeof(current_error_message)); + + /* it is NOT a repeating message */ + return 0; + } +} + +/*================================================================= + * Public Functions + *================================================================*/ + +/* Windows note: sngbus.sys, sprotocol.sys and wanpipe.sys + * will NOT initialize/use Logger Device, but writing to wanpipelog.txt + * file is still possible by calling this function.*/ + +void wp_logger_input(u_int32_t logger_type, u_int32_t evt_type, const char * fmt, ...) +{ + va_list va_arg_list; + + /* Independently of Logger Device state, write the message into log file. */ + + /* the paramter list must start at fmt, not at evt_type */ + va_start (va_arg_list, fmt); + + #ifdef __WINDOWS__ + /* Write the message to wanpipelog.txt */ + vOutputLogString(fmt, va_arg_list); + #else + /* Write the message to kernel log. */ + vprintk(fmt, va_arg_list); + #endif + + va_end (va_arg_list); + + if (WANPIPE_MAGIC == logger_api_dev.magic_no && + wan_test_bit(0, &logger_api_dev.used) && + wan_test_bit(0, &logger_api_dev.init)){ + + wp_logger_event_t *p_wp_logger_event; + netskb_t *skb; + + skb = wp_logger_get_free_skb(); + if(skb){ + wan_skb_init(skb,0); + wan_skb_trim(skb,0); + + /* since we have the free skb buffer we should copy data into it + * and push it into event queue. */ + p_wp_logger_event = (wp_logger_event_t*)wan_skb_put(skb, sizeof(wp_logger_event_t)); + + memset(p_wp_logger_event, 0, sizeof(wp_logger_event_t)); + + WAN_LOGGER_SET_DATA(p_wp_logger_event, logger_type, evt_type, fmt); + + wp_logger_push_event(skb); + + WAN_TASKQ_SCHEDULE((&logger_api_dev.wp_logger_task)); + } + } +} + +/* This function during module load - the 'logger_api_dev' structure + * is initialized for first time. */ +int wp_logger_create(void) +{ + wanpipe_cdev_t *cdev; + int err; + + if(WANPIPE_MAGIC == logger_api_dev.magic_no){ + DEBUG_LOGGER("%s(): Logger Dev already exist - not creating\n", __FUNCTION__); + /* Already initialized. */ + return 0; + } + + memset(&logger_api_dev, 0, sizeof(wp_logger_api_dev_t)); + logger_api_dev.magic_no = WANPIPE_MAGIC; + + snprintf(logger_api_dev.name, sizeof(logger_api_dev.name), "WP Logger"); + + wp_logger_api_fops.open = wp_logger_api_open; + wp_logger_api_fops.close = wp_logger_api_release; + wp_logger_api_fops.ioctl = wp_logger_api_ioctl; + wp_logger_api_fops.read = NULL; + wp_logger_api_fops.write = NULL; + wp_logger_api_fops.poll = wp_logger_api_poll; + + cdev = wan_kmalloc(sizeof(wanpipe_cdev_t)); + if (!cdev) { + return -ENOMEM; + } + memset(cdev, 0, sizeof(wanpipe_cdev_t)); + + DEBUG_LOGGER("%s(): Registering Wanpipe Logger Device!\n",__FUNCTION__); + + wan_skb_queue_init(&logger_api_dev.wp_event_list); + wan_skb_queue_init(&logger_api_dev.wp_event_free_list); + + WAN_TASKQ_INIT((&logger_api_dev.wp_logger_task),0,wp_logger_task_func, &logger_api_dev); + + wan_spin_lock_init(&logger_api_dev.lock, "wp_logger_lock"); + + if(wp_logger_alloc_q(&logger_api_dev.wp_event_free_list)){ + logger_api_dev.magic_no = 0; + return -ENOMEM; + } + + cdev->dev_ptr = &logger_api_dev; + + logger_api_dev.cdev = cdev; + + memcpy(&cdev->ops, &wp_logger_api_fops, sizeof(cdev->ops)); + + err = wanpipe_cdev_logger_create(cdev); + + if(err){ + logger_api_dev.magic_no = 0; + return err; + } + + wan_set_bit(0,&logger_api_dev.init); + + return 0; +} + +/* This function called during module unload. */ +void wp_logger_delete(void) +{ + int err; + + if(wan_test_bit(0,&logger_api_dev.used)){ + DEBUG_LOGGER("%s(): Logger Dev in use - not deleting\n", __FUNCTION__); + /* If open file descriptor exist, deletion of Logger Device + * will fail (under Windows). */ + return; + } + + if(WANPIPE_MAGIC != logger_api_dev.magic_no){ + /* make sure logger deleted exactly one time */ + return; + } + + err = WAN_TASKQ_STOP((&logger_api_dev.wp_logger_task)); + + DEBUG_EVENT("%s\n", err ? "Logger TASKQ Successfully Stopped" : "Logger TASKQ Not Running"); + + logger_api_dev.magic_no = 0; + wan_clear_bit(0, &logger_api_dev.init); + + wp_logger_free_q(&logger_api_dev.wp_event_list); + wp_logger_free_q(&logger_api_dev.wp_event_free_list); + + if (logger_api_dev.cdev) { + wanpipe_cdev_free(logger_api_dev.cdev); + wan_free(logger_api_dev.cdev); + logger_api_dev.cdev=NULL; + } +} + +#if defined(__LINUX__) +EXPORT_SYMBOL(wp_logger_level_default); +EXPORT_SYMBOL(wp_logger_level_te1); +EXPORT_SYMBOL(wp_logger_level_hwec); +EXPORT_SYMBOL(wp_logger_level_tdmapi); +EXPORT_SYMBOL(wp_logger_level_fe); +EXPORT_SYMBOL(wp_logger_level_bri); +EXPORT_SYMBOL(wp_logger_input); +EXPORT_SYMBOL(wp_logger_repeating_message_filter); +#endif +/****** End ****************************************************************/ diff --git a/patches/kdrivers/src/net/wanpipe_multppp.c b/patches/kdrivers/src/net/wanpipe_multppp.c index 99ebb20..94885b3 100644 --- a/patches/kdrivers/src/net/wanpipe_multppp.c +++ b/patches/kdrivers/src/net/wanpipe_multppp.c @@ -97,6 +97,9 @@ #define MAX_TRACE_QUEUE 100 #define MAX_RX_QUEUE 100 + +WAN_DECLARE_NETDEV_OPS(wan_netdev_ops) + /******Data Structures*****************************************************/ /* This structure is placed in the private data area of the device structure. @@ -293,7 +296,7 @@ int wp_mprot_init (sdla_t* card, wandev_conf_t* conf) if (conf->comm_port != card->next->u.c.comm_port){ card->u.c.comm_port = conf->comm_port; }else{ - DEBUG_EVENT("%s: ERROR - %s port used!\n", + DEBUG_ERROR("%s: ERROR - %s port used!\n", card->wandev.name, PORT(conf->comm_port)); return -EINVAL; } @@ -301,7 +304,7 @@ int wp_mprot_init (sdla_t* card, wandev_conf_t* conf) card->u.c.comm_port = conf->comm_port; } }else{ - DEBUG_EVENT("%s: ERROR - Invalid Port Selected!\n", + DEBUG_ERROR("%s: ERROR - Invalid Port Selected!\n", card->wandev.name); return -EINVAL; } @@ -445,7 +448,7 @@ int wp_mprot_init (sdla_t* card, wandev_conf_t* conf) if ((card->u.c.comm_port == WANOPT_SEC && conf->electrical_interface == WANOPT_V35)&& card->type != SDLA_S514){ - DEBUG_EVENT("%s: ERROR - V35 Interface not supported on S508 %s port \n", + DEBUG_ERROR("%s: ERROR - V35 Interface not supported on S508 %s port \n", card->devname, PORT(card->u.c.comm_port)); return -EIO; } @@ -650,7 +653,7 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) } if(++card->wandev.new_if_cnt > 1) { - DEBUG_EVENT("%s: Error: Interface already configured!\n", + DEBUG_ERROR("%s: Error: Interface already configured!\n", card->devname); --card->wandev.new_if_cnt; return -EEXIST; @@ -894,7 +897,14 @@ static int new_if (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) wan_netif_set_priv(dev, chan); chan->dev=dev; #if defined(__LINUX__) - dev->init = &if_init; + WAN_NETDEV_OPS_BIND(dev,wan_netdev_ops); + WAN_NETDEV_OPS_INIT(dev,wan_netdev_ops,&if_init); + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&if_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&if_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&if_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&if_stats); + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&if_tx_timeout); + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,&if_do_ioctl); #else chan->common.iface.open = &if_open; chan->common.iface.close = &if_close; @@ -1019,7 +1029,8 @@ static int del_if (wan_device_t* wandev, netdevice_t* dev) * We must manually remove the ioctl call binding * since in some cases (mrouted) daemons continue * to call ioctl() after the device has gone down */ - dev->do_ioctl = NULL; + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,NULL); + #endif if (chan->common.prot_ptr){ @@ -1083,12 +1094,13 @@ static int if_init (netdevice_t* dev) */ /* Initialize device driver entry points */ - dev->open = &if_open; - dev->stop = &if_close; - dev->hard_start_xmit = &if_send; - dev->get_stats = &if_stats; + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&if_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&if_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&if_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&if_stats); + #if defined(LINUX_2_4)||defined(LINUX_2_6) - dev->tx_timeout = &if_tx_timeout; + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&if_tx_timeout); dev->watchdog_timeo = TX_TIMEOUT; #endif @@ -1103,7 +1115,7 @@ static int if_init (netdevice_t* dev) /* Overwrite the sppp ioctl, because we need to run * our debugging commands via ioctl(). However * call syncppp ioctl with in it :) */ - dev->do_ioctl = &if_do_ioctl; + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,&if_do_ioctl); /* Initialize hardware parameters */ dev->irq = wandev->irq; @@ -1983,7 +1995,7 @@ static void rx_intr (sdla_t* card) skb = wan_skb_alloc(len+2); if (skb == NULL) { if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: Error: No memory available!\n", + DEBUG_ERROR("%s: Error: No memory available!\n", card->devname); } ++card->wandev.stats.rx_dropped; @@ -2029,7 +2041,7 @@ static void rx_intr (sdla_t* card) if (wan_skb_queue_len(&chan->rx_queue) > MAX_RX_QUEUE){ wan_skb_free(skb); if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: Error Rx queue full, dropping pkt!\n", + DEBUG_ERROR("%s: Error Rx queue full, dropping pkt!\n", card->devname); } ++card->wandev.stats.rx_dropped; @@ -2052,7 +2064,7 @@ static void rx_intr (sdla_t* card) if (wan_skb_queue_len(&chan->rx_queue) > MAX_RX_QUEUE){ wan_skb_free(skb); if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: Error:(STACK) Rx queue full, dropping pkt!\n", + DEBUG_ERROR("%s: Error:(STACK) Rx queue full, dropping pkt!\n", card->devname); } ++card->wandev.stats.rx_dropped; @@ -2067,7 +2079,7 @@ static void rx_intr (sdla_t* card) if (wan_skb_queue_len(&chan->rx_queue) > MAX_RX_QUEUE){ wan_skb_free(skb); if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: Error Rx queue full, dropping pkt!\n", + DEBUG_ERROR("%s: Error Rx queue full, dropping pkt!\n", card->devname); } ++card->wandev.stats.rx_dropped; @@ -2183,7 +2195,7 @@ static int set_asy_config(sdla_t* card) chdlc_error (card, err, mb); if (err == 0x4F){ - DEBUG_EVENT("%s: Error: ASYNC Not Supported by Firmware!\n", + DEBUG_ERROR("%s: Error: ASYNC Not Supported by Firmware!\n", card->devname); } return err; @@ -2591,7 +2603,7 @@ if_do_ioctl(netdevice_t *dev, struct ifreq *ifr, wan_ioctl_cmd_t cmd) * thus we can release the irq */ if (wan_atomic_read(&chan->udp_pkt_len) > sizeof(wan_udp_pkt_t)){ - DEBUG_EVENT("%s: Error: Pipemon buf too bit on the way up! %i\n", + DEBUG_ERROR("%s: Error: Pipemon buf too bit on the way up! %i\n", card->devname,wan_atomic_read(&chan->udp_pkt_len)); wan_atomic_set(&chan->udp_pkt_len,0); return -EINVAL; @@ -2715,7 +2727,7 @@ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, wan_set_bit (0,&trace_info->tracing_enabled); }else{ - DEBUG_EVENT("%s: Error: Trace running!\n", + DEBUG_ERROR("%s: Error: Trace running!\n", card->devname); udp_hdr->wan_udphdr_return_code = 2; } @@ -2749,7 +2761,7 @@ static int process_udp_mgmt_pkt(sdla_t* card, netdevice_t* dev, if(wan_test_bit(0,&trace_info->tracing_enabled)){ trace_info->trace_timeout = SYSTEM_TICKS; }else{ - DEBUG_EVENT("%s: Error trace not enabled\n", + DEBUG_ERROR("%s: Error trace not enabled\n", card->devname); /* set return code */ udp_hdr->wan_udphdr_return_code = 1; @@ -3626,7 +3638,7 @@ static void wp_bh (void *data, int pending) memset(rx_hdr,0,sizeof(api_rx_hdr_t)); }else{ if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("%s: Error Rx pkt headroom %d < %d\n", + DEBUG_ERROR("%s: Error Rx pkt headroom %d < %d\n", chan->if_name, wan_skb_headroom(skb), (int)sizeof(api_rx_hdr_t)); diff --git a/patches/kdrivers/src/net/wanpipe_syncppp.c b/patches/kdrivers/src/net/wanpipe_syncppp.c index e1f9bd1..77a4249 100644 --- a/patches/kdrivers/src/net/wanpipe_syncppp.c +++ b/patches/kdrivers/src/net/wanpipe_syncppp.c @@ -139,7 +139,7 @@ #define CHAP_MD5 5 /* hash algorithm - MD5 */ - +WAN_DECLARE_NETDEV_OPS(wan_netdev_ops) struct ppp_header { u8 address; @@ -2316,14 +2316,14 @@ void wp_sppp_attach(struct ppp_device *pd) /* * These 4 are callers but MUST also call sppp_ functions */ - dev->do_ioctl = wp_sppp_do_ioctl; + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,wp_sppp_do_ioctl); + #if 0 dev->get_stats = NULL; /* Let the driver override these */ dev->open = wp_sppp_open; dev->stop = wp_sppp_close; #endif - dev->change_mtu = wp_sppp_change_mtu; - + WAN_NETDEV_OPS_MTU(dev,wan_netdev_ops,wp_sppp_change_mtu); dev->flags = IFF_MULTICAST|IFF_POINTOPOINT|IFF_NOARP; #if 0 diff --git a/patches/kdrivers/src/net/wanpipe_tdm_api.c b/patches/kdrivers/src/net/wanpipe_tdm_api.c index ed6c024..38cfd53 100644 --- a/patches/kdrivers/src/net/wanpipe_tdm_api.c +++ b/patches/kdrivers/src/net/wanpipe_tdm_api.c @@ -5,20 +5,28 @@ * * Authors: Nenad Corbic * -* Copyright: (c) 2003-2009 Sangoma Technologies Inc. +* Copyright: (c) 2003-2010 Sangoma Technologies Inc. * * 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. * ============================================================================ -* Oct 04, 2005 Nenad Corbic Initial version. -* Jul 27, 2006 David Rokhvarg -* Ported to Windows. -* Mar 10, 2008 David Rokhvarg -* Added BRI LoopBack control. +* +* Nov 16, 2009 David Rokhvarg +* Enabled support for WP_API_CMD_SET_RX_GAINS and +* WP_API_CMD_SET_TX_GAINS commands in Sangoma Windows Driver. +* * Nov 19, 2008 David Rokhvarg * Added RBS Chan/Span poll. +* +* Mar 10, 2008 David Rokhvarg +* Added BRI LoopBack control. +* +* Jul 27, 2006 David Rokhvarg +* Ported to Windows. +* +* Oct 04, 2005 Nenad Corbic Initial version. *****************************************************************************/ @@ -31,13 +39,7 @@ #include "wanpipe_tdm_api.h" #include "wanpipe_cdev_iface.h" #include "wanpipe_timer_iface.h" - -#if defined(__WINDOWS__) - -#define POLLWRNORM 0 -#define POLLRDNORM 0 - -#endif/* __WINDOWS__ */ +#include "wanpipe_mtp1.h" /*============================================================== @@ -69,9 +71,10 @@ #undef DEBUG_API_POLL #ifdef __WINDOWS__ -#define wptdm_os_lock_irq(card,flags) wan_spin_lock_irq(card,flags) -#define wptdm_os_unlock_irq(card,flags) wan_spin_unlock_irq(card,flags) +# define wptdm_os_lock_irq(card,flags) wan_spin_lock_irq(card,flags) +# define wptdm_os_unlock_irq(card,flags) wan_spin_unlock_irq(card,flags) #else + #if 0 #warning "WANPIPE TDM API - OS LOCK DEFINED -- DEBUGGING" #define wptdm_os_lock_irq(card,flags) wan_spin_lock_irq(card,flags) @@ -89,7 +92,6 @@ static u8 *rx_gains; static u8 *tx_gains; static wanpipe_tdm_api_dev_t tdmapi_ctrl; - /*============================================================== Prototypes */ @@ -134,6 +136,15 @@ static void wp_api_task_func (IN PKDPC Dpc, IN PVOID tdmapi_ptr, IN PVOID System static void wp_api_task_func (void * tdmapi_ptr, int arg); #endif + +#if 0 +static int wp_tdm_api_mtp1_rx_data (void *priv_ptr, u8 *rx_data, int rx_len); +static int wp_tdm_api_mtp1_rx_reject (void *priv_prt, char *reason); +static int wp_tdm_api_mtp1_rx_suerm (void *priv_ptr); +static int wp_tdm_api_tmp1_wakup(void *priv_ptr); +#endif +static int wp_tdmapi_tx_timeout(void *obj); + /*============================================================== Global Variables */ @@ -242,7 +253,7 @@ static void wp_tdmapi_init_buffs(wanpipe_tdm_api_dev_t *tdm_api, int option) } if (option == WP_API_FLUSH_ALL || option == WP_API_FLUSH_EVENT) { - /* The global ctrl device does not use event_free_list */ + /* The global Ctrl device does NOT use event_free_list */ if (tdm_api == &tdmapi_ctrl) { while((skb=wan_skb_dequeue(&tdm_api->wp_event_list))) { wan_skb_init(skb,hdr_sz); @@ -347,11 +358,10 @@ static inline void wp_wakeup_tx_tdmapi(wanpipe_tdm_api_dev_t *tdm_api) } } + static int wp_tdmapi_reg_globals(void) { int err=0; - wanpipe_tdm_api_dev_t *tdm_api; - wanpipe_cdev_t *cdev; rx_gains=NULL; tx_gains=NULL; @@ -359,52 +369,49 @@ static int wp_tdmapi_reg_globals(void) /* Header Sanity Check */ if ((sizeof(wp_api_hdr_t)!=WAN_MAX_HDR_SZ) || (sizeof(wp_api_hdr_t)!=WAN_MAX_HDR_SZ)) { - DEBUG_EVENT("%s: Internal Error: RxHDR=%i != Expected RxHDR=%i TxHDR=%i != Expected TxHDR=%i\n", + DEBUG_ERROR("%s: Internal Error: RxHDR=%i != Expected RxHDR=%i TxHDR=%i != Expected TxHDR=%i\n", __FUNCTION__,sizeof(wp_api_hdr_t),WAN_MAX_HDR_SZ,sizeof(wp_api_hdr_t),WAN_MAX_HDR_SZ); return -EINVAL; } /* Internal Sanity Check */ if (WANPIPE_API_CMD_SZ != sizeof(wanpipe_api_cmd_t)) { - DEBUG_EVENT("%s: Internal Error: Wanpipe API CMD Structure Exceeds Limit=%i Size=%i!\n", + DEBUG_ERROR("%s: Internal Error: Wanpipe API CMD Structure Exceeds Limit=%i Size=%i!\n", __FUNCTION__, WANPIPE_API_CMD_SZ,sizeof(wanpipe_api_cmd_t)); return -EINVAL; } /* Internal Sanity Check */ if (WAN_MAX_EVENT_SZ != sizeof(wp_api_event_t)) { - DEBUG_EVENT("%s: Internal Error: Wanpipe Event Structure Exceeds Limit=%i Size=%i!\n", + DEBUG_ERROR("%s: Internal Error: Wanpipe Event Structure Exceeds Limit=%i Size=%i!\n", __FUNCTION__, WAN_MAX_EVENT_SZ,sizeof(wp_api_event_t)); return EINVAL; } /* Internal Sanity Check */ if (WAN_MAX_HDR_SZ != sizeof(wp_api_hdr_t)) { - DEBUG_EVENT("%s: Internal Error: Wanpipe Header Structure Exceeds Limit=%i Size=%i!\n", + DEBUG_ERROR("%s: Internal Error: Wanpipe Header Structure Exceeds Limit=%i Size=%i!\n", __FUNCTION__, WAN_MAX_HDR_SZ,sizeof(wp_api_event_t)); return EINVAL; } /* Internal Sanity Check */ if (WANPIPE_API_DEV_CFG_SZ != sizeof(wanpipe_api_dev_cfg_t)) { - DEBUG_EVENT("%s: Internal Error: Wanpipe Dev Cfg Structure Exceeds Limit=%i Size=%i!\n", + DEBUG_ERROR("%s: Internal Error: Wanpipe Dev Cfg Structure Exceeds Limit=%i Size=%i!\n", __FUNCTION__, WANPIPE_API_DEV_CFG_SZ,sizeof(wanpipe_api_dev_cfg_t)); return EINVAL; } + if (WANPIPE_API_DEV_CFG_MAX_SZ != sizeof(wanpipe_api_dev_cfg_t)) { + DEBUG_ERROR("%s: Internal Error: Wanpipe Dev Cfg Structure Exceeds MAX Limit=%i Size=%i!\n", + __FUNCTION__, WANPIPE_API_DEV_CFG_MAX_SZ,sizeof(wanpipe_api_dev_cfg_t)); + return EINVAL; + } + DEBUG_EVENT("WANPIPE API: HDR_SZ=%i CMD_SZ=%i EVENT_SZ=%i DEV_CFG_SZ=%i\n", sizeof(wp_api_hdr_t),sizeof(wanpipe_api_cmd_t), sizeof(wp_api_event_t),sizeof(wanpipe_api_dev_cfg_t)); - cdev=wan_kmalloc(sizeof(wanpipe_cdev_t)); - if (!cdev) { - return -ENOMEM; - } - - memset(cdev,0,sizeof(wanpipe_cdev_t)); - - DEBUG_TDMAPI("%s: Registering Wanpipe TDM Device!\n",__FUNCTION__); - wp_tdmapi_fops.open = wp_tdmapi_open; wp_tdmapi_fops.close = wp_tdmapi_release; wp_tdmapi_fops.ioctl = wanpipe_tdm_api_ioctl; @@ -413,6 +420,18 @@ static int wp_tdmapi_reg_globals(void) wp_tdmapi_fops.poll = wp_tdmapi_poll; { + wanpipe_cdev_t *cdev; + wanpipe_tdm_api_dev_t *tdm_api; + + cdev=wan_kmalloc(sizeof(wanpipe_cdev_t)); + if (!cdev) { + return -ENOMEM; + } + + memset(cdev,0,sizeof(wanpipe_cdev_t)); + + DEBUG_TDMAPI("%s(): Registering Wanpipe CTRL Device!\n",__FUNCTION__); + tdm_api=&tdmapi_ctrl; memset(tdm_api,0,sizeof(wanpipe_tdm_api_dev_t)); @@ -437,10 +456,14 @@ static int wp_tdmapi_reg_globals(void) memcpy(&cdev->ops, &wp_tdmapi_fops, sizeof(wanpipe_cdev_ops_t)); err=wanpipe_cdev_tdm_ctrl_create(cdev); - + if(err){ + return err; + } } - wanpipe_wandev_timer_create(); +#if !defined(__WINDOWS__) + err=wanpipe_wandev_timer_create(); +#endif wp_tdmapi_fops.read = wp_tdmapi_read_msg; wp_tdmapi_fops.write = wp_tdmapi_write_msg; @@ -448,23 +471,24 @@ static int wp_tdmapi_reg_globals(void) return err; } -static int wp_tdmapi_unreg_globals(void) +static void wp_ctrl_dev_delete(void) { unsigned long timeout=SYSTEM_TICKS; - DEBUG_EVENT("%s: Unregistering Wanpipe TDM Device!\n",__FUNCTION__); - - wanpipe_wandev_timer_free(); wan_clear_bit(0,&tdmapi_ctrl.init); - + while(wan_test_bit(0,&tdmapi_ctrl.used)) { if (SYSTEM_TICKS - timeout > HZ*2) { - DEBUG_EVENT("wanpipe_tdm_api: Warning: Ctrl Device still in use on shutdown!\n"); + if(wan_test_bit(0,&tdmapi_ctrl.used)){ + DEBUG_WARNING("wanpipe_tdm_api: Warning: Ctrl Device still in use on shutdown!\n"); + } break; } WP_DELAY(1000); - WP_SCHEDULE("tdmapi_ctlr",10); - wp_wakeup_rx_tdmapi(&tdmapi_ctrl); + if(wan_test_bit(0,&tdmapi_ctrl.used)){ + wp_wakeup_rx_tdmapi(&tdmapi_ctrl); + WP_SCHEDULE("tdmapi_ctlr",10); + } } wp_tdmapi_free_buffs(&tdmapi_ctrl); @@ -474,6 +498,17 @@ static int wp_tdmapi_unreg_globals(void) wan_free(tdmapi_ctrl.cdev); tdmapi_ctrl.cdev=NULL; } +} + +static int wp_tdmapi_unreg_globals(void) +{ + DEBUG_EVENT("%s(): Unregistering Global API Devices!\n",__FUNCTION__); + +#if !defined(__WINDOWS__) + wanpipe_wandev_timer_free(); +#endif + + wp_ctrl_dev_delete(); if (tx_gains) { wan_free(tx_gains); @@ -483,6 +518,7 @@ static int wp_tdmapi_unreg_globals(void) wan_free(rx_gains); rx_gains=NULL; } + return 0; } @@ -500,7 +536,6 @@ int wanpipe_tdm_api_reg(wanpipe_tdm_api_dev_t *tdm_api) wan_spin_lock_init(&tdm_api->lock, "wan_tdmapi_lock"); - if (wp_tdmapi_global_cnt == 0){ err=wp_tdmapi_reg_globals(); if (err) { @@ -607,7 +642,7 @@ int wanpipe_tdm_api_reg(wanpipe_tdm_api_dev_t *tdm_api) /* We are expecting tx_q_len for hdlc * to be configured from upper layer */ if (tdm_api->cfg.tx_queue_sz == 0) { - DEBUG_EVENT("%s: Internal Error: Tx Q Len not specified by lower layer!\n", + DEBUG_ERROR("%s: Internal Error: Tx Q Len not specified by lower layer!\n", __FUNCTION__); err=-EINVAL; goto tdm_api_reg_error_exit; @@ -625,7 +660,7 @@ int wanpipe_tdm_api_reg(wanpipe_tdm_api_dev_t *tdm_api) if (!tdm_api->cfg.hw_mtu_mru || !tdm_api->cfg.usr_period || !tdm_api->cfg.usr_mtu_mru) { - DEBUG_EVENT("%s: Internal Error: API hwmtu=%i period=%i usr_mtu_mru=%i!\n", + DEBUG_ERROR("%s: Internal Error: API hwmtu=%i period=%i usr_mtu_mru=%i!\n", __FUNCTION__, tdm_api->cfg.hw_mtu_mru, tdm_api->cfg.usr_period, tdm_api->cfg.usr_mtu_mru); err=-EINVAL; @@ -649,7 +684,7 @@ int wanpipe_tdm_api_reg(wanpipe_tdm_api_dev_t *tdm_api) /* We are expecting tx_q_len for hdlc * to be configured from upper layer */ if (tdm_api->cfg.tx_queue_sz == 0) { - DEBUG_EVENT("%s: Internal Error: Tx Q Len not specified by lower layer!\n", + DEBUG_ERROR("%s: Internal Error: Tx Q Len not specified by lower layer!\n", __FUNCTION__); err=-EINVAL; goto tdm_api_reg_error_exit; @@ -694,6 +729,9 @@ int wanpipe_tdm_api_reg(wanpipe_tdm_api_dev_t *tdm_api) #if defined(__WINDOWS__) /* Analog always supports DTMF detection */ tdm_api->dtmfsupport = WANOPT_YES; + /* Need pointer to PnP Fdo Device Extension for + * handling of PnP notifications. */ + cdev->PnpFdoPdx = card->level2_fdo_pdx; #endif cdev->dev_ptr=tdm_api; @@ -705,6 +743,28 @@ int wanpipe_tdm_api_reg(wanpipe_tdm_api_dev_t *tdm_api) sprintf(cdev->name,tmp_name,sizeof(cdev->name)); memcpy(&cdev->ops, &wp_tdmapi_fops, sizeof(wanpipe_cdev_ops_t)); +#if 0 + if (tdm_api->operation_mode == WP_TDM_OPMODE_MTP1) { + wp_mtp1_reg_t reg; + + reg.priv_ptr=tdm_api; + reg.rx_data=wp_tdm_api_mtp1_rx_data; + reg.rx_reject=wp_tdm_api_mtp1_rx_reject; + reg.rx_suerm=wp_tdm_api_mtp1_rx_suerm; + reg.wakeup=wp_tdm_api_tmp1_wakup; + + tdm_api->mtp1_dev = wp_mtp1_register(®); + if (tdm_api->mtp1_dev == NULL) { + goto tdm_api_reg_error_exit; + } + } +#endif + + if (tdm_api->hdlc_framing) { + /* timeout may occur only on HDLC channels */ + cdev->ops.tx_timeout = wp_tdmapi_tx_timeout; + } + err=wanpipe_cdev_tdm_create(cdev); if (err) { goto tdm_api_reg_error_exit; @@ -723,8 +783,10 @@ int wanpipe_tdm_api_reg(wanpipe_tdm_api_dev_t *tdm_api) /* Enable default events for analog */ if (card->wandev.config_id == WANCONFIG_AFT_ANALOG) { + memset(&wp_cmd, 0x00, sizeof(wp_cmd)); wp_cmd.cmd = WP_API_CMD_SET_EVENT; wp_cmd.event.wp_api_event_mode = WP_API_EVENT_ENABLE; + wp_cmd.chan = tdm_api->tdm_chan; wp_cmd_ptr=&wp_cmd; if (fe->rm_param.mod[(tdm_api->tdm_chan) - 1].type == MOD_TYPE_FXS) { @@ -801,16 +863,23 @@ int wanpipe_tdm_api_unreg(wanpipe_tdm_api_dev_t *tdm_api) wan_set_bit(WP_TDM_DOWN,&tdm_api->critical); if (wan_test_bit(0,&tdm_api->used)) { +#if defined(__WINDOWS__) + /* During transition to System StandBy state an application may keep + * open file descriptors. Do NOT fail unregister in this case. */ + DEBUG_WARNING("%s: Warning: unregister request for Span=%i Chan=%i while Open Handles exist!\n", + tdm_api->name, tdm_api->tdm_span, tdm_api->tdm_chan); +#else DEBUG_EVENT("%s Failed to unreg Span=%i Chan=%i: BUSY!\n", tdm_api->name,tdm_api->tdm_span,tdm_api->tdm_chan); return -EBUSY; +#endif } - if (wp_tdmapi_global_cnt == 1 && - wan_test_bit(0,&tdmapi_ctrl.used)) { - DEBUG_EVENT("%s Failed to unreg Span=%i Chan=%i: CTRL DEVICE BUSY!\n", - tdm_api->name,tdm_api->tdm_span,tdm_api->tdm_chan); - return -EBUSY; + if (wp_tdmapi_global_cnt == 1){ + if(wan_test_bit(0,&tdmapi_ctrl.used)) { + DEBUG_EVENT("%s: Failed to unreg CTRL DEVICE - BUSY!\n", tdm_api->name); + return -EBUSY; + } } #if defined(WAN_TASKQ_STOP) @@ -945,7 +1014,7 @@ int wanpipe_tdm_api_rbsbits_poll(sdla_t * card) x, WAN_TE_RBS_UPDATE|WAN_TE_RBS_REPORT); } else { - DEBUG_EVENT("%s: Internal Error [%s:%d]!\n", card->devname, __FUNCTION__,__LINE__); + DEBUG_ERROR("%s: Internal Error [%s:%d]!\n", card->devname, __FUNCTION__,__LINE__); } } } @@ -953,7 +1022,7 @@ int wanpipe_tdm_api_rbsbits_poll(sdla_t * card) } else { if (card->wandev.fe_iface.check_rbsbits == NULL){ - DEBUG_EVENT("%s: Internal Error [%s:%d]!\n", + DEBUG_ERROR("%s: Internal Error [%s:%d]!\n", card->devname, __FUNCTION__,__LINE__); return -EINVAL; @@ -992,17 +1061,21 @@ static int wp_tdmapi_open(void *obj) return -ENODEV; } - if (tdm_api != &tdmapi_ctrl && !tdm_api->card) { - DEBUG_EVENT("%s: Error: Wanpipe API Device does not have a card pointer! Internal error!\n",tdm_api->name); - return -ENODEV; + if (!tdm_api->card) { + if(tdm_api != &tdmapi_ctrl){ + DEBUG_ERROR("%s: Error: Wanpipe API Device does not have a card pointer! Internal error!\n",tdm_api->name); + return -ENODEV; + } } cnt = wp_tdm_inc_open_cnt(tdm_api); + if (cnt == 1) { wp_tdmapi_init_buffs(tdm_api, WP_API_FLUSH_ALL); tdm_api->rx_gain=NULL; tdm_api->tx_gain=NULL; + tdm_api->cfg.loop=0; } wan_set_bit(0,&tdm_api->used); @@ -1076,19 +1149,21 @@ static int wp_tdmapi_read_msg(void *obj , netskb_t **skb_ptr, wp_api_hdr_t *hdr, wan_skb_len(skb) < sizeof(wp_api_hdr_t)){ - DEBUG_EVENT("%s:%d TDMAPI READ: Error: Count=%i < Skb=%i < HDR=%i Critical Error\n", + DEBUG_ERROR("%s:%d TDMAPI READ: Error: Count=%i < Skb=%i < HDR=%i Critical Error\n", __FUNCTION__,__LINE__,count,wan_skb_len(skb),sizeof(wp_api_hdr_t)); wan_skb_free(skb); hdr->wp_api_hdr_operation_status = SANG_STATUS_BUFFER_TOO_SMALL; return -EFAULT; } - buf=(u8*)wan_skb_data(skb); + /* copy header info (such as time stamp) */ + memcpy(hdr, buf, sizeof(wp_api_hdr_t)); + if (WPTDM_SPAN_OP_MODE(tdm_api) || tdm_api->hdlc_framing) { - memcpy(hdr,buf,sizeof(wp_api_hdr_t)); + /* do nothing */ } else { netskb_t *tmp_skb; @@ -1102,6 +1177,7 @@ static int wp_tdmapi_read_msg(void *obj , netskb_t **skb_ptr, wp_api_hdr_t *hdr, } } + hdr->wp_api_rx_hdr_number_of_frames_in_queue = (u8)rx_q_len; hdr->wp_api_rx_hdr_max_queue_length = (u8)tdm_api->cfg.rx_queue_sz; @@ -1111,7 +1187,7 @@ static int wp_tdmapi_read_msg(void *obj , netskb_t **skb_ptr, wp_api_hdr_t *hdr, if (wan_skb_len(skb) >= sizeof(wp_api_hdr_t)) { memcpy(buf,hdr,sizeof(wp_api_hdr_t)); } else { - DEBUG_EVENT("%s: Internal Error: Rx Data Invalid %i\n",tdm_api->name,wan_skb_len(skb)); + DEBUG_ERROR("%s: Internal Error: Rx Data Invalid %i\n",tdm_api->name,wan_skb_len(skb)); } *skb_ptr = skb; @@ -1135,6 +1211,8 @@ static int wp_tdmapi_tx(wanpipe_tdm_api_dev_t *tdm_api, netskb_t *skb, wp_api_h int err=-EINVAL; if (wan_test_and_set_bit(WP_TDM_HDLC_TX,&tdm_api->critical)){ + DEBUG_ERROR("%s: Error: %s() WP_TDM_HDLC_TX critical\n", + tdm_api->name, __FUNCTION__); hdr->wp_api_hdr_operation_status = SANG_STATUS_GENERAL_ERROR; return -EINVAL; } @@ -1142,6 +1220,8 @@ static int wp_tdmapi_tx(wanpipe_tdm_api_dev_t *tdm_api, netskb_t *skb, wp_api_h if (tdm_api->write_hdlc_frame) { err=tdm_api->write_hdlc_frame(tdm_api->chan,skb, hdr); } else { + DEBUG_ERROR("%s: Error: %s() tdm_api->write_hdlc_frame is NULL: critical\n", + tdm_api->name, __FUNCTION__); return -EINVAL; } @@ -1198,7 +1278,9 @@ static int wp_tdmapi_write_msg(void *obj, netskb_t *skb, wp_api_hdr_t *hdr) if (tdm_api == &tdmapi_ctrl) { hdr->wp_api_hdr_operation_status = SANG_STATUS_INVALID_DEVICE; WP_AFT_CHAN_ERROR_STATS(tdm_api->cfg.stats,tx_errors); - DEBUG_EVENT("Error: Wanpipe API: wp_tdmapi_write_msg() on ctrl device!\n"); + if (WAN_NET_RATELIMIT()) { + DEBUG_ERROR("Error: Wanpipe API: wp_tdmapi_write_msg() on ctrl device!\n"); + } return -EINVAL; } @@ -1208,30 +1290,38 @@ static int wp_tdmapi_write_msg(void *obj, netskb_t *skb, wp_api_hdr_t *hdr) if (wan_test_bit(WP_TDM_DOWN,&tdm_api->critical)) { hdr->wp_api_hdr_operation_status = SANG_STATUS_GENERAL_ERROR; WP_AFT_CHAN_ERROR_STATS(tdm_api->cfg.stats,tx_errors); - DEBUG_EVENT("Error: Wanpipe API: wp_tdmapi_write_msg() WP_TDM_DOWN"); + if (WAN_NET_RATELIMIT()) { + DEBUG_ERROR("Error: Wanpipe API: wp_tdmapi_write_msg() WP_TDM_DOWN\n"); + } return -ENODEV; } if (wan_skb_len(skb) <= sizeof(wp_api_hdr_t)) { hdr->wp_api_hdr_operation_status = SANG_STATUS_BUFFER_TOO_SMALL; WP_AFT_CHAN_ERROR_STATS(tdm_api->cfg.stats,tx_errors); - DEBUG_EVENT("Error: Wanpipe API: wp_tdmapi_write_msg() datalen=%i < hdrlen=%i\n", + if (WAN_NET_RATELIMIT()) { + DEBUG_ERROR("Error: Wanpipe API: wp_tdmapi_write_msg() datalen=%i < hdrlen=%i\n", wan_skb_len(skb),sizeof(wp_api_hdr_t)); + } return -EINVAL; } if (WPTDM_SPAN_OP_MODE(tdm_api) || tdm_api->hdlc_framing) { if (wan_skb_len(skb) > (int)(tdm_api->cfg.usr_mtu_mru + sizeof(wp_api_hdr_t))) { - DEBUG_EVENT("%s: Error: TDM API Tx packet too big %d Max=%d\n", - tdm_api->name,wan_skb_len(skb), (tdm_api->cfg.usr_mtu_mru + sizeof(wp_api_hdr_t))); + if (WAN_NET_RATELIMIT()) { + DEBUG_ERROR("%s: Error: TDM API Tx packet too big %d Max=%d\n", + tdm_api->name,wan_skb_len(skb), (tdm_api->cfg.usr_mtu_mru + sizeof(wp_api_hdr_t))); + } hdr->wp_api_hdr_operation_status =SANG_STATUS_TX_DATA_TOO_LONG; WP_AFT_CHAN_ERROR_STATS(tdm_api->cfg.stats,tx_errors); return -EFBIG; } } else { if (wan_skb_len(skb) > (WP_TDM_API_MAX_LEN+sizeof(wp_api_hdr_t))) { - DEBUG_EVENT("%s: Error: TDM API Tx packet too big %d\n", - tdm_api->name, wan_skb_len(skb)); + if (WAN_NET_RATELIMIT()) { + DEBUG_ERROR("%s: Error: TDM API Tx packet too big %d\n", + tdm_api->name, wan_skb_len(skb)); + } hdr->wp_api_hdr_operation_status =SANG_STATUS_TX_DATA_TOO_LONG; WP_AFT_CHAN_ERROR_STATS(tdm_api->cfg.stats,tx_errors); return -EFBIG; @@ -1257,6 +1347,9 @@ static int wp_tdmapi_write_msg(void *obj, netskb_t *skb, wp_api_hdr_t *hdr) if (wan_skb_queue_len(&tdm_api->wp_tx_list) >= (int)tdm_api->cfg.tx_queue_sz){ wp_tdm_api_stop(tdm_api); + hdr->tx_h.current_number_of_frames_in_tx_queue = (u8)wan_skb_queue_len(&tdm_api->wp_tx_list); + hdr->tx_h.tx_idle_packets = tdm_api->cfg.stats.tx_idle_packets; + hdr->tx_h.max_tx_queue_length = (u8)tdm_api->cfg.tx_queue_sz; err=1; DEBUG_TDMAPI("Error: Wanpipe API: wp_tdmapi_write_msg() TxList=%i >= MaxTxList=%i\n", wan_skb_queue_len(&tdm_api->wp_tx_list), tdm_api->cfg.tx_queue_sz); @@ -1283,6 +1376,7 @@ static int wp_tdmapi_write_msg(void *obj, netskb_t *skb, wp_api_hdr_t *hdr) wptdm_os_lock_irq(&card->wandev.lock,&irq_flags); wan_skb_queue_tail(&tdm_api->wp_tx_list, skb); hdr->tx_h.current_number_of_frames_in_tx_queue = (u8)wan_skb_queue_len(&tdm_api->wp_tx_list); + hdr->tx_h.tx_idle_packets = tdm_api->cfg.stats.tx_idle_packets; wptdm_os_unlock_irq(&card->wandev.lock,&irq_flags); hdr->tx_h.max_tx_queue_length = (u8)tdm_api->cfg.tx_queue_sz; @@ -1317,6 +1411,20 @@ wp_tdmapi_write_msg_exit: } +static int wp_tdmapi_tx_timeout(void *obj) +{ + wanpipe_tdm_api_dev_t *tdm_api = (wanpipe_tdm_api_dev_t*)obj; + int err=-EINVAL; + + if (tdm_api->write_hdlc_timeout) { + /* Call timeout function with lock parameter set */ + err=tdm_api->write_hdlc_timeout(tdm_api->chan, 1); + } + + DEBUG_TDMAPI("%s(): %s\n", __FUNCTION__, tdm_api->name); + + return err; +} static int wp_tdmapi_release(void *obj) { @@ -1353,7 +1461,7 @@ static int wan_skb_push_to_ctrl_event (netskb_t *skb) netskb_t *ctrl_skb; if (!skb || !tdm_api) { - DEBUG_EVENT("TDMAPI: Error: Null skb argument!\n"); + DEBUG_ERROR("TDMAPI: Error: Null skb argument!\n"); return -ENODEV; } @@ -1371,6 +1479,7 @@ static int wan_skb_push_to_ctrl_event (netskb_t *skb) return 0; } + #ifdef DEBUG_API_POLL # if defined(__WINDOWS__) # pragma message ("POLL Debugging Enabled") @@ -1382,7 +1491,7 @@ static int gpoll_cnt=0; static unsigned int wp_tdmapi_poll(void *obj) { - int ret=0; + int ret=0,rc; wanpipe_tdm_api_dev_t *tdm_api = (wanpipe_tdm_api_dev_t*)obj; sdla_t *card; wan_smp_flag_t irq_flags; @@ -1399,13 +1508,14 @@ static unsigned int wp_tdmapi_poll(void *obj) if (tdm_api == &tdmapi_ctrl) { /* intentionally NOT locking! */ - if (wan_skb_queue_len(&tdmapi_ctrl.wp_event_list)) { + if (wan_skb_queue_len(&tdm_api->wp_event_list)) { /* Indicate an exception */ ret |= POLLPRI; } return ret; } + card = tdm_api->card; if (!card) { return -ENODEV; @@ -1426,7 +1536,7 @@ static unsigned int wp_tdmapi_poll(void *obj) } #endif - wptdm_os_lock_irq(&card->wandev.lock,&irq_flags); + wan_spin_lock_irq(&card->wandev.lock,&irq_flags); /* Tx Poll */ if (!wan_test_bit(0,&tdm_api->cfg.tx_disable)){ @@ -1436,12 +1546,25 @@ static unsigned int wp_tdmapi_poll(void *obj) ret |= POLLOUT | POLLWRNORM; } } else { - if (!is_tdm_api_stopped(tdm_api)){ + + rc=1; + if (tdm_api->write_hdlc_check) { + rc=tdm_api->write_hdlc_check(tdm_api->chan, 0); + } + + if (!is_tdm_api_stopped(tdm_api) || rc == 0 ){ +#if 0 + if (rc == 0 && is_tdm_api_stopped(tdm_api)) { +#warning "NENAD" + DEBUG_EVENT("%s: POLL is_tdm_api_stopped()=%i but rc=%i\n", + tdmapi->name,is_tdm_api_stopped(tdm_api),rc); + } +#endif wp_tdm_api_start(tdm_api); ret |= POLLOUT | POLLWRNORM; } } - } + } /* Rx Poll */ if (!wan_test_bit(0,&tdm_api->cfg.rx_disable) && @@ -1454,7 +1577,7 @@ static unsigned int wp_tdmapi_poll(void *obj) ret |= POLLPRI; } - wptdm_os_unlock_irq(&card->wandev.lock,&irq_flags); + wan_spin_unlock_irq(&card->wandev.lock,&irq_flags); return ret; } @@ -1476,7 +1599,10 @@ static int wanpipe_tdm_api_ioctl_handle_tdm_api_cmd(wanpipe_tdm_api_dev_t *tdm_a sdla_fe_t *fe = NULL; sdla_t *card=NULL; wanpipe_tdm_api_card_dev_t *tdm_card_dev=NULL; - wan_smp_flag_t flags,irq_flags; + wan_smp_flag_t flags,irq_flags; + + int channel=tdm_api->tdm_chan; + int span=0; flags=0; irq_flags=0; @@ -1502,6 +1628,24 @@ static int wanpipe_tdm_api_ioctl_handle_tdm_api_cmd(wanpipe_tdm_api_dev_t *tdm_a #endif cmd=usr_tdm_api.cmd; + + if (WPTDM_SPAN_OP_MODE(tdm_api)) { + + if (usr_tdm_api.span) { + span=usr_tdm_api.span; + } + + if (usr_tdm_api.chan) { + channel=usr_tdm_api.chan; + } + } + + if (channel >= MAX_TDM_API_CHANNELS) { + DEBUG_EVENT("%s: %s(): Error: Invalid channel selected %i (Max=%i)\n", + tdm_api->name, __FUNCTION__, channel, MAX_TDM_API_CHANNELS); + return -EINVAL; + } + usr_tdm_api.result=SANG_STATUS_SUCCESS; DEBUG_TDMAPI("%s: TDM API CMD: %i CMD Size=%i HDR Size=%i Even Size=%i\n", @@ -1549,7 +1693,7 @@ static int wanpipe_tdm_api_ioctl_handle_tdm_api_cmd(wanpipe_tdm_api_dev_t *tdm_a break; } - memcpy(&usr_tdm_api.event,wan_skb_data(skb),sizeof(wp_api_event_t)); + memcpy(&usr_tdm_api.event, wan_skb_data(skb), sizeof(wp_api_event_t)); wan_skb_free(skb); err=0; @@ -1713,13 +1857,15 @@ static int wanpipe_tdm_api_ioctl_handle_tdm_api_cmd(wanpipe_tdm_api_dev_t *tdm_a usr_tdm_api.ec_tap = tdm_api->cfg.ec_tap; break; - case WP_API_CMD_ENABLE_HWEC: if (card && card->wandev.ec_enable) { wan_smp_flag_t smp_flags1; card->hw_iface.hw_lock(card->hw,&smp_flags1); - card->wandev.ec_enable(card, 1, tdm_api->tdm_chan); + err=card->wandev.ec_enable(card, 1, channel); card->hw_iface.hw_unlock(card->hw,&smp_flags1); + } else{ + usr_tdm_api.result=SANG_STATUS_OPTION_NOT_SUPPORTED; + err = -EOPNOTSUPP; } break; @@ -1727,7 +1873,7 @@ static int wanpipe_tdm_api_ioctl_handle_tdm_api_cmd(wanpipe_tdm_api_dev_t *tdm_a if (card && card->wandev.ec_enable) { wan_smp_flag_t smp_flags1; card->hw_iface.hw_lock(card->hw,&smp_flags1); - card->wandev.ec_enable(card, 0, tdm_api->tdm_chan); + err=card->wandev.ec_enable(card, 0, channel); card->hw_iface.hw_unlock(card->hw,&smp_flags1); } else { usr_tdm_api.result=SANG_STATUS_OPTION_NOT_SUPPORTED; @@ -1735,19 +1881,45 @@ static int wanpipe_tdm_api_ioctl_handle_tdm_api_cmd(wanpipe_tdm_api_dev_t *tdm_a } break; - case WP_API_CMD_GET_HW_DTMF: - if (card && card->wandev.ec_enable && card->u.aft.tdmv_hw_tone == WANOPT_YES) { - usr_tdm_api.hw_dtmf = WANOPT_YES; + case WP_API_CMD_GET_HW_EC: + if (card && card->wandev.ec_state) { + wan_hwec_dev_state_t ecdev_state = {0}; + card->wandev.ec_state(card,&ecdev_state); + if (ecdev_state.ec_state) { + usr_tdm_api.hw_ec = WANOPT_YES; + } else { + usr_tdm_api.hw_ec = WANOPT_NO; + } } else { - usr_tdm_api.hw_dtmf = WANOPT_NO; + usr_tdm_api.hw_ec = WANOPT_NO; } break; - case WP_API_CMD_GET_HW_EC: - if (card && card->wandev.ec_enable && card->wandev.ec_enable_map) { - usr_tdm_api.hw_ec = WANOPT_YES; + case WP_API_CMD_GET_HW_DTMF: + if (card && card->wandev.ec_state) { + wan_hwec_dev_state_t ecdev_state = {0}; + card->wandev.ec_state(card,&ecdev_state); + if (wan_test_bit(channel,&ecdev_state.dtmf_map)) { + usr_tdm_api.hw_dtmf = WANOPT_YES; + } else { + usr_tdm_api.hw_dtmf = WANOPT_NO; + } } else { - usr_tdm_api.hw_ec = WANOPT_NO; + usr_tdm_api.hw_fax = WANOPT_NO; + } + break; + + case WP_API_CMD_GET_HW_FAX_DETECT: + if (card && card->wandev.ec_state) { + wan_hwec_dev_state_t ecdev_state = {0}; + card->wandev.ec_state(card,&ecdev_state); + if (wan_test_bit(channel,&ecdev_state.fax_calling_map)) { + usr_tdm_api.hw_fax = WANOPT_YES; + } else { + usr_tdm_api.hw_fax = WANOPT_NO; + } + } else { + usr_tdm_api.hw_fax = WANOPT_NO; } break; @@ -1769,9 +1941,23 @@ static int wanpipe_tdm_api_ioctl_handle_tdm_api_cmd(wanpipe_tdm_api_dev_t *tdm_a err = -EOPNOTSUPP; } } else { + int rx_fifo=0, rx_over=0, tx_fifo=0; + if (tdm_api->driver_ctrl) { + err = tdm_api->driver_ctrl(tdm_api->chan,usr_tdm_api.cmd,&usr_tdm_api); + if (err == 0) { + rx_fifo=usr_tdm_api.stats.rx_fifo_errors; + rx_over=usr_tdm_api.stats.rx_over_errors; + tx_fifo=usr_tdm_api.stats.tx_fifo_errors; + } else { + DEBUG_EVENT("Error: Failed to exec driver_ctrl\n"); + } + } memcpy(&usr_tdm_api.stats,&tdm_api->cfg.stats,sizeof(tdm_api->cfg.stats)); usr_tdm_api.stats.max_rx_queue_length = (u8)tdm_api->cfg.rx_queue_sz; usr_tdm_api.stats.max_tx_queue_length = (u8)tdm_api->cfg.tx_queue_sz; + usr_tdm_api.stats.rx_fifo_errors = rx_fifo; + usr_tdm_api.stats.rx_over_errors = rx_over; + usr_tdm_api.stats.tx_fifo_errors = tx_fifo; wptdm_os_lock_irq(&card->wandev.lock,&irq_flags); usr_tdm_api.stats.current_number_of_frames_in_tx_queue = wan_skb_queue_len(&tdm_api->wp_tx_list); usr_tdm_api.stats.current_number_of_frames_in_rx_queue = wan_skb_queue_len(&tdm_api->wp_rx_list); @@ -1791,13 +1977,14 @@ static int wanpipe_tdm_api_ioctl_handle_tdm_api_cmd(wanpipe_tdm_api_dev_t *tdm_a break; case WP_API_CMD_RESET_STATS: - if (WPTDM_SPAN_OP_MODE(tdm_api) || tdm_api->hdlc_framing) { - if (tdm_api->driver_ctrl) { - err = tdm_api->driver_ctrl(tdm_api->chan,usr_tdm_api.cmd,&usr_tdm_api); - } else { - usr_tdm_api.result=SANG_STATUS_OPTION_NOT_SUPPORTED; - err = -EOPNOTSUPP; - } + + /* Always reset both set of stats for SPAN & CHAN mode, because + chan mode uses fifo errors stats from lower layer */ + if (tdm_api->driver_ctrl) { + err = tdm_api->driver_ctrl(tdm_api->chan,usr_tdm_api.cmd,&usr_tdm_api); + } else { + usr_tdm_api.result=SANG_STATUS_OPTION_NOT_SUPPORTED; + err = -EOPNOTSUPP; } /* Alwasy flush the tdm_api stats SPAN or CHAN mode*/ memset(&tdm_api->cfg.stats, 0x00, sizeof(tdm_api->cfg.stats)); @@ -1823,7 +2010,7 @@ static int wanpipe_tdm_api_ioctl_handle_tdm_api_cmd(wanpipe_tdm_api_dev_t *tdm_a case WP_API_CMD_ENABLE_RBS_EVENTS: /* 'usr_tdm_api.rbs_poll' is the user provided 'number of polls per second' */ if (usr_tdm_api.rbs_poll < 20 || usr_tdm_api.rbs_poll > 100) { - DEBUG_EVENT("%s: Error: Invalid RBS Poll Count Min=20 Max=100\n", + DEBUG_ERROR("%s: Error: Invalid RBS Poll Count Min=20 Max=100\n", tdm_api->name); usr_tdm_api.result=SANG_STATUS_INVALID_PARAMETER; @@ -1899,7 +2086,7 @@ static int wanpipe_tdm_api_ioctl_handle_tdm_api_cmd(wanpipe_tdm_api_dev_t *tdm_a (u8)usr_tdm_api.rbs_tx_bits); if (err) { usr_tdm_api.result=SANG_STATUS_IO_ERROR; - DEBUG_EVENT("%s: WRITE RBS Error (%i)\n",tdm_api->name,err); + DEBUG_ERROR("%s: WRITE RBS Error (%i)\n",tdm_api->name,err); } } else { usr_tdm_api.result=SANG_STATUS_OPTION_NOT_SUPPORTED; @@ -1916,7 +2103,7 @@ static int wanpipe_tdm_api_ioctl_handle_tdm_api_cmd(wanpipe_tdm_api_dev_t *tdm_a (u8*)&usr_tdm_api.rbs_rx_bits); if (err) { usr_tdm_api.result=SANG_STATUS_IO_ERROR; - DEBUG_EVENT("%s: READ RBS Error (%d)\n",tdm_api->name,err); + DEBUG_ERROR("%s: READ RBS Error (%d)\n",tdm_api->name,err); } DEBUG_TEST("%s: READ RBS (0x%X)\n",tdm_api->name,usr_tdm_api.rbs_rx_bits); } else { @@ -1935,7 +2122,7 @@ static int wanpipe_tdm_api_ioctl_handle_tdm_api_cmd(wanpipe_tdm_api_dev_t *tdm_a }else{ usr_tdm_api.result=SANG_STATUS_OPTION_NOT_SUPPORTED; err=-EOPNOTSUPP; - DEBUG_EVENT("%s: Warning: WP_API_CMD_GET_FE_STATUS request is not supported!\n", + DEBUG_WARNING("%s: Warning: WP_API_CMD_GET_FE_STATUS request is not supported!\n", tdm_api->name); } break; @@ -1950,7 +2137,7 @@ static int wanpipe_tdm_api_ioctl_handle_tdm_api_cmd(wanpipe_tdm_api_dev_t *tdm_a }else{ usr_tdm_api.result=SANG_STATUS_OPTION_NOT_SUPPORTED; err=-EOPNOTSUPP; - DEBUG_EVENT("%s: Warning: WP_API_CMD_SET_FE_STATUS request is not supported!\n", + DEBUG_WARNING("%s: Warning: WP_API_CMD_SET_FE_STATUS request is not supported!\n", tdm_api->name); } break; @@ -2013,10 +2200,6 @@ static int wanpipe_tdm_api_ioctl_handle_tdm_api_cmd(wanpipe_tdm_api_dev_t *tdm_a } } -#if defined(__WINDOWS__) - /*FIXME: test the memcpy() here - memcpy(rx_gains, utdmapi->data, usr_tdm_api.data_len);*/ -#else if (WAN_COPY_FROM_USER(rx_gains, utdmapi->data, usr_tdm_api.data_len)){ @@ -2024,7 +2207,7 @@ static int wanpipe_tdm_api_ioctl_handle_tdm_api_cmd(wanpipe_tdm_api_dev_t *tdm_a err=-EFAULT; goto tdm_api_unlocked_exit; } -#endif + wan_spin_lock(&tdm_api->lock,&flags); tdm_api->rx_gain = rx_gains; @@ -2060,20 +2243,16 @@ static int wanpipe_tdm_api_ioctl_handle_tdm_api_cmd(wanpipe_tdm_api_dev_t *tdm_a } } -#if defined(__WINDOWS__) - /*FIXME: test the memcpy() here - memcpy(tx_gains, utdmapi->data, usr_tdm_api.data_len);*/ -#else if (WAN_COPY_FROM_USER(tx_gains, - utdmapi->data, - usr_tdm_api.data_len)){ - usr_tdm_api.result=SANG_STATUS_FAILED_TO_LOCK_USER_MEMORY; + utdmapi->data, + usr_tdm_api.data_len)){ + usr_tdm_api.result=SANG_STATUS_FAILED_TO_LOCK_USER_MEMORY; err=-EFAULT; goto tdm_api_unlocked_exit; } -#endif - wan_spin_lock(&tdm_api->lock,&flags); - tdm_api->tx_gain = tx_gains; + wan_spin_lock(&tdm_api->lock,&flags); + + tdm_api->tx_gain = tx_gains; } else { usr_tdm_api.result=SANG_STATUS_FAILED_TO_LOCK_USER_MEMORY; @@ -2153,17 +2332,6 @@ static int wanpipe_tdm_api_ioctl_handle_tdm_api_cmd(wanpipe_tdm_api_dev_t *tdm_a usr_tdm_api.rx_queue_sz = tdm_api->cfg.rx_queue_sz; break; - case WP_API_CMD_DRIVER_VERSION: - case WP_API_CMD_FIRMWARE_VERSION: - case WP_API_CMD_CPLD_VERSION: - case WP_API_CMD_GEN_FIFO_ERR: - if (tdm_api->driver_ctrl) { - err = tdm_api->driver_ctrl(tdm_api->chan,usr_tdm_api.cmd,&usr_tdm_api); - } else { - err=-EFAULT; - } - break; - case WP_API_CMD_SET_RM_RXFLASHTIME: if (card->wandev.config_id == WANCONFIG_AFT_ANALOG) { if (fe->rm_param.mod[(tdm_api->tdm_chan) - 1].type == MOD_TYPE_FXS) { @@ -2174,17 +2342,37 @@ static int wanpipe_tdm_api_ioctl_handle_tdm_api_cmd(wanpipe_tdm_api_dev_t *tdm_a usr_tdm_api.rxflashtime); fe->rm_param.mod[(tdm_api->tdm_chan) - 1].u.fxs.rxflashtime=usr_tdm_api.rxflashtime; } else { - DEBUG_EVENT("Warning %s: Module %d: WP_API_CMD_SET_RM_RXFLASHTIME is only valid for FXS module!\n", + DEBUG_WARNING("Warning %s: Module %d: WP_API_CMD_SET_RM_RXFLASHTIME is only valid for FXS module!\n", fe->name,tdm_api->tdm_chan); err=-EOPNOTSUPP; } } else { - DEBUG_EVENT("Warning %s : Unsupported Protocol/Card %s WP_API_CMD_SET_RM_RXFLASHTIME is only valid for analog!\n", + DEBUG_WARNING("Warning %s : Unsupported Protocol/Card %s WP_API_CMD_SET_RM_RXFLASHTIME is only valid for analog!\n", fe->name,SDLA_DECODE_PROTOCOL(card->wandev.config_id)); err=-EOPNOTSUPP; } break; + case WP_API_CMD_ENABLE_LOOP: + tdm_api->cfg.loop=1; + break; + + case WP_API_CMD_DISABLE_LOOP: + tdm_api->cfg.loop=0; + break; + + case WP_API_CMD_DRIVER_VERSION: + case WP_API_CMD_FIRMWARE_VERSION: + case WP_API_CMD_CPLD_VERSION: + case WP_API_CMD_GEN_FIFO_ERR_TX: + case WP_API_CMD_GEN_FIFO_ERR_RX: + if (tdm_api->driver_ctrl) { + err = tdm_api->driver_ctrl(tdm_api->chan,usr_tdm_api.cmd,&usr_tdm_api); + } else { + err=-EFAULT; + } + break; + default: DEBUG_EVENT("%s: Invalid TDM API CMD %i\n", tdm_api->name,cmd); usr_tdm_api.result=SANG_STATUS_OPTION_NOT_SUPPORTED; @@ -2221,7 +2409,7 @@ static int wanpipe_tdm_api_ioctl(void *obj, int cmd, void *udata) int err=0; if (!tdm_api->chan && tdm_api != &tdmapi_ctrl){ - DEBUG_EVENT("%s:%d Error: TDM API Not initialized! chan=NULL!\n", + DEBUG_ERROR("%s:%d Error: TDM API Not initialized! chan=NULL!\n", __FUNCTION__,__LINE__); return -EFAULT; } @@ -2260,9 +2448,22 @@ wanpipe_tdm_api_event_ioctl(wanpipe_tdm_api_dev_t *tdm_api, wanpipe_api_cmd_t *t wp_api_event_t *tdm_event; wan_event_ctrl_t event_ctrl; sdla_t *card=tdm_api->card; + int channel = tdm_api->tdm_chan; + + if (WPTDM_SPAN_OP_MODE(tdm_api)) { + if (tdm_cmd->chan) { + channel = tdm_cmd->chan; + } + } + + if (channel >= MAX_TDM_API_CHANNELS) { + DEBUG_ERROR("%s: %s(): Error: Invalid channel selected %i (Max=%i)\n", + tdm_api->name, __FUNCTION__, channel, MAX_TDM_API_CHANNELS); + return -EINVAL; + } if (tdm_api->event_ctrl == NULL){ - DEBUG_EVENT("%s: Error: Event control interface doesn't initialized!\n", + DEBUG_ERROR("%s: Error: Event control interface doesn't initialized!\n", tdm_api->name); return -EINVAL; } @@ -2275,43 +2476,62 @@ wanpipe_tdm_api_event_ioctl(wanpipe_tdm_api_dev_t *tdm_api, wanpipe_api_cmd_t *t case WP_API_EVENT_DTMF: - event_ctrl.type = WAN_EVENT_EC_DTMF; + event_ctrl.type = WAN_EVENT_EC_DTMF; - if (!card->wandev.ec_enable || card->wandev.ec_enable_map == 0){ - if (card->wandev.config_id == WANCONFIG_AFT_ANALOG) { - event_ctrl.type = WAN_EVENT_RM_DTMF; - event_ctrl.mod_no = tdm_api->tdm_chan; - DEBUG_EVENT("%s: %s HW RM DTMF event %X!\n", - tdm_api->name, - WP_API_EVENT_MODE_DECODE(tdm_event->wp_api_event_mode), - tdm_api->active_ch); - } - } else { - - DEBUG_TEST("%s: %s HW EC DTMF event %X %p !\n", - tdm_api->name, - WP_API_EVENT_MODE_DECODE(tdm_event->wp_api_event_mode), - tdm_api->active_ch,card->wandev.ec_enable); - - } - - // Octasic DTMF event - if (tdm_event->wp_api_event_mode == WP_API_EVENT_ENABLE){ - event_ctrl.mode = WAN_EVENT_ENABLE; - }else{ - event_ctrl.mode = WAN_EVENT_DISABLE; + if (card && card->wandev.ec_state) { + wan_hwec_dev_state_t ecdev_state = {0}; + card->wandev.ec_state(card,&ecdev_state); + if (ecdev_state.ec_state == 0) { + return -EINVAL; } + } else { + return -EINVAL; + } -#if 0 - if(tdm_event->channel < 1 || tdm_event->channel > NUM_OF_E1_CHANNELS - 1){ - DEBUG_EVENT("%s: Error: DTMF control requested on invalid channel %u!\n", - tdm_api->name, tdm_event->channel); - return -EINVAL; + DEBUG_TEST("%s: %s HW EC DTMF event %X %p !\n", + tdm_api->name, + WP_API_EVENT_MODE_DECODE(tdm_event->wp_api_event_mode), + tdm_api->active_ch,card->wandev.ec_enable); + + // Octasic DTMF event + if (tdm_event->wp_api_event_mode == WP_API_EVENT_ENABLE){ + event_ctrl.mode = WAN_EVENT_ENABLE; + }else{ + event_ctrl.mode = WAN_EVENT_DISABLE; + } + + event_ctrl.channel = channel; + break; + + case WP_API_EVENT_FAX_DETECT: + + event_ctrl.type = WAN_EVENT_EC_FAX_DETECT; + + if (card && card->wandev.ec_state) { + wan_hwec_dev_state_t ecdev_state = {0}; + card->wandev.ec_state(card,&ecdev_state); + if (ecdev_state.ec_state == 0) { + return -EINVAL; } -#endif - event_ctrl.channel = tdm_api->tdm_chan; - break; + } else { + return -EINVAL; + } + DEBUG_TEST("%s: %s HW EC FAX Detect event %X %p !\n", + tdm_api->name, + WP_API_EVENT_MODE_DECODE(tdm_event->wp_api_event_mode), + tdm_api->active_ch,card->wandev.ec_enable); + + // Octasic DTMF event + if (tdm_event->wp_api_event_mode == WP_API_EVENT_ENABLE){ + event_ctrl.mode = WAN_EVENT_ENABLE; + }else{ + event_ctrl.mode = WAN_EVENT_DISABLE; + } + + event_ctrl.channel = channel; + + break; case WP_API_EVENT_RM_DTMF: // A200-Remora DTMF event @@ -2324,7 +2544,7 @@ wanpipe_tdm_api_event_ioctl(wanpipe_tdm_api_dev_t *tdm_api, wanpipe_api_cmd_t *t }else{ event_ctrl.mode = WAN_EVENT_DISABLE; } - event_ctrl.mod_no = tdm_api->tdm_chan; + event_ctrl.mod_no = channel; break; case WP_API_EVENT_RXHOOK: @@ -2337,49 +2557,49 @@ wanpipe_tdm_api_event_ioctl(wanpipe_tdm_api_dev_t *tdm_api, wanpipe_api_cmd_t *t }else{ event_ctrl.mode = WAN_EVENT_DISABLE; } - event_ctrl.mod_no = tdm_api->tdm_chan; + event_ctrl.mod_no = channel; break; case WP_API_EVENT_RING: DEBUG_TDMAPI("%s: %s Ring Event on module %d!\n", tdm_api->name, WP_API_EVENT_MODE_DECODE(tdm_event->wp_api_event_mode), - tdm_api->tdm_chan); + channel); event_ctrl.type = WAN_EVENT_RM_RING; if (tdm_event->wp_api_event_mode == WP_API_EVENT_ENABLE){ event_ctrl.mode = WAN_EVENT_ENABLE; }else{ event_ctrl.mode = WAN_EVENT_DISABLE; } - event_ctrl.mod_no = tdm_api->tdm_chan; + event_ctrl.mod_no = channel; break; case WP_API_EVENT_RING_DETECT: DEBUG_TDMAPI("%s: %s Ring Detection Event on module %d!\n", tdm_api->name, WP_API_EVENT_MODE_DECODE(tdm_event->wp_api_event_mode), - tdm_api->tdm_chan); + channel); event_ctrl.type = WAN_EVENT_RM_RING_DETECT; if (tdm_event->wp_api_event_mode == WP_API_EVENT_ENABLE){ event_ctrl.mode = WAN_EVENT_ENABLE; }else{ event_ctrl.mode = WAN_EVENT_DISABLE; } - event_ctrl.mod_no = tdm_api->tdm_chan; + event_ctrl.mod_no = channel; break; case WP_API_EVENT_RING_TRIP_DETECT: DEBUG_TDMAPI("%s: %s Ring Trip Detection Event on module %d!\n", tdm_api->name, WP_API_EVENT_MODE_DECODE(tdm_event->wp_api_event_mode), - tdm_api->tdm_chan); + channel); event_ctrl.type = WAN_EVENT_RM_RING_TRIP; if (tdm_event->wp_api_event_mode == WP_API_EVENT_ENABLE){ event_ctrl.mode = WAN_EVENT_ENABLE; }else{ event_ctrl.mode = WAN_EVENT_DISABLE; } - event_ctrl.mod_no = tdm_api->tdm_chan; + event_ctrl.mod_no = channel; break; case WP_API_EVENT_TONE: @@ -2388,7 +2608,7 @@ wanpipe_tdm_api_event_ioctl(wanpipe_tdm_api_dev_t *tdm_api, wanpipe_api_cmd_t *t tdm_api->name, WP_API_EVENT_MODE_DECODE(tdm_event->wp_api_event_mode), tdm_event->wp_api_event_tone_type, - tdm_api->tdm_chan); + channel); event_ctrl.type = WAN_EVENT_RM_TONE; if (tdm_event->wp_api_event_mode == WP_API_EVENT_ENABLE){ event_ctrl.mode = WAN_EVENT_ENABLE; @@ -2414,50 +2634,50 @@ wanpipe_tdm_api_event_ioctl(wanpipe_tdm_api_dev_t *tdm_api, wanpipe_api_cmd_t *t }else{ event_ctrl.mode = WAN_EVENT_DISABLE; } - event_ctrl.mod_no = tdm_api->tdm_chan; + event_ctrl.mod_no = channel; break; case WP_API_EVENT_TXSIG_KEWL: DEBUG_TDMAPI("%s: TX Signalling KEWL on module %d!\n", - tdm_api->name, tdm_api->tdm_chan); + tdm_api->name, channel); event_ctrl.type = WAN_EVENT_RM_TXSIG_KEWL; - event_ctrl.mod_no = tdm_api->tdm_chan; + event_ctrl.mod_no = channel; break; case WP_API_EVENT_TXSIG_START: DEBUG_TDMAPI("%s: TX Signalling START for module %d!\n", - tdm_api->name, tdm_api->tdm_chan); + tdm_api->name, channel); event_ctrl.type = WAN_EVENT_RM_TXSIG_START; - event_ctrl.mod_no = tdm_api->tdm_chan; + event_ctrl.mod_no = channel; break; case WP_API_EVENT_TXSIG_OFFHOOK: DEBUG_TDMAPI("%s: TX Signalling OFFHOOK for module %d!\n", - tdm_api->name, tdm_api->tdm_chan); + tdm_api->name, channel); event_ctrl.type = WAN_EVENT_RM_TXSIG_OFFHOOK; - event_ctrl.mod_no = tdm_api->tdm_chan; + event_ctrl.mod_no = channel; break; case WP_API_EVENT_TXSIG_ONHOOK: DEBUG_TDMAPI("%s: TX Signalling ONHOOK for module %d!\n", - tdm_api->name, tdm_api->tdm_chan); + tdm_api->name, channel); event_ctrl.type = WAN_EVENT_RM_TXSIG_ONHOOK; - event_ctrl.mod_no = tdm_api->tdm_chan; + event_ctrl.mod_no = channel; break; case WP_API_EVENT_ONHOOKTRANSFER: DEBUG_TDMAPI("%s: RM ONHOOKTRANSFER for module %d!\n", - tdm_api->name, tdm_api->tdm_chan); + tdm_api->name, channel); event_ctrl.type = WAN_EVENT_RM_ONHOOKTRANSFER; - event_ctrl.mod_no = tdm_api->tdm_chan; + event_ctrl.mod_no = channel; event_ctrl.ohttimer = tdm_event->wp_api_event_ohttimer; break; case WP_API_EVENT_SETPOLARITY: - DEBUG_EVENT("%s: RM SETPOLARITY for module %d!\n", - tdm_api->name, tdm_api->tdm_chan); + DEBUG_TDMAPI("%s: RM SETPOLARITY(%s) for module %d!\n", + tdm_api->name, tdm_event->wp_api_event_polarity == 0 ? "Forward" : "Reverse" ,channel); event_ctrl.type = WAN_EVENT_RM_SETPOLARITY; - event_ctrl.mod_no = tdm_api->tdm_chan; + event_ctrl.mod_no = channel; event_ctrl.polarity = tdm_event->wp_api_event_polarity; break; @@ -2477,6 +2697,23 @@ wanpipe_tdm_api_event_ioctl(wanpipe_tdm_api_dev_t *tdm_api, wanpipe_api_cmd_t *t event_ctrl.channel); break; + case WP_API_EVENT_SET_RM_TX_GAIN: + DEBUG_TDMAPI("%s: Setting TX gain %d on %d!\n", + tdm_api->name, tdm_event->wp_api_event_gain_value ,channel); + event_ctrl.type = WAN_EVENT_RM_SET_TX_GAIN; + event_ctrl.rm_gain = (int)tdm_event->wp_api_event_gain_value; + event_ctrl.mod_no = channel; + break; + + case WP_API_EVENT_SET_RM_RX_GAIN: + DEBUG_TDMAPI("%s: Setting RX gains %d on %d!\n", + tdm_api->name, tdm_event->wp_api_event_gain_value,channel); + event_ctrl.type = WAN_EVENT_RM_SET_RX_GAIN; + event_ctrl.rm_gain = tdm_event->wp_api_event_gain_value; + event_ctrl.mod_no = channel; + break; + + default: DEBUG_EVENT("%s: Unknown TDM API Event Type %02X!\n", tdm_api->name, @@ -2561,6 +2798,18 @@ wanpipe_tdm_api_tx_error: } +static void wanpipe_tdm_timestamp_hdr(wp_api_hdr_t *hdr) +{ + wan_time_t sec; + wan_suseconds_t usec; + + wan_get_timestamp(&sec, &usec); + + hdr->wp_api_hdr_time_stamp_sec = (u32)sec; + hdr->wp_api_hdr_time_stamp_use = usec; + return; +} + static int wanpipe_tdm_api_channelized_rx (wanpipe_tdm_api_dev_t *tdm_api, u8 *rx_data, u8 *tx_data, int len) { u8 *data_ptr; @@ -2627,6 +2876,8 @@ static int wanpipe_tdm_api_channelized_rx (wanpipe_tdm_api_dev_t *tdm_api, u8 *r skblen = wan_skb_len(tdm_api->rx_skb); if (skblen >= tdm_api->mtu_mru) { + wanpipe_tdm_timestamp_hdr(rx_hdr); + tdm_api->cfg.stats.rx_bytes+=skblen-hdrsize; wan_skb_queue_tail(&tdm_api->wp_rx_list,tdm_api->rx_skb); tdm_api->rx_skb=NULL; @@ -2745,7 +2996,7 @@ int wanpipe_tdm_api_span_rx_tx(sdla_t *card, wanpipe_tdm_api_span_t *tdm_span, u for (x=0;xrx_buf_len;x++) { sync++; if (sync != tdm_api->rx_buf[x]) { - DEBUG_EVENT("Error: RX Span 5 chan 15 expecting %02X got %02X diff=%i rlen=%i tlen=%i rxoff=%i tx_off=%i\n", + DEBUG_ERROR("Error: RX Span 5 chan 15 expecting %02X got %02X diff=%i rlen=%i tlen=%i rxoff=%i tx_off=%i\n", sync,tdm_api->rx_buf[x],abs(sync-tdm_api->rx_buf[x]), tdm_api->rx_buf_len, tdm_api->tx_buf_len, card->u.aft.tdm_rx_dma_toggle[i],card->u.aft.tdm_tx_dma_toggle[i]); sync=tdm_api->rx_buf[x]; @@ -2772,6 +3023,11 @@ int wanpipe_tdm_api_span_rx_tx(sdla_t *card, wanpipe_tdm_api_span_t *tdm_span, u wanpipe_tdm_api_channelized_rx (tdm_api, tdm_api->rx_buf, tdm_api->tx_buf, tdm_api->rx_buf_len); wanpipe_tdm_api_channelized_tx (tdm_api, tdm_api->tx_buf, tdm_api->tx_buf_len); + + if (tdm_api->cfg.loop) { + memcpy(tdm_api->tx_buf, tdm_api->rx_buf, tdm_api->rx_buf_len); + } + #if 0 if (tdm_api->tdm_span == 5 && tdm_api->tdm_chan == 15) { static unsigned char tsync=0; @@ -2779,7 +3035,7 @@ int wanpipe_tdm_api_span_rx_tx(sdla_t *card, wanpipe_tdm_api_span_t *tdm_span, u for (x=0;xtx_buf_len;x++) { tsync++; if (tsync != tdm_api->tx_buf[x]) { - DEBUG_EVENT("Error: TX Span 5 chan 15 expecting %02X got %02X diff=%i tx_offset\n", + DEBUG_ERROR("Error: TX Span 5 chan 15 expecting %02X got %02X diff=%i tx_offset\n", tsync,tdm_api->tx_buf[x],abs(tsync-tdm_api->tx_buf[x])); tsync=tdm_api->tx_buf[x]; } @@ -2798,7 +3054,7 @@ int wanpipe_tdm_api_rx_tx (wanpipe_tdm_api_dev_t *tdm_api, u8 *rx_data, u8 *tx_d if (!tdm_api || !rx_data || !tx_data || len == 0) { if (WAN_NET_RATELIMIT()) { - DEBUG_EVENT("%s:%d Internal Error: tdm_api=%p rx_data=%p tx_data=%p len=%i\n", + DEBUG_ERROR("%s:%d Internal Error: tdm_api=%p rx_data=%p tx_data=%p len=%i\n", __FUNCTION__,__LINE__,tdm_api,rx_data,tx_data,len); } return 0; @@ -2822,6 +3078,10 @@ int wanpipe_tdm_api_rx_tx (wanpipe_tdm_api_dev_t *tdm_api, u8 *rx_data, u8 *tx_d wanpipe_tdm_api_channelized_rx (tdm_api, tdm_api->rx_buf, tdm_api->tx_buf, tdm_api->rx_buf_len); wanpipe_tdm_api_channelized_tx (tdm_api, tdm_api->tx_buf, tdm_api->tx_buf_len); + if (tdm_api->cfg.loop) { + memcpy(tdm_api->tx_buf, tdm_api->rx_buf, tdm_api->rx_buf_len); + } + return 0; } @@ -2829,6 +3089,7 @@ int wanpipe_tdm_api_span_rx (wanpipe_tdm_api_dev_t *tdm_api, netskb_t *skb) { sdla_t *card; wan_smp_flag_t flag; + wp_api_hdr_t *rx_hdr; flag=0; @@ -2845,7 +3106,7 @@ int wanpipe_tdm_api_span_rx (wanpipe_tdm_api_dev_t *tdm_api, netskb_t *skb) if (wan_skb_len(skb) <= sizeof(wp_api_hdr_t)) { WP_AFT_CHAN_ERROR_STATS(tdm_api->cfg.stats,rx_errors); if (WAN_NET_RATELIMIT()) { - DEBUG_EVENT("%s: Internal Error RX Data has no rx header!\n", + DEBUG_ERROR("%s: Internal Error RX Data has no rx header!\n", __FUNCTION__); } return -EINVAL; @@ -2856,13 +3117,14 @@ int wanpipe_tdm_api_span_rx (wanpipe_tdm_api_dev_t *tdm_api, netskb_t *skb) wptdm_os_unlock_irq(&card->wandev.lock,&flag); wp_wakeup_rx_tdmapi(tdm_api); WP_AFT_CHAN_ERROR_STATS(tdm_api->cfg.stats,rx_dropped); - if (WAN_NET_RATELIMIT()) { - DEBUG_TEST("%s: Error RX Buffer Overrun!\n", - __FUNCTION__); - } + DEBUG_TEST("%s: Error RX Buffer Overrun!\n",__FUNCTION__); return -EBUSY; } + rx_hdr=(wp_api_hdr_t*)wan_skb_data(skb); + + wanpipe_tdm_timestamp_hdr(rx_hdr); + wan_skb_queue_tail(&tdm_api->wp_rx_list,skb); tdm_api->cfg.stats.rx_packets++; wptdm_os_unlock_irq(&card->wandev.lock,&flag); @@ -2878,7 +3140,7 @@ static wanpipe_tdm_api_dev_t *wp_tdmapi_search(sdla_t *card, int fe_chan) wanpipe_tdm_api_dev_t *tdm_api; if(fe_chan < 0 || fe_chan >= MAX_TDM_API_CHANNELS){ - DEBUG_EVENT("%s(): TDM API Error: Invalid Channel Number=%i!\n", + DEBUG_ERROR("%s(): TDM API Error: Invalid Channel Number=%i!\n", __FUNCTION__, fe_chan); return NULL; } @@ -2964,8 +3226,6 @@ static void wp_tdmapi_report_rbsbits(void* card_id, int channel, unsigned char r } - - static void wp_tdmapi_alarms(void* card_id, wan_event_t *event) { netskb_t *skb; @@ -3101,7 +3361,7 @@ static void wp_tdmapi_tone (void* card_id, wan_event_t *event) ntone = tdm_api->dtmf_check[event->channel].dtmf_tone_present + 1; if (ntone != event->digit) { - DEBUG_EVENT("%s: Error: Digit mismatch Ch=%i Expecting %c Received %c\n", + DEBUG_ERROR("%s: Error: Digit mismatch Ch=%i Expecting %c Received %c\n", tdm_api->name,event->channel, ntone, event->digit); } } @@ -3113,7 +3373,7 @@ static void wp_tdmapi_tone (void* card_id, wan_event_t *event) ntone = tdm_api->dtmf_check[event->channel].dtmf_tone_stop + 1; if (ntone != event->digit) { - DEBUG_EVENT("%s: Error: Digit mismatch Ch=%i Expecting %c Received %c\n", + DEBUG_ERROR("%s: Error: Digit mismatch Ch=%i Expecting %c Received %c\n", tdm_api->name,event->channel, ntone, event->digit); } } @@ -3163,7 +3423,7 @@ static void wp_tdmapi_hook (void* card_id, wan_event_t *event) tdm_api = wp_tdmapi_search(card, event->channel); if (tdm_api == NULL){ - DEBUG_EVENT("%s: Error: failed to find API device. Discarding RM LC Event at TDM_API (%d:%s)!\n", + DEBUG_ERROR("%s: Error: failed to find API device. Discarding RM LC Event at TDM_API (%d:%s)!\n", card->devname, event->channel, (event->rxhook==WAN_EVENT_RXHOOK_OFF)?"OFF-HOOK":"ON-HOOK"); @@ -3173,7 +3433,7 @@ static void wp_tdmapi_hook (void* card_id, wan_event_t *event) skb=wan_skb_dequeue(&tdm_api->wp_event_free_list); if (skb == NULL) { WP_AFT_CHAN_ERROR_STATS(tdm_api->cfg.stats,rx_events_dropped); - DEBUG_EVENT("%s: Error: no free buffers for API event. Discarding RM LC Event at TDM_API (%d:%s)!\n", + DEBUG_ERROR("%s: Error: no free buffers for API event. Discarding RM LC Event at TDM_API (%d:%s)!\n", card->devname, event->channel, (event->rxhook==WAN_EVENT_RXHOOK_OFF)?"OFF-HOOK":"ON-HOOK"); @@ -3533,13 +3793,13 @@ static int __store_tdm_api_pointer_in_card(sdla_t *card, wanpipe_tdm_api_dev_t * { if(fe_chan >= MAX_TDM_API_CHANNELS){ - DEBUG_EVENT("%s(): TDM API Error (TE1): Invalid Channel Number=%i (Span=%d)!\n", + DEBUG_ERROR("%s(): TDM API Error (TE1): Invalid Channel Number=%i (Span=%d)!\n", __FUNCTION__, fe_chan, tdm_api->tdm_span); return 1; } if(card->wp_tdmapi_hash[fe_chan] != NULL){ - DEBUG_EVENT("%s(): TDM API Error: device SPAN=%i CHAN=%i already in use!\n", + DEBUG_ERROR("%s(): TDM API Error: device SPAN=%i CHAN=%i already in use!\n", __FUNCTION__, tdm_api->tdm_span, fe_chan); return 1; } @@ -3592,19 +3852,19 @@ static int __remove_tdm_api_pointer_from_card(wanpipe_tdm_api_dev_t *tdm_api,int #endif if(card == NULL){ - DEBUG_EVENT("%s(): TDM API Error: Invalid 'card' pointer!\n", __FUNCTION__); + DEBUG_ERROR("%s(): TDM API Error: Invalid 'card' pointer!\n", __FUNCTION__); return 1; } if(fe_chan >= MAX_TDM_API_CHANNELS){ - DEBUG_EVENT("%s(): TDM API Error (TE1): Invalid Channel Number=%i (Span=%d)!\n", + DEBUG_ERROR("%s(): TDM API Error (TE1): Invalid Channel Number=%i (Span=%d)!\n", __FUNCTION__, fe_chan, tdm_api->tdm_span); return 1; } if(card->wp_tdmapi_hash[fe_chan] == NULL){ - DEBUG_EVENT("%s: TDM API Warning: device SPAN=%i CHAN=%i was NOT initialized!\n", + DEBUG_WARNING("%s: TDM API Warning: device SPAN=%i CHAN=%i was NOT initialized!\n", __FUNCTION__, tdm_api->tdm_span, fe_chan); } @@ -3650,11 +3910,11 @@ static int wp_tdmapi_push_to_bh_queue(wanpipe_tdm_api_dev_t *tdm_api, netskb_t * netskb_t *ctrl_skb; if (!skb || !tdm_api) { - DEBUG_EVENT("TDMAPI: Error: Null skb argument!\n"); + DEBUG_ERROR("TDMAPI: Error: Null skb argument!\n"); return -ENODEV; } if (tdm_api == &tdmapi_ctrl) { - DEBUG_EVENT("%s:%d: TDMAPI: Error: tdm_api == tdmapi_ctrl!\n",__FUNCTION__,__LINE__); + DEBUG_ERROR("%s:%d: TDMAPI: Error: tdm_api == tdmapi_ctrl!\n",__FUNCTION__,__LINE__); return -EINVAL; } @@ -3668,7 +3928,7 @@ static int wp_tdmapi_push_to_bh_queue(wanpipe_tdm_api_dev_t *tdm_api, netskb_t * u8 *buf; if (len > wan_skb_tailroom(ctrl_skb)) { if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("TDMAPI: Error: TDM API CTRL Event Buffer Overflow Elen=%i Max=%i!\n", + DEBUG_ERROR("TDMAPI: Error: TDM API CTRL Event Buffer Overflow Elen=%i Max=%i!\n", len,wan_skb_tailroom(ctrl_skb)); } WP_AFT_CHAN_ERROR_STATS(tdm_api->cfg.stats,rx_events_dropped); @@ -3678,7 +3938,9 @@ static int wp_tdmapi_push_to_bh_queue(wanpipe_tdm_api_dev_t *tdm_api, netskb_t * memcpy(buf,wan_skb_data(skb),len); wan_skb_queue_tail(&tdm_api->wp_event_bh_list,ctrl_skb); - WAN_TASKQ_SCHEDULE((&tdm_api->wp_api_task)); + if (!wan_test_bit(WP_TDM_DOWN,&tdm_api->critical)) { + WAN_TASKQ_SCHEDULE((&tdm_api->wp_api_task)); + } } else { WP_AFT_CHAN_ERROR_STATS(tdm_api->cfg.stats,rx_events_dropped); @@ -3803,6 +4065,76 @@ static void wp_api_task_func (void * tdmapi_ptr, int arg) return; } +#if 0 +static int wp_tdm_api_mtp1_rx_data (void *priv_ptr, u8 *rx_data, int rx_len) +{ + wanpipe_tdm_api_dev_t *tdm_api = (wanpipe_tdm_api_dev_t*)priv_ptr; + sdla_t *card; + netskb_t *skb; + u8 *data; + + if (!tdm_api->chan || !wan_test_bit(0,&tdm_api->init) || !tdm_api->card){ + return -ENODEV; + } + + if (!wan_test_bit(0,&tdm_api->used)) { + return -ENODEV; + } + + card=tdm_api->card; + + skb=wan_skb_alloc(sizeof(wp_api_hdr_t)+rx_len); + if (!skb) { + return -ENOBUFS; + } + + wan_skb_put(skb,sizeof(wp_api_hdr_t)); + data=wan_skb_put(skb,rx_len); + memcpy(data,rx_data,rx_len); + + wptdm_os_lock_irq(&card->wandev.lock,&flag); + if (wan_skb_queue_len(&tdm_api->wp_rx_list) >= (int)tdm_api->cfg.rx_queue_sz) { + wptdm_os_unlock_irq(&card->wandev.lock,&flag); + wp_wakeup_rx_tdmapi(tdm_api); + WP_AFT_CHAN_ERROR_STATS(tdm_api->cfg.stats,rx_dropped); + if (WAN_NET_RATELIMIT()) { + DEBUG_TEST("%s: Error RX Buffer Overrun!\n", + __FUNCTION__); + } + return -EBUSY; + } + + wan_skb_queue_tail(&tdm_api->wp_rx_list,skb); + tdm_api->cfg.stats.rx_packets++; + wptdm_os_unlock_irq(&card->wandev.lock,&flag); + + wp_wakeup_rx_tdmapi(tdm_api); + + return 0; +} + +static int wp_tdm_api_mtp1_rx_reject (void *priv_prt, char *reason) +{ + /* Do nothing */ + return 0; + +} + +static int wp_tdm_api_mtp1_rx_suerm (void *priv_ptr) +{ + /* Pass up suerm event */ + wanpipe_tdm_api_dev_t *tdm_api = (wanpipe_tdm_api_dev_t*)priv_ptr; + wp_wakeup_rx_tdmapi(tdm_api); + return 0; +} + +static int wp_tdm_api_tmp1_wakup(void *priv_ptr) +{ + wanpipe_tdm_api_dev_t *tdm_api = (wanpipe_tdm_api_dev_t*)priv_ptr; + wp_wakeup_rx_tdmapi(tdm_api); + return 0; +} +#endif #else diff --git a/patches/kdrivers/src/net/wanpipe_timer_dev.c b/patches/kdrivers/src/net/wanpipe_timer_dev.c index 56ece50..b804592 100644 --- a/patches/kdrivers/src/net/wanpipe_timer_dev.c +++ b/patches/kdrivers/src/net/wanpipe_timer_dev.c @@ -31,7 +31,12 @@ * Type Defines *================================================*/ -#define DEBUG_TDEV DEBUG_EVENT +#if defined(__WINDOWS__) +# define DEBUG_TDEV DbgPrint +#else +# define DEBUG_TDEV DEBUG_EVENT +#endif + #define MAX_WAN_TDEV_IDX_SZ 20 typedef struct wanpipe_tdev @@ -62,30 +67,40 @@ static int wan_alloc_skb_list(wanpipe_tdev_t *dev, int elements); * Global Defines *================================================*/ -static sdla_t *wan_timer_card; -static u32 wan_timer_initialized; +static sdla_t *wan_timer_card = NULL; +static u32 wan_timer_initialized = 0; static wanpipe_tdev_t *wan_tdev_idx[MAX_WAN_TDEV_IDX_SZ]; -static int wan_tdev_cnt; -static u32 wan_event_seq_cnt; +static int wan_tdev_cnt = 0; +static u32 wan_event_seq_cnt = 0; -#if defined(__WINDOWS__) static wanpipe_cdev_ops_t wan_tdev_fops; -#else -static wanpipe_cdev_ops_t wan_tdev_fops = { - open: wp_tdev_open, - close: wp_tdev_close, - ioctl: wp_tdev_ioctl, - poll: wp_tdev_poll, -}; -#endif + +static void wanpipe_wandev_timer_init_globals() +{ + memset(wan_tdev_idx, 0x00, sizeof(wan_tdev_idx)); + + wan_timer_card = NULL; + wan_timer_initialized = 0; + wan_tdev_cnt = 0; + wan_event_seq_cnt = 0; + + memset(&wan_tdev_fops, 0x00, sizeof(wan_tdev_fops)); + wan_tdev_fops.open = wp_tdev_open; + wan_tdev_fops.close = wp_tdev_close; + wan_tdev_fops.ioctl = wp_tdev_ioctl; + wan_tdev_fops.poll = wp_tdev_poll; +} int wanpipe_wandev_timer_create(void) { int err=-EINVAL; wanpipe_tdev_t *wan_tdev; + wanpipe_cdev_t *cdev; - wanpipe_cdev_t *cdev = wan_kmalloc(sizeof(wanpipe_cdev_t)); + wanpipe_wandev_timer_init_globals(); + + cdev = wan_kmalloc(sizeof(wanpipe_cdev_t)); if (!cdev) { return -ENOMEM; } @@ -119,7 +134,8 @@ int wanpipe_wandev_timer_create(void) wan_timer_card=NULL; wan_set_bit(0,&wan_timer_initialized); - DEBUG_TDEV("%s: WAN TDEV CREATE \n",__FUNCTION__); + DEBUG_TDEV("%s: WAN TDEV CREATE (err:%d, wan_tdev_cnt:%d) \n", + __FUNCTION__, err, wan_tdev_cnt); return err; @@ -197,9 +213,9 @@ static int wp_tdev_ioctl(void *obj, int cmd, void *udata) #if defined(__WINDOWS__) /* udata is a pointer to wanpipe_tdm_api_cmd_t */ - memcpy(&utcmd, udata, sizeof(wanpipe_timer_api_t)); + memcpy(utcmd, udata, sizeof(wanpipe_timer_api_t)); #else - if (WAN_COPY_FROM_USER(&utcmd, + if (WAN_COPY_FROM_USER(&utcmd, /* davidr: looks like a wrong pointer */ udata, sizeof(wanpipe_timer_api_t))){ return -EFAULT; @@ -244,7 +260,7 @@ static int wp_tdev_ioctl(void *obj, int cmd, void *udata) #if defined(__WINDOWS__) /* udata is a pointer to wanpipe_tdm_api_cmd_t */ - memcpy(udata, &utcmd, sizeof(wanpipe_timer_api_t)); + memcpy(udata, utcmd, sizeof(wanpipe_timer_api_t)); #else if (WAN_COPY_FROM_USER(&udata, &utcmd, diff --git a/patches/kdrivers/src/net/wanpipe_usb.c b/patches/kdrivers/src/net/wanpipe_usb.c index be8621a..9b1ac82 100644 --- a/patches/kdrivers/src/net/wanpipe_usb.c +++ b/patches/kdrivers/src/net/wanpipe_usb.c @@ -35,6 +35,8 @@ # include "sdla_usb_remora.h" #endif +#if defined(CONFIG_PRODUCT_WANPIPE_USB) + /***************************************************************************** * DEFINES/MACROS *****************************************************************************/ @@ -44,6 +46,8 @@ # define WP_USB_FUNC_DEBUG() DEBUG_EVENT("%s:%d\n",__FUNCTION__,__LINE__) #endif +WAN_DECLARE_NETDEV_OPS(wan_netdev_ops) + /* Private critical flags */ enum { CARD_DOWN = 0x01, @@ -296,7 +300,7 @@ wp_usb_new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* con chan->common.usedby = TDM_VOICE; WAN_TDMV_CALL(check_mtu, (card, conf->active_ch, &chan->mtu), err); if (err){ - DEBUG_EVENT("Error: TMDV mtu check failed!"); + DEBUG_ERROR("Error: TMDV mtu check failed!"); return -EINVAL; } @@ -308,7 +312,7 @@ wp_usb_new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* con chan->common.dev), channel); if (channel < 0){ - DEBUG_EVENT("%s: Error: Failed to register TDMV channel!\n", + DEBUG_ERROR("%s: Error: Failed to register TDMV channel!\n", chan->if_name); return -EINVAL; @@ -321,7 +325,7 @@ wp_usb_new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* con card->hw_iface.usb_rxtx_data_init(card->hw, channel, &chan->rxdata, &chan->txdata); //sdla_usb_rxtx_data_init(card->hw, channel, &chan->rxdata, &chan->txdata); #else - DEBUG_EVENT("%s: Error: TDMV_VOICE Zaptel Option not compiled into the driver!\n", + DEBUG_ERROR("%s: Error: TDMV_VOICE Zaptel Option not compiled into the driver!\n", card->devname); return -EINVAL; #endif @@ -354,7 +358,14 @@ wp_usb_new_if_private (wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* con chan->time_slot_map=conf->active_ch; #if defined(__LINUX__) - dev->init = &wp_usb_if_init; + WAN_NETDEV_OPS_BIND(dev,wan_netdev_ops); + WAN_NETDEV_OPS_INIT(dev,wan_netdev_ops,&wp_usb_if_init); + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&wp_usb_if_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&wp_usb_if_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&wp_usb_if_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&wp_usb_if_stats); + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&wp_usb_if_tx_timeout); + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,&wp_usb_if_do_ioctl); # if defined(CONFIG_PRODUCT_WANPIPE_GENERIC) wp_usb_if_init(dev); # endif @@ -407,13 +418,13 @@ wp_usb_if_tdmv_init(wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) break; } if (err){ - DEBUG_EVENT("%s: Error: Failed to initialize tdmv functions!\n", + DEBUG_ERROR("%s: Error: Failed to initialize tdmv functions!\n", card->devname); return -EINVAL; } WAN_TDMV_CALL(create, (card, &card->tdmv_conf), err); if (err){ - DEBUG_EVENT("%s: Error: Failed to create tdmv span!\n", + DEBUG_ERROR("%s: Error: Failed to create tdmv span!\n", card->devname); return err; } @@ -485,13 +496,13 @@ wp_usb_new_if(wan_device_t* wandev, netdevice_t* dev, wanif_conf_t* conf) err = wp_usb_if_api_init(wandev, dev, conf); }else{ - DEBUG_EVENT( "%s: Error: Invalid IF operation mode %s\n", + DEBUG_ERROR( "%s: Error: Invalid IF operation mode %s\n", card->devname,conf->usedby); err=-EINVAL; goto wp_usb_new_if_error; } if (err){ - DEBUG_EVENT( "%s: Error: Failed initialize for %s operation mode!\n", + DEBUG_ERROR( "%s: Error: Failed initialize for %s operation mode!\n", card->devname,conf->usedby); err=-EINVAL; goto wp_usb_new_if_error; @@ -521,14 +532,14 @@ static int wp_usb_del_if_private (wan_device_t* wandev, netdevice_t* dev) int err; if (!chan){ - DEBUG_EVENT("%s: Critical Error del_if_private() chan=NULL!\n", + DEBUG_ERROR("%s: Critical Error del_if_private() chan=NULL!\n", wan_netif_name(dev)); return 0; } card = chan->card; if (!card){ - DEBUG_EVENT("%s: Critical Error del_if_private() chan=NULL!\n", + DEBUG_ERROR("%s: Critical Error del_if_private() chan=NULL!\n", wan_netif_name(dev)); return 0; } @@ -659,13 +670,13 @@ wp_usb_del_if (wan_device_t* wandev, netdevice_t* dev) WP_USB_FUNC_DEBUG(); if (!chan){ - DEBUG_EVENT("%s: Critical Error del_if() chan=NULL!\n", + DEBUG_ERROR("%s: Critical Error del_if() chan=NULL!\n", wan_netif_name(dev)); return 0; } if (!(card=chan->card)){ - DEBUG_EVENT("%s: Critical Error del_if() chan=NULL!\n", + DEBUG_ERROR("%s: Critical Error del_if() chan=NULL!\n", wan_netif_name(dev)); return 0; } @@ -697,7 +708,7 @@ static int wp_usb_add_device(char *devname, void *hw) } } if (card == NULL){ - DEBUG_EVENT("%s: INTERNAL ERROR: Failed to find the device in a list!\n", + DEBUG_ERROR("%s: INTERNAL ERROR: Failed to find the device in a list!\n", devname); return -EINVAL; } @@ -723,7 +734,7 @@ static int wp_usb_delete_device(char *devname) } } if (card == NULL){ - DEBUG_EVENT("%s: INTERNAL ERROR: Failed to find the device in a list!\n", + DEBUG_ERROR("%s: INTERNAL ERROR: Failed to find the device in a list!\n", devname); return -EINVAL; } @@ -767,21 +778,20 @@ static int wp_usb_if_init (netdevice_t* dev) wp_usb_softc_t *chan = wan_netif_priv(dev); /* Initialize device driver entry points */ - dev->open = &wp_usb_if_open; - dev->stop = &wp_usb_if_close; - dev->hard_start_xmit = &wp_usb_if_send; - dev->get_stats = &wp_usb_if_stats; + WAN_NETDEV_OPS_OPEN(dev,wan_netdev_ops,&wp_usb_if_open); + WAN_NETDEV_OPS_STOP(dev,wan_netdev_ops,&wp_usb_if_close); + WAN_NETDEV_OPS_XMIT(dev,wan_netdev_ops,&wp_usb_if_send); + WAN_NETDEV_OPS_STATS(dev,wan_netdev_ops,&wp_usb_if_stats); if (chan->common.usedby == TDM_VOICE || chan->common.usedby == TDM_VOICE_API){ - dev->tx_timeout = NULL; + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,NULL); }else{ - dev->tx_timeout = &wp_usb_if_tx_timeout; + WAN_NETDEV_OPS_TIMEOUT(dev,wan_netdev_ops,&wp_usb_if_tx_timeout); } dev->watchdog_timeo = 2*HZ; - dev->do_ioctl = wp_usb_if_do_ioctl; - + WAN_NETDEV_OPS_IOCTL(dev,wan_netdev_ops,&wp_usb_if_do_ioctl); dev->flags |= IFF_POINTOPOINT; dev->flags |= IFF_NOARP; dev->type = ARPHRD_PPP; @@ -960,7 +970,7 @@ wp_usb_if_do_ioctl(netdevice_t *dev, struct ifreq *ifr, wan_ioctl_cmd_t cmd) * PIPEMON commands due to udp_pkt_len * thus we can release the irq */ if (wan_atomic_read(&chan->udp_pkt_len) > sizeof(wan_udp_pkt_t)){ - DEBUG_EVENT( "%s: Error: Pipemon buf too bit on the way up! %d\n", + DEBUG_ERROR( "%s: Error: Pipemon buf too bit on the way up! %d\n", card->devname,wan_atomic_read(&chan->udp_pkt_len)); wan_atomic_set(&chan->udp_pkt_len,0); return -EINVAL; @@ -1077,7 +1087,7 @@ wp_usb_process_udp(sdla_t* card, netdevice_t* dev, wp_usb_softc_t *chan) wan_set_bit (0,&trace_info->tracing_enabled); }else{ - DEBUG_EVENT("%s: Error: ATM trace running!\n", + DEBUG_ERROR("%s: Error: ATM trace running!\n", card->devname); wan_udp_pkt->wan_udp_return_code = 2; } @@ -1112,7 +1122,7 @@ wp_usb_process_udp(sdla_t* card, netdevice_t* dev, wp_usb_softc_t *chan) if(wan_test_bit(0,&trace_info->tracing_enabled)){ trace_info->trace_timeout = SYSTEM_TICKS; }else{ - DEBUG_EVENT("%s: Error ATM trace not enabled\n", + DEBUG_ERROR("%s: Error ATM trace not enabled\n", card->devname); /* set return code */ wan_udp_pkt->wan_udp_return_code = 1; @@ -1403,7 +1413,7 @@ static void wp_usb_task (void * data, int arg) chan=(wp_usb_softc_t*)card->u.usb.dev_to_ch_map[i]; if (!chan){ - DEBUG_EVENT("%s: Error: No Dev for Rx logical ch=%d\n", + DEBUG_ERROR("%s: Error: No Dev for Rx logical ch=%d\n", card->devname,i); continue; } @@ -1449,7 +1459,7 @@ static void wp_usb_isr(void *arg) } chan=(wp_usb_softc_t*)card->u.usb.dev_to_ch_map[i]; if (!chan){ - DEBUG_EVENT("%s: Error: No Dev for Rx logical ch=%d\n", + DEBUG_ERROR("%s: Error: No Dev for Rx logical ch=%d\n", card->devname,i); continue; } @@ -1465,7 +1475,7 @@ static void wp_usb_isr(void *arg) #endif } if (chan->rxdata == NULL || chan->txdata == NULL){ - DEBUG_EVENT("%s: %s:%d ASSERT ERROR TxDma=%p RxDma=%p\n", + DEBUG_ERROR("%s: %s:%d ASSERT ERROR TxDma=%p RxDma=%p\n", card->devname,__FUNCTION__,__LINE__, chan->rxdata,chan->txdata); return; @@ -1627,7 +1637,7 @@ static int wp_usb_devel_ioctl(sdla_t *card, struct ifreq *ifr) int err = -EINVAL; if (!ifr || !ifr->ifr_data){ - DEBUG_EVENT("%s: Error: No ifr or ifr_data\n",__FUNCTION__); + DEBUG_ERROR("%s: Error: No ifr or ifr_data\n",__FUNCTION__); return -EFAULT; } @@ -1653,3 +1663,5 @@ static int wp_usb_devel_ioctl(sdla_t *card, struct ifreq *ifr) return err; } + +#endif /* #if defined(CONFIG_PRODUCT_WANPIPE_USB) */ diff --git a/patches/kdrivers/src/net/wanpipe_utils.c b/patches/kdrivers/src/net/wanpipe_utils.c index a6cbb0a..c41218e 100644 --- a/patches/kdrivers/src/net/wanpipe_utils.c +++ b/patches/kdrivers/src/net/wanpipe_utils.c @@ -653,7 +653,7 @@ void wanpipe_debugging (ulong_ptr_t data) if (card->wandev.clocking == WANOPT_EXTERNAL){ if (card->wan_debug_last_msg != WAN_DEBUG_LINERROR_MSG){ /* External clocking */ - DEBUG_EVENT("%s: Your line is experiencing errors!\n", + DEBUG_ERROR("%s: Your line is experiencing errors!\n", card->devname); DEBUG_EVENT("%s: Check your DSU/CSU and line configuration\n", card->devname); @@ -666,7 +666,7 @@ void wanpipe_debugging (ulong_ptr_t data) if (card->wan_debug_last_msg != WAN_DEBUG_CLK_MSG){ DEBUG_EVENT("%s: You are set for Internal clocking, \n", card->devname); - DEBUG_EVENT("%s: causing line errors!\n", + DEBUG_ERROR("%s: causing line errors!\n", card->devname); DEBUG_EVENT("%s: Set to External clocking except for\n", card->devname); @@ -973,7 +973,7 @@ int wan_tracing_enabled(wan_trace_t *trace_info) } if (WAN_NET_RATELIMIT()){ - DEBUG_EVENT("wanpipe: Warning: trace queue overflow %d (max=%d)!\n", + DEBUG_WARNING("wanpipe: Warning: trace queue overflow %d (max=%d)!\n", wan_skb_queue_len(&trace_info->trace_queue), trace_info->max_trace_queue); } @@ -1400,7 +1400,7 @@ int init_atm_idle_buffer(unsigned char *buff, int buff_len, char *if_name, char number_of_cells_fit_idle_buffer = buff_len / ATM_CELL_SIZE; if(number_of_cells_fit_idle_buffer == 0 /*|| buff_len % ATM_CELL_SIZE*/){ - DEBUG_EVENT("%s: Error: Invalid Idle buffer length=%d, not multiple of ATM_CELL_SIZE (53)!\n", + DEBUG_ERROR("%s: Error: Invalid Idle buffer length=%d, not multiple of ATM_CELL_SIZE (53)!\n", if_name, buff_len); return 1; } @@ -1418,7 +1418,7 @@ int atm_add_data_to_skb(void* skb, void *data, int data_len, char *if_name) unsigned char *skb_data_ptr; if (data_len != ATM_CELL_SIZE) { - DEBUG_EVENT("%s: %s(): Error, invalid datalen=%i\n", + DEBUG_ERROR("%s: %s(): Error, invalid datalen=%i\n", if_name, __FUNCTION__, data_len); return 1; } @@ -1456,7 +1456,7 @@ int atm_pad_idle_cells_in_tx_skb(void *skb, void *tx_idle_skb, char *if_name) DEBUG_ATM("num_of_cells_to_pad: %d\n", num_of_cells_to_pad); if(empty_space % ATM_CELL_SIZE){ - DEBUG_EVENT("%s: %s(): Error, empty space length (%d) is not multiple of ATM_CELL_SIZE!\n", + DEBUG_ERROR("%s: %s(): Error, empty space length (%d) is not multiple of ATM_CELL_SIZE!\n", if_name, __FUNCTION__, empty_space); return 1; } diff --git a/patches/kdrivers/src/wanrouter/af_wanpipe.c b/patches/kdrivers/src/wanrouter/af_wanpipe.c index 9c8bff1..63b1013 100644 --- a/patches/kdrivers/src/wanrouter/af_wanpipe.c +++ b/patches/kdrivers/src/wanrouter/af_wanpipe.c @@ -93,6 +93,7 @@ extern int wanpipe_bind_sk_to_parent(struct sock *sk, netdevice_t *dev, struct w extern int wanpipe_sk_parent_rx(struct sock *parent_sk, struct sk_buff *skb); #endif +WAN_DECLARE_NETDEV_OPS(wan_netdev_ops) /* The code below is used to test memory leaks. It prints out @@ -289,12 +290,12 @@ dev_private_ioctl: if (!dev) return -ENODEV; - if (!dev->do_ioctl) + if (!WAN_NETDEV_TEST_IOCTL(dev)) return -ENODEV; ifr.ifr_data = (void*)arg; - return dev->do_ioctl(dev,&ifr,cmd); + return WAN_NETDEV_IOCTL(dev,&ifr,cmd); } DEBUG_EVENT("%s: Ioctl call not supported DevPriv %i Cmd %i \n", @@ -426,7 +427,7 @@ static int wanpipe_listen_rcv (struct sk_buff *skb, struct sock *sk) /* Bind the new socket into the lower layer. The lower * layer will increment the sock reference count. */ ifr.ifr_data = (void*)newsk; - if (!dev->do_ioctl || dev->do_ioctl(dev,&ifr,SIOC_ANNEXG_BIND_SK) != 0){ + if (!WAN_NETDEV_TEST_IOCTL(dev) || WAN_NETDEV_IOCTL(dev,&ifr,SIOC_ANNEXG_BIND_SK) != 0){ wanpipe_kill_sock(newsk); return -ENODEV; } @@ -603,9 +604,9 @@ static int wanpipe_accept(struct socket *sock, struct socket *newsock, int flags if (wansk_is_zapped(newsk) && SK_PRIV(newsk) && (dev = (struct net_device *)SK_PRIV(newsk)->dev)){ - if (dev && dev->do_ioctl){ + if (dev && WAN_NETDEV_TEST_IOCTL(dev)){ struct sock* dev_sk; - dev->do_ioctl(dev,&ifr,SIOC_ANNEXG_GET_SK); + WAN_NETDEV_IOCTL(dev,&ifr,SIOC_ANNEXG_GET_SK); if ((dev_sk=(struct sock*)ifr.ifr_data)!=NULL){ __sock_put(dev_sk); @@ -623,8 +624,8 @@ static int wanpipe_accept(struct socket *sock, struct socket *newsock, int flags } ifr.ifr_data=(void*)newsk; - if (dev->do_ioctl(dev,&ifr,SIOC_ANNEXG_UNBIND_SK)==0){ - dev->do_ioctl(dev,NULL,SIOC_ANNEXG_CLEAR_CALL); + if (WAN_NETDEV_IOCTL(dev,&ifr,SIOC_ANNEXG_UNBIND_SK)==0){ + WAN_NETDEV_IOCTL(dev,NULL,SIOC_ANNEXG_CLEAR_CALL); } }else{ printk(KERN_INFO "af_wanpipe: Accept killing newsk, lower layer down!\n"); @@ -973,7 +974,7 @@ static int wanpipe_sendmsg(struct socket *sock, struct msghdr *msg, int len, #endif AF_SKB_DEC(skb->truesize); - if (!dev->hard_start_xmit(skb,dev)){ + if (!WAN_NETDEV_XMIT(skb,dev)){ return(len); }else{ err = -EBUSY; @@ -1082,11 +1083,11 @@ static void release_queued_pending_sockets(struct sock *sk) if (SK_PRIV(deadsk)){ dev = (struct net_device *)SK_PRIV(deadsk)->dev; - if (dev && dev->do_ioctl){ + if (dev && WAN_NETDEV_TEST_IOCTL(dev)){ struct ifreq ifr; ifr.ifr_data=(void*)sk; - if (dev->do_ioctl(dev,&ifr,SIOC_ANNEXG_UNBIND_SK)==0){ - dev->do_ioctl(dev,NULL,SIOC_ANNEXG_CLEAR_CALL); + if (WAN_NETDEV_IOCTL(dev,&ifr,SIOC_ANNEXG_UNBIND_SK)==0){ + WAN_NETDEV_IOCTL(dev,NULL,SIOC_ANNEXG_CLEAR_CALL); } } } @@ -1162,11 +1163,11 @@ static int wanpipe_release(struct socket *sock, struct socket *peersock) netdevice_t *dev = (struct net_device *)SK_PRIV(sk)->dev; if (dev){ - if(dev->do_ioctl){ + if(WAN_NETDEV_TEST_IOCTL(dev)){ struct ifreq ifr; ifr.ifr_data=(void*)sk; - if (dev->do_ioctl(dev,&ifr,SIOC_ANNEXG_UNBIND_SK)==0){ - dev->do_ioctl(dev,NULL,SIOC_ANNEXG_CLEAR_CALL); + if (WAN_NETDEV_IOCTL(dev,&ifr,SIOC_ANNEXG_UNBIND_SK)==0){ + WAN_NETDEV_IOCTL(dev,NULL,SIOC_ANNEXG_CLEAR_CALL); } } }else{ @@ -1175,10 +1176,10 @@ static int wanpipe_release(struct socket *sock, struct socket *peersock) }else if (wansk_is_zapped(sk)){ netdevice_t *dev = (struct net_device *)SK_PRIV(sk)->dev; if (dev){ - if(dev->do_ioctl){ + if(WAN_NETDEV_TEST_IOCTL(dev)){ struct ifreq ifr; ifr.ifr_data=(void*)sk; - dev->do_ioctl(dev,&ifr,SIOC_ANNEXG_UNBIND_SK); + WAN_NETDEV_IOCTL(dev,&ifr,SIOC_ANNEXG_UNBIND_SK); } }else{ DEBUG_EVENT("%s: No dev on pvc release !\n",__FUNCTION__); @@ -1465,8 +1466,8 @@ wanpipe_svc_listen_skip: } #endif - if (dev->do_ioctl) - err=dev->do_ioctl(dev,&ifr,SIOC_ANNEXG_BIND_SK); + if (WAN_NETDEV_TEST_IOCTL(dev)) + err=WAN_NETDEV_IOCTL(dev,&ifr,SIOC_ANNEXG_BIND_SK); if (err == 0){ sk->sk_bound_dev_if = dev->ifindex; @@ -1478,7 +1479,7 @@ wanpipe_svc_listen_skip: SK_PRIV(sk)->num == htons(DSP_PROT)){ sk->sk_state = WANSOCK_DISCONNECTED; }else{ - err=dev->do_ioctl(dev,&ifr,SIOC_WANPIPE_DEV_STATE); + err=WAN_NETDEV_IOCTL(dev,&ifr,SIOC_WANPIPE_DEV_STATE); if (err == WANSOCK_CONNECTED){ sk->sk_state = WANSOCK_CONNECTED; }else{ @@ -1622,8 +1623,8 @@ static int wanpipe_recvmsg(struct socket *sock, struct msghdr *msg, int len, dev = (struct net_device *)SK_PRIV(sk)->dev; if (dev){ - if (dev->do_ioctl){ - dev->do_ioctl(dev,NULL,SIOC_ANNEXG_KICK); + if (WAN_NETDEV_TEST_IOCTL(dev)){ + WAN_NETDEV_IOCTL(dev,NULL,SIOC_ANNEXG_KICK); } } @@ -1966,13 +1967,13 @@ static int wanpipe_connect(struct socket *sock, struct sockaddr *uaddr, int addr return -ENETUNREACH; } - if (!dev->do_ioctl) + if (!WAN_NETDEV_TEST_IOCTL(dev)) return -ENETUNREACH; sock->state = SS_CONNECTING; sk->sk_state = WANSOCK_CONNECTING; - err=dev->do_ioctl(dev,NULL,SIOC_ANNEXG_PLACE_CALL); + err=WAN_NETDEV_IOCTL(dev,NULL,SIOC_ANNEXG_PLACE_CALL); if (err){ sk->sk_state = WANSOCK_DISCONNECTED; sock->state = SS_UNCONNECTED; diff --git a/patches/kdrivers/src/wanrouter/af_wanpipe_datascope.c b/patches/kdrivers/src/wanrouter/af_wanpipe_datascope.c index 44cfb2c..3c6b9a2 100644 --- a/patches/kdrivers/src/wanrouter/af_wanpipe_datascope.c +++ b/patches/kdrivers/src/wanrouter/af_wanpipe_datascope.c @@ -231,8 +231,8 @@ int wanpipe_bind_sk_to_parent(struct sock *sk, netdevice_t *dev, struct wan_sock ifr.ifr_data = (void*)parent_sk; err=-EINVAL; - if (dev->do_ioctl) - err=dev->do_ioctl(dev,&ifr,SIOC_WANPIPE_BIND_SK); + if (WAN_NETDEV_TEST_IOCTL(dev)) + err=WAN_NETDEV_IOCTL(dev,&ifr,SIOC_WANPIPE_BIND_SK); if (err != 0){ DEBUG_EVENT("%s: Error: Dev busy with another protocol!\n", @@ -256,7 +256,7 @@ int wanpipe_bind_sk_to_parent(struct sock *sk, netdevice_t *dev, struct wan_sock PPRIV(parent_sk)->seven_bit_hdlc?7:8); PPRIV(parent_sk)->time_slots = - dev->do_ioctl(dev,NULL,SIOC_WANPIPE_GET_TIME_SLOTS); + WAN_NETDEV_IOCTL(dev,NULL,SIOC_WANPIPE_GET_TIME_SLOTS); if (PPRIV(parent_sk)->time_slots < 0){ DEBUG_EVENT("%s: Error, failed to obtain time slots from driver!\n", dev->name); @@ -265,7 +265,7 @@ int wanpipe_bind_sk_to_parent(struct sock *sk, netdevice_t *dev, struct wan_sock } PPRIV(parent_sk)->media = - dev->do_ioctl(dev,NULL,SIOC_WANPIPE_GET_MEDIA_TYPE); + WAN_NETDEV_IOCTL(dev,NULL,SIOC_WANPIPE_GET_MEDIA_TYPE); if (PPRIV(parent_sk)->media < 0){ DEBUG_EVENT("%s: Error, failed to obtain media type from driver!\n", dev->name); @@ -302,7 +302,7 @@ int wanpipe_bind_sk_to_parent(struct sock *sk, netdevice_t *dev, struct wan_sock #endif write_unlock_irqrestore(&wanpipe_parent_sklist_lock,flags); - err=dev->do_ioctl(dev,&ifr,SIOC_WANPIPE_DEV_STATE); + err=WAN_NETDEV_IOCTL(dev,&ifr,SIOC_WANPIPE_DEV_STATE); if (err == WANSOCK_CONNECTED){ parent_sk->sk_state = WANSOCK_CONNECTED; }else{ @@ -1726,12 +1726,12 @@ static void wanpipe_free_parent_sock(struct sock *sk) write_lock_irqsave(&PPRIV(sk)->lock,flags); dev=SK_PRIV(sk)->dev; - if (dev && dev->do_ioctl){ + if (dev && WAN_NETDEV_TEST_IOCTL(dev)){ struct ifreq ifr; memset(&ifr,0,sizeof(struct ifreq)); ifr.ifr_data = (void*)sk; DEBUG_TEST("%s: UNBINDING SK dev=%s\n",__FUNCTION__,dev->name); - dev->do_ioctl(dev,&ifr,SIOC_WANPIPE_UNBIND_SK); + WAN_NETDEV_IOCTL(dev,&ifr,SIOC_WANPIPE_UNBIND_SK); } sk->sk_socket = NULL; diff --git a/patches/kdrivers/src/wanrouter/wanmain.c b/patches/kdrivers/src/wanrouter/wanmain.c index 1b25966..129fb6f 100644 --- a/patches/kdrivers/src/wanrouter/wanmain.c +++ b/patches/kdrivers/src/wanrouter/wanmain.c @@ -54,7 +54,6 @@ #include "wanrouter.h" /* WAN router API definitions */ #include "wanpipe.h" /* WAN router API definitions */ #include "if_wanpipe.h" -#include "wanpipe_cdev_iface.h" #include #include @@ -255,10 +254,7 @@ int __init wanrouter_init (void) "%s: can't create entry in proc filesystem!\n", modname); } - err=wanpipe_global_cdev_init(); - if (err == 0) { - wanpipe_wandev_create(); - } + wanpipe_wandev_create(); #ifdef CONFIG_PRODUCT_WANPIPE_ANNEXG UNREG_PROTOCOL_FUNC(dsp_protocol); @@ -294,7 +290,6 @@ void __exit wanrouter_exit (void) UNREG_PROTOCOL_FUNC(wp_fw_protocol); wanrouter_proc_cleanup(); wanpipe_wandev_free(); - wanpipe_global_cdev_free(); } module_init(wanrouter_init); diff --git a/patches/kdrivers/src/wanrouter/wanproc.c b/patches/kdrivers/src/wanrouter/wanproc.c index 5efd602..adb2b1b 100644 --- a/patches/kdrivers/src/wanrouter/wanproc.c +++ b/patches/kdrivers/src/wanrouter/wanproc.c @@ -848,6 +848,9 @@ static int probe_get_info(char* buf, char** start, off_t offs, int len, int dumm if (hw_cnt->aft_a600_adapters){ PROC_ADD_LINE(m, "B600=%d ", hw_cnt->aft_a600_adapters); } + if (hw_cnt->aft_b601_adapters){ + PROC_ADD_LINE(m, "B601=%d ", hw_cnt->aft_b601_adapters); + } PROC_ADD_LINE(m, "\n"); PROC_ADD_RET(m); @@ -889,7 +892,7 @@ static int probe_get_info_legacy(char* buf, char** start, off_t offs, int len, i hw_cnt=(sdla_hw_type_cnt_t*)sdla_get_hw_adptr_cnt(); PROC_ADD_LINE(m, - "\nCard Cnt: S508=%d S514X=%d S518=%d A101-2=%d A104=%d A300=%d A200=%d A108=%d A056=%d\n A500=%d A14x=%d A600=%d\n", + "\nCard Cnt: S508=%d S514X=%d S518=%d A101-2=%d A104=%d A300=%d A200=%d A108=%d A056=%d\n A500=%d A14x=%d A600=%d B601=%d\n", hw_cnt->s508_adapters, hw_cnt->s514x_adapters, hw_cnt->s518_adapters, @@ -901,7 +904,8 @@ static int probe_get_info_legacy(char* buf, char** start, off_t offs, int len, i hw_cnt->aft_56k_adapters, hw_cnt->aft_isdn_adapters, hw_cnt->aft_serial_adapters, - hw_cnt->aft_a600_adapters + hw_cnt->aft_a600_adapters, + hw_cnt->aft_b601_adapters ); PROC_ADD_RET(m); @@ -987,6 +991,9 @@ static int probe_get_info_verbose(char* buf, char** start, off_t offs, int len, if (hw_cnt->aft_a600_adapters){ PROC_ADD_LINE(m, "B600=%d ", hw_cnt->aft_a600_adapters); } + if (hw_cnt->aft_b601_adapters){ + PROC_ADD_LINE(m, "B601=%d ", hw_cnt->aft_b601_adapters); + } PROC_ADD_LINE(m, "\n"); PROC_ADD_RET(m); @@ -1024,9 +1031,8 @@ static int probe_get_info_dump(char* buf, char** start, off_t offs, int len, int hw_cnt=(sdla_hw_type_cnt_t*)sdla_get_hw_adptr_cnt(); - PROC_ADD_LINE(m, - "|Card Cnt|S508=%d|S514X=%d|S518=%d|A101-2=%d|A104=%d|A300=%d|A200=%d|A108=%d|A056=%d|A500=%d|B700=%d|B600=%d|A14x=%d\n", + "|Card Cnt|S508=%d|S514X=%d|S518=%d|A101-2=%d|A104=%d|A300=%d|A200=%d|A108=%d|A056=%d|A500=%d|B700=%d|B600=%d|B601=%d|A14x=%d\n", hw_cnt->s508_adapters, hw_cnt->s514x_adapters, hw_cnt->s518_adapters, @@ -1039,6 +1045,7 @@ static int probe_get_info_dump(char* buf, char** start, off_t offs, int len, int hw_cnt->aft_isdn_adapters, hw_cnt->aft_a700_adapters, hw_cnt->aft_a600_adapters, + hw_cnt->aft_b601_adapters, hw_cnt->aft_serial_adapters ); @@ -2094,7 +2101,7 @@ int proc_add_line(struct seq_file* m, char* frm, ...) size = vsprintf(tmp, frm, arg); if (m->stop_cnt){ if (m->stop_cnt < size){ - DEBUG_EVENT("!!! Error in writting in proc buffer !!!\n"); + DEBUG_ERROR("!!! Error in writting in proc buffer !!!\n"); m->stop_cnt = size; } m->stop_cnt -= size; @@ -2135,7 +2142,7 @@ int proc_add_line(struct seq_file* m, char* frm, ...) size = vsprintf(tmp, frm, arg); if (m->stop_cnt){ if (m->stop_cnt < size){ - DEBUG_EVENT("!!! Error in writting in proc buffer !!!\n"); + DEBUG_ERROR("!!! Error in writting in proc buffer !!!\n"); m->stop_cnt = size; } m->stop_cnt -= size; diff --git a/patches/kdrivers/wanec/.tmp_versions/wanec.mod b/patches/kdrivers/wanec/.tmp_versions/wanec.mod index 751b15b..daeeed3 100644 --- a/patches/kdrivers/wanec/.tmp_versions/wanec.mod +++ b/patches/kdrivers/wanec/.tmp_versions/wanec.mod @@ -1,2 +1,2 @@ -/root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/wanec.ko -/root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/wanec_iface.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/wanec_cmd.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/wanec_utils.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/wanec_dev.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/apilib/bt/octapi_bt0.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/apilib/largmath/octapi_largmath.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/apilib/llman/octapi_llman.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_apimi/oct6100_mask_interrupts.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_adpcm_chan.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_channel.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_chip_open.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_chip_stats.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_conf_bridge.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_debug.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_events.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_interrupts.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_memory.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_miscellaneous.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_mixer.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_phasing_tsst.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_playout_buf.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_remote_debug.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_tlv.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_tone_detection.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_tsi_cnct.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_tsst.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_user.o +/root/3.5/wanpipe/patches/kdrivers/wanec/wanec.ko +/root/3.5/wanpipe/patches/kdrivers/wanec/wanec_iface.o /root/3.5/wanpipe/patches/kdrivers/wanec/wanec_cmd.o /root/3.5/wanpipe/patches/kdrivers/wanec/wanec_utils.o /root/3.5/wanpipe/patches/kdrivers/wanec/wanec_dev.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/apilib/bt/octapi_bt0.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/apilib/largmath/octapi_largmath.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/apilib/llman/octapi_llman.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_apimi/oct6100_mask_interrupts.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_adpcm_chan.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_channel.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_chip_open.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_chip_stats.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_conf_bridge.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_debug.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_events.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_interrupts.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_memory.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_miscellaneous.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_mixer.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_phasing_tsst.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_playout_buf.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_remote_debug.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_tlv.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_tone_detection.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_tsi_cnct.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_tsst.o /root/3.5/wanpipe/patches/kdrivers/wanec/oct6100_api/octdeviceapi/oct6100api/oct6100_api/oct6100_user.o diff --git a/patches/kdrivers/wanec/.wanec.ko.cmd b/patches/kdrivers/wanec/.wanec.ko.cmd deleted file mode 100644 index 1e1b059..0000000 --- a/patches/kdrivers/wanec/.wanec.ko.cmd +++ /dev/null @@ -1 +0,0 @@ -cmd_/root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/wanec.ko := ld -m elf_i386 -m elf_i386 -r -o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/wanec.ko /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/wanec.o /root/development/3.5/wanpipe-3.5.5.1/patches/kdrivers/wanec/wanec.mod.o diff --git a/patches/kdrivers/wanec/Module.markers b/patches/kdrivers/wanec/Module.markers new file mode 100644 index 0000000..e69de29 diff --git a/patches/kdrivers/wanec/Module.symvers b/patches/kdrivers/wanec/Module.symvers deleted file mode 100644 index a5b76b9..0000000 --- a/patches/kdrivers/wanec/Module.symvers +++ /dev/null @@ -1,63 +0,0 @@ -0xf7567a8a wanpipe_api_buf_check /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0xf27fa082 wanpipe_lip_connect /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x1c28a8bc wanpipe_cdev_tdm_create /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0xe656360e sdladrv_hw_mode /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/sdladrv EXPORT_SYMBOL -0xa072c237 wanpipe_cdev_rx_wake /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0xfa708564 wanpipe_cdev_tx_wake /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x78e79b7b sdla_register /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/sdladrv EXPORT_SYMBOL -0x57e6ed60 wanpipe_api_sock_rx /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x0c1a3edc wanpipe_cdev_event_wake /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x218b0f0d wanrouter_proc_add_interface /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x59fb2682 sdla_hw_probe /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/sdladrv EXPORT_SYMBOL -0x9a7a6658 wanpipe_lip_rx /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x8f31cdeb proc_router /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0xf8c8ebb2 register_wanec_iface /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x1036a1ab bind_api_listen_to_protocol /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x524e2f20 register_wanpipe_fw_protocol /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0xeea52827 register_wan_device /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0xbbee2caf wanrouter_proc_add_protocol /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0xf3e2adf8 wanpipe_ec_event_ctrl /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x7cd3c2bc wanpipe_ec_poll /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x3f0eecc5 wanrouter_proc_delete_protocol /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x040b23de wanpipe_cdev_free /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0xdfd0f6c6 sdla_get_hw_adptr_cnt /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/sdladrv EXPORT_SYMBOL -0xc2143791 wanpipe_ec_register /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0xb275ea7a wanpipe_cdev_cfg_ctrl_create /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0xb8da2b4c wanpipe_global_cdev_free /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x32ff30e9 wanpipe_cdev_tdm_ctrl_create /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0xaa81d611 wanpipe_global_cdev_init /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x02364d27 wanrouter_encapsulate /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x136b2537 wan_skb_destructor /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0xfa083e32 register_wanpipe_api_socket /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x4de93988 sdla_get_hw_usb_adptr_cnt /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/sdladrv EXPORT_SYMBOL -0xade2d0d7 sdla_get_hw_probe /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/sdladrv EXPORT_SYMBOL -0xcd18a15e sdladrv_callback /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/sdladrv EXPORT_SYMBOL -0xfd0d2b7b wanpipe_cdev_timer_create /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x62d85b61 sdla_get_hwinfo /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/sdladrv EXPORT_SYMBOL -0xef48df91 proc_add_line /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x22a2999f sdla_unregister /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/sdladrv EXPORT_SYMBOL -0x85c05713 wanrouter_type_trans /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0xfd74fadb wan_set_ip_address /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0xc98aebe6 wan_get_ip_address /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x7d68ed07 protocol_disconnected /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0xc5224c15 bind_api_to_protocol /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x38eb6851 unregister_wanpipe_api_socket /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0xaac011d2 wanpipe_lip_kick /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x3b604364 unbind_api_listen_from_protocol /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x6df6c4e0 wan_run_wanrouter /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0xeb418feb sdla_hw_bridge_probe /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/sdladrv EXPORT_SYMBOL -0xea9f3201 gl_usb_rw_fast /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/sdladrv EXPORT_SYMBOL -0x04df932b unregister_wanec_iface /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x562d2ade wanpipe_ec_isr /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x13405f6b unregister_wanpipe_fw_protocol /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0xe72f44cb wanpipe_api_listen_rx /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x5b7efb57 wanpipe_api_poll_wake /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x651a840a wan_add_gateway /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x2f21a326 unregister_wanpipe_lip_protocol /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x6fbbaa79 wanrouter_proc_delete_interface /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x0ebe03d1 unregister_wan_device /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x22317d82 wanpipe_lip_disconnect /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0xed6a48f7 protocol_connected /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x53350bd4 wanpipe_ec_unregister /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x986d6be9 register_wanpipe_lip_protocol /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL -0x00525339 protocol_connecting /root/development/3.5/wanpipe-3.5.5.1/kdrvtmp/wanrouter EXPORT_SYMBOL diff --git a/patches/kdrivers/wanec/OCT612x-01.01.01/octdeviceapi/oct6100api/oct6100_api/oct6100_channel.c b/patches/kdrivers/wanec/OCT612x-01.01.01/octdeviceapi/oct6100api/oct6100_api/oct6100_channel.c index 463f077..f9a512e 100644 --- a/patches/kdrivers/wanec/OCT612x-01.01.01/octdeviceapi/oct6100api/oct6100_api/oct6100_channel.c +++ b/patches/kdrivers/wanec/OCT612x-01.01.01/octdeviceapi/oct6100api/oct6100_api/oct6100_channel.c @@ -115,12 +115,16 @@ UINT32 Oct6100ChannelOpenDef( f_pChannelOpen->VqeConfig.lRinLevelControlGainDb = 0; f_pChannelOpen->VqeConfig.fSoutLevelControl = FALSE; f_pChannelOpen->VqeConfig.lSoutLevelControlGainDb = 0; + f_pChannelOpen->VqeConfig.fRinAutomaticLevelControl = FALSE; f_pChannelOpen->VqeConfig.lRinAutomaticLevelControlTargetDb = -20; + f_pChannelOpen->VqeConfig.fSoutAutomaticLevelControl = FALSE; f_pChannelOpen->VqeConfig.lSoutAutomaticLevelControlTargetDb = -20; + f_pChannelOpen->VqeConfig.fRinHighLevelCompensation = FALSE; f_pChannelOpen->VqeConfig.lRinHighLevelCompensationThresholdDb = -10; + f_pChannelOpen->VqeConfig.fSoutAdaptiveNoiseReduction = FALSE; f_pChannelOpen->VqeConfig.fSoutNoiseBleaching = FALSE; f_pChannelOpen->VqeConfig.fSoutConferencingNoiseReduction = FALSE; @@ -146,6 +150,7 @@ UINT32 Oct6100ChannelOpenDef( f_pChannelOpen->VqeConfig.lRoutNoiseReductionLevelGainDb = -18; f_pChannelOpen->VqeConfig.lAnrSnrEnhancementDb = -18; f_pChannelOpen->VqeConfig.ulAnrVoiceNoiseSegregation = 6; + f_pChannelOpen->VqeConfig.ulToneDisablerVqeActivationDelay = 300; f_pChannelOpen->VqeConfig.fEnableMusicProtection = FALSE; /* Older images have idle code detection hard-coded to enabled. */ diff --git a/patches/kdrivers/wanec/wanec.mod.c b/patches/kdrivers/wanec/wanec.mod.c index 74f5fb1..d43aa5c 100644 --- a/patches/kdrivers/wanec/wanec.mod.c +++ b/patches/kdrivers/wanec/wanec.mod.c @@ -17,10 +17,12 @@ static const struct modversion_info ____versions[] __attribute_used__ __attribute__((section("__versions"))) = { { 0x89e24b9c, "struct_module" }, + { 0x3f234f3e, "wp_logger_input" }, { 0x12da5bb2, "__kmalloc" }, { 0x7e3f931f, "_spin_trylock" }, { 0xec7bc0d, "__mod_timer" }, { 0xd6ee688f, "vmalloc" }, + { 0xc12eb167, "wp_logger_level_hwec" }, { 0x4827a016, "del_timer" }, { 0xf26c4b72, "class_device_destroy" }, { 0xb5513e49, "class_device_create" }, @@ -31,13 +33,14 @@ __attribute__((section("__versions"))) = { { 0x2fd1d81c, "vfree" }, { 0x1d26aa98, "sprintf" }, { 0x7d11c268, "jiffies" }, - { 0xf8c8ebb2, "register_wanec_iface" }, - { 0x1b7d4074, "printk" }, + { 0x5487ec8, "wp_logger_level_default" }, + { 0x77da8dee, "register_wanec_iface" }, { 0x5152e605, "memcmp" }, { 0x2da418b5, "copy_to_user" }, { 0x2e1de6c1, "class_create" }, { 0x19070091, "kmem_cache_alloc" }, { 0x4df932b, "unregister_wanec_iface" }, + { 0xdeeaedb, "wp_logger_repeating_message_filter" }, { 0x4086729e, "register_chrdev" }, { 0xd0b91f9b, "init_timer" }, { 0xf6ebc03b, "net_ratelimit" }, @@ -52,7 +55,7 @@ __attribute__((section("__versions"))) = { static const char __module_depends[] __attribute_used__ __attribute__((section(".modinfo"))) = -"depends=wanrouter"; +"depends=sdladrv,wanrouter"; -MODULE_INFO(srcversion, "D37B2ECDB244E4C430F264D"); +MODULE_INFO(srcversion, "76D1AB69F84D05B5AD36F13"); diff --git a/patches/kdrivers/wanec/wanec_cmd.c b/patches/kdrivers/wanec/wanec_cmd.c index bd75113..e7ee1fd 100644 --- a/patches/kdrivers/wanec/wanec_cmd.c +++ b/patches/kdrivers/wanec/wanec_cmd.c @@ -188,7 +188,6 @@ int wanec_fe2ec_channel(wan_ec_dev_t *ec_dev, int fe_channel) /* WANEC_MAX_PORT_RANGE = 32 */ /* For A500, fe_lineno 1-> 24 */ - /* DavidY Clean up later */ if (ec_dev->card->adptr_type == AFT_ADPTR_FLEXBRI) { if (ec_dev->fe_lineno == 4){ //if (ec_dev->fe_media == WAN_MEDIA_FXOFXS){ @@ -202,9 +201,11 @@ int wanec_fe2ec_channel(wan_ec_dev_t *ec_dev, int fe_channel) ec_channel = WANEC_MAX_PORT_RANGE; } ec_channel += (ec_dev->fe_lineno * WANEC_MAX_BRI_PORT_RANGE + (fe_channel-1)); - }else if (ec_dev->fe_media == WAN_MEDIA_T1 || ec_dev->fe_media == WAN_MEDIA_FXOFXS){ + }else if (ec_dev->fe_media == WAN_MEDIA_T1 || + ec_dev->fe_media == WAN_MEDIA_FXOFXS || + ec_dev->card->adptr_type == AFT_ADPTR_B601) { ec_channel = ec_dev->fe_lineno * WANEC_MAX_PORT_RANGE + (fe_channel-1); - }else{ + } else { /*ec_channel = ec_dev->fe_lineno * ec_dev->fe_max_chans + channel;*/ ec_channel = ec_dev->fe_lineno * WANEC_MAX_PORT_RANGE + fe_channel; } @@ -226,7 +227,8 @@ static int wanec_ec2fe_channel(wan_ec_t *ec, int ec_chan, wan_ec_dev_t **ec_dev) fe_chan++; }else{ if (ec_dev_tmp->fe_media == WAN_MEDIA_T1 || - ec_dev_tmp->fe_media == WAN_MEDIA_FXOFXS){ + ec_dev_tmp->fe_media == WAN_MEDIA_FXOFXS || + ec_dev_tmp->card->adptr_type == AFT_ADPTR_B601){ fe_chan++; } } @@ -494,7 +496,7 @@ int wanec_ChipOpenPrep(wan_ec_dev_t *ec_dev, char *devname, wanec_config_t *conf ec->f_Context.ec_dev = ec_dev; /* Interface name to driver */ - strlcpy(ec->f_Context.devname, devname, WAN_DRVNAME_SZ); + wp_strlcpy(ec->f_Context.devname, devname, WAN_DRVNAME_SZ); ulResult = Oct6100GetInstanceSize(&ec->f_OpenChip, &InstanceSize); if ( ulResult != cOCT6100_ERR_OK ){ @@ -679,8 +681,7 @@ int wanec_ChannelOpen(wan_ec_dev_t *ec_dev, INT ec_chan, int verbose) (pcm_law_type == cOCT6100_PCM_U_LAW) ? "MULAW":"ALAW"); - DEBUG_EVENT("%s: Opening HW Echo Canceller (NoiseRed=%s)\n", - ec->name,card->hwec_conf.noise_reduction?"On":"Off"); + Oct6100ChannelOpenDef( &EchoChannelOpen ); @@ -734,10 +735,20 @@ int wanec_ChannelOpen(wan_ec_dev_t *ec_dev, INT ec_chan, int verbose) if (card->hwec_conf.noise_reduction) { EchoChannelOpen.VqeConfig.fSoutAdaptiveNoiseReduction = TRUE; + EchoChannelOpen.VqeConfig.fRinAutomaticLevelControl = TRUE; + EchoChannelOpen.VqeConfig.fSoutAutomaticLevelControl = TRUE; } else { EchoChannelOpen.VqeConfig.fSoutAdaptiveNoiseReduction = FALSE; + EchoChannelOpen.VqeConfig.fRinAutomaticLevelControl = FALSE; + EchoChannelOpen.VqeConfig.fSoutAutomaticLevelControl = FALSE; } + EchoChannelOpen.VqeConfig.ulToneDisablerVqeActivationDelay = ((UINT16)(1500 / 512) + 1) * 512 + 300; /*300;*/ + + DEBUG_EVENT("%s: Opening HW Echo Canceller (NoiseRed=%s VQE=%i)\n", + ec->name,(EchoChannelOpen.VqeConfig.fSoutAdaptiveNoiseReduction == TRUE)?"On":"Off", + EchoChannelOpen.VqeConfig.ulToneDisablerVqeActivationDelay); + EchoChannelOpen.VqeConfig.ulComfortNoiseMode = cOCT6100_COMFORT_NOISE_NORMAL; /* cOCT6100_COMFORT_NOISE_NORMAL diff --git a/patches/kdrivers/wanec/wanec_dev.c b/patches/kdrivers/wanec/wanec_dev.c index 23a5f7a..40e9706 100644 --- a/patches/kdrivers/wanec/wanec_dev.c +++ b/patches/kdrivers/wanec/wanec_dev.c @@ -88,7 +88,12 @@ devfs_handle_t devfs_handle; static int wanec_dev_open(struct inode*, struct file*); static int wanec_dev_release(struct inode*, struct file*); + +#if defined(__WINDOWS__) +int wanec_dev_ioctl(void *data, char *card_devname); +#else static int wanec_dev_ioctl(struct inode*, struct file*, unsigned int, unsigned long); +#endif #if !defined(__WINDOWS__) /*============================================================== @@ -203,7 +208,41 @@ static int wanec_dev_release(struct inode *inode, struct file *file) return 0; } -#if !defined(__WINDOWS__) +#if defined(__WINDOWS__) +extern int wanec_ioctl(void*); +int wanec_dev_ioctl(void *data, char *card_devname) +{ + int rc; + wan_ec_api_t *ec_api; + char original_ec_api_dev_name[WAN_DRVNAME_SZ]; + + if (data == 0){ + DEBUG_ERROR("%s(): Error: data pointer is NULL!\n",__FUNCTION__); + return EINVAL; + } + + ec_api = (wan_ec_api_t*)data; +#if 0 + DbgPrint("%s(): card_devname: %s, ec_api->devname: %s", + __FUNCTION__, card_devname, ec_api->devname); +#endif + /* keep the original name which was set in user mode */ + snprintf(original_ec_api_dev_name, WAN_DRVNAME_SZ, "%s", ec_api->devname); + + /* In user mode the 'ec_api->devname' is set to interface name. + For example "wanpipe2_if0". But wanec code needs 'card name', + ("wanpipe2") not 'interface name' when the wanec_search() is called. + Change 'ec_api->devname' to be the 'card name': */ + snprintf(ec_api->devname, WAN_DRVNAME_SZ, "%s", card_devname); + + rc = wanec_ioctl(data); + + /* restore the original name which was set in user mode */ + snprintf(ec_api->devname, WAN_DRVNAME_SZ, "%s", original_ec_api_dev_name); + + return rc; +} +#else extern int wanec_ioctl(unsigned int, void*); static int wanec_dev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data) diff --git a/patches/kdrivers/wanec/wanec_iface.c b/patches/kdrivers/wanec/wanec_iface.c index 114dd2c..ac0cd71 100644 --- a/patches/kdrivers/wanec/wanec_iface.c +++ b/patches/kdrivers/wanec/wanec_iface.c @@ -142,6 +142,7 @@ static int wanec_api_monitor(wan_ec_dev_t *ec_dev, wan_ec_api_t *ec_api); static wan_ec_dev_t *wanec_search(char *devname); static int wanec_enable(void *pcard, int enable, int fe_chan); + static int wanec_poll(void *arg, void *pcard); #if defined(__FreeBSD__) || defined(__OpenBSD__) @@ -194,17 +195,16 @@ int wan_ec_read_internal_dword(wan_ec_dev_t *ec_dev, u32 addr1, u32 *data) card = ec_dev->card; addr = convert_addr(addr1); if (addr == 0x00){ - DEBUG_EVENT("%s: %s:%d: Internal Error (EC off %X)\n", + DEBUG_ERROR("%s: %s:%d: Internal Error (EC off %X)\n", card->devname, __FUNCTION__,__LINE__, addr1); return -EINVAL; } - if (IS_A600_CARD(card)) { - addr+=0x1000; - } - + if (IS_A600_CARD(card) || IS_B601_CARD(card)) { + addr+=0x1000; + } err = card->hw_iface.bus_read_4(card->hw, addr, data); @@ -227,16 +227,16 @@ int wan_ec_write_internal_dword(wan_ec_dev_t *ec_dev, u32 addr1, u32 data) card = ec_dev->card; addr = convert_addr(addr1); if (addr == 0x00){ - DEBUG_EVENT("%s: %s:%d: Internal Error (EC off %X)\n", + DEBUG_ERROR("%s: %s:%d: Internal Error (EC off %X)\n", card->devname, __FUNCTION__,__LINE__, addr1); return -EINVAL; } - if (IS_A600_CARD(card)) { - addr+=0x1000; - } + if (IS_A600_CARD(card) || IS_B601_CARD(card)) { + addr+=0x1000; + } err = card->hw_iface.bus_write_4(card->hw, addr, data); @@ -271,17 +271,56 @@ static int wanec_reset(wan_ec_dev_t *ec_dev, int reset) return err; } + +static int wanec_state(void *pcard, wan_hwec_dev_state_t *ecdev_state) +{ + sdla_t *card = (sdla_t*)pcard; + wan_ec_dev_t *ec_dev = NULL; + wan_ec_t *ec; + + ec_dev = card->wandev.ec_dev; + + WAN_ASSERT(ec_dev == NULL); + WAN_ASSERT(ec_dev->ec == NULL); + ec = ec_dev->ec; + + if (ec->state == WAN_EC_STATE_CHIP_OPEN) { + ec_dev->ecdev_state.ec_state=1; + } else { + ec_dev->ecdev_state.ec_state=0; + } + + memcpy(ecdev_state,&ec_dev->ecdev_state,sizeof(wan_hwec_dev_state_t)); + + DEBUG_HWEC("%s: wanecdev_state ec_state=%i, ec_mode_map=0x%08X, dtmf_map=0x%08X, fax_called=0x%08X, fax_calling=0x%08X\n", + ec_dev->name, + ecdev_state->ec_state, + ecdev_state->ec_mode_map, + ecdev_state->dtmf_map, + ecdev_state->fax_called_map, + ecdev_state->fax_calling_map); + + return 0; +} + static int wanec_enable(void *pcard, int enable, int fe_chan) { sdla_t *card = (sdla_t*)pcard; + wan_ec_t *ec = NULL; wan_ec_dev_t *ec_dev = NULL; wan_smp_flag_t flags; int err; - ec_dev = - wanec_search(card->devname); + ec_dev = card->wandev.ec_dev; WAN_ASSERT(ec_dev == NULL); + WAN_ASSERT(ec_dev->ec == NULL); + ec = ec_dev->ec; + + if (ec->state != WAN_EC_STATE_CHIP_OPEN){ + return -EINVAL; + } + #if defined(WANEC_BYDEFAULT_NORMAL) WAN_ASSERT(ec_dev->ec == NULL); @@ -327,8 +366,9 @@ wanec_bypass(wan_ec_dev_t *ec_dev, int fe_chan, int enable, int verbose) if (!wan_test_bit(fe_chan, &ec_dev->fe_channel_map)){ PRINT1(verbose, "%s: FE channel %d is not available (fe_chan_map=%X)!\n", ec->name, fe_chan, ec_dev->fe_channel_map); - return 0; + return -ENODEV; } + ec_chan = wanec_fe2ec_channel(ec_dev, fe_chan); if (enable){ if (wan_test_bit(fe_chan, &card->wandev.fe_ec_map)){ @@ -350,7 +390,7 @@ wanec_bypass(wan_ec_dev_t *ec_dev, int fe_chan, int enable, int verbose) err=wan_ec_update_and_check(ec,enable); if (err) { - DEBUG_EVENT("%s: Error: Maximum EC Channels Reached! MaxEC=%i\n", + DEBUG_ERROR("%s: Error: Maximum EC Channels Reached! MaxEC=%i\n", ec->name,ec->max_ec_chans); return err; } @@ -471,6 +511,7 @@ static int wanec_channel_opmode_modify(wan_ec_dev_t *ec_dev, int fe_chan, UINT32 { wan_ec_t *ec = NULL; u_int32_t ec_chan = 0; + int err=0; WAN_ASSERT(ec_dev == NULL); WAN_ASSERT(ec_dev->ec == NULL); @@ -513,7 +554,44 @@ static int wanec_channel_opmode_modify(wan_ec_dev_t *ec_dev, int fe_chan, UINT32 (opmode == cOCT6100_ECHO_OP_MODE_SPEECH_RECOGNITION) ? "Speech Recognition" : "Unknown", fe_chan); ec_chan = wanec_fe2ec_channel(ec_dev, fe_chan); - return wanec_ChannelModifyOpmode(ec_dev, ec_chan, opmode, verbose); + err=wanec_ChannelModifyOpmode(ec_dev, ec_chan, opmode, verbose); + + if (err == 0) { + if (opmode == cOCT6100_ECHO_OP_MODE_POWER_DOWN) { + wan_clear_bit(fe_chan,&ec_dev->ecdev_state.ec_mode_map); + } else { + wan_set_bit(fe_chan,&ec_dev->ecdev_state.ec_mode_map); + } + } + + return err; +} +static int wanec_update_tone_status (wan_ec_dev_t *ec_dev, + int fe_chan, + int cmd, + wanec_tone_config_t *tone) +{ + if (tone->id == WP_API_EVENT_TONE_DTMF) { + if (cmd == WAN_TRUE) { + wan_set_bit(fe_chan,&ec_dev->ecdev_state.dtmf_map); + } else { + wan_clear_bit(fe_chan,&ec_dev->ecdev_state.dtmf_map); + } + } else if (tone->id == WP_API_EVENT_TONE_FAXCALLING) { + if (cmd == WAN_TRUE) { + wan_set_bit(fe_chan,&ec_dev->ecdev_state.fax_calling_map); + } else { + wan_clear_bit(fe_chan,&ec_dev->ecdev_state.fax_calling_map); + } + } else if (tone->id == WP_API_EVENT_TONE_FAXCALLED) { + if (cmd == WAN_TRUE) { + wan_set_bit(fe_chan,&ec_dev->ecdev_state.fax_called_map); + } else { + wan_clear_bit(fe_chan,&ec_dev->ecdev_state.fax_called_map); + } + } + + return 0; } static int wanec_channel_tone( wan_ec_dev_t *ec_dev, @@ -554,6 +632,9 @@ static int wanec_channel_tone( wan_ec_dev_t *ec_dev, ec_chan = wanec_fe2ec_channel(ec_dev, fe_chan); err = wanec_TonesCtrl(ec, cmd, ec_chan, tone, verbose); if (err == WAN_EC_API_RC_OK){ + + wanec_update_tone_status(ec_dev, fe_chan, cmd, tone); + if (cmd == WAN_TRUE){ wan_set_bit(WAN_EC_BIT_EVENT_TONE, &ec_dev->events); ec->tone_verbose = verbose; @@ -787,7 +868,7 @@ int wanec_api_release(wan_ec_dev_t *ec_dev, wan_ec_api_t *ec_api, int verbose) if (ec_dev_tmp->state == WAN_EC_STATE_CHAN_READY){ /* This EC device is still connected */ ec->f_Context.ec_dev = ec_dev_tmp; - strlcpy(ec->f_Context.devname, ec_dev_tmp->devname, WAN_DRVNAME_SZ); + wp_strlcpy(ec->f_Context.devname, ec_dev_tmp->devname, WAN_DRVNAME_SZ); break; } } @@ -804,9 +885,14 @@ int wanec_api_release(wan_ec_dev_t *ec_dev, wan_ec_api_t *ec_api, int verbose) } if (ec_dev->fe_media == WAN_MEDIA_E1 && fe_chan == 0) continue; ec_chan = wanec_fe2ec_channel(ec_dev, fe_chan); + + wanec_bypass(ec_dev, fe_chan, 0, 0); wanec_ChannelClose(ec_dev, ec_chan, verbose); } } + + memset(&ec_dev->ecdev_state,0,sizeof(ec_dev->ecdev_state)); + ec_dev->state = WAN_EC_STATE_RESET; if (ec_dev_tmp){ /* EC device is still in used */ @@ -878,7 +964,9 @@ static int wanec_api_modify(wan_ec_dev_t *ec_dev, wan_ec_api_t *ec_api) ec_dev, fe_chan, cOCT6100_ECHO_OP_MODE_NORMAL, ec_api->verbose); - if (err) return WAN_EC_API_RC_FAILED; + if (err) { + return WAN_EC_API_RC_FAILED; + } err = wanec_bypass(ec_dev, fe_chan, 1, ec_api->verbose); }else{ wanec_bypass(ec_dev, fe_chan, 0, ec_api->verbose); @@ -1009,16 +1097,6 @@ static int wanec_api_modify_bypass(wan_ec_dev_t *ec_dev, wan_ec_api_t *ec_api) WAN_ASSERT(ec_dev->ec == NULL); ec = ec_dev->ec; -#if 1 - if (ec_dev->state != WAN_EC_STATE_CHAN_READY){ - DEBUG_EVENT("DAVIDY Ignoring EC state - put back later\n"); - DEBUG_EVENT( - "ERROR: %s: Invalid Echo Canceller %s API state (%s)\n", - ec_dev->devname, - ec->name, - WAN_EC_STATE_DECODE(ec->state)); - } -#else if (ec_dev->state != WAN_EC_STATE_CHAN_READY){ DEBUG_EVENT( "ERROR: %s: Invalid Echo Canceller %s API state (%s)\n", @@ -1027,7 +1105,6 @@ static int wanec_api_modify_bypass(wan_ec_dev_t *ec_dev, wan_ec_api_t *ec_api) WAN_EC_STATE_DECODE(ec->state)); return WAN_EC_API_RC_INVALID_STATE; } -#endif if (ec_api->fe_chan_map == 0xFFFFFFFF){ /* All channels selected */ @@ -1595,6 +1672,8 @@ static int wan_ec_devnum(char *ptr) return num; } + + static void* wanec_register(void *pcard, u_int32_t fe_port_mask, int max_fe_chans, int max_ec_chans, void *pconf) { @@ -1608,7 +1687,7 @@ wanec_register(void *pcard, u_int32_t fe_port_mask, int max_fe_chans, int max_ec WAN_LIST_FOREACH(ec, &wan_ec_head, next){ WAN_LIST_FOREACH(ec_dev, &ec->ec_dev_head, next){ if (ec_dev->card == NULL || ec_dev->card == card){ - DEBUG_EVENT("%s: Internal Error (%s:%d)\n", + DEBUG_ERROR("%s: Internal Error (%s:%d)\n", card->devname, __FUNCTION__,__LINE__); return NULL; } @@ -1624,7 +1703,7 @@ wanec_register(void *pcard, u_int32_t fe_port_mask, int max_fe_chans, int max_ec if (ec && ec_dev){ if(wan_test_bit(WAN_EC_BIT_CRIT_ERROR, &ec_dev->ec->critical)){ - DEBUG_EVENT("%s: Echo Canceller chip has Critical Error flag set!\n", + DEBUG_ERROR("%s: Echo Canceller chip has Critical Error flag set!\n", card->devname); return NULL; } @@ -1705,7 +1784,13 @@ wanec_register(void *pcard, u_int32_t fe_port_mask, int max_fe_chans, int max_ec #if 1 if (IS_A600_CARD(card)) { - ec_dev_new->fe_max_chans = 5; //max_line_no; // + ec_dev_new->fe_max_chans = 5; + } else if (IS_B601_CARD(card)) { + if (IS_TE1_CARD(card)) { + ec_dev_new->fe_max_chans = WAN_FE_MAX_CHANNELS(&card->fe); + } else { + ec_dev_new->fe_max_chans = 5; + } } else if (IS_A700_CARD(card)) { ec_dev_new->fe_max_chans = 2; //max_line_no; // } else { @@ -1746,6 +1831,7 @@ wanec_register(void *pcard, u_int32_t fe_port_mask, int max_fe_chans, int max_ec /* Initialize hwec_bypass pointer */ card->wandev.ec_enable = wanec_enable; + card->wandev.ec_state = wanec_state; card->wandev.fe_ec_map = 0; memcpy(ec_dev_new->devname, card->devname, sizeof(card->devname)); @@ -1953,6 +2039,7 @@ wanec_poll_done: return err; } + static int wanec_event_ctrl(void *arg, void *pcard, wan_event_ctrl_t *event_ctrl) { wan_ec_t *ec = NULL; @@ -1982,6 +2069,17 @@ static int wanec_event_ctrl(void *arg, void *pcard, wan_event_ctrl_t *event_ctrl err=wanec_channel_tone(ec_dev, event_ctrl->channel, enable, &tone, wanec_verbose); } break; + + case WAN_EVENT_EC_FAX_DETECT: + { + wanec_tone_config_t tone; + tone.id = WP_API_EVENT_TONE_FAXCALLING; + tone.port_map = WAN_EC_CHANNEL_PORT_SOUT; + tone.type = WAN_EC_TONE_PRESENT; + enable = (event_ctrl->mode == WAN_EVENT_ENABLE) ? WAN_TRUE : WAN_FALSE; + err=wanec_channel_tone(ec_dev, event_ctrl->channel, enable, &tone, wanec_verbose); + } + case WAN_EVENT_EC_H100_REPORT: if (event_ctrl->mode == WAN_EVENT_DISABLE){ ec->ignore_H100 = 1; @@ -2005,7 +2103,7 @@ int wanec_init(void *arg) { #if defined(__WINDOWS__) if(wanec_iface.reg != NULL){ - DEBUG_EVENT("%s(): Warning: Initialization already done!\n", __FUNCTION__); + DEBUG_WARNING("%s(): Warning: Initialization already done!\n", __FUNCTION__); return 0; } #endif diff --git a/patches/kdrivers/wanec/wanec_iface.h b/patches/kdrivers/wanec/wanec_iface.h index 7427e2d..55811a3 100644 --- a/patches/kdrivers/wanec/wanec_iface.h +++ b/patches/kdrivers/wanec/wanec_iface.h @@ -211,15 +211,18 @@ typedef struct _OCTPCIDRV_USER_PROCESS_CONTEXT_ #if defined(WAN_KERNEL) #if defined(__WINDOWS__) -# define PRINT1 if(0)Debug -# define PRINT2 if(0)Debug +# define PRINT1(v,...) \ + if (v & WAN_EC_VERBOSE_EXTRA1) DEBUG_EVENT(## __VA_ARGS__) +# define PRINT2(v,...) \ + if (v & WAN_EC_VERBOSE_EXTRA2) DEBUG_EVENT(## __VA_ARGS__) #else -# define PRINT1(v,format,msg...) \ +# define PRINT1(v,format,msg...) \ if (v & WAN_EC_VERBOSE_EXTRA1) DEBUG_EVENT(format,##msg) -# define PRINT2(v,format,msg...) \ +# define PRINT2(v,format,msg...) \ if (v & WAN_EC_VERBOSE_EXTRA2) DEBUG_EVENT(format,##msg) #endif + #define WANEC_IGNORE (TRUE+1) #define WANEC_BYDEFAULT_NORMAL @@ -276,10 +279,8 @@ typedef struct wan_ec_dev_ int poll_channel; u_int32_t events; /* enable events map */ -#if 0 - /* NC: Moved to wan_ec device */ - wan_ticks_t lastint_ticks; -#endif + + wan_hwec_dev_state_t ecdev_state; struct wan_ec_ *ec; WAN_LIST_ENTRY(wan_ec_dev_) next; diff --git a/patches/kdrivers/wanec/wanec_tones.h b/patches/kdrivers/wanec/wanec_tones.h index 766275c..45b58ba 100644 --- a/patches/kdrivers/wanec/wanec_tones.h +++ b/patches/kdrivers/wanec/wanec_tones.h @@ -1,53 +1,53 @@ -#define OCT6100_IMAGE_NAME "SANGOMA_6_CAPACITY:16" -#define OCT6100_IMAGE_VERSION "01.06.01" -#define OCT6100_IMAGE_DATE "Fri May 23 11:46:50 2008" -#define OCT6100_PROJECT_ID 0x00280019 -#define OCT6100_NUM_TONE 44 - -#define WANEC_TONE_SOUT_MASK 0x40000000 -#define WANEC_TONE_ROUT_MASK 0x10000000 - -#define SOUT_G168_2100GB_ON 0x40000000 -#define SOUT_G168_2100GB_WSPR 0x40000002 -#define ROUT_G168_2100GB_ON 0x10000000 -#define ROUT_G168_2100GB_WSPR 0x10000002 -#define ROUT_SOUT_G168_2100HB_END 0x50000003 -#define SOUT_G168_1100GB_ON 0x40000004 -#define ROUT_G168_1100GB_ON 0x10000004 -#define SIN_SYSTEM5_2400 0x20000020 -#define SIN_SYSTEM5_2600 0x20000021 -#define SIN_SYSTEM5_2400_2600 0x20000022 -#define SIN_SYSTEM7_2000 0x20000023 -#define SIN_SYSTEM7_1780 0x20000024 -#define SOUT_DTMF_1 0x40000011 -#define SOUT_DTMF_2 0x40000012 -#define SOUT_DTMF_3 0x40000013 -#define SOUT_DTMF_A 0x4000001A -#define SOUT_DTMF_4 0x40000014 -#define SOUT_DTMF_5 0x40000015 -#define SOUT_DTMF_6 0x40000016 -#define SOUT_DTMF_B 0x4000001B -#define SOUT_DTMF_7 0x40000017 -#define SOUT_DTMF_8 0x40000018 -#define SOUT_DTMF_9 0x40000019 -#define SOUT_DTMF_C 0x4000001C -#define SOUT_DTMF_STAR 0x4000001E -#define SOUT_DTMF_0 0x40000010 -#define SOUT_DTMF_POUND 0x4000001F -#define SOUT_DTMF_D 0x4000001D -#define ROUT_DTMF_1 0x10000011 -#define ROUT_DTMF_2 0x10000012 -#define ROUT_DTMF_3 0x10000013 -#define ROUT_DTMF_A 0x1000001A -#define ROUT_DTMF_4 0x10000014 -#define ROUT_DTMF_5 0x10000015 -#define ROUT_DTMF_6 0x10000016 -#define ROUT_DTMF_B 0x1000001B -#define ROUT_DTMF_7 0x10000017 -#define ROUT_DTMF_8 0x10000018 -#define ROUT_DTMF_9 0x10000019 -#define ROUT_DTMF_C 0x1000001C -#define ROUT_DTMF_STAR 0x1000001E -#define ROUT_DTMF_0 0x10000010 -#define ROUT_DTMF_POUND 0x1000001F -#define ROUT_DTMF_D 0x1000001D +#define OCT6100_IMAGE_NAME "SANGOMA_6_CAPACITY:16" +#define OCT6100_IMAGE_VERSION "01.06.01" +#define OCT6100_IMAGE_DATE "Fri May 23 11:46:50 2008" +#define OCT6100_PROJECT_ID 0x00280019 +#define OCT6100_NUM_TONE 44 + +#define WANEC_TONE_SOUT_MASK 0x40000000 +#define WANEC_TONE_ROUT_MASK 0x10000000 + +#define SOUT_G168_2100GB_ON 0x40000000 +#define SOUT_G168_2100GB_WSPR 0x40000002 +#define ROUT_G168_2100GB_ON 0x10000000 +#define ROUT_G168_2100GB_WSPR 0x10000002 +#define ROUT_SOUT_G168_2100HB_END 0x50000003 +#define SOUT_G168_1100GB_ON 0x40000004 +#define ROUT_G168_1100GB_ON 0x10000004 +#define SIN_SYSTEM5_2400 0x20000020 +#define SIN_SYSTEM5_2600 0x20000021 +#define SIN_SYSTEM5_2400_2600 0x20000022 +#define SIN_SYSTEM7_2000 0x20000023 +#define SIN_SYSTEM7_1780 0x20000024 +#define SOUT_DTMF_1 0x40000011 +#define SOUT_DTMF_2 0x40000012 +#define SOUT_DTMF_3 0x40000013 +#define SOUT_DTMF_A 0x4000001A +#define SOUT_DTMF_4 0x40000014 +#define SOUT_DTMF_5 0x40000015 +#define SOUT_DTMF_6 0x40000016 +#define SOUT_DTMF_B 0x4000001B +#define SOUT_DTMF_7 0x40000017 +#define SOUT_DTMF_8 0x40000018 +#define SOUT_DTMF_9 0x40000019 +#define SOUT_DTMF_C 0x4000001C +#define SOUT_DTMF_STAR 0x4000001E +#define SOUT_DTMF_0 0x40000010 +#define SOUT_DTMF_POUND 0x4000001F +#define SOUT_DTMF_D 0x4000001D +#define ROUT_DTMF_1 0x10000011 +#define ROUT_DTMF_2 0x10000012 +#define ROUT_DTMF_3 0x10000013 +#define ROUT_DTMF_A 0x1000001A +#define ROUT_DTMF_4 0x10000014 +#define ROUT_DTMF_5 0x10000015 +#define ROUT_DTMF_6 0x10000016 +#define ROUT_DTMF_B 0x1000001B +#define ROUT_DTMF_7 0x10000017 +#define ROUT_DTMF_8 0x10000018 +#define ROUT_DTMF_9 0x10000019 +#define ROUT_DTMF_C 0x1000001C +#define ROUT_DTMF_STAR 0x1000001E +#define ROUT_DTMF_0 0x10000010 +#define ROUT_DTMF_POUND 0x1000001F +#define ROUT_DTMF_D 0x1000001D diff --git a/patches/kdrivers/wanec/wanec_utils.c b/patches/kdrivers/wanec/wanec_utils.c index d935a0c..d8a6ccf 100644 --- a/patches/kdrivers/wanec/wanec_utils.c +++ b/patches/kdrivers/wanec/wanec_utils.c @@ -209,7 +209,7 @@ set_conf_param ( wan_ec_t *ec, int verbose) { /* Search a keyword in configuration definition table */ - for (; dtab->key && strncasecmp(dtab->key, param->name, strlen(param->name)); ++dtab); + for (; dtab->key && wp_strncasecmp(dtab->key, param->name, strlen(param->name)); ++dtab); if (dtab->key == NULL){ DEBUG_EVENT("%s: Failed to find EC parameter (name=%s)\n", ec->name, param->name); @@ -219,7 +219,7 @@ set_conf_param ( wan_ec_t *ec, /* Search a keyword in configuration definition table */ if (dtab->dtype != DTYPE_BOOL && strlen(param->sValue)){ value_word_t *ValueParam = ValueParamList; - for (; ValueParam->sValue && strncasecmp(ValueParam->sValue, param->sValue, strlen(param->sValue)); ++ValueParam); + for (; ValueParam->sValue && wp_strncasecmp(ValueParam->sValue, param->sValue, strlen(param->sValue)); ++ValueParam); if (ValueParam->sValue == NULL){ DEBUG_EVENT("%s: Failed to find EC parameter value (sValue=%s)\n", ec->name, param->sValue); diff --git a/patches/makefile.af_wanpipe.patch b/patches/makefile.af_wanpipe.patch deleted file mode 100644 index f48faac..0000000 --- a/patches/makefile.af_wanpipe.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- kernel-source-2.4.5.orig/net/wanrouter/Makefile Sat May 19 20:47:55 2001 -+++ kernel-source-2.4.5/net/wanrouter/Makefile Thu Aug 16 13:41:40 2001 -@@ -12,7 +12,7 @@ - export-objs := wanmain.o - - obj-y := wanproc.o wanmain.o --obj-m := $(O_TARGET) -+obj-m := $(O_TARGET) af_wanpipe.o - ifneq ($(CONFIG_VENDOR_SANGOMA),n) - obj-m += $(O_TARGET) - endif diff --git a/patches/makefile.patch.2.2.X b/patches/makefile.patch.2.2.X deleted file mode 100644 index 7d74768..0000000 --- a/patches/makefile.patch.2.2.X +++ /dev/null @@ -1,125 +0,0 @@ ---- kernel-source.orig/drivers/net/Makefile Sun Mar 25 11:37:34 2001 -+++ kernel-source/drivers/net/Makefile Thu Aug 30 11:32:57 2001 -@@ -1059,6 +1059,78 @@ - endif - endif - -+ -+ifeq ($(CONFIG_VENDOR_SANGOMA),y) -+ LX_OBJS += sdladrv.o wanpipe_syncppp.o -+ L_OBJS += sdlamain.o -+ L_OBJS += sdla_ft1.o -+ L_OBJS += sdla_te1.o -+ L_OBJS += sdla_56k.o wanpipe_utils.o -+ ifeq ($(CONFIG_WANPIPE_X25),y) -+ L_OBJS += sdla_x25.o -+ endif -+ ifeq ($(CONFIG_WANPIPE_FR),y) -+ L_OBJS += sdla_fr.o -+ endif -+ ifeq ($(CONFIG_WANPIPE_PPP),y) -+ L_OBJS += sdla_ppp.o -+ endif -+ ifeq ($(CONFIG_WANPIPE_CHDLC),y) -+ L_OBJS += sdla_chdlc.o -+ endif -+ ifeq ($(CONFIG_WANPIPE_MULTPPP),y) -+ L_OBJS += wanpipe_multppp.o -+ CONFIG_SYNCPPP_BUILTIN=y -+ endif -+ ifeq ($(CONFIG_WANPIPE_BITSTRM),y) -+ L_OBJS += sdla_bitstrm.o -+ endif -+ ifeq ($(CONFIG_WANPIPE_EDU), y) -+ L_OBJS += sdla_edu.o -+ endif -+ ifeq ($(CONFIG_WANPIPE_BSC), y) -+ L_OBJS += sdla_bsc.o -+ endif -+endif -+ -+ifeq ($(CONFIG_VENDOR_SANGOMA),m) -+ MX_OBJS += sdladrv.o wanpipe_syncppp.o -+ M_OBJS += wanpipe.o -+ WANPIPE_OBJS = sdlamain.o -+ WANPIPE_OBJS += sdla_ft1.o -+ WANPIPE_OBJS += sdla_te1.o -+ WANPIPE_OBJS += sdla_56k.o wanpipe_utils.o -+ ifeq ($(CONFIG_WANPIPE_X25),y) -+ WANPIPE_OBJS += sdla_x25.o -+ endif -+ ifeq ($(CONFIG_WANPIPE_FR),y) -+ WANPIPE_OBJS += sdla_fr.o -+ endif -+ ifeq ($(CONFIG_WANPIPE_PPP),y) -+ WANPIPE_OBJS += sdla_ppp.o -+ endif -+ ifeq ($(CONFIG_WANPIPE_CHDLC),y) -+ WANPIPE_OBJS += sdla_chdlc.o -+ endif -+ ifeq ($(CONFIG_WANPIPE_MULTPPP),y) -+ WANPIPE_OBJS += wanpipe_multppp.o -+ CONFIG_SYNCPPP_MODULE=y -+ endif -+ ifeq ($(CONFIG_WANPIPE_BITSTRM),y) -+ WANPIPE_OBJS += sdla_bitstrm.o -+ endif -+ ifeq ($(CONFIG_WANPIPE_EDU), y) -+ WANPIPE_OBJS += sdla_edu.o -+ endif -+ ifeq ($(CONFIG_WANPIPE_BSC), y) -+ WANPIPE_OBJS += sdla_bsc.o -+ endif -+endif -+ -+ifeq ($(CONFIG_ETHER_FR),m) -+ M_OBJS += ether_fr.o -+endif -+ - # If anything built-in uses syncppp, then build it into the kernel also. - # If not, but a module uses it, build as a module. - -@@ -1354,43 +1414,6 @@ - ifeq ($(CONFIG_NCR885E),m) - M_OBJS += ncr885e.o - endif --endif -- --ifeq ($(CONFIG_VENDOR_SANGOMA),y) -- LX_OBJS += sdladrv.o -- L_OBJS += sdlamain.o -- ifeq ($(CONFIG_WANPIPE_X25),y) -- L_OBJS += sdla_x25.o -- L_OBJS += sdla_x25api.o -- endif -- ifeq ($(CONFIG_WANPIPE_FR),y) -- L_OBJS += sdla_fr.o -- endif -- ifeq ($(CONFIG_WANPIPE_PPP),y) -- L_OBJS += sdla_ppp.o -- endif -- ifeq ($(CONFIG_WANPIPE_CHDLC),y) -- L_OBJS += sdla_chdlc.o -- endif --endif -- --ifeq ($(CONFIG_VENDOR_SANGOMA),m) -- MX_OBJS += sdladrv.o -- M_OBJS += wanpipe.o -- WANPIPE_OBJS = sdlamain.o -- ifeq ($(CONFIG_WANPIPE_X25),y) -- WANPIPE_OBJS += sdla_x25.o -- WANPIPE_OBJS += sdla_x25api.o -- endif -- ifeq ($(CONFIG_WANPIPE_FR),y) -- WANPIPE_OBJS += sdla_fr.o -- endif -- ifeq ($(CONFIG_WANPIPE_PPP),y) -- WANPIPE_OBJS += sdla_ppp.o -- endif -- ifeq ($(CONFIG_WANPIPE_CHDLC),y) -- WANPIPE_OBJS += sdla_chdlc.o -- endif - endif - - ifeq ($(CONFIG_X25_ASY),y) diff --git a/patches/makefile.patch.2.4.0 b/patches/makefile.patch.2.4.0 deleted file mode 100644 index c24463c..0000000 --- a/patches/makefile.patch.2.4.0 +++ /dev/null @@ -1,37 +0,0 @@ ---- kernel-source-2.4.0.orig/drivers/net/wan/Makefile Wed Oct 10 17:43:38 2001 -+++ kernel-source-2.4.0/drivers/net/wan/Makefile Thu Dec 5 10:55:02 2002 -@@ -9,14 +9,19 @@ - - O_TARGET := wan.o - --export-objs = z85230.o syncppp.o comx.o sdladrv.o cycx_drv.o -+export-objs = z85230.o syncppp.o comx.o sdladrv.o cycx_drv.o wanpipe_syncppp.o - list-multi = wanpipe.o cyclomx.o - --wanpipe-objs = sdlamain.o $(wanpipe-y) -+wanpipe-objs = sdlamain.o sdla_ft1.o sdla_te1.o sdla_56k.o wanpipe_utils.o $(wanpipe-y) - wanpipe-$(CONFIG_WANPIPE_X25) += sdla_x25.o - wanpipe-$(CONFIG_WANPIPE_FR) += sdla_fr.o - wanpipe-$(CONFIG_WANPIPE_CHDLC) += sdla_chdlc.o - wanpipe-$(CONFIG_WANPIPE_PPP) += sdla_ppp.o -+wanpipe-$(CONFIG_WANPIPE_MULTPPP) += wanpipe_multppp.o -+wanpipe-$(CONFIG_WANPIPE_BITSTRM) += sdla_bitstrm.o -+wanpipe-$(CONFIG_WANPIPE_EDU) += sdla_edu.o -+wanpipe-$(CONFIG_WANPIPE_BSC) += sdla_bsc.o -+ - - cyclomx-objs = cycx_main.o $(cyclomx-y) - cyclomx-$(CONFIG_CYCLOMX_X25) += cycx_x25.o -@@ -44,7 +49,11 @@ - - obj-$(CONFIG_DLCI) += dlci.o - obj-$(CONFIG_SDLA) += sdla.o --obj-$(CONFIG_VENDOR_SANGOMA) += sdladrv.o wanpipe.o -+ifeq ($(CONFIG_WANPIPE_MULTPPP),y) -+ obj-$(CONFIG_VENDOR_SANGOMA) += sdladrv.o wanpipe.o wanpipe_syncppp.o -+else -+ obj-$(CONFIG_VENDOR_SANGOMA) += sdladrv.o wanpipe.o wanpipe_syncppp.o -+endif - obj-$(CONFIG_CYCLADES_SYNC) += cycx_drv.o cyclomx.o - obj-$(CONFIG_LAPBETHER) += lapbether.o - obj-$(CONFIG_SBNI) += sbni.o diff --git a/patches/makefile.patch.2.4.4 b/patches/makefile.patch.2.4.4 deleted file mode 100644 index 02692b6..0000000 --- a/patches/makefile.patch.2.4.4 +++ /dev/null @@ -1,35 +0,0 @@ ---- kernel-source-2.4.4.orig/drivers/net/wan/Makefile Thu Apr 12 15:11:39 2001 -+++ kernel-source-2.4.4/drivers/net/wan/Makefile Thu Dec 5 10:51:58 2002 -@@ -9,15 +9,18 @@ - - O_TARGET := wan.o - --export-objs = z85230.o syncppp.o comx.o sdladrv.o cycx_drv.o hdlc.o -+export-objs = z85230.o syncppp.o comx.o sdladrv.o cycx_drv.o hdlc.o wanpipe_syncppp.o - list-multi = wanpipe.o cyclomx.o - --wanpipe-objs = sdlamain.o sdla_ft1.o $(wanpipe-y) -+wanpipe-objs = sdlamain.o sdla_ft1.o sdla_te1.o sdla_56k.o wanpipe_utils.o $(wanpipe-y) - wanpipe-$(CONFIG_WANPIPE_X25) += sdla_x25.o - wanpipe-$(CONFIG_WANPIPE_FR) += sdla_fr.o - wanpipe-$(CONFIG_WANPIPE_CHDLC) += sdla_chdlc.o - wanpipe-$(CONFIG_WANPIPE_PPP) += sdla_ppp.o - wanpipe-$(CONFIG_WANPIPE_MULTPPP) += wanpipe_multppp.o -+wanpipe-$(CONFIG_WANPIPE_BITSTRM) += sdla_bitstrm.o -+wanpipe-$(CONFIG_WANPIPE_EDU) += sdla_edu.o -+wanpipe-$(CONFIG_WANPIPE_BSC) += sdla_bsc.o - - cyclomx-objs = cycx_main.o $(cyclomx-y) - cyclomx-$(CONFIG_CYCLOMX_X25) += cycx_x25.o -@@ -47,9 +50,9 @@ - obj-$(CONFIG_DLCI) += dlci.o - obj-$(CONFIG_SDLA) += sdla.o - ifeq ($(CONFIG_WANPIPE_MULTPPP),y) -- obj-$(CONFIG_VENDOR_SANGOMA) += sdladrv.o wanpipe.o syncppp.o -+ obj-$(CONFIG_VENDOR_SANGOMA) += sdladrv.o wanpipe.o wanpipe_syncppp.o - else -- obj-$(CONFIG_VENDOR_SANGOMA) += sdladrv.o wanpipe.o -+ obj-$(CONFIG_VENDOR_SANGOMA) += sdladrv.o wanpipe.o wanpipe_syncppp.o - endif - obj-$(CONFIG_CYCLADES_SYNC) += cycx_drv.o cyclomx.o - obj-$(CONFIG_LAPBETHER) += lapbether.o diff --git a/patches/makefile.patch.utils.2.2.X b/patches/makefile.patch.utils.2.2.X deleted file mode 100644 index c347268..0000000 --- a/patches/makefile.patch.utils.2.2.X +++ /dev/null @@ -1,31 +0,0 @@ ---- linux/drivers/net/Makefile.orig Thu Dec 5 11:53:14 2002 -+++ linux/drivers/net/Makefile Thu Dec 5 11:50:22 2002 -@@ -1061,11 +1061,11 @@ - - - ifeq ($(CONFIG_VENDOR_SANGOMA),y) -- LX_OBJS += sdladrv.o -+ LX_OBJS += sdladrv.o wanpipe_syncppp.o - L_OBJS += sdlamain.o - L_OBJS += sdla_ft1.o - L_OBJS += sdla_te1.o -- L_OBJS += sdla_56k.o -+ L_OBJS += sdla_56k.o wanpipe_utils.o - ifeq ($(CONFIG_WANPIPE_X25),y) - L_OBJS += sdla_x25.o - endif -@@ -1094,12 +1094,12 @@ - endif - - ifeq ($(CONFIG_VENDOR_SANGOMA),m) -- MX_OBJS += sdladrv.o -+ MX_OBJS += sdladrv.o wanpipe_syncppp.o - M_OBJS += wanpipe.o - WANPIPE_OBJS = sdlamain.o - WANPIPE_OBJS += sdla_ft1.o - WANPIPE_OBJS += sdla_te1.o -- WANPIPE_OBJS += sdla_56k.o -+ WANPIPE_OBJS += sdla_56k.o wanpipe_utils.o - ifeq ($(CONFIG_WANPIPE_X25),y) - WANPIPE_OBJS += sdla_x25.o - endif diff --git a/patches/makefile.patch.utils.2.4.X b/patches/makefile.patch.utils.2.4.X deleted file mode 100644 index d553eeb..0000000 --- a/patches/makefile.patch.utils.2.4.X +++ /dev/null @@ -1,27 +0,0 @@ ---- linux/drivers/net/wan/Makefile.orig Thu Dec 5 10:48:52 2002 -+++ linux/drivers/net/wan/Makefile Thu Dec 5 10:59:07 2002 -@@ -9,10 +9,10 @@ - - O_TARGET := wan.o - --export-objs = z85230.o syncppp.o comx.o sdladrv.o cycx_drv.o hdlc.o -+export-objs = z85230.o syncppp.o comx.o sdladrv.o cycx_drv.o hdlc.o wanpipe_syncppp.o - list-multi = wanpipe.o cyclomx.o - --wanpipe-objs = sdlamain.o sdla_ft1.o sdla_te1.o sdla_56k.o $(wanpipe-y) -+wanpipe-objs = sdlamain.o sdla_ft1.o sdla_te1.o sdla_te3.o sdla_56k.o wanpipe_linux_iface.o wanpipe_utils.o wanpipe_abstr.o $(wanpipe-y) - wanpipe-$(CONFIG_WANPIPE_X25) += sdla_x25.o - wanpipe-$(CONFIG_WANPIPE_FR) += sdla_fr.o - wanpipe-$(CONFIG_WANPIPE_CHDLC) += sdla_chdlc.o -@@ -59,9 +59,9 @@ - obj-$(CONFIG_DLCI) += dlci.o - obj-$(CONFIG_SDLA) += sdla.o - ifeq ($(CONFIG_WANPIPE_MULTPPP),y) -- obj-$(CONFIG_VENDOR_SANGOMA) += sdladrv.o wanpipe.o syncppp.o -+ obj-$(CONFIG_VENDOR_SANGOMA) += sdladrv.o wanpipe.o wanpipe_syncppp.o - else -- obj-$(CONFIG_VENDOR_SANGOMA) += sdladrv.o wanpipe.o -+ obj-$(CONFIG_VENDOR_SANGOMA) += sdladrv.o wanpipe.o wanpipe_syncppp.o - endif - obj-$(CONFIG_CYCLADES_SYNC) += cycx_drv.o cyclomx.o - obj-$(CONFIG_LAPBETHER) += lapbether.o diff --git a/patches/wanrouter-v2213.gz b/patches/wanrouter-v2213.gz deleted file mode 100644 index 2ee7339f47d88664d970063956981133f6bd18b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16237 zcmaKSQ;aSEmt@=KZQr(S+qP|Ye{I{gZQHhO+jigP{k={g#B*!g>%=AtkB$& zds@gPmxn@7n$`W`_3_KbgtUvdGt2qcbJ8P3CAR|q_wqyRr}#H!&aLCG=@DibzLuWf z$Aed+s;4He+~)=Jggij8O7xumUZL9Tn3G)vPmcl*l8Sli7})+yvr%PaFkVV+l+mqi zvfmSR$d9tTw)}}_P3+OtUL5?>aa7+S&?7k%?Sm zu8>v%eGLr(%Zbbe#+3RqWQ8>X^q9f z;0KNY^vg~KqV;E9n4(vyGNQx^DZ0acB+Zo}SbXu$(LRr@&@o`q&xNXpmK^!Qpv^k_ zIj54ZWg)Hn=ELG8Ppwi<>!ms7wG=52?edOH(3gnFuhzp-4~wT)z@ha*AIw>+**5cZ zbPy0FLdmexdAj3Rb4&o9&ODn0_|NZ`;Bg^-`crHj4KP(c$smb9@?3X+5rf82E?nfp ziDs)VlRa+uiSC0zTnWJ+DJ8O4g1cK@Vmuxj3_<5j42T^qvG-t# zfRyz+)IlJ?ILN2*n$~Hih^mfgEt;Dt);lI$l;>R0vdhL!^<^&m!`D6=x6`Ci(WNmn zz<6vq+;uqIWk0t)9qfz*@A4Z}+w$spINs=vABUJ?Rp1VeS6a}bkV2EAnf*+X)U5Ti zX1%SDV~R%Gkfr*!(;bswe~3oVuCD3{S#w(kL#E>e_Se?yp>3iu(!-iqIQqaqi!dE& zx(j^TIIk!(m!sVejYE^>K*6tP)fq;tUx{0ol{^W_C6O~&K}yzBOsrb%ZI0|?rc81s zq<-SnDNjY(>2$SiCbD!#b7fyc)vYLEDZ1+j3WGQut^;+hYU;cu+dz2?yYic#s35`vo-?rM}k~fFh8|kpM$_ z0qYNt`P^RZJu0q0pNpI9&y<<6eRXn3;id=8{W+nn=s#y0hTm{?%R}SnX5ghB=vXiU z257bE>ye9(iYryzFt&{o8|bDNGn6vX$Co;1iWSt^vc%`BDO-rLyga|}dA)Mst%s!i zTvhv@J@m?vs7R(rW}|ixDNGmOdE_;9xw-jOZxGfwEy=Kb^BtkON{#`#<9Jy3%K!WZ z#y}lILK(eP)WWEf0+7JNjGF!cj+! zLTcrH`1lGme`bFlb@8tQ6BS{J5vfs`&a$>a|E*Lf$?Yz0sd{brC%DCFL;95McmL

T}_2UjA~jHoU7LOL8}Xpui9jn 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 ZT+W*V$7Z76!S65VW6+XB1~D+u{{dtJ&M5!@ diff --git a/patches/wanrouter-v2214.gz b/patches/wanrouter-v2214.gz deleted file mode 100644 index 30d8447f4d7173c1b4b4e9ef73cd61b5c9f92831..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20149 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 Td+yiBYgYZhQpzzZ0KvZi*Z}@u diff --git a/patches/wanrouter-v2218.gz b/patches/wanrouter-v2218.gz deleted file mode 100644 index b4802d6e7cffa31f45697f7732dada927f3bf7cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20384 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 diff --git a/patches/wanrouter-v2219.gz b/patches/wanrouter-v2219.gz deleted file mode 100644 index a3364c60d587d03e581c67ec957f2c5bc9eea868..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20339 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%0D1*+eFvQ^+#{HV0K4q5U|FB>L{-F8Vw!KZ^Rh)1 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 S>vO`ze7enT)h1dZz`p?1O*T9L diff --git a/patches/wanrouter-v240.gz b/patches/wanrouter-v240.gz deleted file mode 100644 index 5fbd91b878e2c40fa9c16f84c00ef74443c0a972..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21704 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 diff --git a/patches/wanrouter-v249.gz b/patches/wanrouter-v249.gz deleted file mode 100644 index 30f1bf24e732e85644b808eaa02c59009ac27aa4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6459 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 - 3.5.11 +=================================================================== + +- Fix for 2.6.31 and higher kernels +- TDM API Analog rx gain feature +- Disabled default NOISE REDUCTION feature in hwec + that was enabled in 3.5.9 release. +- Updates to T1/E1 Loopback and BERT Test + + +* Wed Jan 11 2010 Nenad Corbic - 3.5.10 +=================================================================== + +- Release cleanup script earsed libsangoma.c during release packaging. + I have update release procedure so this does not happen again. + This release has no functionl differences aside from the missing + file from 3.5.9 release. + +* Wed Dec 30 2009 Nenad Corbic - 3.5.9 +=================================================================== + +- New logger dev feature +- Bug fix in tx fifo handler +- Dahdi 2.2 broke wanpipe rbs support. +- Fixed free run interrupt supported on V38 (A108) +- Fixed RBS signalling for E1 channel 31 +- Added Front end Reset Detection + -> Support for new A108 Firmware V40 +- Fixed RTP TAP bug: Caused high system load on RTP TAP usage. +- Added excessive fifo error sanity check. Fixes random pci dma errors. +- HWEC: Increased EC VQE Delay: Fixes random fax failure due to hwec. +- HWEC: Check state before bypass enable. +- HWEC: Disable bypass on release +- HWEC: Enabled Noise Reduction by default +- HWEC: Enabled Auto Gain Control by default +- HWEC: To disable Noise Reduction and Gain control set + -> HWEC_NOISE_REDUCTION_DISABLE=NO in [wanpipe] section of wanpipe1.conf + To check if Noise Reduction or Gain control are set + -> wan_ec_client wanpipe1 stats 1 + + +* Thu Oct 02 2009 Nenad Corbic - 3.5.8 +=================================================================== + +- Bug fix in sangoma_prid PRI stack for FreeSwitch & Asterisk. + There was a slow memory leak. + + +* Thu Sep 04 2009 Nenad Corbic - 3.5.7 +=================================================================== + +- New Telesoft PRI Stack Support for FreeSwitch & Asterisk + For Asterisk: The new stack uses the existing Sangoma Media Gateway + architecture currently used by SS7 and BRI. + -> run: wancfg_dahdi or wancfg_zaptel to configure + for sangoma prid stack. + + For FreeSwitch: The new stack binds to openzap directly just like + current SS7 and BRI. + -> run: wancfg_fs to configure freeswitch for + sangoma prid, brid, ss7. + +- Fixed Tx Tristate + +- Updated yellow alarm handling for Dallas maxim cards + (A101/2/4/8) + +- Autodetect USB support so that driver will compile + correctly on kernel without USB support + +- Added DAHDI Red alarm for Analog + + * Thu Aug 20 2009 Nenad Corbic - 3.5.6 =================================================================== - Update to T1 Yellow Alarm handling. In some cases Yellow alarm did not turn off poperly causing line to stay down an card startup. +- Update configuration utility + wancfg_fs updated for sangoma_prid configuration. Added wancfg_openzap + for OpenZap Configuration * Mon Aug 17 2009 Nenad Corbic - 3.5.5 diff --git a/rpmspec/wanpipe-util.spec b/rpmspec/wanpipe-util.spec index 9fcc0b4..388b358 100644 --- a/rpmspec/wanpipe-util.spec +++ b/rpmspec/wanpipe-util.spec @@ -1,7 +1,7 @@ %define KERNEL_VERSION %{?kern_ver} %define WANPIPE_VER wanpipe-util %define name %{WANPIPE_VER} -%define version 3.5.6 +%define version 3.5.11 %define release 0 %define serial 1 %define ETC_DIR /etc @@ -328,12 +328,89 @@ enable_smg_log; %changelog + +* Thu Apr 08 2010 Nenad Corbic - 3.5.11 +=================================================================== + +- Fix for 2.6.31 and higher kernels +- TDM API Analog rx gain feature +- Disabled default NOISE REDUCTION feature in hwec + that was enabled in 3.5.9 release. +- Updates to T1/E1 Loopback and BERT Test + + +* Wed Jan 11 2010 Nenad Corbic - 3.5.10 +=================================================================== + +- Release cleanup script earsed libsangoma.c during release packaging. + I have update release procedure so this does not happen again. + This release has no functionl differences aside from the missing + file from 3.5.9 release. + +* Wed Dec 30 2009 Nenad Corbic - 3.5.9 +=================================================================== + +- New logger dev feature +- Bug fix in tx fifo handler +- Dahdi 2.2 broke wanpipe rbs support. +- Fixed free run interrupt supported on V38 (A108) +- Fixed RBS signalling for E1 channel 31 +- Added Front end Reset Detection + -> Support for new A108 Firmware V40 +- Fixed RTP TAP bug: Caused high system load on RTP TAP usage. +- Added excessive fifo error sanity check. Fixes random pci dma errors. +- HWEC: Increased EC VQE Delay: Fixes random fax failure due to hwec. +- HWEC: Check state before bypass enable. +- HWEC: Disable bypass on release +- HWEC: Enabled Noise Reduction by default +- HWEC: Enabled Auto Gain Control by default +- HWEC: To disable Noise Reduction and Gain control set + -> HWEC_NOISE_REDUCTION_DISABLE=NO in [wanpipe] section of wanpipe1.conf + To check if Noise Reduction or Gain control are set + -> wan_ec_client wanpipe1 stats 1 + + +* Thu Oct 02 2009 Nenad Corbic - 3.5.8 +=================================================================== + +- Bug fix in sangoma_prid PRI stack for FreeSwitch & Asterisk. + There was a slow memory leak. + + +* Thu Sep 04 2009 Nenad Corbic - 3.5.7 +=================================================================== + +- New Telesoft PRI Stack Support for FreeSwitch & Asterisk + For Asterisk: The new stack uses the existing Sangoma Media Gateway + architecture currently used by SS7 and BRI. + -> run: wancfg_dahdi or wancfg_zaptel to configure + for sangoma prid stack. + + For FreeSwitch: The new stack binds to openzap directly just like + current SS7 and BRI. + -> run: wancfg_fs to configure freeswitch for + sangoma prid, brid, ss7. + +- Fixed Tx Tristate + +- Updated yellow alarm handling for Dallas maxim cards + (A101/2/4/8) + +- Autodetect USB support so that driver will compile + correctly on kernel without USB support + +- Added DAHDI Red alarm for Analog + + * Thu Aug 20 2009 Nenad Corbic - 3.5.6 =================================================================== - Update to T1 Yellow Alarm handling. In some cases Yellow alarm did not turn off poperly causing line to stay down an card startup. +- Update configuration utility + wancfg_fs updated for sangoma_prid configuration. Added wancfg_openzap + for OpenZap Configuration * Mon Aug 17 2009 Nenad Corbic - 3.5.5 diff --git a/rpmspec/wanpipe.spec b/rpmspec/wanpipe.spec index a1366d9..b015240 100644 --- a/rpmspec/wanpipe.spec +++ b/rpmspec/wanpipe.spec @@ -1,7 +1,7 @@ %define KERNEL_VERSION %{?kern_ver} %define WANPIPE_VER wanpipe %define name %{WANPIPE_VER} -%define version 3.5.6 +%define version 3.5.11 %define release 0 %define serial 1 %define UTILS_DIR /usr/sbin @@ -255,12 +255,89 @@ install_init; %changelog + +* Thu Apr 08 2010 Nenad Corbic - 3.5.11 +=================================================================== + +- Fix for 2.6.31 and higher kernels +- TDM API Analog rx gain feature +- Disabled default NOISE REDUCTION feature in hwec + that was enabled in 3.5.9 release. +- Updates to T1/E1 Loopback and BERT Test + + +* Wed Jan 11 2010 Nenad Corbic - 3.5.10 +=================================================================== + +- Release cleanup script earsed libsangoma.c during release packaging. + I have update release procedure so this does not happen again. + This release has no functionl differences aside from the missing + file from 3.5.9 release. + +* Wed Dec 30 2009 Nenad Corbic - 3.5.9 +=================================================================== + +- New logger dev feature +- Bug fix in tx fifo handler +- Dahdi 2.2 broke wanpipe rbs support. +- Fixed free run interrupt supported on V38 (A108) +- Fixed RBS signalling for E1 channel 31 +- Added Front end Reset Detection + -> Support for new A108 Firmware V40 +- Fixed RTP TAP bug: Caused high system load on RTP TAP usage. +- Added excessive fifo error sanity check. Fixes random pci dma errors. +- HWEC: Increased EC VQE Delay: Fixes random fax failure due to hwec. +- HWEC: Check state before bypass enable. +- HWEC: Disable bypass on release +- HWEC: Enabled Noise Reduction by default +- HWEC: Enabled Auto Gain Control by default +- HWEC: To disable Noise Reduction and Gain control set + -> HWEC_NOISE_REDUCTION_DISABLE=NO in [wanpipe] section of wanpipe1.conf + To check if Noise Reduction or Gain control are set + -> wan_ec_client wanpipe1 stats 1 + + +* Thu Oct 02 2009 Nenad Corbic - 3.5.8 +=================================================================== + +- Bug fix in sangoma_prid PRI stack for FreeSwitch & Asterisk. + There was a slow memory leak. + + +* Thu Sep 04 2009 Nenad Corbic - 3.5.7 +=================================================================== + +- New Telesoft PRI Stack Support for FreeSwitch & Asterisk + For Asterisk: The new stack uses the existing Sangoma Media Gateway + architecture currently used by SS7 and BRI. + -> run: wancfg_dahdi or wancfg_zaptel to configure + for sangoma prid stack. + + For FreeSwitch: The new stack binds to openzap directly just like + current SS7 and BRI. + -> run: wancfg_fs to configure freeswitch for + sangoma prid, brid, ss7. + +- Fixed Tx Tristate + +- Updated yellow alarm handling for Dallas maxim cards + (A101/2/4/8) + +- Autodetect USB support so that driver will compile + correctly on kernel without USB support + +- Added DAHDI Red alarm for Analog + + * Thu Aug 20 2009 Nenad Corbic - 3.5.6 =================================================================== - Update to T1 Yellow Alarm handling. In some cases Yellow alarm did not turn off poperly causing line to stay down an card startup. +- Update configuration utility + wancfg_fs updated for sangoma_prid configuration. Added wancfg_openzap + for OpenZap Configuration * Mon Aug 17 2009 Nenad Corbic - 3.5.5 diff --git a/samples/wanrouter b/samples/wanrouter index 75d23e4..d15ccf5 100644 --- a/samples/wanrouter +++ b/samples/wanrouter @@ -2208,7 +2208,7 @@ init_global_params() { if [ $OSYSTEM = "Linux" ]; then - ROUTER_VERSION=3.5.6 + ROUTER_VERSION=3.5.11 IFCONFIG_LIST=ifconfig MODULE_STAT=lsmod WAN_DRIVERS="wanpipe" diff --git a/scripts/Compile.sh b/scripts/Compile.sh index faeb6c5..8061590 100644 --- a/scripts/Compile.sh +++ b/scripts/Compile.sh @@ -1,5 +1,5 @@ #!/bin/sh -make CFLAGS=" -Wp,-MD,.wanpipe.o.d -nostdinc -iwithprefix include -D__LINUX__ -Dlinux -D__KERNEL__ -I/lib/modules/2.6.18-92.1.22.el5/build/include -DMODULE -DAF_WANPIPE_2612_FORCE_UPDATE -DWANPIPE_MOD_266_FORCE_UPDATE -m32 -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -Wstrict-prototypes -Wundef -Werror-implicit-function-declaration -Os -pipe -msoft-float -fno-builtin-sprintf -fno-builtin-log2 -fno-builtin-puts -mpreferred-stack-boundary=2 -march=i686 -mtune=generic -mtune=generic -mregparm=3 -ffreestanding -I/lib/modules/2.6.18-92.1.22.el5/build/include/asm-i386/mach-generic -I/lib/modules/2.6.18-92.1.22.el5/build/include/asm-i386/mach-default -fomit-frame-pointer -g -fno-stack-protector -Wdeclaration-after-statement -Wno-pointer-sign -D__KERNEL__ -Iinclude -include include/linux/autoconf.h " PROTOCOL_DEFINES="-DCONFIG_PRODUCT_WANPIPE_BASE -DCONFIG_PRODUCT_WANPIPE_FR -DCONFIG_PRODUCT_WANPIPE_CHDLC -DCONFIG_PRODUCT_WANPIPE_PPP -DCONFIG_PRODUCT_WANPIPE_X25 -DCONFIG_PRODUCT_WANPIPE_ADSL -DCONFIG_PRODUCT_WANPIPE_LIP_ATM -DCONFIG_PRODUCT_WANPIPE_ATM -DCONFIG_PRODUCT_WANPIPE_MULTPROT -DCONFIG_PRODUCT_WANPIPE_AFT -DCONFIG_PRODUCT_WANPIPE_AFT_CORE -DCONFIG_PRODUCT_WANPIPE_AFT_TE1 -DCONFIG_PRODUCT_WANPIPE_AFT_56K -DCONFIG_PRODUCT_WANPIPE_AFT_RM -DCONFIG_PRODUCT_WANPIPE_CODEC_SLINEAR_LAW -DCONFIG_PRODUCT_WANPIPE_AFT_BRI -DCONFIG_PRODUCT_WANPIPE_AFT_SERIAL -DCONFIG_PRODUCT_WANPIPE_AFT_A600 -DCONFIG_PRODUCT_WANPIPE_USB -DCONFIG_PRODUCT_WANPIPE_AFT_A700 -DCONFIG_PRODUCT_WANPIPE_AFT_TE3 -DWANPIPE_USE_I_PRIVATE -DCONFIG_WANPIPE_HWEC " +make CFLAGS=" -Wp,-MD,.wanpipe.o.d -nostdinc -iwithprefix include -D__LINUX__ -Dlinux -D__KERNEL__ -I/lib/modules/2.6.18-164.el5/build/include -DMODULE -DAF_WANPIPE_2612_FORCE_UPDATE -DWANPIPE_MOD_266_FORCE_UPDATE -m32 -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -Wstrict-prototypes -Wundef -Werror-implicit-function-declaration -fno-delete-null-pointer-checks -fwrapv -Os -pipe -msoft-float -fno-builtin-sprintf -fno-builtin-log2 -fno-builtin-puts -mpreferred-stack-boundary=2 -march=i686 -mtune=generic -mtune=generic -mregparm=3 -ffreestanding -I/lib/modules/2.6.18-164.el5/build/include/asm-i386/mach-generic -I/lib/modules/2.6.18-164.el5/build/include/asm-i386/mach-default -fomit-frame-pointer -g -fno-stack-protector -Wdeclaration-after-statement -Wno-pointer-sign -D__KERNEL__ -Iinclude -include include/linux/autoconf.h -I/usr/src/dahdi" PROTOCOL_DEFINES="-DCONFIG_PRODUCT_WANPIPE_BASE -DCONFIG_PRODUCT_WANPIPE_TDM_VOICE -DCONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN -DCONFIG_PRODUCT_WANPIPE_TDM_VOICE_DCHAN_ZAPTEL -DDAHDI_ISSUES -DCONFIG_PRODUCT_WANPIPE_FR -DCONFIG_PRODUCT_WANPIPE_CHDLC -DCONFIG_PRODUCT_WANPIPE_PPP -DCONFIG_PRODUCT_WANPIPE_X25 -DCONFIG_PRODUCT_WANPIPE_ADSL -DCONFIG_PRODUCT_WANPIPE_LIP_ATM -DCONFIG_PRODUCT_WANPIPE_ATM -DCONFIG_PRODUCT_WANPIPE_MULTPROT -DCONFIG_PRODUCT_WANPIPE_AFT -DCONFIG_PRODUCT_WANPIPE_AFT_CORE -DCONFIG_PRODUCT_WANPIPE_AFT_TE1 -DCONFIG_PRODUCT_WANPIPE_AFT_56K -DCONFIG_PRODUCT_WANPIPE_AFT_RM -DCONFIG_PRODUCT_WANPIPE_CODEC_SLINEAR_LAW -DCONFIG_PRODUCT_WANPIPE_AFT_BRI -DCONFIG_PRODUCT_WANPIPE_AFT_SERIAL -DCONFIG_PRODUCT_WANPIPE_AFT_A600 -DCONFIG_PRODUCT_WANPIPE_AFT_B601 -DCONFIG_PRODUCT_WANPIPE_AFT_A700 -DCONFIG_PRODUCT_WANPIPE_USB -DCONFIG_PRODUCT_WANPIPE_AFT_TE3 -DWANPIPE_USE_I_PRIVATE -DCONFIG_WANPIPE_HWEC " diff --git a/ssmg/sangoma_bri/ChangeLog b/ssmg/sangoma_bri/ChangeLog index dbc8dac..0f0b947 100644 --- a/ssmg/sangoma_bri/ChangeLog +++ b/ssmg/sangoma_bri/ChangeLog @@ -5,6 +5,10 @@ Author: David Yat Sin Copyright (c) 2007-2008 Sangoma Technologies Inc. ------------------------------------------------------------------------------ +* Tue Feb 23 2010 David Yat Sin - 2.21 +============================================================ +- Support for boost version 103 + * Thu May 07 2009 David Yat Sin - 2.06 ================================================================= - Fix for segfault on invalid ACU_DIGIT_IN diff --git a/ssmg/sangoma_bri/Makefile b/ssmg/sangoma_bri/Makefile index 040a917..163009d 100644 --- a/ssmg/sangoma_bri/Makefile +++ b/ssmg/sangoma_bri/Makefile @@ -13,9 +13,14 @@ all: install: install -D $(SANGOMA_BRID) $(INSTALLPREFIX)/usr/sbin/sangoma_brid + @echo "Installing smg_ctrl scripts" + @cp -f ../sangoma_mgd.trunk/smg_ctrl $(DESTDIR)/usr/sbin/smg_ctrl + @cp -f ../sangoma_mgd.trunk/safe_sangoma $(DESTDIR)/usr/sbin/safe_sangoma + @cp -f ../sangoma_mgd.trunk/rc/smg.rc.bri_only $(DESTDIR)/etc/wanpipe/smg.rc + @echo @echo "Sangoma BRI Install Done" - @echo "Run: /usr/sbin/wancfg_smg to configure wanpipe BRI" + @echo "Run: /usr/sbin/wancfg_fs to configure wanpipe BRI" @echo diff --git a/ssmg/sangoma_bri/sangoma_brid.i686 b/ssmg/sangoma_bri/sangoma_brid.i686 index 370ef244dd43ca035dd1d9ac12a5f95b2ce859bf..415bf086235073a9fef721038a9228009f5ab5b2 100755 GIT binary patch delta 71989 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 Mc`wUu7cT1m2ld2SxBvhE delta 71643 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

G*`mxrV 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 PV0u+|$J&Eyt9sxUIDbp% delta 81279 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�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 diff --git a/ssmg/sangoma_mgd.trunk/.bri b/ssmg/sangoma_mgd.trunk/.bri new file mode 100644 index 0000000..5e35d1b --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/.bri @@ -0,0 +1 @@ +NO diff --git a/ssmg/sangoma_mgd.trunk/.pri b/ssmg/sangoma_mgd.trunk/.pri new file mode 100644 index 0000000..5e35d1b --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/.pri @@ -0,0 +1 @@ +NO diff --git a/ssmg/sangoma_mgd.trunk/.svn/all-wcprops b/ssmg/sangoma_mgd.trunk/.svn/all-wcprops index c2540f1..12ac7d8 100644 --- a/ssmg/sangoma_mgd.trunk/.svn/all-wcprops +++ b/ssmg/sangoma_mgd.trunk/.svn/all-wcprops @@ -1,19 +1,13 @@ K 25 svn:wc:ra_dav:version-url V 35 -/svn/sangoma_mgd/!svn/ver/193/trunk +/svn/sangoma_mgd/!svn/ver/276/trunk END sigboost.h K 25 svn:wc:ra_dav:version-url V 46 -/svn/sangoma_mgd/!svn/ver/188/trunk/sigboost.h -END -__smg_ctrl_common -K 25 -svn:wc:ra_dav:version-url -V 53 -/svn/sangoma_mgd/!svn/ver/193/trunk/__smg_ctrl_common +/svn/sangoma_mgd/!svn/ver/265/trunk/sigboost.h END sound.raw K 25 @@ -25,7 +19,7 @@ sangoma_mgd.conf.sample K 25 svn:wc:ra_dav:version-url V 59 -/svn/sangoma_mgd/!svn/ver/157/trunk/sangoma_mgd.conf.sample +/svn/sangoma_mgd/!svn/ver/253/trunk/sangoma_mgd.conf.sample END switch_buffer.h K 25 @@ -37,7 +31,7 @@ safe_sangoma K 25 svn:wc:ra_dav:version-url V 48 -/svn/sangoma_mgd/!svn/ver/174/trunk/safe_sangoma +/svn/sangoma_mgd/!svn/ver/232/trunk/safe_sangoma END callgettest.sh K 25 @@ -45,12 +39,6 @@ svn:wc:ra_dav:version-url V 48 /svn/sangoma_mgd/!svn/ver/1/trunk/callgettest.sh END -smg_ctrl_pri -K 25 -svn:wc:ra_dav:version-url -V 48 -/svn/sangoma_mgd/!svn/ver/184/trunk/smg_ctrl_pri -END smg_capabilities.h K 25 svn:wc:ra_dav:version-url @@ -73,7 +61,7 @@ call_signal.c K 25 svn:wc:ra_dav:version-url V 49 -/svn/sangoma_mgd/!svn/ver/186/trunk/call_signal.c +/svn/sangoma_mgd/!svn/ver/275/trunk/call_signal.c END sangoma_mgd_logger.c K 25 @@ -85,7 +73,7 @@ sangoma_mgd.c K 25 svn:wc:ra_dav:version-url V 49 -/svn/sangoma_mgd/!svn/ver/193/trunk/sangoma_mgd.c +/svn/sangoma_mgd/!svn/ver/277/trunk/sangoma_mgd.c END woomera.conf K 25 @@ -105,6 +93,12 @@ svn:wc:ra_dav:version-url V 42 /svn/sangoma_mgd/!svn/ver/133/trunk/list.h END +sangoma_mgd_ip_bridge.c +K 25 +svn:wc:ra_dav:version-url +V 59 +/svn/sangoma_mgd/!svn/ver/228/trunk/sangoma_mgd_ip_bridge.c +END Changelog.sangoma_mgd K 25 svn:wc:ra_dav:version-url @@ -115,7 +109,7 @@ sangoma_mgd.h K 25 svn:wc:ra_dav:version-url V 49 -/svn/sangoma_mgd/!svn/ver/189/trunk/sangoma_mgd.h +/svn/sangoma_mgd/!svn/ver/267/trunk/sangoma_mgd.h END q931_cause.h K 25 @@ -133,19 +127,7 @@ install K 25 svn:wc:ra_dav:version-url V 43 -/svn/sangoma_mgd/!svn/ver/186/trunk/install -END -smg_ctrl_bri -K 25 -svn:wc:ra_dav:version-url -V 48 -/svn/sangoma_mgd/!svn/ver/175/trunk/smg_ctrl_bri -END -smg_ctrl_ss7 -K 25 -svn:wc:ra_dav:version-url -V 48 -/svn/sangoma_mgd/!svn/ver/181/trunk/smg_ctrl_ss7 +/svn/sangoma_mgd/!svn/ver/260/trunk/install END sangoma_mgd_memdbg.c K 25 @@ -159,6 +141,12 @@ svn:wc:ra_dav:version-url V 56 /svn/sangoma_mgd/!svn/ver/189/trunk/sangoma_mgd_common.h END +smg_ctrl +K 25 +svn:wc:ra_dav:version-url +V 44 +/svn/sangoma_mgd/!svn/ver/274/trunk/smg_ctrl +END switch_buffer.c K 25 svn:wc:ra_dav:version-url @@ -169,7 +157,7 @@ Makefile K 25 svn:wc:ra_dav:version-url V 44 -/svn/sangoma_mgd/!svn/ver/192/trunk/Makefile +/svn/sangoma_mgd/!svn/ver/261/trunk/Makefile END sangoma_mgd_memdbg.h K 25 diff --git a/ssmg/sangoma_mgd.trunk/.svn/entries b/ssmg/sangoma_mgd.trunk/.svn/entries index 9985f7c..799b022 100644 --- a/ssmg/sangoma_mgd.trunk/.svn/entries +++ b/ssmg/sangoma_mgd.trunk/.svn/entries @@ -1,14 +1,14 @@ 8 dir -193 +276 https://www.sangomapbx.com/svn/sangoma_mgd/trunk https://www.sangomapbx.com/svn/sangoma_mgd -2009-08-17T22:04:54.377308Z -193 +2010-04-05T23:09:34.203565Z +276 ncorbic @@ -35,11 +35,11 @@ file -2009-07-17T17:07:00.000000Z -d7641fb196ec8d94dd9e843e92a01ed2 -2009-07-17T17:32:27.268768Z -188 -ncorbic +2010-03-20T17:24:17.000000Z +af313c49b7aabcf6f057871a0be83de4 +2010-03-10T23:50:12.710328Z +265 +davidy app dir @@ -50,10 +50,10 @@ file -2009-03-30T18:03:13.000000Z -93628c98a30a5b4897dd901064d2e937 -2009-03-28T00:40:33.375650Z -157 +2010-02-10T23:06:44.000000Z +11262370daa48d3160b4c178af639597 +2010-01-28T09:08:02.606353Z +253 ncorbic switch_buffer.h @@ -62,25 +62,12 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:41.000000Z f4832443a621cbb88e92535898d11b83 2007-09-21T20:53:51.260136Z 1 root -__smg_ctrl_common -file - - - - -2009-08-17T21:23:46.000000Z -65a9acd49617d8c8a018c7196dcc4443 -2009-08-17T22:04:54.377308Z -193 -ncorbic -has-props - conf_bri dir @@ -90,23 +77,10 @@ file -2009-05-14T20:09:14.000000Z -1c73ae51836adae50fc242f5a117bf1c -2009-05-13T20:38:52.699491Z -174 -ncorbic -has-props - -smg_ctrl_pri -file - - - - -2009-08-07T15:35:04.000000Z -c7d20fb219b6f09c9839d1e790825e8a -2009-07-13T22:25:11.834953Z -184 +2009-12-22T16:05:53.000000Z +3d813e0a45e749dd5eaa956f1c4150ea +2009-11-21T18:46:33.894854Z +232 ncorbic has-props @@ -119,7 +93,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:41:34.000000Z f20dab977386b2508903e24a307ca2d6 2007-09-21T20:53:51.260136Z 1 @@ -138,7 +112,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:41.000000Z 1cd822495b7d97b8e089c93bac1ad71a 2007-09-25T02:04:57.936973Z 20 @@ -151,10 +125,10 @@ file -2009-07-17T17:04:47.000000Z -641a4b3ba4c49a8f39bcc4709d042f0c -2009-07-16T21:35:39.883599Z -186 +2010-04-01T16:08:54.000000Z +c91b0970d927067fbe753ea15a7124c5 +2010-04-01T18:09:22.612764Z +275 ncorbic call_signal.h @@ -163,31 +137,46 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:41.000000Z 4e6ba549d7f09356a5fc135ff0bc1dae 2008-07-25T16:52:12.448857Z 105 ncorbic +sangoma_mgd_ip_bridge.c +file + + + + +2009-12-22T16:05:53.000000Z +056608fa90a921a6753c8c49f05678f2 +2009-11-19T00:43:00.203201Z +228 +ncorbic + Changelog.sangoma_mgd file -2009-04-07T22:03:07.000000Z +2009-08-25T20:44:41.000000Z 9962aba3b7b24fcdadcaee80082fb7fd 2009-04-06T03:34:04.453946Z 161 ncorbic +rc +dir + g711.h file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:41.000000Z 0f725f95ced42af15dcaef21f3a1722b 2007-09-21T20:53:51.260136Z 1 @@ -202,7 +191,7 @@ file -2009-07-17T17:04:47.000000Z +2009-08-25T20:44:41.000000Z fe3b0fc36c134075ad84f65d308652b5 2009-07-16T21:35:39.883599Z 186 @@ -214,35 +203,35 @@ file -2009-08-17T21:22:22.000000Z +2009-08-25T20:44:42.000000Z 3d8959b1ec61651dde9581f2298c1d61 2009-07-24T16:42:00.883153Z 189 davidy -sangoma_mgd_memdbg.h -file - - - - -2009-03-30T18:03:13.000000Z -81957c6e3d9a6caa2e223e954c15c49c -2008-02-06T22:23:25.673829Z -62 -ncorbic - Makefile file -2009-08-17T21:22:22.000000Z -f65c77a3e5f570fee7081286296d14ea -2009-08-10T15:44:42.284278Z -192 -davidy +2010-02-26T00:13:02.000000Z +a4074b881c589fbfcd007ec9705ed64b +2010-02-26T02:12:30.478391Z +261 +ncorbic + +sangoma_mgd_memdbg.h +file + + + + +2009-08-25T20:44:42.000000Z +81957c6e3d9a6caa2e223e954c15c49c +2008-02-06T22:23:25.673829Z +62 +ncorbic sound.raw file @@ -250,7 +239,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:41.000000Z da36acc78b83d5047481df0cca63d969 2007-09-21T20:53:51.260136Z 1 @@ -263,7 +252,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:41.000000Z 2403f191bdc1959c4cfd186b5030b9b1 2007-09-21T20:53:51.260136Z 1 @@ -276,7 +265,7 @@ file -2009-07-17T17:04:47.000000Z +2009-08-25T20:44:42.000000Z 8810ad1f8858f4970bf765b5f2c5c642 2009-07-16T21:35:39.883599Z 186 @@ -288,43 +277,43 @@ file -2009-08-17T21:22:22.000000Z +2009-08-25T20:44:41.000000Z e37c1c63f8594ae025098e6510327ee0 2009-07-24T16:42:00.883153Z 189 davidy +sangoma_mgd.c +file +277 + + + +2010-04-14T17:12:02.000000Z +0dec4f9ac97c468656e719b01a6fae68 +2010-04-14T19:19:19.339100Z +277 +ncorbic + woomera.conf file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:41.000000Z 356d6fc18e0670efac6de6001e58648e 2007-09-21T20:53:51.260136Z 1 root -sangoma_mgd.c -file - - - - -2009-08-17T21:22:22.000000Z -96a2446359704f7ef0373f40439328be -2009-08-17T22:04:54.377308Z -193 -ncorbic - list.h file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:41.000000Z 5d8ec1b599fe45c004e87f6f1c1fa3df 2008-11-04T17:41:26.259250Z 133 @@ -336,10 +325,10 @@ file -2009-08-17T21:22:22.000000Z -adac2a08453aa9f2077dcb8abc5d7d5b -2009-07-24T16:42:00.883153Z -189 +2010-03-20T17:24:17.000000Z +75677e239c9eb1ee8aad8cd27724758c +2010-03-15T20:40:36.509244Z +267 davidy q931_cause.h @@ -348,7 +337,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:41.000000Z a31121fb876e49155ae5d77812b68cc3 2007-09-21T20:53:51.260136Z 1 @@ -360,37 +349,24 @@ file -2009-07-17T17:04:47.000000Z -3c105b0995d2039c5cc25b4fe22c2b46 -2009-07-16T21:35:39.883599Z -186 +2010-03-02T19:54:58.000000Z +509ca2377cfab7778a1fa2ddaff8dc8a +2010-02-26T01:56:12.800709Z +260 ncorbic has-props -smg_ctrl_bri +smg_ctrl file -2009-06-03T20:13:05.000000Z -2208383568f70510d310fc3be44856eb -2009-05-13T21:19:52.845605Z -175 -ncorbic -has-props - -smg_ctrl_ss7 -file - - - - -2009-07-06T15:54:34.000000Z -e8e3f3c1dac4922fb7167d7cd0221256 -2009-06-30T21:49:00.856416Z -181 -ncorbic +2010-03-31T15:59:24.000000Z +97c91d4b12eddb4678eb26e553bc32e9 +2010-03-25T16:29:21.097923Z +274 +jpatel has-props switch_buffer.c @@ -399,7 +375,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:41.000000Z c8cc3aa052980e6c7cb766ae564882d1 2008-02-06T22:22:53.766395Z 61 diff --git a/ssmg/sangoma_mgd.trunk/.svn/prop-base/__smg_ctrl_common.svn-base b/ssmg/sangoma_mgd.trunk/.svn/prop-base/smg_ctrl.svn-base similarity index 100% rename from ssmg/sangoma_mgd.trunk/.svn/prop-base/__smg_ctrl_common.svn-base rename to ssmg/sangoma_mgd.trunk/.svn/prop-base/smg_ctrl.svn-base diff --git a/ssmg/sangoma_mgd.trunk/.svn/text-base/Makefile.svn-base b/ssmg/sangoma_mgd.trunk/.svn/text-base/Makefile.svn-base index 67d8e83..b41306c 100644 --- a/ssmg/sangoma_mgd.trunk/.svn/text-base/Makefile.svn-base +++ b/ssmg/sangoma_mgd.trunk/.svn/text-base/Makefile.svn-base @@ -33,12 +33,34 @@ CC = gcc ifndef DESTDIR ifdef INSTALLPREFIX - DESTDIR=$(INSTALL_PREFIX) + DESTDIR=$(INSTALLPREFIX) else DESTDIR= endif endif +ifndef BRI +BRI:=NO +ifneq (,$(wildcard ./.bri)) + BRI=$(shell cat .bri) +endif +endif + +ifndef PRI +PRI:=NO +ifneq (,$(wildcard ./.pri)) + PRI=$(shell cat .pri) +endif +endif + + +$(shell echo $(BRI) > .bri) +$(shell echo $(PRI) > .pri) + + +SAFE_D=/etc/wanpipe/safe_sangoma.d +SMGCTRL_D=/etc/wanpipe/smg_ctrl.d + INCLUDES = -I ../../ssmg/libsangoma.trunk -I. -I ../../patches/kdrivers/include -I ../../patches/kdrivers/wanec/oct6100_api/include -I ../../patches/kdrivers/wanec -I/usr/local/include -I../../patches/kdrivers/include -I/usr/include/wanpipe -Ilib/libteletone/src CFLAGS = -D__LINUX__ -D_REENTRANT -D_GNU_SOURCE -O6 @@ -91,12 +113,15 @@ sangoma_mgd_memdbg.o: sangoma_mgd_memdbg.c sangoma_mgd_memdbg.h sangoma_mgd_logger.o: sangoma_mgd_logger.c $(CC) $(CCFLAGS) $(INCLUDES) $(CFLAGS) -c -o sangoma_mgd_logger.o sangoma_mgd_logger.c +sangoma_mgd_ip_bridge.o: sangoma_mgd_ip_bridge.c + $(CC) $(CCFLAGS) $(INCLUDES) $(CFLAGS) -c -o sangoma_mgd_ip_bridge.o sangoma_mgd_ip_bridge.c + sangoma_mgd.o: sangoma_mgd.c sangoma_mgd.h sigboost.h $(CC) $(CCFLAGS) $(INCLUDES) $(CFLAGS) -c -o sangoma_mgd.o sangoma_mgd.c -sangoma_mgd: sangoma_mgd.o sangoma_mgd_memdbg.o sangoma_mgd_logger.o call_signal.o switch_buffer.o sigboost.h sangoma_mgd_memdbg.h +sangoma_mgd: sangoma_mgd.o sangoma_mgd_memdbg.o sangoma_mgd_logger.o sangoma_mgd_ip_bridge.o call_signal.o switch_buffer.o sigboost.h sangoma_mgd_memdbg.h rm -fr core* - $(CC) $(CCFLAGS) $(INCLUDES) $(CFLAGS) -o sangoma_mgd sangoma_mgd.o sangoma_mgd_memdbg.o sangoma_mgd_logger.o switch_buffer.o call_signal.o $(LDFLAGS) + $(CC) $(CCFLAGS) $(INCLUDES) $(CFLAGS) -o sangoma_mgd sangoma_mgd.o sangoma_mgd_memdbg.o sangoma_mgd_logger.o sangoma_mgd_ip_bridge.o switch_buffer.o call_signal.o $(LDFLAGS) @@ -117,29 +142,49 @@ install_smg: old_cleanup fi install -D -m 755 ./safe_sangoma $(DESTDIR)/usr/sbin/safe_sangoma - install -D -m 755 ./__smg_ctrl_common $(DESTDIR)/usr/sbin/__smg_ctrl_common + install -D -m 755 ./smg_ctrl $(DESTDIR)/usr/sbin/smg_ctrl + + @if [ ! -f $(DESTDIR)/etc/wanpipe/safe_sangoma.rc ]; then \ + install -D -m 755 ./rc/safe_sangoma.rc $(DESTDIR)/etc/wanpipe/safe_sangoma.rc; \ + fi + + @if [ ! -d ${SAFE_D} ]; then \ + mkdir -p ${SAFE_D}; \ + fi + @if [ ! -d ${SMGCTRL_D} ]; then \ + mkdir -p ${SMGCTRL_D}; \ + fi + ifeq "${PRI}" "YES" @echo "PRI control scripts installed" - @if [ ! -e $(INSTALLPREFIX)/etc/sangoma_mgd.conf ]; then \ - install -D -m 755 sangoma_mgd.conf.sample.pri $(INSTALLPREFIX)/etc/sangoma_mgd.conf; \ + @if [ ! -e $(DESTDIR)/etc/sangoma_mgd.conf ]; then \ + install -D -m 755 sangoma_mgd.conf.sample.pri $(DESTDIR)/etc/sangoma_mgd.conf; \ fi - install -D -m 755 smg_ctrl_pri $(INSTALLPREFIX)/usr/sbin/smg_ctrl + install -D -m 755 rc/smg.rc.pri $(DESTDIR)/etc/wanpipe/smg.rc else ifeq "${BRI}" "YES" @echo "BRI control scripts installed" @if [ ! -e $(DESTDIR)/etc/sangoma_mgd.conf ]; then \ install -D -m 755 sangoma_mgd.conf.sample.bri $(DESTDIR)/etc/sangoma_mgd.conf; \ fi - install -D -m 755 smg_ctrl_bri $(DESTDIR)/usr/sbin/smg_ctrl + install -D -m 755 rc/smg.rc.bri $(DESTDIR)/etc/wanpipe/smg.rc else @echo "SS7 control scripts installed" @if [ ! -e $(DESTDIR)/etc/sangoma_mgd.conf ]; then \ install -D -m 755 sangoma_mgd.conf.sample.ss7 $(DESTDIR)/etc/sangoma_mgd.conf; \ fi - install -D -m 755 smg_ctrl_ss7 $(DESTDIR)/usr/sbin/smg_ctrl + @if [ -e $(DESTDIR)/usr/local/ss7box/ss7boost ]; then \ + echo "install -D -m 755 rc/smg.rc.ss7boost $(DESTDIR)/etc/wanpipe/smg.rc"; \ + install -D -m 755 rc/smg.rc.ss7boost $(DESTDIR)/etc/wanpipe/smg.rc; \ + else \ + echo "install -D -m 755 rc/smg.rc.isupd $(DESTDIR)/etc/wanpipe/smg.rc"; \ + install -D -m 755 rc/smg.rc.isupd $(DESTDIR)/etc/wanpipe/smg.rc; \ + fi + install -D -m 755 scripts/init.d/smgss7_init_ctrl $(DESTDIR)/etc/init.d/smgss7_init_ctrl install -D -m 755 scripts/init.d/smgss7_init_ctrl $(DESTDIR)/usr/sbin/smgss7_init_ctrl + install -D -m 755 scripts/init.d/ss7boxd_monitor.sh $(DESTDIR)/usr/sbin/ss7boxd_monitor.sh endif endif diff --git a/ssmg/sangoma_mgd.trunk/.svn/text-base/call_signal.c.svn-base b/ssmg/sangoma_mgd.trunk/.svn/text-base/call_signal.c.svn-base index 4ead23b..db0decd 100644 --- a/ssmg/sangoma_mgd.trunk/.svn/text-base/call_signal.c.svn-base +++ b/ssmg/sangoma_mgd.trunk/.svn/text-base/call_signal.c.svn-base @@ -38,7 +38,8 @@ static struct call_signal_map call_signal_table[] = { {SIGBOOST_EVENT_HEARTBEAT, "HEARTBEAT"}, {SIGBOOST_EVENT_INSERT_CHECK_LOOP, "LOOP START"}, {SIGBOOST_EVENT_REMOVE_CHECK_LOOP, "LOOP STOP"}, - {SIGBOOST_EVENT_DIGIT_IN, "DIGIT_IN"} + {SIGBOOST_EVENT_DIGIT_IN, "DIGIT_IN"}, + {SIGBOOST_EVENT_CALL_PROGRESS, "CALL_PROGRESS"} }; #define USE_SCTP 1 @@ -79,6 +80,22 @@ static int create_udp_socket(call_signal_connection_t *mcon, char *local_ip, int (char *)&flag, sizeof(int)); #endif + + rc = fcntl(mcon->socket, F_GETFL); + if ( rc < 0 ) { + close(mcon->socket); + mcon->socket = -1; + return mcon->socket; + } + + rc |= O_NONBLOCK; + rc = fcntl(mcon->socket, F_SETFL, rc); + if ( rc < 0 ) { + close(mcon->socket); + mcon->socket = -1; + return mcon->socket; + } + if ((rc = bind(mcon->socket, (struct sockaddr *) &mcon->local_addr, sizeof(mcon->local_addr))) < 0) { @@ -89,7 +106,7 @@ static int create_udp_socket(call_signal_connection_t *mcon, char *local_ip, int rc=listen(mcon->socket,100); if (rc) { close(mcon->socket); - mcon->socket = -1; + mcon->socket = -1; } #endif } @@ -119,23 +136,24 @@ static int smg_event_dbg=SMG_LOG_BOOST; static void clog_print_event_call(call_signal_connection_t *mcon,call_signal_event_t *event, int priority, int dir) { clog_printf((event->event_id==SIGBOOST_EVENT_HEARTBEAT)?SMG_LOG_DEBUG_CALL:smg_event_dbg, mcon->log, - "%s EVENT (%s): %s:(%X) [w%dg%d] CSid=%i Seq=%i Cn=[%s] Cd=[%s] Ci=[%s]\n", + "%s EVENT (%s): %s:(%X) [s%dc%d] Tg=%i CSid=%i Seq=%i Cn=[%s] Cd=[%s] Ci=[%s]\n", dir ? "TX":"RX", priority ? "P":"N", call_signal_event_id_name(event->event_id), event->event_id, event->span+1, event->chan+1, + event->trunk_group, event->call_setup_id, event->fseqno, - strlen(event->calling_name)?event->calling_name:"N/A", + 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")); } static void clog_print_event_short(call_signal_connection_t *mcon,short_signal_event_t *event, int priority, int dir) { clog_printf((event->event_id==SIGBOOST_EVENT_HEARTBEAT)?SMG_LOG_DEBUG_CALL:smg_event_dbg, mcon->log, - "%s EVENT (%s): %s:(%X) [w%dg%d] Rc=%i CSid=%i Seq=%i \n", + "%s EVENT (%s): %s:(%X) [s%dc%d] Rc=%i CSid=%i Seq=%i \n", dir ? "TX":"RX", priority ? "P":"N", call_signal_event_id_name(event->event_id), @@ -213,6 +231,9 @@ call_signal_event_t *call_signal_connection_read(call_signal_connection_t *mcon, mcon->event.event_id,mcon->rxseq,mcon->event.fseqno); clog_printf(SMG_LOG_ALL, mcon->log, "------------------------------------------\n"); + + /* NC: Recover from a sequence error */ + mcon->rxseq = mcon->event.fseqno; } #ifdef SANGOMA_UNIT_TESTER @@ -350,11 +371,10 @@ int call_signal_connection_writep(call_signal_connection_t *mcon, call_signal_ev } #endif - pthread_mutex_lock(&mcon->lock); event->version=SIGBOOST_VERSION; + /* Sendto is thread safe so no lock needed */ err=sendto(mcon->socket, event, event_size, 0, (struct sockaddr *) &mcon->remote_addr, sizeof(mcon->remote_addr)); - pthread_mutex_unlock(&mcon->lock); if (err != event_size) { err = -1; @@ -422,8 +442,13 @@ int call_signal_connection_write(call_signal_connection_t *mcon, call_signal_eve 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)); + + if (err != event_size) { + mcon->txseq--; + } pthread_mutex_unlock(&mcon->lock); if (err != event_size) { diff --git a/ssmg/sangoma_mgd.trunk/.svn/text-base/install.svn-base b/ssmg/sangoma_mgd.trunk/.svn/text-base/install.svn-base index 218e0cd..5d3c2e6 100644 --- a/ssmg/sangoma_mgd.trunk/.svn/text-base/install.svn-base +++ b/ssmg/sangoma_mgd.trunk/.svn/text-base/install.svn-base @@ -49,7 +49,7 @@ cfgdir="/etc/asterisk" mysyslog=" " smg_installed=0 chan_woomera_installed=0 - +no_woomera="false" while [ ! -z $1 ] @@ -62,6 +62,9 @@ do noss7="flase" elif [ $1 = '-bri' ]; then noss7="true" + elif [ $1 = '-no_woomera' ]; then + noss7="true" + no_woomera="true" elif [ $1 = '-pri' ]; then noss7="true" pri="true" @@ -118,12 +121,10 @@ fi if [ $noss7 != 'true' ]; then if [ ! -e /usr/local/ss7box/$ss7boost ]; then - echo "Error: ss7boost not found in /usr/local/ss7box dir"; - exit 1 + echo "Warning: $ss7boost not found in /usr/local/ss7box dir"; fi if [ ! -e /usr/local/ss7box/$ss7boxd ]; then - echo "Error: ss7boxd not found in /usr/local/ss7box dir"; - exit 1 + echo "Warning: ss7boxd not found in /usr/local/ss7box dir"; fi fi @@ -242,7 +243,7 @@ if [ ! -e /usr/include/netinet/sctp.h ] && [ ! -e /usr/include/sctp.h ]; then echo echo "Error: lksctp-tools-devel package missing" echo "Please install sctp using a package manager" - echo "eg: yum install ksctp-tools-devel" + echo "eg: yum install lksctp-tools-devel" echo exit 1 fi @@ -325,42 +326,44 @@ else smg_installed=1; fi -if [ -d chan_woomera.trunk ]; then +if [ "$no_woomera" == "false" ]; then + if [ -d chan_woomera.trunk ]; then - cd chan_woomera.trunk + cd chan_woomera.trunk - echo "Compiling Woomera Channel ..." + echo "Compiling Woomera Channel ..." - if [ ! -e $pbxdir ]; then - echo - echo "Error: $pbxdir directory does not exist!" - echo " Please provide the path to Asterik or CallWeaver source." - echo " Then re-run eg: ./install -pbxdir /usr/src/asterisk " - echo - getyn "Would you like to continue installation?" - if [ $? -ne 0 ]; then - exit 1; + if [ ! -e $pbxdir ]; then + echo + echo "Error: $pbxdir directory does not exist!" + echo " Please provide the path to Asterik or CallWeaver source." + echo " Then re-run eg: ./install -pbxdir /usr/src/asterisk " + echo + getyn "Would you like to continue installation?" + if [ $? -ne 0 ]; then + exit 1; + fi + else + eval "make clean 2> /dev/null > /dev/null" + eval "make PBXDIR=$pbxdir" + if [ $? -ne 0 ]; then + exit 1; + fi + make INSTALLPREFIX=$rootdir install + if [ $? -ne 0 ]; then + echo + echo "Error: Failed to compile chan_woomera into PBX source!" + echo + exit 1; + fi + echo "Ok." + chan_woomera_installed=1; fi + else - eval "make clean 2> /dev/null > /dev/null" - eval "make PBXDIR=$pbxdir" - if [ $? -ne 0 ]; then - exit 1; - fi - make INSTALLPREFIX=$rootdir install - if [ $? -ne 0 ]; then - echo - echo "Error: Failed to compile chan_woomera into PBX source!" - echo - exit 1; - fi - echo "Ok." - chan_woomera_installed=1; + echo "Warning: chan_woomera directory does not exist!" + exit 1 fi - -else - echo "Warning: chan_woomera directory does not exist!" - exit 1 fi if [ $smg_installed -eq 1 ]; then @@ -389,6 +392,10 @@ if [ $chan_woomera_installed -eq 1 ]; then echo "--> Start: start $pbxd (part of $pbxd)" echo echo "---------------------------------" +elif [ $no_woomera = "true" ];then + echo + echo " You chose not to install Chan Woomera" + echo else echo echo " Chan Woomera Installatin Failed" diff --git a/ssmg/sangoma_mgd.trunk/.svn/text-base/safe_sangoma.svn-base b/ssmg/sangoma_mgd.trunk/.svn/text-base/safe_sangoma.svn-base index 5c7c604..19a6bfe 100644 --- a/ssmg/sangoma_mgd.trunk/.svn/text-base/safe_sangoma.svn-base +++ b/ssmg/sangoma_mgd.trunk/.svn/text-base/safe_sangoma.svn-base @@ -7,31 +7,46 @@ shift APPARGS="$*" # Grab any args passed to safe_sangoma TTY= # TTY (if you want one) for APP to run on CONSOLE=no # Whether or not you want a console -#NOTIFY=user@email.com # Who to notify about crashes -#EXEC=/path/to/somescript # Run this command if Asterisk crashes MACHINE=`hostname` # To specify which machine has crashed when getting the mail DUMPDROP=/tmp SLEEPSECS=2 BINDIR=/usr/sbin +BINDIR_SS7="/usr/local/ss7box" PIDFILE=/var/run/$APP.pid CORENAME=$(cat /proc/sys/kernel/core_pattern) VARDIR=/var/log +PROD="safe_sangoma" + +WAN_HOME=/etc/wanpipe +META_SMG_CONF=$WAN_HOME/safe_sangoma.rc + +# Read meta-configuration file. +# metconf file should define NOTIFY or EXEC path script +# to run +if [ -f $META_SMG_CONF ] + then . $META_SMG_CONF +fi if [ -z $APP ]; then echo - echo "Error: APP argument not specified" + logit "Error: APP argument not specified" echo - echo "Usage: safe_sangoma " + logit "Usage: safe_sangoma " echo exit 1 fi +if [ -e $BINDIR_SS7/$APP ]; then + BINDIR=$BINDIR_SS7 +fi + if [ ! -e $BINDIR/$APP ]; then echo - echo "Error: APP not found: $BINDIR/$APP" + logit "Error: APP not found: $BINDIR/$APP" exit 1 fi + # run APP with this priority PRIORITY=0 @@ -46,12 +61,12 @@ MAXFILES=32768 # starting safe_sangoma when APP is running is very bad. PID=`pidof $APP` if [ ! -z $PID ]; then - echo "Error: $APP already running. $0 will exit now." + logit "Error: $APP already running. $0 will exit now." exit 1 fi if [ -f $PIDFILE ]; then - echo "Error: $APP pid file $PIDFILE exists!. $0 will exit now." + logit "Error: $APP pid file $PIDFILE exists!. $0 will exit now." exit 1 fi @@ -61,8 +76,8 @@ fi # if we're not root, fall back to standard everything. if [ `id -u` != 0 ] then - echo "Oops. I'm not root. Falling back to standard prio and file max." >&2 - echo "This is NOT suitable for large systems." >&2 + logit "Oops. I'm not root. Falling back to standard prio and file max." >&2 + logit "This is NOT suitable for large systems." >&2 PRIORITY=0 else if `echo $OSTYPE | grep linux 2>&1 > /dev/null ` @@ -124,7 +139,7 @@ ulimit -c unlimited #fi if [ ! -w ${DUMPDROP} ]; then - echo "Cannot write to ${DUMPDROP}" >&2 + logit "Cannot write to ${DUMPDROP}" >&2 exit 1 fi @@ -136,15 +151,24 @@ trap '' PIPE # # Run scripts to set any environment variables or do any other system-specific setup needed # - if [ -d /etc/wanpipe/safe_startup.d ]; then for script in /etc/wanpipe/safe_startup.d/*.sh; do - if [ -x ${script} ]; then - source ${script} + if [ -x $script ]; then + logit "Executing startup script: $script" + source $script fi done fi + +logit() +{ + local data="$1" + + echo "$PROD: $data" + logger "$PROD: $data" +} + run_sangoma() { while :; do @@ -153,26 +177,38 @@ run_sangoma() nice -n $PRIORITY ${BINDIR}/$APP ${APPARGS} EXITSTATUS=$? - echo "$APP ended with exit status $EXITSTATUS" + logit "$APP ended with exit status $EXITSTATUS" if [ "$EXITSTATUS" = "0" ]; then # Properly shutdown.... - echo "$APP shutdown normally." + logit "$APP shutdown normally." exit 0 elif [ $EXITSTATUS -gt 128 ]; then let EXITSIGNAL=EXITSTATUS-128 - echo "$APP exited on signal $EXITSIGNAL." + logit "$APP exited on signal $EXITSIGNAL." else - echo "$APP died with code $EXITSTATUS." + logit "$APP died with code $EXITSTATUS." fi if [ "$NOTIFY" != "" ]; then - echo "$APP on $MACHINE exited on signal $EXITSIGNAL. Might want to take a peek." | \ - mail -s "$APP Died" $NOTIFY + logfile="/etc/wanpipe/safe_sangoma_crash_"$APP"_"$$".log" + echo > $logfile + date >> $logfile + echo >> $logfile + echo "$APP on $MACHINE exited on signal $EXITSIGNAL. " >> $logfile + echo >> $logfile + echo "/var/log/messages" >> $logfile + echo "====================" >> $logfile + tail -n 50 /var/log/messages >> $logfile + echo "====================" >> $logfile + echo >> $logfile + cat $logfile | \ + mail -s "$APP on $MACHINE Died" $NOTIFY + ret=$? + logit "Email sent to $NOTIFY result $ret" fi if [ "$EXEC" != "" ]; then $EXEC fi - PID=`cat ${PIDFILE}` if [ -f /tmp/$CORENAME.${PID} ]; then mv /tmp/$CORENAME.${PID} ${DUMPDROP}/core.$APP.`hostname`-`date -Iseconds` & @@ -180,7 +216,7 @@ run_sangoma() mv /tmp/$CORENAME ${DUMPDROP}/core.$APP.`hostname`-`date -Iseconds` & fi - echo "Automatically restarting $APP." + logit "Automatically restarting $APP." sleep $SLEEPSECS \rm -f ${PIDFILE} done diff --git a/ssmg/sangoma_mgd.trunk/.svn/text-base/sangoma_mgd.c.svn-base b/ssmg/sangoma_mgd.trunk/.svn/text-base/sangoma_mgd.c.svn-base index d0d7366..852ec4a 100644 --- a/ssmg/sangoma_mgd.trunk/.svn/text-base/sangoma_mgd.c.svn-base +++ b/ssmg/sangoma_mgd.trunk/.svn/text-base/sangoma_mgd.c.svn-base @@ -9,7 +9,72 @@ * * ============================================= * + * v1.68 David Yat Sin + * Mar 18 2010 + * Media and RING bits implemented + * + * v1.67 David Yat Sin + * Mar 16 2010 + * Added sanity check for span chan on call stopped and call answered + + * v1.66 David Yat Sin + * Mar 15 2010 + * Fix for WFLAG_SYSTEM_RESET being cleared if when + * no boost msg was received from signalling daemon + * + * v1.65 David Yat Sin + * Mar 10 2010 + * Support for TON and NPI passthrough * + * v1.64 David Yat Sin + * Feb 22 2010 + * Updated to sigboost 103 + * Added checks for hwec present + * + * v1.63 Nenad Corbic + * Jan 27 2010 + * Enabled media pass through so that + * two woomera servers pass media directly. + * + * v1.62 Konrad Hammel + * Jan 26 2010 + * Added rbs relay code + * + * v1.61 Konrad Hammel + * Jan 19 2010 + * changed all_ckt_busy to be per trunk group. + * + * v1.60 Nenad Corbic + * Jan 14 2010 + * Added media sequencing option. + * Check if server has it enabled. + * + * v1.59 Nenad Corbic + * Changed w1g1 to s1c1 + * + * v1.58 Nenad Corbic + * Added bridge tdm to ip functionality + * + * v1.57 Nenad Corbic + * Support for woomera multiple profiles + * + * v1.56 David Yat Sin + * Changed BRI to run with HWEC in non-persist mode + * + * v1.55 Nenad Corbic + * Updated the base media port to 10000 and max media ports to 5000 + * + * v1.54 Nenad Corbic + * Bug added in 1.51 release causing call on channel 31 to fail. + * + * v1.53 Nenad Corbic + * Added progress message + * + * v1.52 David Yat Sin + * Changed sangoma_open_span_chan to __sangoma_span_chan + * to enabled shared used of file descriptors when using + * with PRI in NFAS mode + * * v1.51 David Yat Sin * MAX_SPANS increased to 32. * Fix for server.process_table declared incorrectly @@ -289,14 +354,23 @@ static char ps_progname[]="sangoma_mgd"; static struct woomera_interface woomera_dead_dev; + +#ifdef BRI_PROT +static unsigned char tdmv_hwec_persist = 0; +#else +static unsigned char tdmv_hwec_persist = 1; +#endif struct woomera_server server; +struct smg_tdm_ip_bridge g_smg_ip_bridge_idx[MAX_SMG_BRIDGE]; +pthread_mutex_t g_smg_ip_bridge_lock; + #if 0 #define DOTRACE #endif -#define SMG_VERSION "v1.51" +#define SMG_VERSION "v1.68" /* enable early media */ #if 1 @@ -309,6 +383,8 @@ struct woomera_server server; #define SMG_DTMF_RATE 8000 #define SMG_DEFAULT_CALL_TIMEOUT 300 +#define SANGOMA_USR_PERIOD 20 + #if 0 #define MEDIA_SOCK_SHUTDOWN 1 #endif @@ -336,9 +412,17 @@ static int drop_seq=0; #undef SMG_DROP_SEQ #endif + +#if 0 +#define SMG_NO_MEDIA +#warning "SMG No Media Defined" +#else +#undef SMG_NO_MEDIA +#endif + const char WELCOME_TEXT[] = "================================================================================\n" -"Sangoma Media Gateway Daemon v1.51 \n" +"Sangoma Media Gateway Daemon v1.68 \n" "\n" "TDM Signal Media Gateway for Sangoma/Wanpipe Cards\n" "Copyright 2005, 2006, 2007 \n" @@ -368,6 +452,8 @@ static int launch_woomera_thread(struct woomera_interface *woomera); static void woomera_check_digits (struct woomera_interface *woomera); static struct woomera_interface *alloc_woomera(void); static void handle_event_dtmf(struct woomera_interface *woomera, unsigned char dtmf_digit); +static int handle_event_rbs(struct woomera_interface *woomera, unsigned char rbs_digit); +static int handle_dequeue_and_woomera_tx_event_rbs(struct woomera_interface *woomera); q931_cause_to_str_array_t q931_cause_to_str_array[255]; bearer_cap_to_str_array_t bearer_cap_to_str_array[255]; @@ -378,7 +464,7 @@ static int isup_exec_command(int span, int chan, int id, int cmd, int cause) short_signal_event_t oevent; int retry=5; - call_signal_event_init(&oevent, cmd, chan, span); + call_signal_event_init((short_signal_event_t*)&oevent, cmd, chan, span); oevent.release_cause = cause; if (id >= 0) { @@ -405,6 +491,31 @@ isup_exec_cmd_retry: return 0; } +static int isup_exec_event(call_signal_event_t *event) +{ + int retry=5; + +isup_exec_cmd_retry: + if (call_signal_connection_write(&server.mcon, event) < 0){ + + --retry; + if (retry <= 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket: %s\n", + strerror(errno)); + return -1; + } else { + log_printf(SMG_LOG_ALL, server.log, + "System Warning: Failed to tx on ISUP socket: %s :retry %i\n", + strerror(errno),retry); + } + + goto isup_exec_cmd_retry; + } + + return 0; +} + static int isup_exec_commandp(int span, int chan, int id, int cmd, int cause) { @@ -442,8 +553,14 @@ isup_exec_cmd_retry: static int get_span_chan_from_interface(char* interface, int *span_ptr, int *chan_ptr) { int span, chan; + int err; - if (sscanf(interface, "w%dg%d", &span, &chan) == 2) { + err=sscanf(interface, "s%dc%d", &span, &chan); + if (err!=2) { + err=sscanf(interface, "w%dg%d", &span, &chan); + } + + if (err==2) { if (smg_validate_span_chan(span,chan) != 0) { log_printf(SMG_LOG_DEBUG_CALL, server.log, "WOOMERA Warning invalid span chan in interface %s\n", @@ -761,6 +878,41 @@ static void add_listener(struct woomera_interface *woomera) pthread_mutex_unlock(&server.listen_lock); } +static int wanpipe_send_rbs(struct woomera_interface *woomera, char *digits) +{ + struct media_session *ms = woomera_get_ms(woomera); + int err; + wanpipe_tdm_api_t tdm_api; + unsigned int digit; + + memset(&tdm_api,0,sizeof(tdm_api)); + + if (!ms) { + log_printf(SMG_LOG_PROD, server.log, "[%s]: wanpipe_send_rbs (%X) Error no ms\n", + woomera->interface,digit); + return -EINVAL; + } + + if ((woomera->span+1) <= 0 || !server.rbs_relay[woomera->span+1]) { + log_printf(SMG_LOG_PROD, server.log, "[%s]: wanpipe_send_rbs (%X) Error rbs relay not enabled on span %i\n", + woomera->interface,digit,woomera->span+1); + return -EINVAL; + } + + err=sscanf(digits,"%x",&digit); + if (err == 1) { + log_printf(SMG_LOG_PROD, server.log, "[%s]: Transmitting RBS 0x%X\n", + woomera->interface,digit); + +#ifdef LIBSANGOMA_VERSION + err=sangoma_tdm_write_rbs(ms->sangoma_sock, &tdm_api, woomera->chan+1, digit); +#else + err=sangoma_tdm_write_rbs(ms->sangoma_sock, &tdm_api, digit); +#endif + } + + return err; +} static int wanpipe_send_dtmf(struct woomera_interface *woomera, char *digits) @@ -932,7 +1084,7 @@ waitfor_socket_tryagain: } -static int waitfor_2sockets(int fda, int fdb, char *a, char *b, int timeout) +int waitfor_2sockets(int fda, int fdb, char *a, char *b, int timeout) { struct pollfd pfds[2]; int res = 0; @@ -956,8 +1108,6 @@ waitfor_2sockets_tryagain: pfds[0].events = POLLIN | errflags; pfds[1].events = POLLIN | errflags; - - res = poll(pfds, 2, timeout); if (res > 0) { @@ -990,58 +1140,73 @@ waitfor_2sockets_tryagain: return res; } +static int woomera_raw_to_ip(struct media_session *ms, char *raw) +{ + int x; + char *p; + + for(x = 0; x < strlen(raw) ; x++) { + if (raw[x] == ':') { + break; + } + if (raw[x] == '/') { + break; + } + } + + media_set_raw(ms,raw); + + if (ms->ip) { + smg_free(ms->ip); + } + ms->ip = smg_strndup(raw, x); + p = raw + (x+1); + ms->port = atoi(p); + + return 0; +} static struct media_session *media_session_new(struct woomera_interface *woomera) { - struct media_session *ms = NULL; - int x; - char *p; - int span,chan; - + struct media_session *ms = NULL; + int span,chan; + span=woomera->span; chan=woomera->chan; - log_printf(SMG_LOG_DEBUG_CALL, server.log,"Starting new MEDIA session [%s] [%s]\n", - woomera->interface,woomera->raw?woomera->raw:"N/A"); + log_printf(SMG_LOG_DEBUG_CALL, server.log,"Starting new MEDIA session [%s] [%s]\n", + woomera->interface,woomera->raw?woomera->raw:"N/A"); - if ((ms = smg_malloc(sizeof(struct media_session)))) { + if ((ms = smg_malloc(sizeof(struct media_session)))) { memset(ms, 0, sizeof(struct media_session)); if (woomera->loop_tdm != 1) { - for(x = 0; x < strlen(woomera->raw) ; x++) { - if (woomera->raw[x] == ':') { - break; - } - if (woomera->raw[x] == '/') { - break; - } - } - ms->ip = smg_strndup(woomera->raw, x); + woomera_raw_to_ip(ms,woomera->raw); time(&ms->started); - p = woomera->raw + (x+1); - ms->port = atoi(p); + } time(&ms->started); - woomera_set_ms(woomera,ms); ms->woomera = woomera; /* Setup artificial DTMF stuff */ memset(&ms->tone_session, 0, sizeof(ms->tone_session)); if (teletone_init_session(&ms->tone_session, 0, NULL, NULL)) { - log_printf(SMG_LOG_ALL, server.log, "ERROR: Failed to initialize TONE [w%ig%i]!\n", + log_printf(SMG_LOG_ALL, server.log, "ERROR: Failed to initialize TONE [s%ic%i]!\n", span+1,chan+1); } - + ms->tone_session.rate = SMG_DTMF_RATE; ms->tone_session.duration = server.dtmf_on * (ms->tone_session.rate / 1000); ms->tone_session.wait = server.dtmf_off * (ms->tone_session.rate / 1000); teletone_dtmf_detect_init (&ms->dtmf_detect, SMG_DTMF_RATE); - - } else { - log_printf(SMG_LOG_ALL, server.log, "ERROR: Memory Alloc Failed [w%ig%i]!\n", + + woomera_set_ms(woomera,ms); + + } else { + log_printf(SMG_LOG_ALL, server.log, "ERROR: Memory Alloc Failed [s%ic%i]!\n", span+1,chan+1); } @@ -1053,7 +1218,10 @@ static void media_session_free(struct media_session *ms) if (ms->ip) { smg_free(ms->ip); } - + if (ms->raw) { + smg_free(ms->raw); + } + teletone_destroy_session(&ms->tone_session); switch_buffer_destroy(&ms->dtmf_buffer); @@ -1062,6 +1230,20 @@ static void media_session_free(struct media_session *ms) smg_free(ms); } +static int update_udp_socket(struct media_session *ms, char *ip, int port) +{ + struct hostent *result; + char buf[512]; + int err = 0; + + memset(&ms->remote_hp, 0, sizeof(ms->remote_hp)); + gethostbyname_r(ip, &ms->remote_hp, buf, sizeof(buf), &result, &err); + ms->remote_addr.sin_family = ms->remote_hp.h_addrtype; + memcpy((char *) &ms->remote_addr.sin_addr.s_addr, ms->remote_hp.h_addr_list[0], ms->remote_hp.h_length); + ms->remote_addr.sin_port = htons(port); + + return 0; +} static int create_udp_socket(struct media_session *ms, char *local_ip, int local_port, char *ip, int port) { @@ -1237,13 +1419,16 @@ static void media_loop_run(struct media_session *ms) char filename[100]; FILE *filed=NULL; int loops=0,flags_out=0; + int open_cnt = 0; + + open_cnt=0; sangoma_api_hdr_t hdrframe; memset(&hdrframe,0,sizeof(hdrframe)); memset(circuit_frame,0,sizeof(circuit_frame)); retry_loop: - ms->sangoma_sock = sangoma_open_tdmapi_span_chan(woomera->span+1, woomera->chan+1); + ms->sangoma_sock = open_span_chan(woomera->span+1, woomera->chan+1); log_printf(SMG_LOG_PROD, server.log, "Media Loop Started %s fd=%i\n", woomera->interface,ms->sangoma_sock); @@ -1254,7 +1439,7 @@ retry_loop: usleep(500000); goto retry_loop; } - log_printf(SMG_LOG_ALL, server.log, "WANPIPE MEDIA Socket Error (%s) if=[%s] [w%ig%i]\n", + log_printf(SMG_LOG_ALL, server.log, "WANPIPE MEDIA Socket Error (%s) if=[%s] [s%ic%i]\n", strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); } else { @@ -1268,25 +1453,38 @@ retry_loop: errs++; } - if (sangoma_tdm_set_usr_period(ms->sangoma_sock, &tdm_api, 20) < 0 ) { + if (sangoma_tdm_set_usr_period(ms->sangoma_sock, &tdm_api, SANGOMA_USR_PERIOD) < 0 ) { errs++; } sangoma_frame_len = sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); +#ifdef LIBSANGOMA_VERSION + ms->has_hwec = sangoma_tdm_get_hw_ec(ms->sangoma_sock, &tdm_api); + if (ms->has_hwec) { + sangoma_tdm_disable_hwec(ms->sangoma_sock,&tdm_api); + } +#else + ms->has_hwec=1; sangoma_tdm_disable_hwec(ms->sangoma_sock,&tdm_api); - +#endif + ms->oob_disable = 0; +#ifdef LIBSANGOMA_VERSION + open_cnt = sangoma_get_open_cnt(ms->sangoma_sock, &tdm_api); + if (open_cnt > 1) { + ms->oob_disable = 1; + } +#endif } - - if (errs) { + if (errs) { log_printf(SMG_LOG_ALL, server.log, "Media Loop: failed to open tdm device %s\n", woomera->interface); return; } if (server.loop_trace) { - sprintf(filename,"/smg/w%ig%i-loop.trace",woomera->span+1,woomera->chan+1); + sprintf(filename,"/smg/s%ic%i-loop.trace",woomera->span+1,woomera->chan+1); unlink(filename); filed = safe_fopen(filename, "w"); } @@ -1302,21 +1500,6 @@ retry_loop: continue; } -#if 0 - if (res == SMG_SOCKET_EVENT_NVAL) { - close_socket(&ms->sangoma_sock); - - if (woomera_test_flag(woomera, WFLAG_MEDIA_END)) { - break; - } - - ms->sangoma_sock = sangoma_open_tdmapi_span_chan(woomera->span+1, woomera->chan+1); - log_printf(SMG_LOG_ALL, server.log, "Media Loop Restart %s\n", - woomera->interface); - continue; - } -#endif - res = sangoma_readmsg_socket(ms->sangoma_sock, &hdrframe, sizeof(hdrframe), @@ -1363,9 +1546,11 @@ retry_loop: fclose(filed); } - sangoma_tdm_enable_hwec(ms->sangoma_sock,&tdm_api); + if (ms->has_hwec) { + sangoma_tdm_enable_hwec(ms->sangoma_sock,&tdm_api); + } - close_socket(&ms->sangoma_sock); + close_span_chan(&ms->sangoma_sock, woomera->span+1, woomera->chan+1); if (loops < 1) { log_printf(SMG_LOG_ALL, server.log, "Media Loop FAILED %s Master=%i MediaEnd=%i Loops=%i\n", @@ -1415,7 +1600,12 @@ static void *media_thread_run(void *obj) wanpipe_tdm_api_t tdm_api; FILE *tx_fd=NULL; int sock_timeout=200; - int hwec_reenable=0; + + int hwec_enabled=0, hwec_reenable=0; + + int open_cnt = 0; + + open_cnt=0; if (woomera_test_flag(woomera, WFLAG_MEDIA_END) || !woomera->interface || @@ -1478,19 +1668,19 @@ static void *media_thread_run(void *obj) } #else media_retry: - ms->sangoma_sock = sangoma_open_tdmapi_span_chan(woomera->span+1, woomera->chan+1); + ms->sangoma_sock = open_span_chan(woomera->span+1, woomera->chan+1); if (ms->sangoma_sock < 0) { if (!woomera_test_flag(woomera, WFLAG_MEDIA_END)) { media_retry_cnt++; if (media_retry_cnt < 5) { - log_printf(SMG_LOG_ALL, server.log, "WANPIPE Socket Retry [w%ig%i]\n", + log_printf(SMG_LOG_ALL, server.log, "WANPIPE Socket Retry [s%ic%i]\n", woomera->span+1, woomera->chan+1); usleep(100000); goto media_retry; } - log_printf(SMG_LOG_ALL, server.log, "WANPIPE Socket Error (%s) if=[%s] [w%ig%i]\n", + log_printf(SMG_LOG_ALL, server.log, "WANPIPE Socket Error (%s) if=[%s] [s%ic%i]\n", strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); /* Switch Congestion */ @@ -1513,10 +1703,16 @@ media_retry: errs++; } - if (sangoma_tdm_set_usr_period(ms->sangoma_sock, &tdm_api, 20) < 0 ) { + if (sangoma_tdm_set_usr_period(ms->sangoma_sock, &tdm_api, SANGOMA_USR_PERIOD) < 0 ) { errs++; } +#ifdef LIBSANGOMA_VERSION + ms->has_hwec = sangoma_tdm_get_hw_ec(ms->sangoma_sock, &tdm_api); +#else + ms->has_hwec = 1; +#endif + # ifdef CODEC_LAW_DEFAULT # ifdef LIBSANGOMA_GET_HWCODING ms->hw_coding=sangoma_tdm_get_hw_coding(ms->sangoma_sock, &tdm_api); @@ -1530,36 +1726,67 @@ media_retry: # ifdef LIBSANGOMA_GET_HWDTMF + + if (ms->has_hwec) { ms->hw_dtmf=sangoma_tdm_get_hw_dtmf(ms->sangoma_sock, &tdm_api); - if (ms->hw_dtmf) { - log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Supported [w%ig%i]\n", + if (ms->hw_dtmf) { + log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Supported [s%ic%i]\n", strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); } else { - log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Not Supported [w%ig%i]\n", + log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Not Supported [s%ic%i]\n", strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); } + } + #else ms->hw_dtmf=0; - log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Not Supported [w%ig%i]\n", + log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Not Supported [s%ic%i]\n", strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); # warning "libsangoma missing hwdtmf feature: not up to date!" #endif - if (!bearer_cap_is_audio(woomera->bearer_cap)) { - int err; - err=sangoma_tdm_disable_hwec(ms->sangoma_sock, &tdm_api); - if (err == 0) { - hwec_reenable=1; - log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] Disabling hwec Ok\n",woomera->interface); + ms->oob_disable = 0; +#ifdef LIBSANGOMA_VERSION + open_cnt = sangoma_get_open_cnt(ms->sangoma_sock, &tdm_api); + if (open_cnt > 1) { + ms->oob_disable = 1; + } +#endif + + + if (ms->has_hwec) { + if (!tdmv_hwec_persist) { + // BRI cards start with HWEC in bypass disable state + if (bearer_cap_is_audio(woomera->bearer_cap)) { + int err; + err=sangoma_tdm_enable_hwec(ms->sangoma_sock, &tdm_api); + if (err == 0) { + hwec_enabled=1; + log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] Enabling hwec Ok\n",woomera->interface); + } else { + log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] Enabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + } + } } else { - log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] Disabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + if (!bearer_cap_is_audio(woomera->bearer_cap)) { + int err; + err=sangoma_tdm_disable_hwec(ms->sangoma_sock, &tdm_api); + if (err == 0) { + hwec_reenable=1; + log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] Disabling hwec Ok\n",woomera->interface); + } else { + log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] Disabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + } + } } - } sangoma_frame_len = sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); + if (server.rbs_relay[woomera->span+1]) { + sangoma_tdm_enable_rbs_events(ms->sangoma_sock, &tdm_api, 20); + } } #endif } @@ -1569,10 +1796,12 @@ media_retry: #ifdef WP_HPTDM_API /* No tdm thread */ #else +#ifndef SMG_NO_MEDIA if (!errs && launch_media_tdm_thread(woomera)) { errs++; } +#endif #endif if (errs) { @@ -1584,6 +1813,8 @@ media_retry: unsigned char udp_frame[4096]; unsigned int fromlen = sizeof(struct sockaddr_in); int flags_out=0; + int rc; + unsigned int carrier=0,carrier_diff=0; sangoma_api_hdr_t hdrframe; memset(&hdrframe,0,sizeof(hdrframe)); @@ -1632,7 +1863,14 @@ media_retry: log_printf(SMG_LOG_ALL,server.log, "FAILED TO OPEN Sound file!\n"); } } - + + for (;;) { + if ((packet_len = recvfrom(ms->udp_sock, udp_frame, sizeof(udp_frame), + MSG_DONTWAIT, (struct sockaddr *) &ms->local_addr, &fromlen)) < 1) { + break; + } + } + for (;;) { @@ -1644,12 +1882,17 @@ media_retry: break; } + res = waitfor_socket(ms->udp_sock, sock_timeout, POLLIN, &flags_out); if (res < 0) { break; } +#ifdef SMG_NO_MEDIA + continue; +#endif + if (res == 0) { if (woomera_dtmf_transmit(ms,sangoma_frame_len) == 0) { @@ -1690,6 +1933,21 @@ media_retry: if (packet_len > 0) { + if (server.udp_seq && packet_len > 4) { + packet_len-=4; + if ( woomera->rx_udp_seq != *(unsigned int*)&udp_frame[packet_len]) { + woomera->rx_udp_seq = *(unsigned int*)&udp_frame[packet_len]; + log_printf(SMG_LOG_WOOMERA,server.log,"RX UDP SEQ=%i Expected=%i\n", + *(unsigned int*)&udp_frame[packet_len],woomera->rx_udp_seq); + + pthread_mutex_lock(&server.thread_count_lock); + server.media_rx_seq_err++; + pthread_mutex_unlock(&server.thread_count_lock); + } else { + woomera->rx_udp_seq++; + } + } + #if 0 /* NC: This can cause skb_over panic must be retested */ if (packet_len != sangoma_frame_len && ms->udp_sync_cnt <= 5) { @@ -1748,17 +2006,45 @@ media_retry: tx_fd=NULL; } + #ifdef WP_HPTDM_API if (ms->tdmchan->push) { ms->tdmchan->push(ms->tdmchan,udp_frame,packet_len); } #else - sangoma_sendmsg_socket(ms->sangoma_sock, + rc=sangoma_sendmsg_socket(ms->sangoma_sock, &hdrframe, sizeof(hdrframe), udp_frame, packet_len, 0); + if (rc != packet_len) { + log_printf(SMG_LOG_PROD, server.log, "Error: Sangoma Tx error [%s] len=%i (%s)\n",woomera->interface, packet_len, strerror(errno)); + pthread_mutex_lock(&server.thread_count_lock); + server.media_rx_seq_err++; + pthread_mutex_unlock(&server.thread_count_lock); + } + + sangoma_get_full_cfg(ms->sangoma_sock,&tdm_api); + if (carrier == 0) { + carrier=tdm_api.wp_tdm_cmd.stats.tx_carrier_errors; + } + if (carrier != tdm_api.wp_tdm_cmd.stats.tx_carrier_errors) { + carrier_diff+=tdm_api.wp_tdm_cmd.stats.tx_carrier_errors-carrier; + carrier=tdm_api.wp_tdm_cmd.stats.tx_carrier_errors; + log_printf(SMG_LOG_WOOMERA, server.log, "Error: Sangoma Tx carrier error [%s] cnt=%i\n", + woomera->interface, carrier_diff); + pthread_mutex_lock(&server.thread_count_lock); + server.media_rx_seq_err++; + pthread_mutex_unlock(&server.thread_count_lock); + + sangoma_sendmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + udp_frame, + packet_len, 0); + } + #endif } @@ -1826,22 +2112,38 @@ media_retry: media_thread_exit: - if (hwec_reenable) { - int err; - err=sangoma_tdm_enable_hwec(ms->sangoma_sock, &tdm_api); - if (err==0) { - log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] Re-enabling hwec ok\n",woomera->interface); + if (ms->has_hwec) { + + if (!tdmv_hwec_persist) { + if (hwec_enabled) { + int err; + err=sangoma_tdm_disable_hwec(ms->sangoma_sock, &tdm_api); + if (err==0) { + log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] disabling hwec ok\n",woomera->interface); + } else { + log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] disabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + } + } } else { - log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] Re-enabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + if (hwec_reenable) { + int err; + err=sangoma_tdm_enable_hwec(ms->sangoma_sock, &tdm_api); + if (err==0) { + log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] Re-enabling hwec ok\n",woomera->interface); + } else { + log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] Re-enabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + } + } } + } - + if (woomera_test_flag(woomera, WFLAG_MEDIA_TDM_RUNNING)) { woomera_set_flag(woomera, WFLAG_MEDIA_END); /* Dont wait for the other thread */ close_socket(&ms->udp_sock); - close_socket(&ms->sangoma_sock); + close_span_chan(&ms->sangoma_sock, woomera->span+1, woomera->chan+1); while(woomera_test_flag(woomera, WFLAG_MEDIA_TDM_RUNNING)) { usleep(1000); sched_yield(); @@ -1850,7 +2152,7 @@ media_thread_exit: close_socket(&ms->udp_sock); - close_socket(&ms->sangoma_sock); + close_span_chan(&ms->sangoma_sock, woomera->span+1, woomera->chan+1); if (tx_fd){ fclose(tx_fd); @@ -1887,10 +2189,7 @@ static void *media_tdm_thread_run(void *obj) unsigned char circuit_frame[1024]; sangoma_api_hdr_t hdrframe; int flags_out; - -#if 0 - int tdm_cnt=0; -#endif + int poll_opt = POLLIN | POLLPRI; memset(&hdrframe,0,sizeof(hdrframe)); memset(circuit_frame,0,sizeof(circuit_frame)); @@ -1911,6 +2210,16 @@ static void *media_tdm_thread_run(void *obj) log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA TDM session for [%s] started (ptr=%p)\n", woomera->interface,woomera); + for (;;) { + res = sangoma_readmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + circuit_frame, + sizeof(circuit_frame), 0); + if (res < 0) { + break; + } + } for (;;) { @@ -1920,9 +2229,14 @@ static void *media_tdm_thread_run(void *obj) res=0; break; } + - - res = waitfor_socket(ms->sangoma_sock, 1000, (POLLIN | POLLPRI), &flags_out); + if (ms->oob_disable) { + poll_opt = POLLIN; + } else { + poll_opt = POLLIN | POLLPRI; + } + res = waitfor_socket(ms->sangoma_sock, 1000, poll_opt, &flags_out); if (res < 0) { @@ -1941,6 +2255,10 @@ static void *media_tdm_thread_run(void *obj) if (flags_out & POLLIN) { + if (server.rbs_relay[woomera->span+1]) { + handle_dequeue_and_woomera_tx_event_rbs(woomera); + } + res = sangoma_readmsg_socket(ms->sangoma_sock, &hdrframe, sizeof(hdrframe), @@ -1956,6 +2274,13 @@ static void *media_tdm_thread_run(void *obj) break; } + + if (server.udp_seq) { + *(unsigned int*)&circuit_frame[res] = woomera->tx_udp_seq; + woomera->tx_udp_seq++; + res+=4; + } + res = sendto(ms->udp_sock, circuit_frame, res, 0, @@ -1964,7 +2289,6 @@ static void *media_tdm_thread_run(void *obj) if (res < 0) { log_printf(SMG_LOG_DEBUG_CALL, server.log, "UDP Sento Error: %s\n", strerror(errno)); - } } @@ -1997,6 +2321,12 @@ static void *media_tdm_thread_run(void *obj) } break; #endif + case WP_TDMAPI_EVENT_RBS: + log_printf(SMG_LOG_WOOMERA, server.log, "[%s] Rx RBS Event Bits=0x%02X\n", + woomera->interface, rx_event->wp_tdm_api_event_rbs_bits); + handle_event_rbs(woomera, rx_event->wp_tdm_api_event_rbs_bits); + break; + default: log_printf(SMG_LOG_ALL, server.log, "TDM API Unknown OOB Event %i\n", rx_event->wp_tdm_api_event_type); @@ -2114,9 +2444,9 @@ static struct woomera_interface * launch_woomera_loop_thread(short_signal_event_ struct woomera_interface *woomera = NULL; char callid[20]; - sprintf(callid, "w%dg%d", event->span+1,event->chan+1); + sprintf(callid, "s%dc%d", event->span+1,event->chan+1); - if ((woomera = alloc_woomera())) { + if ((woomera = alloc_woomera())) { woomera->chan = event->chan; woomera->span = event->span; @@ -2515,7 +2845,101 @@ static int add_to_holding_tank(struct woomera_interface *woomera) } - +static int handle_event_rbs(struct woomera_interface *woomera, unsigned char rbs_digit) +{ + woomera_rbs_relay_t *rbsrelay; + + if (smg_validate_span_chan(woomera->span,woomera->chan) != 0) { + log_printf(SMG_LOG_ALL, server.log, "[%s] handle_event_rbs invalid span chan\n", + woomera->interface); + return -1; + } + + if (!server.rbs_relay[woomera->span+1]) { + log_printf(SMG_LOG_ALL, server.log, "[%s] handle_event_rbs rbs_relan not enabled\n", + woomera->interface); + return -1; + } + + rbsrelay=&server.process_table[woomera->span][woomera->chan].rbs_relay; + + if (rbsrelay->rbs_bits[rbsrelay->rx_idx].init) { + log_printf(SMG_LOG_ALL, server.log, "[%s] Critical Error Rx RBS Overrun\n", + woomera->interface); + return -1; + } + + log_printf(SMG_LOG_ALL, server.log, "[%s] woomera rx queue rbs %X idx %i\n", + woomera->interface,rbs_digit, rbsrelay->rx_idx); + + rbsrelay->rbs_bits[rbsrelay->rx_idx].abcd=rbs_digit; + rbsrelay->rbs_bits[rbsrelay->rx_idx].init=500/SANGOMA_USR_PERIOD; + rbsrelay->rx_idx = ((rbsrelay->rx_idx + 1) % WOOMERA_MAX_RBS_BITS); + + return 0; +} + +static int handle_dequeue_and_woomera_tx_event_rbs(struct woomera_interface *woomera) +{ + struct woomera_event wevent; + woomera_rbs_relay_t *rbsrelay; + unsigned char abcd; + + if (smg_validate_span_chan(woomera->span,woomera->chan) != 0) { + log_printf(SMG_LOG_ALL, server.log, "[%s] handle_dequeue_and_woomera_tx_event_rbs invalid span chan\n", + woomera->interface); + return -1; + } + + if (!server.rbs_relay[woomera->span+1]) { + log_printf(SMG_LOG_ALL, server.log, "[%s] handle_dequeue_and_woomera_tx_event_rbs rbs_relan not enabled\n", + woomera->interface); + return -1; + } + + if (!woomera_test_flag(woomera,WFLAG_ANSWER)) { + return 0; + } + + rbsrelay=&server.process_table[woomera->span][woomera->chan].rbs_relay; + + if (rbsrelay->rbs_bits[rbsrelay->tx_idx].init == 0) { + return -1; + } + + if (rbsrelay->rbs_bits[rbsrelay->tx_idx].init > 1) { + rbsrelay->rbs_bits[rbsrelay->tx_idx].init--; + return -1; + } + + + abcd = rbsrelay->rbs_bits[rbsrelay->tx_idx].abcd; + rbsrelay->rbs_bits[rbsrelay->tx_idx].init=0; + + + log_printf(SMG_LOG_ALL, server.log, "[%s] woomera tx rbs %X idx %i\n", + woomera->interface,abcd, rbsrelay->tx_idx); + + rbsrelay->tx_idx = ((rbsrelay->tx_idx + 1) % WOOMERA_MAX_RBS_BITS); + + memset(&wevent, 0, sizeof(struct woomera_event)); + + new_woomera_event_printf(&wevent, "EVENT RBS %sUnique-Call-Id:%s%sContent-Length:%d%s%s%X%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + 1, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + abcd, + WOOMERA_RECORD_SEPERATOR); + + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + return 0; +} + static void handle_event_dtmf(struct woomera_interface *woomera, unsigned char dtmf_digit) { struct woomera_event wevent; @@ -2537,22 +2961,76 @@ static void handle_event_dtmf(struct woomera_interface *woomera, unsigned char d return; } +static int handle_woomera_progress(struct woomera_interface *woomera, + struct woomera_message *wmsg) +{ + call_signal_event_t event; + int err=-1; + + memset(&event, 0, sizeof(event)); + call_signal_event_init((short_signal_event_t*)&event, SIGBOOST_EVENT_CALL_PROGRESS, woomera->chan, woomera->span); + sprintf(event.isup_in_rdnis,"SMG003-EVI-2"); + event.isup_in_rdnis_size=strlen(event.isup_in_rdnis); + if (woomera->index >= 0) { + event.call_setup_id = woomera->index; + } + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s]\n", + wmsg->command, woomera->interface); + + if (!woomera_check_running(woomera)) { + socket_printf(woomera->socket, "405 PROGRESS Channel already hungup%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + if (!woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + + socket_printf(woomera->socket, "405 PROGRESS Channel not aceked%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + err=isup_exec_event(&event); + if (err == 0) { + socket_printf(woomera->socket, + "200 %s PROGRESS OK%s" + "Unique-Call-Id: %s%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } else { + socket_printf(woomera->socket, "405 PROGRESS Boost failure%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } + + return err; +} static int handle_woomera_media_accept_answer(struct woomera_interface *woomera, struct woomera_message *wmsg, int media, int answer, int accept) { char *raw = woomera_message_header(wmsg, "raw-audio"); + char *media_available = woomera_message_header(wmsg, "xMedia"); + char *ring_available = woomera_message_header(wmsg, "xRing"); struct woomera_event wevent; - log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s]\n", - wmsg->command, woomera->interface); + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s] raw=%s\n", + wmsg->command, woomera->interface, raw?raw:"N/A"); - - if (woomera_test_flag(woomera, WFLAG_HANGUP) || - !woomera_test_flag(woomera, WFLAG_RUNNING) || - woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + if (!woomera_check_running(woomera)) { log_printf(SMG_LOG_DEBUG_CALL, server.log, "ERROR! call was cancelled MEDIA on HANGUP or MEDIA END!\n"); @@ -2633,6 +3111,18 @@ static int handle_woomera_media_accept_answer(struct woomera_interface *woomera, woomera->timeout=0; return -1; } + } else if (media && raw && woomera->ms) { + if (strncmp(raw,woomera->raw,20) != 0) { + log_printf(SMG_LOG_WOOMERA, server.log, "[%s] MEDIA Address Change from %s to %s\n", + woomera->interface,woomera->raw, raw); + + pthread_mutex_lock(&woomera->ms_lock); + woomera_set_raw(woomera,raw); + woomera_raw_to_ip(woomera->ms,woomera->raw); + update_udp_socket(woomera->ms,woomera->ms->ip, woomera->ms->port); + + pthread_mutex_unlock(&woomera->ms_lock); + } } if (!woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { @@ -2650,12 +3140,19 @@ static int handle_woomera_media_accept_answer(struct woomera_interface *woomera, if (accept) { if (!autoacm && !woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + short_signal_event_t event; + memset(&event, 0, sizeof(event)); + call_signal_event_init(&event, SIGBOOST_EVENT_CALL_START_ACK, woomera->chan, woomera->span); + if (media_available && (atoi(media_available) == 1)) { + event.flags |= SIGBOOST_PROGRESS_MEDIA; + } + + if (ring_available && (atoi(ring_available) == 1)) { + event.flags |= SIGBOOST_PROGRESS_RING; + } woomera_set_flag(woomera,WFLAG_CALL_ACKED); - isup_exec_command(woomera->span, - woomera->chan, - -1, - SIGBOOST_EVENT_CALL_START_ACK, - 0); + + isup_exec_event((call_signal_event_t*)&event); } } @@ -2671,23 +3168,33 @@ static int handle_woomera_media_accept_answer(struct woomera_interface *woomera, if (ms) { if (!autoacm && !woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + short_signal_event_t event; + memset(&event, 0, sizeof(event)); + call_signal_event_init(&event, SIGBOOST_EVENT_CALL_START_ACK, woomera->chan, woomera->span); + if (media_available && (atoi(media_available) == 1)) { + event.flags |= SIGBOOST_PROGRESS_MEDIA; + } + + if (ring_available && (atoi(ring_available) == 1)) { + event.flags |= SIGBOOST_PROGRESS_RING; + } woomera_set_flag(woomera,WFLAG_CALL_ACKED); - isup_exec_command(woomera->span, - woomera->chan, - -1, - SIGBOOST_EVENT_CALL_START_ACK, - 0); + + isup_exec_event((call_signal_event_t*)&event); } + woomera_set_flag(woomera, WFLAG_ANSWER); + isup_exec_command(woomera->span, woomera->chan, -1, SIGBOOST_EVENT_CALL_ANSWERED, 0); log_printf(SMG_LOG_DEBUG_CALL, server.log, - "Sent SIGBOOST_EVENT_CALL_ANSWERED [w%dg%d]\n", + "Sent SIGBOOST_EVENT_CALL_ANSWERED [s%dc%d]\n", woomera->span+1,woomera->chan+1); } else { + struct woomera_event wevent; log_printf(SMG_LOG_ALL, server.log, "WOOMERA ANSWER: FAILED [%s] no Media \n", @@ -2736,7 +3243,7 @@ static int handle_woomera_media_accept_answer(struct woomera_interface *woomera, enqueue_event(woomera, &wevent,EVENT_FREE_DATA); - return 0; + return 0; } static int handle_woomera_call_start (struct woomera_interface *woomera, @@ -2753,36 +3260,24 @@ static int handle_woomera_call_start (struct woomera_interface *woomera, char *rdnis = woomera_message_header(wmsg, "RDNIS"); char *bearer_cap = woomera_message_header(wmsg, "Bearer-Cap"); char *uil1p = woomera_message_header(wmsg, "uil1p"); + char *called = wmsg->callid; char *grp = wmsg->callid; + char *profile; char *p; int cause = 34; int tg = 0; int huntgroup = SIGBOOST_HUNTGRP_SEQ_ASC; - if (smg_check_all_busy() || - woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ - - - socket_printf(woomera->socket, "EVENT HANGUP %s" - "Cause: %s%s" - "Q931-Cause-Code: %d%s", - WOOMERA_LINE_SEPERATOR, - q931_rel_to_str(cause), - WOOMERA_LINE_SEPERATOR, - cause, - WOOMERA_RECORD_SEPERATOR); - - socket_printf(woomera->socket, - "405 SMG Server All Ckt Busy!%s", WOOMERA_RECORD_SEPERATOR); - - log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "SMG Server Full %d (ckt busy cnt=%i)\n", - server.call_count, server.all_ckt_busy); - return -1; + /* Remove profile name out of destiantion */ + if ((profile = strchr(called, ':'))) { + profile++; + called=profile; + grp=profile; } - - log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "New Call %d/%d\n", server.call_count, server.max_calls); + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "New Call %d/%d origdest=%s newdest=%s\n", + server.call_count, server.max_calls, wmsg->callid, called); switch(called[0]) { case 'g': @@ -2814,6 +3309,50 @@ static int handle_woomera_call_start (struct woomera_interface *woomera, } woomera->trunk_group=tg; + + if ( woomera->trunk_group > SMG_MAX_TG ) { + + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, + "405 SMG Server: trunk group value not valid!%s", WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "SMG Server: trunk group value not valid %d\n", + woomera->trunk_group); + + return -1; + } + + if (smg_check_all_busy(woomera->trunk_group) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ + + + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, + "405 SMG Server All Ckt Busy!%s", WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "SMG Server Full %d (ckt busy cnt=%i on tg=%d)\n", + server.call_count, server.all_ckt_busy[woomera->trunk_group],woomera->trunk_group); + return -1; + } + + + if (raw) { woomera_set_raw(woomera, raw); } @@ -2891,6 +3430,13 @@ static int handle_woomera_call_start (struct woomera_interface *woomera, event.trunk_group = tg; event.hunt_group = huntgroup; + set_digits_info(&event.calling.ton, woomera_message_header(wmsg, "xCallingTon")); + set_digits_info(&event.calling.npi, woomera_message_header(wmsg, "xCallingNpi")); + set_digits_info(&event.called.ton, woomera_message_header(wmsg, "xCalledTon")); + set_digits_info(&event.called.npi, woomera_message_header(wmsg, "xCalledNpi")); + set_digits_info(&event.rdnis.ton, woomera_message_header(wmsg, "xRdnisTon")); + set_digits_info(&event.rdnis.npi, woomera_message_header(wmsg, "xRdnisNpi")); + if (call_signal_connection_write(&server.mcon, &event) < 0) { log_printf(SMG_LOG_ALL, server.log, "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", @@ -3061,8 +3607,13 @@ static void interpret_command(struct woomera_interface *woomera, struct woomera_ char *session=NULL; int span, chan; char ifname[100]; + int err; + /* If session does not exist this is an incoming call */ - sscanf(unique_id, "w%dg%d", &span, &chan); + err=sscanf(unique_id, "s%dc%d", &span, &chan); + if (err!=2) { + err=sscanf(unique_id, "w%dg%d", &span, &chan); + } span--; chan--; @@ -3081,7 +3632,7 @@ static void interpret_command(struct woomera_interface *woomera, struct woomera_ WOOMERA_LINE_SEPERATOR, cause, WOOMERA_RECORD_SEPERATOR); - socket_printf(woomera->socket, "404 Invalid span/chan in session%s" + socket_printf(woomera->socket, "404 Invalid span/chan in session%s", WOOMERA_RECORD_SEPERATOR); log_printf(SMG_LOG_DEBUG_CALL, woomera->log, @@ -3134,7 +3685,7 @@ static void interpret_command(struct woomera_interface *woomera, struct woomera_ cause, WOOMERA_RECORD_SEPERATOR); - socket_printf(woomera->socket, "404 Invalid/Expired Session%s" + socket_printf(woomera->socket, "404 Invalid/Expired Session%s", WOOMERA_RECORD_SEPERATOR); log_printf(SMG_LOG_DEBUG_MISC, woomera->log, @@ -3149,14 +3700,14 @@ static void interpret_command(struct woomera_interface *woomera, struct woomera_ int clients=server.process_table[span][chan].clients; if (server.process_table[span][chan].clients < 0) { - log_printf(SMG_LOG_ALL, woomera->log, "WOOMERA CMD: Warning Clients (%i) Race condition on Hangup [w%dg%d]\n", + log_printf(SMG_LOG_ALL, woomera->log, "WOOMERA CMD: Warning Clients (%i) Race condition on Hangup [s%dc%d]\n", clients, span+1,chan+1); } else if (server.process_table[span][chan].clients > 1) { server.process_table[span][chan].clients--; pthread_mutex_unlock(&server.process_lock); - log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Got Hungup on Multiple Clients %i, skipping hangup [w%dg%d]\n", + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Got Hungup on Multiple Clients %i, skipping hangup [s%dc%d]\n", clients, span+1,chan+1); cause=16; @@ -3175,14 +3726,14 @@ static void interpret_command(struct woomera_interface *woomera, struct woomera_ woomera_set_flag(woomera, WFLAG_HANGUP); return; } - log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Hanging up channel [w%dg%d]\n", + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Hanging up channel [s%dc%d]\n", span+1,chan+1); } #endif server.process_table[span][chan].dev=woomera; strncpy(woomera->session,unique_id,sizeof(woomera->session)); - sprintf(ifname,"w%dg%d",span+1,chan+1); + sprintf(ifname,"s%dc%d",span+1,chan+1); woomera_set_interface(woomera, ifname); woomera->span=span; @@ -3202,27 +3753,41 @@ static void interpret_command(struct woomera_interface *woomera, struct woomera_ } else if (strncmp(woomera->session,unique_id,sizeof(woomera->session))) { - cause=81; - socket_printf(woomera->socket, "EVENT HANGUP %s" - "Cause: %s%s" - "Q931-Cause-Code: %d%s", - WOOMERA_LINE_SEPERATOR, - q931_rel_to_str(cause), - WOOMERA_LINE_SEPERATOR, - cause, - WOOMERA_RECORD_SEPERATOR); + cause=81; + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); socket_printf(woomera->socket, "404 Session Mis-match%s" WOOMERA_RECORD_SEPERATOR); - woomera_set_flag(woomera, WFLAG_HANGUP); + woomera_set_flag(woomera, WFLAG_HANGUP); return; } - - if (!strcasecmp(wmsg->command, "dtmf")) { + if (!strcasecmp(wmsg->command, "rbs")) { - log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: DTMF Received: [%s] Digit %s Body %s\n", - woomera->interface, wmsg->command_args, wmsg->body); + log_printf(SMG_LOG_PROD, server.log, + "WOOMERA CMD: RBS Received: [%s] Digit %s Body %s\n", + woomera->interface, wmsg->command_args, wmsg->body); + + wanpipe_send_rbs(woomera,wmsg->body); + + socket_printf(woomera->socket, "200 RBS OK%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + } else if (!strcasecmp(wmsg->command, "dtmf")) { + + log_printf(SMG_LOG_WOOMERA, woomera->log, + "WOOMERA CMD: DTMF Received: [%s] Digit %s Body %s\n", + woomera->interface, wmsg->command_args, wmsg->body); wanpipe_send_dtmf(woomera,wmsg->body); @@ -3260,7 +3825,7 @@ static void interpret_command(struct woomera_interface *woomera, struct woomera_ } - log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "Hangup Received: [w%dg%d]\n", + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "Hangup Received: [s%dc%d]\n", span+1,chan+1); @@ -3273,8 +3838,7 @@ static void interpret_command(struct woomera_interface *woomera, struct woomera_ WOOMERA_LINE_SEPERATOR, woomera->session, WOOMERA_RECORD_SEPERATOR); - - + } else if (!strcasecmp(wmsg->command, "proceed")) { log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s]\n", @@ -3287,7 +3851,12 @@ static void interpret_command(struct woomera_interface *woomera, struct woomera_ WOOMERA_LINE_SEPERATOR, woomera->session, WOOMERA_RECORD_SEPERATOR); - + + } else if (!strcasecmp(wmsg->command, "progress")) { + + handle_woomera_progress(woomera,wmsg); + + } else if ((media = !strcasecmp(wmsg->command, "media")) || (answer = !strcasecmp(wmsg->command, "answer")) || (accept = !strcasecmp(wmsg->command, "accept"))) { @@ -3320,12 +3889,18 @@ static void interpret_command(struct woomera_interface *woomera, struct woomera_ static void handle_call_answer(short_signal_event_t *event) { - struct woomera_interface *woomera = NULL; - int kill = 0; + struct woomera_interface *woomera = NULL; + int kill = 0; - pthread_mutex_lock(&server.process_lock); - woomera = server.process_table[event->span][event->chan].dev; - pthread_mutex_unlock(&server.process_lock); + if (smg_validate_span_chan(event->span,event->chan)) { + log_printf(0,server.log, "Error: invalid span=% chan=%i on call answer [s%dc%d]!\n", + event->span+1, event->chan+1, event->span+1,event->chan+1); + return; + } + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + pthread_mutex_unlock(&server.process_lock); if (woomera) { char callid[80]; @@ -3359,7 +3934,7 @@ static void handle_call_answer(short_signal_event_t *event) } else { int err; err=0; - sprintf(callid, "w%dg%d", event->span + 1, event->chan + 1); + sprintf(callid, "s%dc%d", event->span + 1, event->chan + 1); woomera_set_interface(woomera, callid); #ifndef WOOMERA_EARLY_MEDIA err=launch_media_thread(woomera); @@ -3397,7 +3972,7 @@ static void handle_call_answer(short_signal_event_t *event) #endif if (!kill) { - new_woomera_event_printf(&wevent, "EVENT CONNECT w%dg%d%s" + new_woomera_event_printf(&wevent, "EVENT CONNECT s%dc%d%s" "Unique-Call-Id: %s%s", event->span+1, event->chan+1, @@ -3409,7 +3984,7 @@ static void handle_call_answer(short_signal_event_t *event) } } } else { - log_printf(SMG_LOG_PROD, server.log, "Answer requested on non-existant session. [w%dg%d]\n", + log_printf(SMG_LOG_PROD, server.log, "Answer requested on non-existant session. [s%dc%d]\n", event->span+1, event->chan+1); kill++; } @@ -3430,10 +4005,10 @@ handle_call_answer_end: SIGBOOST_EVENT_CALL_STOPPED, SIGBOOST_RELEASE_CAUSE_NORMAL); - log_printf(SMG_LOG_PROD, server.log, "Sent CALL STOP to Answer without session [w%dg%d]\n", + log_printf(SMG_LOG_PROD, server.log, "Sent CALL STOP to Answer without session [s%dc%d]\n", event->span+1, event->chan+1); #endif - log_printf(SMG_LOG_ALL, server.log, "WARNING: Received Answer with no session [w%dg%d]\n", + log_printf(SMG_LOG_ALL, server.log, "WARNING: Received Answer with no session [s%dc%d]\n", event->span+1, event->chan+1); } } @@ -3455,7 +4030,7 @@ static void handle_call_start_ack(short_signal_event_t *event) int err, span, chan; pull_from_holding_tank(event->call_setup_id,event->span,event->chan); - sprintf(callid, "w%dg%d", event->span + 1, event->chan + 1); + sprintf(callid, "s%dc%d", event->span + 1, event->chan + 1); span = event->span; chan = event->chan; @@ -3469,7 +4044,7 @@ static void handle_call_start_ack(short_signal_event_t *event) struct woomera_interface *tmp_woomera = server.process_table[span][chan].dev; woomera_set_flag(tmp_woomera,WFLAG_HANGUP); woomera_set_flag(tmp_woomera,WFLAG_MEDIA_END); - log_printf(SMG_LOG_ALL,server.log,"Call Overrun on [w%dg%d] - Call ACK!\n", event->span+1, event->chan+1); + log_printf(SMG_LOG_ALL,server.log,"Call Overrun on [s%dc%d] - Call ACK!\n", event->span+1, event->chan+1); kill++; } @@ -3483,7 +4058,8 @@ static void handle_call_start_ack(short_signal_event_t *event) sizeof(server.process_table[span][chan].digits)); server.process_table[span][chan].digits_len = 0; - + memset(&server.process_table[event->span][event->chan].rbs_relay,0, + sizeof(server.process_table[event->span][event->chan].rbs_relay)); pthread_mutex_unlock(&server.process_lock); if (kill) { @@ -3527,7 +4103,7 @@ static void handle_call_start_ack(short_signal_event_t *event) #endif if (!kill) { - new_woomera_event_printf(&wevent, "EVENT PROCEED w%dg%d%s" + new_woomera_event_printf(&wevent, "EVENT PROCEED s%dc%d%s" "Channel-Name: g%d/%d%s" "Unique-Call-Id: %s%s", event->span+1, @@ -3550,13 +4126,13 @@ static void handle_call_start_ack(short_signal_event_t *event) enqueue_event(woomera, &wevent,EVENT_FREE_DATA); - log_printf(SMG_LOG_DEBUG_MISC, server.log, "Call Answered Event ID = %d Device = w%dg%d!\n", + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Call Answered Event ID = %d Device = s%dc%d!\n", event->call_setup_id,woomera->span+1,woomera->chan+1); } } } else { log_printf(SMG_LOG_PROD, server.log, - "Event (START ACK) %d referrs to a non-existant session (%d) [w%dg%d]!\n", + "Event (START ACK) %d referrs to a non-existant session (%d) [s%dc%d]!\n", event->event_id, event->call_setup_id,event->span+1, event->chan+1); kill++; } @@ -3572,7 +4148,7 @@ woomera_call_ack_skip: SIGBOOST_EVENT_CALL_STOPPED, SIGBOOST_RELEASE_CAUSE_NORMAL); - log_printf(SMG_LOG_PROD, server.log, "Sent CALL STOP to CALL START ACK without session [w%dg%d]\n", + log_printf(SMG_LOG_PROD, server.log, "Sent CALL STOP to CALL START ACK without session [s%dc%d]\n", event->span+1, event->chan+1); } } @@ -3612,7 +4188,7 @@ static void handle_call_start_nack(short_signal_event_t *event) woomera_set_cause_topbx(woomera,event->release_cause); woomera_set_flag(woomera, (WFLAG_HANGUP|WFLAG_HANGUP_NACK_ACK)); - new_woomera_event_printf(&wevent, "EVENT HANGUP w%dg%d%s" + new_woomera_event_printf(&wevent, "EVENT HANGUP s%dc%d%s" "Unique-Call-Id: %s%s" "Cause: %s%s" "Q931-Cause-Code: %d%s", @@ -3671,7 +4247,7 @@ static void handle_call_start_nack(short_signal_event_t *event) if (woomera) { - log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event START NACK on w%dg%d ptr=%p ms=%p\n", + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event START NACK on s%dc%d ptr=%p ms=%p\n", woomera->span+1,woomera->chan+1,woomera,woomera->ms); if (!woomera_test_flag(woomera,WFLAG_HANGUP)){ @@ -3691,23 +4267,24 @@ static void handle_call_start_nack(short_signal_event_t *event) } else { /* Valid state when we are not in autoacm mode */ ack++; - log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event: NACK no woomera on span chan [w%dg%d]!\n", + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event: NACK no woomera on span chan [s%dc%d]!\n", event->span+1, event->chan+1); } } else { log_printf(SMG_LOG_ALL, server.log, - "Error: Start Nack Invalid State Should not happen [%d] [w%dg%d]!\n", + "Error: Start Nack Invalid State Should not happen [%d] [s%dc%d]!\n", event->call_setup_id, event->span+1, event->chan+1); ack++; } if (event->release_cause == SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY) { - smg_all_ckt_busy(); - log_printf(SMG_LOG_ALL, server.log, "WARNING: All ckt busy Timeout=%i!\n",server.all_ckt_busy); + smg_all_ckt_busy(woomera->trunk_group); + log_printf(SMG_LOG_ALL, server.log, "WARNING: All ckt busy Timeout=%i on tg=%i!\n", + server.all_ckt_busy[woomera->trunk_group], woomera->trunk_group); } if (event->release_cause == SIGBOOST_CALL_SETUP_CSUPID_DBL_USE) { - log_printf(SMG_LOG_ALL, server.log, "WARNING: Double use on [w%ig%i] setup id %i!\n", + log_printf(SMG_LOG_ALL, server.log, "WARNING: Double use on [s%ic%i] setup id %i!\n", event->span+1,event->chan+1,event->call_setup_id); } @@ -3715,7 +4292,7 @@ static void handle_call_start_nack(short_signal_event_t *event) #if 0 if (event->release_cause == SIGBOOST_CALL_SETUP_NACK_AUTO_CALL_GAP) { log_printf(SMG_LOG_ALL, server.log, "WARNING: Call Gapping Detected!\n"); - smg_all_ckt_gap(); + smg_all_ckt_gap(woomera->trunk_group); } #endif @@ -3734,7 +4311,7 @@ static void handle_call_start_nack(short_signal_event_t *event) 0); if (!woomera) { - log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event (NACK ACK) %d referrs to a non-existant session (%d) [w%dg%d]!\n", + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event (NACK ACK) %d referrs to a non-existant session (%d) [s%dc%d]!\n", event->event_id,event->call_setup_id, event->span+1, event->chan+1); } } @@ -3757,7 +4334,7 @@ static void handle_call_loop_start(short_signal_event_t *event) log_printf(SMG_LOG_PROD, server.log, - "Sent (From Handle Loop START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [w%dg] ptr=%d\n", + "Sent (From Handle Loop START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [s%dc%d] ptr=%d\n", event->span+1, event->chan+1, server.process_table[event->span][event->chan].dev); pthread_mutex_unlock(&server.process_lock); @@ -3765,7 +4342,7 @@ static void handle_call_loop_start(short_signal_event_t *event) } - sprintf(callid, "w%dg%d", event->span+1,event->chan+1); + sprintf(callid, "s%dc%d", event->span+1,event->chan+1); sprintf(server.process_table[event->span][event->chan].session, "%s-%i-%i",callid,rand(),rand()); session=server.process_table[event->span][event->chan].session; @@ -3781,7 +4358,7 @@ static void handle_call_loop_start(short_signal_event_t *event) SIGBOOST_EVENT_CALL_START_NACK, 17); log_printf(SMG_LOG_PROD, server.log, - "Sent (From Handle Loop START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [w%dg] ptr=%d\n", + "Sent (From Handle Loop START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [s%dc%d] ptr=%d\n", event->span+1, event->chan+1, server.process_table[event->span][event->chan].dev); } @@ -3796,24 +4373,34 @@ static void handle_call_start(call_signal_event_t *event) struct woomera_event wevent; char callid[20]; char *session; + struct woomera_interface *tmp_woomera=NULL; int clients; - remove_end_of_digits_char((unsigned char*)event->called_number_digits); - remove_end_of_digits_char((unsigned char*)event->calling_number_digits); + remove_end_of_digits_char((unsigned char*)event->called.digits); + remove_end_of_digits_char((unsigned char*)event->calling.digits); + remove_end_of_digits_char((unsigned char*)event->rdnis.digits); + remove_end_of_digits_char((unsigned char*)event->custom_data); if (server.strip_cid_non_digits) { - validate_number((unsigned char*)event->called_number_digits); - validate_number((unsigned char*)event->calling_number_digits); + validate_number((unsigned char*)event->called.digits); + validate_number((unsigned char*)event->calling.digits); + validate_number((unsigned char*)event->rdnis.digits); + } + + if (smg_validate_span_chan(event->span,event->chan)) { + log_printf(0,server.log, + "Error: invalid span=% chan=%i on incoming call [s%dc%d] - Call START!\n", + event->span+1, event->chan+1, event->span+1,event->chan+1); + return; } pthread_mutex_lock(&server.process_lock); - if (server.process_table[event->span][event->chan].dev) { + if ((tmp_woomera=server.process_table[event->span][event->chan].dev)) { - struct woomera_interface *tmp_woomera = server.process_table[event->span][event->chan].dev; woomera_set_flag(tmp_woomera,WFLAG_HANGUP); woomera_set_flag(tmp_woomera,WFLAG_MEDIA_END); - log_printf(SMG_LOG_ALL,server.log,"Call Overrun on [w%dg%d] - Call START!\n", event->span+1, event->chan+1); + log_printf(SMG_LOG_ALL,server.log,"Call Overrun on [s%dc%d] - Call START!\n", event->span+1, event->chan+1); isup_exec_command(event->span, event->chan, @@ -3822,14 +4409,14 @@ static void handle_call_start(call_signal_event_t *event) 17); log_printf(SMG_LOG_ALL, server.log, - "Sent (From Handle START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [w%dg%d]\n", + "Sent (From Handle START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [s%dc%d]\n", event->span+1, event->chan+1); pthread_mutex_unlock(&server.process_lock); return; } - sprintf(callid, "w%dg%d", event->span+1,event->chan+1); + sprintf(callid, "s%dc%d", event->span+1,event->chan+1); sprintf(server.process_table[event->span][event->chan].session, "%s-%i-%i",callid,rand(),rand()); session=server.process_table[event->span][event->chan].session; @@ -3838,6 +4425,8 @@ static void handle_call_start(call_signal_event_t *event) server.process_table[event->span][event->chan].digits_len = 0; server.process_table[event->span][event->chan].bearer_cap = event->bearer.capability; server.process_table[event->span][event->chan].clients=0; + memset(&server.process_table[event->span][event->chan].rbs_relay,0, + sizeof(server.process_table[event->span][event->chan].rbs_relay)); pthread_mutex_unlock(&server.process_lock); if (autoacm) { @@ -3860,7 +4449,7 @@ static void handle_call_start(call_signal_event_t *event) } } - new_woomera_event_printf(&wevent, "EVENT INCOMING w%dg%d%s" + new_woomera_event_printf(&wevent, "EVENT INCOMING s%dc%d%s" "Unique-Call-Id: %s%s" "Remote-Number: %s%s" "Remote-Name: %s%s" @@ -3882,19 +4471,20 @@ static void handle_call_start(call_signal_event_t *event) "RDNIS: %s%s" "Bearer-Cap: %s%s" "uil1p: %s%s" + "xCustom: %s%s" , event->span+1, event->chan+1, WOOMERA_LINE_SEPERATOR, session, WOOMERA_LINE_SEPERATOR, - event->calling_number_digits, + event->calling.digits, WOOMERA_LINE_SEPERATOR, event->calling_name, WOOMERA_LINE_SEPERATOR, WOOMERA_LINE_SEPERATOR, WOOMERA_LINE_SEPERATOR, - event->called_number_digits, + event->called.digits, WOOMERA_LINE_SEPERATOR, event->trunk_group+1, (event->span*max_chans)+event->chan+1, @@ -3905,11 +4495,13 @@ static void handle_call_start(call_signal_event_t *event) WOOMERA_LINE_SEPERATOR, event->calling_number_screening_ind, WOOMERA_LINE_SEPERATOR, - event->isup_in_rdnis, + event->rdnis.digits, WOOMERA_LINE_SEPERATOR, bearer_cap_to_str(event->bearer.capability), WOOMERA_LINE_SEPERATOR, - uil1p_to_str(event->bearer.uil1p), + uil1p_to_str(event->bearer.uil1p), + WOOMERA_LINE_SEPERATOR, + event->custom_data, WOOMERA_RECORD_SEPERATOR ); @@ -3928,7 +4520,7 @@ static void handle_call_start(call_signal_event_t *event) SIGBOOST_EVENT_CALL_STOPPED, 17); log_printf(SMG_LOG_ALL, server.log, - "CALL INCOMING: Enqueue Error Sent SIGBOOST_EVENT_CALL_STOPPED [w%dg%d]\n", + "CALL INCOMING: Enqueue Error Sent SIGBOOST_EVENT_CALL_STOPPED [s%dc%d]\n", event->span+1, event->chan+1); } else { @@ -3938,13 +4530,15 @@ static void handle_call_start(call_signal_event_t *event) SIGBOOST_EVENT_CALL_START_NACK, 17); log_printf(SMG_LOG_ALL, server.log, - "CALL INCOMING: Enqueue Error Sent SIGBOOST_EVENT_CALL_START_NACK [w%dg%d]\n", + "CALL INCOMING: Enqueue Error Sent SIGBOOST_EVENT_CALL_START_NACK [s%dc%d]\n", event->span+1, event->chan+1); } } else { - server.process_table[event->span][event->chan].clients=clients; + //pthread_mutex_lock(&server.process_lock); + server.process_table[event->span][event->chan].clients = clients; + //pthread_mutex_unlock(&server.process_lock); } destroy_woomera_event_data(&wevent); @@ -3957,20 +4551,20 @@ static void handle_incoming_digit(call_signal_event_t *event) int digits_len; if (smg_validate_span_chan(event->span,event->chan)) { - log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event DTMF on invalid span chan [w%dg%d] !\n", + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event DTMF on invalid span chan [s%dc%d] !\n", event->span+1, event->chan+1); return; } if (!event->called_number_digits_count) { - log_printf(SMG_LOG_ALL, server.log, "Error Incoming digit with len %s %d [w%dg%d]\n", + log_printf(SMG_LOG_ALL, server.log, "Error Incoming digit with len %s %d [s%dc%d]\n", event->called_number_digits, event->called_number_digits_count, event->span+1, event->chan+1); } - log_printf(SMG_LOG_DEBUG_9, server.log, "Queuing incoming digits %s [w%dg%d]\n", + log_printf(SMG_LOG_DEBUG_9, server.log, "Queuing incoming digits %s [s%dc%d]\n", event->called_number_digits, event->span+1, event->chan+1); @@ -3999,7 +4593,7 @@ static void handle_gap_abate(short_signal_event_t *event) { log_printf(SMG_LOG_ALL, server.log, "NOTICE: GAP Cleared!\n", event->span+1, event->chan+1); - smg_clear_ckt_gap(); + smg_clear_ckt_gap(event->trunk_group+1); } static void handle_restart(call_signal_connection_t *mcon, short_signal_event_t *event) @@ -4013,7 +4607,10 @@ static void handle_restart(call_signal_connection_t *mcon, short_signal_event_t log_printf(SMG_LOG_ALL, server.log, "RESTART Received: resetting all threads\n"); - smg_all_ckt_busy(); + int i=0; + for ( i =0 ; i < SMG_MAX_TG ; i++ ){ + smg_all_ckt_busy(i); + } woomera_set_flag(&server.master_connection, WFLAG_SYSTEM_RESET); gettimeofday(&server.restart_timeout,NULL); @@ -4041,7 +4638,7 @@ static void handle_restart(call_signal_connection_t *mcon, short_signal_event_t static void handle_restart_ack(call_signal_connection_t *mcon, short_signal_event_t *event) { - + woomera_clear_flag(&server.master_connection, WFLAG_SYSTEM_NEED_RESET_ACK); /* Do not reset WFLAG_SYSTEM_RESET flag here!. The flag should only be reset on restart from boost */ //woomera_clear_flag(&server.master_connection, WFLAG_SYSTEM_RESET); @@ -4067,7 +4664,7 @@ static void handle_remove_loop(short_signal_event_t *event) /* We have to close the socket because At this point we are release span chan */ - log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event REMOVE LOOP on w%dg%d ptr=%p ms=%p\n", + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event REMOVE LOOP on s%dc%d ptr=%p ms=%p\n", woomera->span+1,woomera->chan+1,woomera,woomera->ms); } @@ -4110,7 +4707,7 @@ static void handle_call_stop(short_signal_event_t *event) /* We have to close the socket because At this point we are release span chan */ - log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event CALL STOP on w%dg%d ptr=%p ms=%p\n", + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event CALL STOP on s%dc%d ptr=%p ms=%p\n", woomera->span+1,woomera->chan+1,woomera,woomera->ms); } else { @@ -4129,7 +4726,7 @@ static void handle_call_stop(short_signal_event_t *event) if (!woomera){ /* This is allowed on incoming call if remote app does not answer it */ - log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event CALL STOP referrs to a non-existant session [w%dg%d]!\n", + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event CALL STOP referrs to a non-existant session [s%dc%d]!\n", event->span+1, event->chan+1); } } @@ -4155,6 +4752,12 @@ static void handle_call_stop_ack(short_signal_event_t *event) struct woomera_interface *woomera = NULL; + + if (smg_validate_span_chan(event->span,event->chan)) { + log_printf(0,server.log, "Error: invalid span=% chan=%i on call stopped ack [s%dc%d]\n", + event->span+1, event->chan+1, event->span+1,event->chan+1); + return; + } pthread_mutex_lock(&server.process_lock); woomera = server.process_table[event->span][event->chan].dev; server.process_table[event->span][event->chan].dev=NULL; @@ -4163,14 +4766,14 @@ static void handle_call_stop_ack(short_signal_event_t *event) if (woomera) { - log_printf(SMG_LOG_DEBUG_CALL, server.log, "Stop Ack on [w%dg%d] [Setup ID: %d] [%s]!\n", + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Stop Ack on [s%dc%d] [Setup ID: %d] [%s]!\n", event->span+1, event->chan+1, event->call_setup_id, woomera->interface); woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); } else { - log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event CALL_STOP_ACK(%d) referrs to a non-existant session [w%dg%d] [Setup ID: %d]!\n", + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event CALL_STOP_ACK(%d) referrs to a non-existant session [s%dc%d] [Setup ID: %d]!\n", event->event_id, event->span+1, event->chan+1, event->call_setup_id); } @@ -4209,13 +4812,13 @@ static void handle_call_start_nack_ack(short_signal_event_t *event) * woomera client being down, in this case no * woomera thread is created */ log_printf(SMG_LOG_DEBUG_8, server.log, - "Event NACK ACK [w%dg%d] with valid span/chan no dev!\n", + "Event NACK ACK [s%dc%d] with valid span/chan no dev!\n", event->span+1, event->chan+1); } } else { log_printf(SMG_LOG_DEBUG_CALL, server.log, - "Event NACK ACK referrs to a non-existant session [w%dg%d] [Setup ID: %d]!\n", + "Event NACK ACK referrs to a non-existant session [s%dc%d] [Setup ID: %d]!\n", event->span+1, event->chan+1, event->call_setup_id); } @@ -4230,7 +4833,7 @@ static void handle_call_start_nack_ack(short_signal_event_t *event) static int parse_ss7_event(call_signal_connection_t *mcon, short_signal_event_t *event) { - int ret = 0; + int ret = 0; switch(event->event_id) { @@ -4262,7 +4865,7 @@ static int parse_ss7_event(call_signal_connection_t *mcon, short_signal_event_t handle_call_loop_start(event); break; case SIGBOOST_EVENT_SYSTEM_RESTART: - handle_restart(mcon,event); + handle_restart(mcon,event); break; case SIGBOOST_EVENT_REMOVE_CHECK_LOOP: handle_remove_loop(event); @@ -4276,6 +4879,9 @@ static int parse_ss7_event(call_signal_connection_t *mcon, short_signal_event_t case SIGBOOST_EVENT_DIGIT_IN: handle_incoming_digit((call_signal_event_t*)event); break; + case SIGBOOST_EVENT_CALL_PROGRESS: + /* We always assume early media, so there is no need to do anything on progress */ + break; default: log_printf(SMG_LOG_ALL, server.log, "Warning no handler implemented for [%s val:%d]\n", call_signal_event_id_name(event->event_id), event->event_id); @@ -4294,9 +4900,9 @@ static void *monitor_thread_run(void *obj) call_signal_connection_t *mcon=&server.mcon; call_signal_connection_t *mconp=&server.mconp; - pthread_mutex_lock(&server.thread_count_lock); + pthread_mutex_lock(&server.thread_count_lock); server.thread_count++; - pthread_mutex_unlock(&server.thread_count_lock); + pthread_mutex_unlock(&server.thread_count_lock); woomera_set_flag(&server.master_connection, WFLAG_MONITOR_RUNNING); @@ -4329,6 +4935,7 @@ static void *monitor_thread_run(void *obj) SIGBOOST_EVENT_SYSTEM_RESTART, 0); + woomera_set_flag(&server.master_connection, WFLAG_SYSTEM_NEED_RESET_ACK); woomera_set_flag(&server.master_connection, WFLAG_SYSTEM_RESET); smg_get_current_priority(&policy,&priority); @@ -4396,11 +5003,12 @@ mcon_retry_priority: break; } - if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)) { + if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) && + !woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_NEED_RESET_ACK)) { short_signal_event_t event; struct timeval current; int elapsed; - gettimeofday(¤t,NULL); + gettimeofday(¤t,NULL); elapsed=smg_calc_elapsed(&server.restart_timeout, ¤t); if (elapsed > 5000) { @@ -4415,7 +5023,10 @@ mcon_retry_priority: "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", strerror(errno)); } - smg_all_ckt_busy(); + int i=0; + for ( i =0 ; i < SMG_MAX_TG ; i++ ){ + smg_all_ckt_busy(i); + } woomera_clear_flag(&server.master_connection, WFLAG_SYSTEM_RESET); } } @@ -4535,13 +5146,19 @@ static void *woomera_thread_run(void *obj) "Version: %s%s" "Remote-Address: %s%s" "Remote-Port: %d%s" - "Raw-Format: %s%s", + "Raw-Format: %s%s" + "xUDP-Seq: %s%s" + "xUDP-Seq-Err: %d%s" + "xNative-Bridge: %s%s", WOOMERA_LINE_SEPERATOR, WOOMERA_LINE_SEPERATOR, SMG_VERSION, WOOMERA_LINE_SEPERATOR, inet_ntoa(woomera->addr.sin_addr), WOOMERA_LINE_SEPERATOR, ntohs(woomera->addr.sin_port), WOOMERA_LINE_SEPERATOR, - server.hw_coding?"ALAW":"ULAW", WOOMERA_RECORD_SEPERATOR + server.hw_coding?"ALAW":"ULAW", WOOMERA_LINE_SEPERATOR, + server.udp_seq?"Enabled":"Disabled", WOOMERA_LINE_SEPERATOR, + server.media_rx_seq_err, WOOMERA_LINE_SEPERATOR, + server.media_pass_through?"Enabled":"Disabled", WOOMERA_RECORD_SEPERATOR ); if (err) { @@ -4655,7 +5272,7 @@ woomera_session_close: woomera_set_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK_SENT); - log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Woomera Sent SIGBOOST_EVENT_CALL_STOPPED [w%dg%d] [%s] ptr=%p\n", + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Woomera Sent SIGBOOST_EVENT_CALL_STOPPED [s%dc%d] [%s] ptr=%p\n", span+1, chan+1,woomera->interface,woomera); } else { @@ -4667,11 +5284,11 @@ woomera_session_close: woomera->q931_rel_cause_tosig); woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT); - log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Woomera Sent SIGBOOST_EVENT_CALL_START_NACK [w%dg%d] [%s] ptr=%p\n", + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Woomera Sent SIGBOOST_EVENT_CALL_START_NACK [s%dc%d] [%s] ptr=%p\n", span+1, chan+1,woomera->interface,woomera); } } else { - log_printf(SMG_LOG_ALL, woomera->log, "Woomera Not Sent CALL STOPPED - Instead NACK [w%dg%d] [%s] ptr=%p\n", + log_printf(SMG_LOG_ALL, woomera->log, "Woomera Not Sent CALL STOPPED - Instead NACK [s%dc%d] [%s] ptr=%p\n", span+1, chan+1,woomera->interface,woomera); } @@ -4681,7 +5298,7 @@ woomera_session_close: failure */ if (!woomera->index) { /* In this case we really failed to tx STOP */ - log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "FAILED: Woomera (R) SIGBOOST_EVENT_CALL_STOPPED [w%dg%d] [%s] Index=%d ptr=%p\n", + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "FAILED: Woomera (R) SIGBOOST_EVENT_CALL_STOPPED [s%dc%d] [%s] Index=%d ptr=%p\n", span+1, chan+1, woomera->interface, woomera->index, woomera); } } @@ -4709,14 +5326,14 @@ woo_re_hangup: woomera->q931_rel_cause_tosig); log_printf(SMG_LOG_DEBUG_MISC, woomera->log, - "Sent (Ack) to SIGBOOST_EVENT_CALL_STOPPED_ACK [w%dg%d] [%s] ptr=%p\n", + "Sent (Ack) to SIGBOOST_EVENT_CALL_STOPPED_ACK [s%dc%d] [%s] ptr=%p\n", span+1,chan+1,woomera->interface,woomera); }else{ /* This should never happen! If it does we broke protocol */ log_printf(SMG_LOG_ALL, woomera->log, - "FAILED: Woomera (R) SIGBOOST_EVENT_CALL_STOPPED_ACK [w%dg%d] [%s] Index=%d ptr=%p\n", + "FAILED: Woomera (R) SIGBOOST_EVENT_CALL_STOPPED_ACK [s%dc%d] [%s] Index=%d ptr=%p\n", span+1, chan+1, woomera->interface, woomera->index, woomera); } @@ -4740,7 +5357,7 @@ woo_re_hangup: woomera->q931_rel_cause_tosig); log_printf(SMG_LOG_DEBUG_MISC, woomera->log, - "Sent (Nack Ack) to SIGBOOST_EVENT_CALL_START_NACK_ACK [w%dg%d] [%s] ptr=%p\n", + "Sent (Nack Ack) to SIGBOOST_EVENT_CALL_START_NACK_ACK [s%dc%d] [%s] ptr=%p\n", span+1,chan+1,woomera->interface,woomera); woomera->index=0; @@ -4756,7 +5373,7 @@ woo_re_hangup: } else { log_printf(SMG_LOG_ALL, woomera->log, - "FAILED: Sent (Nack Ack) SIGBOOST_EVENT_CALL_START_NACK_ACK [w%dg%d] [%s] Index=%d ptr=%p\n", + "FAILED: Sent (Nack Ack) SIGBOOST_EVENT_CALL_START_NACK_ACK [s%dc%d] [%s] Index=%d ptr=%p\n", span+1, chan+1, woomera->interface, woomera->index, woomera); } @@ -4982,7 +5599,7 @@ woo_re_hangup: if (server.process_table[span][chan]){ log_printf(SMG_LOG_ALL, server.log, - "Sanity Span Chan Still in use: [w%dg%d] [%s] Index=%d ptr=%p\n", + "Sanity Span Chan Still in use: [s%dc%d] [%s] Index=%d ptr=%p\n", span+1, chan+1, woomera->interface, woomera->index, woomera); //server.process_table[span][chan] = NULL; } @@ -5184,7 +5801,9 @@ static int configure_server(void) struct woomera_config cfg; char *var, *val; int cnt = 0; + struct smg_tdm_ip_bridge *ip_bridge=NULL; + server.dtmf_intr_ch = -1; if (!woomera_open_file(&cfg, server.config_file)) { @@ -5291,19 +5910,93 @@ static int configure_server(void) server.base_media_port = base; server.next_media_port = base; server.max_media_port = server.base_media_port + WOOMERA_MAX_MEDIA_PORTS; - log_printf(SMG_LOG_ALL,server.log, "Server - Base Media Port: %d\n",server.base_media_port); + log_printf(SMG_LOG_ALL,server.log, "Server - Base Media Port: %d\n",server.base_media_port); } } else if (!strcasecmp(var, "max_media_ports")) { int max = atoi(val); - if (max > WOOMERA_MAX_MEDIA_PORTS) { + if (max >= 0) { server.max_media_port = server.base_media_port+max; - log_printf(SMG_LOG_ALL,server.log, "Server - Max Media Port: %d\n",server.max_media_port); + log_printf(SMG_LOG_ALL,server.log, "Server - Max Media Port: %d\n",server.max_media_port); } } else if (!strcasecmp(var, "media_ip")) { strncpy(server.media_ip, val, sizeof(server.media_ip) -1); log_printf(SMG_LOG_ALL,server.log, "Server - Media IP: %s\n",server.media_ip); + + } else if (!strcasecmp(var, "udp_seq")) { + int max = atoi(val); + if (max > 0) { + server.udp_seq=max; + log_printf(SMG_LOG_ALL, server.log, "Server - UDP Seq: %s\n",server.udp_seq?"Enabled":"Disabled"); + } + + } else if (!strcasecmp(var, "media_pass_through")) { + int max = atoi(val); + if (max > 0) { + server.media_pass_through=max; + log_printf(SMG_LOG_ALL, server.log, "Server - Media Pass Through: %s\n",server.media_pass_through?"Enabled":"Disabled"); + } + + + } else if (!strcasecmp(var, "rbs_relay")) { + int max = atoi(val); + if (max > 0) { + server.rbs_relay[max]=max; + log_printf(SMG_LOG_ALL, server.log, "Server - RBS Relay Span: %d\n",max); + } + + } else if (!strcasecmp(var, "bridge_tdm_ip")) { + int err=smg_get_ip_bridge_session(&ip_bridge); + if (err) { + log_printf(SMG_LOG_ALL, server.log, "Error failed to get free ip bridge %i!\n",err); + } else { + log_printf(SMG_LOG_ALL,server.log, "\n======================= \n"); + log_printf(SMG_LOG_ALL,server.log, "Bridge - Configuration \n"); + log_printf(SMG_LOG_ALL,server.log, "======================= \n"); + } + + } else if (!strcasecmp(var, "bridge_span")) { + int max = atoi(val); + if (max > 0 && max <= 32 && ip_bridge) { + ip_bridge->span=max; + log_printf(SMG_LOG_ALL, server.log, "Bridge Span: %i\n",ip_bridge->span); + } else { + log_printf(SMG_LOG_ALL, server.log, "Bridge Span: ERROR: Invalid Value %s\n",val); + } + } else if (!strcasecmp(var, "bridge_chan")) { + int max = atoi(val); + if (max > 0 && max < MAX_SMG_BRIDGE && ip_bridge) { + ip_bridge->chan=max; + log_printf(SMG_LOG_ALL, server.log, "Bridge Chan: %i\n",ip_bridge->chan); + } else { + log_printf(SMG_LOG_ALL, server.log, "Bridge Chan: ERROR: Invalid Value %s\n",val); + } + } else if (!strcasecmp(var, "bridge_local_ip")) { + if (ip_bridge) { + strncpy(ip_bridge->mcon.cfg.local_ip, val, + sizeof(ip_bridge->mcon.cfg.local_ip) -1); + log_printf(SMG_LOG_ALL, server.log, "Bridge Local IP: %s\n",ip_bridge->mcon.cfg.local_ip); + } + } else if (!strcasecmp(var, "bridge_remote_ip")) { + if (ip_bridge) { + strncpy(ip_bridge->mcon.cfg.remote_ip, val, + sizeof(ip_bridge->mcon.cfg.remote_ip) -1); + log_printf(SMG_LOG_ALL, server.log, "Bridge Remote IP: %s\n",ip_bridge->mcon.cfg.remote_ip); + } + } else if (!strcasecmp(var, "bridge_port")) { + int max = atoi(val); + if (max > 0 && ip_bridge) { + ip_bridge->mcon.cfg.local_port=max; + ip_bridge->mcon.cfg.remote_port=max; + log_printf(SMG_LOG_ALL, server.log, "Bridge Port: %i\n",max); + } + } else if (!strcasecmp(var, "bridge_period")) { + int max = atoi(val); + if (max > 0 && ip_bridge) { + ip_bridge->period=max; + log_printf(SMG_LOG_ALL, server.log, "Bridge Period: %ims\n",ip_bridge->period); + } } else { log_printf(SMG_LOG_ALL, server.log, "Invalid Option %s at line %d!\n", var, cfg.lineno); } @@ -5506,10 +6199,10 @@ static int sangoma_tdm_init (int span) return 0; } - static int woomera_startup(int argc, char **argv) { int x = 0, pid = 0, bg = 0; + int span_cnt, chan_cnt; char *cfg=NULL, *debug=NULL, *arg=NULL; while((arg = argv[x++])) { @@ -5590,6 +6283,12 @@ static int woomera_startup(int argc, char **argv) return 0; } + for (span_cnt = 0; span_cnt < CORE_MAX_SPANS; span_cnt++) { + for (chan_cnt = 0; chan_cnt < CORE_MAX_CHAN_PER_SPAN; chan_cnt++) { + pthread_mutex_init(&server.process_table[span_cnt][chan_cnt].media_lock, NULL); + } + } + q931_cause_setup(); bearer_cap_setup(); uil1p_to_str_setup(); @@ -5675,10 +6374,11 @@ static int woomera_startup(int argc, char **argv) usleep(5000); woomera_set_flag(&server.master_connection, WFLAG_RUNNING); + if (launch_monitor_thread()) { woomera_clear_flag(&server.master_connection, WFLAG_RUNNING); - smg_log_cleanup(); - return 0; + smg_log_cleanup(); + return 0; } fprintf(stderr, "%s", WELCOME_TEXT); @@ -5692,6 +6392,13 @@ static int woomera_shutdown(void) { char *event_string; int told = 0, loops = 0; + int span_cnt, chan_cnt; + + for (span_cnt = 0; span_cnt < CORE_MAX_SPANS; span_cnt++) { + for (chan_cnt = 0; chan_cnt < CORE_MAX_CHAN_PER_SPAN; chan_cnt++) { + pthread_mutex_destroy(&server.process_table[span_cnt][chan_cnt].media_lock); + } + } close_socket(&server.master_connection.socket); pthread_mutex_destroy(&server.listen_lock); @@ -5753,7 +6460,8 @@ int main(int argc, char *argv[]) mlockall(MCL_FUTURE); memset(&server, 0, sizeof(server)); memset(&woomera_dead_dev, 0, sizeof(woomera_dead_dev)); - + memset(&g_smg_ip_bridge_idx,0, sizeof(g_smg_ip_bridge_idx)); + ret=nice(-5); sdla_memdbg_init(); @@ -5763,12 +6471,17 @@ int main(int argc, char *argv[]) openlog (ps_progname ,LOG_PID, LOG_LOCAL2); if (! (ret = woomera_startup(argc, argv))) { - exit(0); + exit(0); } - ret = main_thread(); + + ret = smg_ip_bridge_start(); + if (ret == 0) { + ret = main_thread(); + } woomera_shutdown(); + smg_ip_bridge_stop(); sdla_memdbg_free(1); return ret; diff --git a/ssmg/sangoma_mgd.trunk/.svn/text-base/sangoma_mgd.conf.sample.svn-base b/ssmg/sangoma_mgd.trunk/.svn/text-base/sangoma_mgd.conf.sample.svn-base index 625cf63..6b95992 100644 --- a/ssmg/sangoma_mgd.trunk/.svn/text-base/sangoma_mgd.conf.sample.svn-base +++ b/ssmg/sangoma_mgd.trunk/.svn/text-base/sangoma_mgd.conf.sample.svn-base @@ -32,8 +32,8 @@ woomera_port => 42420 # If changing from default values # ensure that woomera client # UDP ports do not conflict. -base_media_port => 9000 -max_media_ports => 899 +base_media_port => 10000 +max_media_ports => 5000 #Used to play Sangoma Rocks on all @@ -83,8 +83,8 @@ strip_cid_non_digits => 0 #This must be enabled on chan_woomera #side as well in order to work. #0-Disable (Default) -#1-Eanble -woomera_udp_seq => 0 +#1-Enable +udp_seq => 0 #Timeout an outboud call, if call setup is @@ -94,3 +94,41 @@ woomera_udp_seq => 0 call_timeout=100 +#Enable woomera sever +#native bridging. On woomer to woomera +#calls between two SMG gateways +#Asterisk chan_woomera will indicate +#to SMG servers to pass media directly +#and not pass it through asterisk +#0-Disable +#1-Enable (Default) +media_pass_through => 1 + +#Enable tdm bridge profile 1 +#Note profile number must not be zero. +#Configure all parameters for the bridge. +#The opposite sangoma_mgd must be configured +#with same parameters except IP address +#need to be swaped. +#bridge_tdm_ip => 1 +#bridge_span => 1 +#bridge_chan => 1 +#bridge_local_ip => 192.168.1.251 +#bridge_remote_ip => 192.168.1.252 +#bridge_port => 60000 +#bridge_period => 10 #milliseconds + + +#Enable tdm bridge profile 2 +#Note profile number must not be zero. +#Configure all parameters for the bridge. +#The opposite sangoma_mgd must be configured +#with same parameters except IP address +#need to be swaped. +#bridge_tdm_ip => 2 +#bridge_span => 1 +#bridge_chan => 2 +#bridge_local_ip => 192.168.1.251 +#bridge_remote_ip => 192.168.1.252 +#bridge_port => 60001 +#bridge_period => 10 #milliseconds diff --git a/ssmg/sangoma_mgd.trunk/.svn/text-base/sangoma_mgd.h.svn-base b/ssmg/sangoma_mgd.trunk/.svn/text-base/sangoma_mgd.h.svn-base index f2cbd15..cd4cc4c 100644 --- a/ssmg/sangoma_mgd.trunk/.svn/text-base/sangoma_mgd.h.svn-base +++ b/ssmg/sangoma_mgd.trunk/.svn/text-base/sangoma_mgd.h.svn-base @@ -56,19 +56,21 @@ #define WOOMERA_MAX_CHAN 31 +#define SMG_MAX_TG 32 + #define SMG_SESSION_NAME_SZ 100 #define SMG_CHAN_NAME_SZ 20 #define PIDFILE "/var/run/sangoma_mgd.pid" #define PIDFILE_UNIT "/var/run/sangoma_mgd_unit.pid" -#define WOOMERA_MAX_MEDIA_PORTS 899 +#define WOOMERA_MAX_MEDIA_PORTS 5000 #define CORE_EVENT_LEN 512 #define WOOMERA_STRLEN 256 #define WOOMERA_ARRAY_LEN 50 #define WOOMERA_BODYLEN 2048 -#define WOOMERA_MIN_MEDIA_PORT 9000 +#define WOOMERA_MIN_MEDIA_PORT 10000 #define WOOMERA_MAX_MEDIA_PORT (WOOMERA_MIN_MEDIA_PORT + WOOMERA_MAX_MEDIA_PORTS) #define WOOMERA_HARD_TIMEOUT 0 #define WOOMERA_LINE_SEPERATOR "\r\n" @@ -106,7 +108,8 @@ typedef enum { WFLAG_WAIT_FOR_NACK_ACK_SENT = (1 << 17), /* Call START NACK was sent out on this channel */ WFLAG_WAIT_FOR_STOPPED_ACK_SENT = (1 << 18), /* Call STOP was sent out on this channel */ WFLAG_SYSTEM_RESET = (1 << 19), /* Initial System Reset Condition no calls allowed */ - WFLAG_WAIT_FOR_ACK_TIMEOUT = (1 << 20), /* Timeout flag indicating that incoming ACK or NACK timedout */ + WFLAG_SYSTEM_NEED_RESET_ACK = (1 << 20), /* We sent a RESTART */ + WFLAG_WAIT_FOR_ACK_TIMEOUT = (1 << 21), /* Timeout flag indicating that incoming ACK or NACK timedout */ } WFLAGS; enum { @@ -203,6 +206,7 @@ struct media_session { int sangoma_sock; char *ip; int port; + char *raw; time_t started; time_t answered; pthread_t thread; @@ -216,6 +220,8 @@ struct media_session { int skip_write_frames; int hw_coding; int hw_dtmf; + int has_hwec; + int udp_sync_cnt; #ifdef WP_HPTDM_API @@ -225,7 +231,7 @@ struct media_session { teletone_dtmf_detect_state_t dtmf_detect; teletone_generation_session_t tone_session; switch_buffer_t *dtmf_buffer; - + unsigned char oob_disable; }; struct woomera_message { @@ -283,23 +289,69 @@ struct woomera_interface { char session[SMG_SESSION_NAME_SZ]; int check_digits; /* set to 1 when session comes up */ int bearer_cap; - struct woomera_interface *next; + unsigned int rx_udp_seq; + unsigned int tx_udp_seq; + struct woomera_interface *next; }; +#define WOOMERA_MAX_RBS_BITS 4 + +typedef struct woomera_rbs_bits +{ + int init; + unsigned char abcd; +}woomera_rbs_bits_t; + +typedef struct woomera_rbs_relay +{ + int init; + woomera_rbs_bits_t rbs_bits[WOOMERA_MAX_RBS_BITS]; + int rx_idx; + int tx_idx; +} woomera_rbs_relay_t; + struct woomera_session { - struct woomera_interface *dev; + struct woomera_interface *dev; char session[SMG_SESSION_NAME_SZ]; char digits[MAX_DIALED_DIGITS+1]; int digits_len; int bearer_cap; int clients; + unsigned char media_used; + pthread_mutex_t media_lock; + woomera_rbs_relay_t rbs_relay; + int sangoma_fd; + int sangoma_fd_usage; }; +struct smg_tdm_ip_bridge { + int init; + int end; + int span; + int chan; +#if 0 + int port; + char local_ip[25]; + char remote_ip[25]; +#endif + int period; + int tdm_fd; + call_signal_connection_t mcon; + pthread_t thread; +}; + +extern struct smg_tdm_ip_bridge g_smg_ip_bridge_idx[]; +extern pthread_mutex_t g_smg_ip_bridge_lock; + + + + +#define MAX_SMG_RBS_RELAY 32 +#define MAX_SMG_BRIDGE 32 #define CORE_TANK_LEN CORE_MAX_CHAN_PER_SPAN*CORE_MAX_SPANS struct woomera_server { -// struct woomera_session process_table[CORE_MAX_CHAN_PER_SPAN][CORE_MAX_SPANS]; - struct woomera_session process_table[CORE_MAX_SPANS][CORE_MAX_CHAN_PER_SPAN]; + struct woomera_session process_table[CORE_MAX_SPANS][CORE_MAX_CHAN_PER_SPAN+1]; struct woomera_interface *holding_tank[CORE_TANK_LEN]; int holding_tank_index; struct woomera_interface master_connection; @@ -336,16 +388,21 @@ struct woomera_server { uint32_t hw_coding; uint32_t loop_trace; uint32_t hungup_waiting; - int all_ckt_gap; - int all_ckt_busy; - struct timeval all_ckt_busy_time; + int all_ckt_gap[SMG_MAX_TG+1]; + int all_ckt_busy[SMG_MAX_TG+1]; + struct timeval all_ckt_busy_time[SMG_MAX_TG+1]; struct timeval restart_timeout; int dtmf_on; - int dtmf_off; - int dtmf_intr_ch; - int dtmf_size; + int dtmf_off; + int dtmf_intr_ch; + int dtmf_size; int strip_cid_non_digits; int call_timeout; + struct smg_tdm_ip_bridge ip_bridge_idx[MAX_SMG_BRIDGE]; + int udp_seq; + unsigned int media_rx_seq_err; + unsigned char rbs_relay[MAX_SMG_RBS_RELAY]; + unsigned char media_pass_through; }; extern struct woomera_server server; @@ -358,6 +415,28 @@ struct woomera_config { int lineno; }; +static inline int smg_get_ip_bridge_session(struct smg_tdm_ip_bridge **ip_bridge) +{ + int i; + for (i=0;itv_sec * 1000) + started->tv_usec / 1000)); } -static inline int smg_check_all_busy(void) +static inline int smg_check_all_busy(int tg) { struct timeval ended; int elapsed; - if (server.all_ckt_gap) { - return server.all_ckt_gap; + if (server.all_ckt_gap[tg]) { + return server.all_ckt_gap[tg]; } - if (server.all_ckt_busy==0) { + if (server.all_ckt_busy[tg]==0) { return 0; } gettimeofday(&ended,NULL); - elapsed = smg_calc_elapsed(&server.all_ckt_busy_time,&ended); + elapsed = smg_calc_elapsed(&server.all_ckt_busy_time[tg],&ended); /* seconds elapsed */ - if (elapsed > server.all_ckt_busy) { - server.all_ckt_busy=0; + if (elapsed > server.all_ckt_busy[tg]) { + server.all_ckt_busy[tg]=0; return 0; } else { return 1; @@ -399,55 +478,55 @@ static inline int smg_check_all_busy(void) #if 0 - if (server.all_ckt_busy > 50) { + if (server.all_ckt_busy[tg] > 50) { /* When in GAP mode wait 10s */ - return server.all_ckt_busy; + return server.all_ckt_busy[tg]; } - --server.all_ckt_busy; - if (server.all_ckt_busy < 0) { - server.all_ckt_busy=0; + --server.all_ckt_busy[tg]; + if (server.all_ckt_busy[tg] < 0) { + server.all_ckt_busy[tg]=0; } - return server.all_ckt_busy; + return server.all_ckt_busy[tg]; #endif } -static inline void smg_all_ckt_busy(void) +static inline void smg_all_ckt_busy(int tg) { if (server.call_count*10 < 1500) { - server.all_ckt_busy+=1500; + server.all_ckt_busy[tg]+=1500; } else { - server.all_ckt_busy+=server.call_count*15; + server.all_ckt_busy[tg]+=server.call_count*15; } - if (server.all_ckt_busy > 10000) { - server.all_ckt_busy = 10000; + if (server.all_ckt_busy[tg] > 10000) { + server.all_ckt_busy[tg] = 10000; } #if 0 - if (server.all_ckt_busy >= 5) { - server.all_ckt_busy=10; - } else if (server.all_ckt_busy >= 10) { - server.all_ckt_busy=15; - } else if (server.all_ckt_busy == 0) { - server.all_ckt_busy=5; + if (server.all_ckt_busy[tg] >= 5) { + server.all_ckt_busy[tg]=10; + } else if (server.all_ckt_busy[tg] >= 10) { + server.all_ckt_busy[tg]=15; + } else if (server.all_ckt_busy[tg] == 0) { + server.all_ckt_busy[tg]=5; } #endif - gettimeofday(&server.all_ckt_busy_time,NULL); + gettimeofday(&server.all_ckt_busy_time[tg],NULL); } -static inline void smg_all_ckt_gap(void) +static inline void smg_all_ckt_gap(int tg) { - server.all_ckt_gap=1; + server.all_ckt_gap[tg]=1; } -static inline void smg_clear_ckt_gap(void) +static inline void smg_clear_ckt_gap(int tg) { - server.all_ckt_gap=0; - gettimeofday(&server.all_ckt_busy_time,NULL); + server.all_ckt_gap[tg]=0; + gettimeofday(&server.all_ckt_busy_time[tg],NULL); } @@ -559,6 +638,21 @@ static inline void woomera_set_raw(struct woomera_interface *woomera, char *raw) } } +static inline void media_set_raw(struct media_session *ms, char *raw) +{ + char *oldraw=ms->raw; + + if (raw) { + ms->raw = smg_strdup(raw); + } else { + ms->raw = NULL; + } + + if (oldraw) { + smg_free(oldraw); + } +} + static inline struct media_session * woomera_get_ms(struct woomera_interface *woomera) { struct media_session *ms; @@ -683,11 +777,70 @@ static inline void validate_number(unsigned char *s) } } +static inline int woomera_check_running(struct woomera_interface *woomera) +{ + if (woomera_test_flag(woomera, WFLAG_HANGUP) || + !woomera_test_flag(woomera, WFLAG_RUNNING) || + woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + + return 0; + } + + return 1; +} +static inline int open_span_chan (unsigned char span, unsigned char chan) +{ + int fd = -1; +#ifndef LIBSANGOMA_VERSION + fd = sangoma_open_tdmapi_span_chan(span, chan); +#else + if (chan == 24) { + pthread_mutex_lock(&server.process_table[span][chan].media_lock); + if(server.process_table[span][chan].media_used > 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical Error: channel already opened [s%ic%i]\n", span, chan); + } else { + server.process_table[span][chan].media_used++; + + fd = __sangoma_open_api_span_chan(span, chan); + } + pthread_mutex_unlock(&server.process_table[span][chan].media_lock); + } else { + fd = sangoma_open_api_span_chan(span, chan); + } +#endif + return fd; +} +static inline void close_span_chan (int *socket, unsigned char span, unsigned char chan) +{ + if (chan == 24) { + pthread_mutex_lock(&server.process_table[span][chan].media_lock); + if(server.process_table[span][chan].media_used > 0) { + server.process_table[span][chan].media_used--; + } + close_socket(socket); + pthread_mutex_unlock(&server.process_table[span][chan].media_lock); + } else { + close_socket(socket); + } +} + +static inline void set_digits_info(unsigned char *target, char* string_val) +{ + if (string_val && (atoi(string_val) >= 0)) { + *target = atoi(string_val); + return; + } + *target = 255; +} extern int smg_log_init(void); extern void smg_log_cleanup(void); +extern int smg_ip_bridge_start(void); +extern int smg_ip_bridge_stop(void); +extern int waitfor_2sockets(int fda, int fdb, char *a, char *b, int timeout); #endif diff --git a/ssmg/sangoma_mgd.trunk/.svn/text-base/sangoma_mgd_ip_bridge.c.svn-base b/ssmg/sangoma_mgd.trunk/.svn/text-base/sangoma_mgd_ip_bridge.c.svn-base new file mode 100644 index 0000000..1a98a77 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/.svn/text-base/sangoma_mgd_ip_bridge.c.svn-base @@ -0,0 +1,376 @@ + +#include "sangoma_mgd.h" +static int bridge_threads=0; + +#define TEST_SEQ 0 + +static void *bridge_thread_run(void *obj) +{ + struct smg_tdm_ip_bridge *bridge= (struct smg_tdm_ip_bridge*)obj; + call_signal_connection_t *mcon = &bridge->mcon; + wanpipe_tdm_api_t tdm_api; + int ss = 0; + char a=0,b=0; + int bytes=0,err; + unsigned char data[1024]; + unsigned int fromlen = sizeof(struct sockaddr_in); + sangoma_api_hdr_t hdrframe; + unsigned int udp_rx=0,udp_tx=0,tdm_rx=0,tdm_tx=0; + unsigned int udp_rx_err=0, udp_tx_err=0; + unsigned int tdm_rx_err=0, tdm_tx_err=0; + int bridge_ip_sync=0; + int err_flag=0; + unsigned char prev_status=0; +#if TEST_SEQ + unsigned char tx_seq_cnt=0; + unsigned char rx_seq_cnt=0; + int i; + int insync=0; +#endif + + memset(&hdrframe,0,sizeof(hdrframe)); + memset(data,0,sizeof(data)); + memset(&tdm_api,0,sizeof(tdm_api)); + + if (bridge->period) { + err=sangoma_tdm_set_usr_period(bridge->tdm_fd, &tdm_api,bridge->period); + if (err) { + log_printf(SMG_LOG_ALL,server.log,"%s: Failed to execute set period %i\n",__FUNCTION__,bridge->period); + } + } + + sangoma_tdm_disable_hwec(bridge->tdm_fd, &tdm_api); + + err=sangoma_tdm_flush_bufs(bridge->tdm_fd, &tdm_api); + if (err) { + log_printf(SMG_LOG_ALL,server.log,"%s: Failed to execute tdm flush\n",__FUNCTION__); + } + + while (!bridge->end && + woomera_test_flag(&server.master_connection, WFLAG_RUNNING)) { + + err_flag=0; + + ss = waitfor_2sockets(mcon->socket, + bridge->tdm_fd, + &a, &b, 1000); + + if (ss > 0) { + + if (a) { + + bytes = recvfrom(mcon->socket, &data[0], sizeof(data), MSG_DONTWAIT, + (struct sockaddr *) &mcon->local_addr, &fromlen); + +#if TEST_SEQ + for (i=0;i 0) { + + if (bridge_ip_sync == 0) { + bridge_ip_sync=1; + log_printf(SMG_LOG_ALL,server.log,"Bridge IP Sync: span=%i chan=%i port=%d len=%i\n",bridge->span,bridge->chan,bridge->mcon.cfg.local_port,bytes); + } + udp_rx++; + + err=sangoma_sendmsg_socket(bridge->tdm_fd, + &hdrframe, + sizeof(hdrframe), + data, + bytes, 0); + if (err != bytes) { + + unsigned char current_status; + unsigned char verbose=1; + + sangoma_tdm_get_link_status(bridge->tdm_fd, &tdm_api, ¤t_status); + if (current_status != WP_TDMAPI_EVENT_LINK_STATUS_CONNECTED) { + if (prev_status == current_status) { + verbose=0; + } + } + + prev_status = current_status; + + if (verbose) { + log_printf(SMG_LOG_ALL,server.log,"%s: Error: Bridge tdm write failed (span=%i,chan=%i)! len=%i status=%s - %s\n", + __FUNCTION__,bridge->span,bridge->chan,bytes, current_status==WP_TDMAPI_EVENT_LINK_STATUS_CONNECTED?"Connected":"Disconnected", + strerror(errno)); + sangoma_tdm_flush_bufs(bridge->tdm_fd, &tdm_api); + err_flag++; + } + + tdm_tx_err++; + + } else { + tdm_tx++; + } + } else { + log_printf(SMG_LOG_ALL,server.log,"%s: Error: Bridge sctp read failed (span=%i,chan=%i)! len=%i - %s\n", + __FUNCTION__,bridge->span,bridge->chan,bytes,strerror(errno)); + udp_rx_err++; + err_flag++; + } + } + + if (b) { + bytes = sangoma_readmsg_socket(bridge->tdm_fd, + &hdrframe, + sizeof(hdrframe), + data, + sizeof(data), 0); + + if (bytes > 0) { + tdm_rx++; + err=sendto(mcon->socket, + data, + bytes, 0, + (struct sockaddr *) &mcon->remote_addr, + sizeof(mcon->remote_addr)); + if (err != bytes) { + log_printf(SMG_LOG_ALL,server.log,"%s: Error: Bridge sctp write failed (span=%i,chan=%i)! len=%i - %s\n",__FUNCTION__,bridge->span,bridge->chan,bytes,strerror(errno)); + udp_tx_err++; + err_flag++; + } else { + udp_tx++; + } + } else { + log_printf(SMG_LOG_ALL,server.log,"%s: Error: Bridge tdm read failed (span=%i,chan=%i)! len=%i - %s\n", + __FUNCTION__,bridge->span,bridge->chan,bytes,strerror(errno)); + tdm_rx_err++; + err_flag++; + } + } + + } else if (ss < 0) { + if (!bridge->end) { + log_printf(SMG_LOG_ALL,server.log,"%s: Poll failed on fd exiting bridge (span=%i,chan=%i)\n", + __FUNCTION__,bridge->span,bridge->chan); + } + break; + + } else if (ss == 0) { + + if (bridge_ip_sync) { + log_printf(SMG_LOG_ALL,server.log,"Bridge IP Timeout: span=%i chan=%i port=%d \n", + bridge->span,bridge->chan,bridge->mcon.cfg.local_port); + err_flag++; + } + bridge_ip_sync=0; + } + + + +#if TEST_SEQ + if (udp_rx % 1000 == 0) { + err_flag++; + } +#endif + + if (err_flag) { + log_printf(SMG_LOG_ALL,server.log,"Bridge (s%02ic%02i) urx/ttx=(%i/%i) ue/te=(%i/%i) | trx/utx=(%i/%i) te/ue=(%i/%i) \n", + bridge->span,bridge->chan, + udp_rx,tdm_tx,udp_rx_err,tdm_tx_err, + tdm_rx,udp_tx,tdm_rx_err,udp_tx_err); + } + + } + + pthread_mutex_lock(&g_smg_ip_bridge_lock); + bridge_threads--; + pthread_mutex_unlock(&g_smg_ip_bridge_lock); + + return NULL; +} + + + +static int launch_bridge_thread(int idx) +{ + pthread_attr_t attr; + int result = 0; + struct sched_param param; + + param.sched_priority = 9; + result = pthread_attr_init(&attr); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + result = pthread_attr_setschedparam (&attr, ¶m); + + log_printf(SMG_LOG_ALL,server.log,"%s: Bridge Priority=%i res=%i \n",__FUNCTION__, + param.sched_priority,result); + + bridge_threads++; + + result = pthread_create(&g_smg_ip_bridge_idx[idx].thread, &attr, bridge_thread_run, &g_smg_ip_bridge_idx[idx]); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Thread! %s\n", + __FUNCTION__,strerror(errno)); + g_smg_ip_bridge_idx[idx].end=1; + bridge_threads--; + } + pthread_attr_destroy(&attr); + + return result; +} + +static int create_udp_socket(call_signal_connection_t *ms, 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; + + log_printf(SMG_LOG_DEBUG_9,server.log,"LocalIP %s:%d IP %s:%d \n",local_ip, local_port, ip, port); + + memset(&ms->remote_hp, 0, sizeof(ms->remote_hp)); + memset(&ms->local_hp, 0, sizeof(ms->local_hp)); + if ((ms->socket = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) { + gethostbyname_r(ip, &ms->remote_hp, buf, sizeof(buf), &result, &err); + gethostbyname_r(local_ip, &ms->local_hp, local_buf, sizeof(local_buf), &local_result, &err); + if (result && local_result) { + ms->remote_addr.sin_family = ms->remote_hp.h_addrtype; + memcpy((char *) &ms->remote_addr.sin_addr.s_addr, ms->remote_hp.h_addr_list[0], ms->remote_hp.h_length); + ms->remote_addr.sin_port = htons(port); + + ms->local_addr.sin_family = ms->local_hp.h_addrtype; + memcpy((char *) &ms->local_addr.sin_addr.s_addr, ms->local_hp.h_addr_list[0], ms->local_hp.h_length); + ms->local_addr.sin_port = htons(local_port); + + rc = bind(ms->socket, (struct sockaddr *) &ms->local_addr, sizeof(ms->local_addr)); + if (rc < 0) { + close(ms->socket); + ms->socket = -1; + + log_printf(SMG_LOG_DEBUG_9,server.log, + "Failed to bind LocalIP %s:%d IP %s:%d (%s)\n", + local_ip, local_port, ip, port,strerror(errno)); + } + + /* OK */ + + } else { + log_printf(SMG_LOG_ALL,server.log, + "Failed to get hostbyname LocalIP %s:%d IP %s:%d (%s)\n", + local_ip, local_port, ip, port,strerror(errno)); + } + } else { + log_printf(SMG_LOG_ALL,server.log, + "Failed to create/allocate UDP socket\n"); + } + + return ms->socket; +} + + + +int smg_ip_bridge_start(void) +{ + int i; + int err; + struct smg_tdm_ip_bridge *bridge; + call_signal_connection_t *mcon; + + pthread_mutex_init(&g_smg_ip_bridge_lock,NULL); + + for (i=0;imcon; + + log_printf(SMG_LOG_ALL, server.log, "Opening Bridge MCON Socket [%d] local %s/%d remote %s/%d \n", + mcon->socket,mcon->cfg.local_ip,mcon->cfg.local_port,mcon->cfg.remote_ip,mcon->cfg.remote_port); + +#if 0 + if (call_signal_connection_open(mcon, + mcon->cfg.local_ip, + mcon->cfg.local_port, + mcon->cfg.remote_ip, + mcon->cfg.remote_port) < 0) { + log_printf(SMG_LOG_ALL, server.log, "Error: Opening Bridge MCON Socket [%d] local %s/%d remote %s/%d %s\n", + mcon->socket,mcon->cfg.local_ip,mcon->cfg.local_port,mcon->cfg.remote_ip,mcon->cfg.remote_port,strerror(errno)); + bridge->end=1; + return -1; + } +#else + if (create_udp_socket(mcon, + mcon->cfg.local_ip, + mcon->cfg.local_port, + mcon->cfg.remote_ip, + mcon->cfg.remote_port) < 0) { + log_printf(SMG_LOG_ALL, server.log, "Error: Opening Bridge MCON Socket [%d] local %s/%d remote %s/%d %s\n", + mcon->socket,mcon->cfg.local_ip,mcon->cfg.local_port,mcon->cfg.remote_ip,mcon->cfg.remote_port,strerror(errno)); + bridge->end=1; + return -1; + } + +#endif + + bridge->tdm_fd=open_span_chan(bridge->span, bridge->chan); + if (bridge->tdm_fd < 0) { + log_printf(SMG_LOG_ALL, server.log, "Error: Failed to open span=%i chan=%i - %s\n", + bridge->span,bridge->chan,strerror(errno)); + return -1; + } + + err=launch_bridge_thread(i); + if (err) { + bridge->end=1; + return -1; + } + } + + return 0; +} + + +int smg_ip_bridge_stop(void) +{ + int i; + int timeout=10; + + for (i=0;i #include +#ifdef HAVE_FREETDM +#include +#endif + enum e_sigboost_event_id_values { SIGBOOST_EVENT_CALL_START = 0x80, /*128*/ @@ -30,6 +34,22 @@ enum e_sigboost_event_id_values 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*/ @@ -68,16 +88,34 @@ enum e_sigboost_huntgroup_values 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 30 +#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 */ -#define SIZE_RDNIS 900 + +/* Should only be used by server */ +#define MAX_CALL_SETUP_ID 0xFFFF + +#define SIZE_CUSTOM 900 +#define SIZE_RDNIS SIZE_CUSTOM #pragma pack(1) @@ -86,7 +124,17 @@ typedef struct { uint8_t capability; uint8_t uil1p; -}t_sigboost_bearer; +} 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 { @@ -99,22 +147,32 @@ typedef struct uint32_t trunk_group; uint8_t span; uint8_t chan; + uint32_t flags; /* struct timeval tv; */ - 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 */ + t_sigboost_digits called; + t_sigboost_digits calling; + t_sigboost_digits rdnis; /* ref. Q.931 Table 4-11 and Q.951 Section 3 */ - uint8_t calling_number_screening_ind; - uint8_t calling_number_presentation; char calling_name[MAX_DIALED_DIGITS + 1]; t_sigboost_bearer bearer; uint8_t hunt_group; - uint16_t isup_in_rdnis_size; - char isup_in_rdnis [SIZE_RDNIS]; /* it's a null terminated string */ + uint16_t custom_data_size; + char custom_data[SIZE_CUSTOM]; /* it's a null terminated string */ + } t_sigboost_callstart; -#define MIN_SIZE_CALLSTART_MSG sizeof(t_sigboost_callstart) - SIZE_RDNIS +#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 { @@ -127,20 +185,22 @@ typedef struct 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) +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: - return 0; + break; } return 0; diff --git a/ssmg/sangoma_mgd.trunk/.svn/text-base/__smg_ctrl_common.svn-base b/ssmg/sangoma_mgd.trunk/.svn/text-base/smg_ctrl.svn-base similarity index 62% rename from ssmg/sangoma_mgd.trunk/.svn/text-base/__smg_ctrl_common.svn-base rename to ssmg/sangoma_mgd.trunk/.svn/text-base/smg_ctrl.svn-base index d64b096..96dab39 100644 --- a/ssmg/sangoma_mgd.trunk/.svn/text-base/__smg_ctrl_common.svn-base +++ b/ssmg/sangoma_mgd.trunk/.svn/text-base/smg_ctrl.svn-base @@ -1,6 +1,7 @@ -#!/bin/bash +#!/bin/bash cmd=$1; sigd=$2; +xtraargs=''; cnt=0; max_retry=10; use_syslog=1; @@ -11,6 +12,7 @@ SIG_LOG=$LOG PRI=0 BRI=0 SS7=0 +PROD="smg_ctrl" ulimit -n 65000 @@ -45,56 +47,30 @@ function usage() return 1 } -if [ $sigd = "ss7boost" ] || [ $sigd = "sangoma_isupd" ]; then - sigd_safe_args="-i" - sigd_bg_args="" - SIG_LOG=/var/log/messages - - eval "type $sigd 2> /dev/null > /dev/null" - if [ $? -ne 0 ]; then - export PATH=$PATH:/usr/local/ss7box - fi - eval "type $sigd 2> /dev/null > /dev/null" - if [ $? -ne 0 ]; then - echo - echo "Error $sigd is not found" - echo - exit 1 - fi - - SS7=1; -elif [ $sigd = "sangoma_brid" ]; then - sigd_safe_args="" - sigd_bg_args="-bg" - BRI=1; -elif [ $sigd = "sangoma_prid" ]; then - sigd_safe_args="" - sigd_bg_args="-bg" - PRI=1; - eval "export LD_LIBRARY_PATH=/usr/lib/sangoma_prid:${LD_LIBRARY_PATH} " -else - echo - usage - exit 1 -fi - -while [ ! -z "$3" ]; -do - args=$args"$3 " - shift -done +logit() +{ + local data="$1" + echo "$PROD: $data" + logger "$PROD: $data" +} function stop_all() { echo " " - echo "Stopping running processes..." + logit "Stopping running processes..." stop_safe_sangoma stop_sigdaemon - stop_sangoma_mgd + #post_args=$post_args" >/dev/null 2>/dev/null &" + if [ "$xtraargs" != "sigdonly" ]; then + stop_sangoma_mgd + fi + remove_pid_files + + stop_scripts } function stop_sigdaemon() @@ -108,6 +84,7 @@ function stop_sigdaemon() echo "OK" else echo "FAILED" + logit "Failed to TERM $sigd" break; fi fi @@ -115,10 +92,10 @@ function stop_sigdaemon() do eval "pidof $sigd >/dev/null 2>/dev/null" if [ $? -ne 0 ]; then - echo "$sigd is stopped" + logit "$sigd is stopped" return; else - echo "waiting for $sigd to finish($i/$max_retry)...." + logit "waiting for $sigd to finish($i/$max_retry)...." sleep 1 fi done @@ -130,6 +107,7 @@ function stop_sigdaemon() echo "OK" else echo "FAILED" + logit "Failed to KILL $sigd" break; fi fi @@ -137,10 +115,10 @@ function stop_sigdaemon() do eval "pidof $sigd >/dev/null 2>/dev/null" if [ $? -ne 0 ]; then - echo "$sigd is stopped" + logit "$sigd is stopped" break; else - echo "waiting for $sigd to finish($i/$max_retry)...." + logit "waiting for $sigd to finish($i/$max_retry)...." sleep 1 fi done @@ -161,7 +139,7 @@ function stop_sangoma_mgd() return 0; fi else - echo "sangoma_mgd not running..." + logit "sangoma_mgd not running..." return; fi @@ -169,10 +147,10 @@ function stop_sangoma_mgd() do eval "pidof sangoma_mgd >/dev/null 2>/dev/null" if [ $? -ne 0 ]; then - echo "sangoma_mgd is stopped" + logit "sangoma_mgd is stopped" return 0 else - echo "waiting for sangoma_mgd to finish($i/$max_retry)...." + logit "waiting for sangoma_mgd to finish($i/$max_retry)...." sleep 1 fi done @@ -201,7 +179,7 @@ function stop_safe_sangoma() if [ $? -eq 0 ]; then echo -n "Sending TERM signal to safe_sangoma..." else - echo "safe_sangoma not running..." + logit "safe_sangoma not running..." return; fi sleep 1 @@ -215,7 +193,7 @@ function stop_safe_sangoma() echo "OK" else echo "FAILED" - echo "Failed to stop safe_sangoma" + logit "Failed to stop safe_sangoma" return 1 fi else @@ -230,14 +208,14 @@ function remove_pid_files() eval "rm -f /var/run/$sigd.pid > /dev/null" if [ $? -ne 0 ]; then echo " " - echo "Failed to remove /var/run/$sigd.pid, try to remove it manually" + logit "Failed to remove /var/run/$sigd.pid, try to remove it manually" fi fi if [ -e /var/run/sangoma_mgd.pid ]; then eval "rm -f /var/run/sangoma_mgd.pid > /dev/null" if [ $? -ne 0 ]; then echo " " - echo "Failed to remove /var/run/sangoma_mgd.pid, try to remove it manually" + logit "Failed to remove /var/run/sangoma_mgd.pid, try to remove it manually" fi fi echo "done" @@ -246,7 +224,7 @@ function remove_pid_files() function start_test() { echo " " - echo "Testing configuration files..." + logit "Testing configuration files..." if [ $use_syslog -eq 1 ]; then eval "$sigd -t" else @@ -254,39 +232,91 @@ function start_test() fi if [ $? -eq 0 ]; then - echo "OK" + logit "OK" else - echo "Failed" + logit "Failed" fi } +function start_scripts() +{ + if [ -d /etc/wanpipe/smg_ctrl.d ]; then + for script in /etc/wanpipe/smg_ctrl.d/*.start; do + if [ -x $script ]; then + logit "Executing startup script: $script" + source $script + fi + done + fi +} + +function stop_scripts() +{ + if [ -d /etc/wanpipe/smg_ctrl.d ]; then + for script in /etc/wanpipe/smg_ctrl.d/*.stop; do + if [ -x $script ]; then + logit "Executing stop script: $script" + source $script + fi + done + fi +} function start_all() { - check_running + #post_args=$post_args" >/dev/null 2>/dev/null &" + if [ "$xtraargs" = "sigdonly" ]; then + start_sigd + ret=$? + else + start_sigd + ret=$? + if [ $ret -eq 0 ]; then + start_smg + ret=$? + fi + fi + + start_scripts + + return $ret +} + +function start_sigd() +{ + check_sigd_running pre_args=""; post_args=""; - #post_args=$post_args" >/dev/null 2>/dev/null &" echo " " - echo "Starting processes..." - echo -n "Loading SCTP..." - eval "modprobe sctp >>$LOG 2>>$LOG" - if [ $? -eq 0 ]; then - echo "OK" - else - echo "Failed" - echo "Failed to load SCTP module, check $LOG" - return 1; + if [ $use_safe -eq 1 ]; then + logit "Starting smg_ctrl in safe mode ..." fi + logit "Starting processes..." + eval "cat /proc/net/protocols | grep sctp >/dev/null 2>/dev/null" + if [ $? -eq 0 ]; then + echo "SCTP support already enabled" + else + echo -n "Loading SCTP..." + eval "modprobe sctp >>$LOG 2>>$LOG" + if [ $? -eq 0 ]; then + echo "OK" + else + logit "Failed" + logit "Failed to load SCTP kernel module, check $LOG" + return 1; + fi + fi + + sleep 1 eval "ls /dev/wptdm* >/dev/null 2>/dev/null" if [ $? -ne 0 ]; then eval "ls /dev/wanpipe* >/dev/null 2>/dev/null" if [ $? -ne 0 ]; then - echo "No Sangoma TDM API interfaces running" - echo "Did you start wanrouter? " + logit "No Sangoma TDM API interfaces running" + logit "Did you start wanrouter? " return 1; fi fi @@ -307,20 +337,36 @@ function start_all() if [ $? -eq 0 ]; then echo "OK" else - echo "Failed" - echo "Failed to start $sigd, check $SIG_LOG for errors" + logit "Failed" + logit "Failed to start $sigd, check $SIG_LOG for errors" return 1; fi sleep 2 if [ ! $(pidof $sigd) ]; then - echo "$sigd failed to start" - echo "check $SIG_LOG for errors" + logit "$sigd failed to start" + logit "check $SIG_LOG for errors" return 1; fi sleep 3 + if [ "$xtraargs" = "sigdonly" ]; then + + logit "Sangoma $sigd running.." + if [ $use_syslog -eq 1 ]; then + logit "log file: $LOG and /var/log/messages" + else + logit "log file: $LOG and /var/log/messages" + fi + echo " " + fi + return 0 +} +function start_smg() +{ + check_smg_running pre_args=""; post_args=""; + if [ $use_safe -eq 1 ]; then pre_args=" safe_sangoma" post_args="" @@ -328,9 +374,8 @@ function start_all() post_args=" -bg" fi - if [ -e /etc/wanpipe/.no_smg_load ]; then - echo "Skipping sangoma_mgd..." + logit "Skipping sangoma_mgd..." return 0; fi @@ -339,62 +384,68 @@ function start_all() if [ $? -eq 0 ]; then echo "OK" else - echo "Failed" - echo "Failed to start sangoma_mgd, check $LOG for errors" + logit "Failed" + logit "Failed to start sangoma_mgd, check $LOG for errors" return 1; fi sleep 2 if [ ! $(pidof sangoma_mgd) ]; then - echo "sangoma_mgd failed to start" - echo "check $LOG for errors" + logit "sangoma_mgd failed to start" + logit "check $LOG for errors" return 1; fi echo "Sangoma SMG running.." if [ $use_syslog -eq 1 ]; then - echo "log file: $LOG and /var/log/messages" + logit "log file: $LOG and /var/log/messages" else - echo "log file: $LOG and /var/log/messages" + logit "log file: $LOG and /var/log/messages" fi echo " " return 0 + + } - -function check_running () +function check_sigd_running() { local rc eval "pidof $sigd 2> /dev/null > /dev/null" rc=$? if [ $rc -eq 0 ]; then - echo "$sigd is currently running" - echo "exiting..." - exit 0 - fi - - eval "pidof sangoma_mgd 2> /dev/null > /dev/null" - rc=$? - if [ $rc -eq 0 ]; then - echo "sangoma_mgd is currently running" - echo "exiting..." + logit "$sigd is currently running" + logit "exiting..." exit 0 fi if [ -e /var/run/$sigd.pid ];then eval "rm -f /var/run/$sigd.pid >/dev/null 2>/dev/null"; fi + return 0 + +} + +function check_smg_running() +{ + local rc + eval "pidof sangoma_mgd 2> /dev/null > /dev/null" + rc=$? + if [ $rc -eq 0 ]; then + logit "sangoma_mgd is currently running" + logit "exiting..." + exit 0 + fi if [ -e /var/run/sangoma_mgd.pid ];then eval "rm -f /var/run/sangoma_mgd.pid >/dev/null 2>/dev/null"; fi - return 0 } function check_pid_sigd() { if [ ! $(pidof $sigd) ]; then - echo "$sigd is not running" + logit "$sigd is not running" exit 1 fi return 0 @@ -407,9 +458,9 @@ function toggle_capture() eval "kill -SIGRTMIN+2 $(pidof $sigd)" rc=$? if [ $rc -eq 0 ]; then - echo "BRI: Protocol capture toggled" + logit "BRI: Protocol capture toggled" else - echo "BRI: Failed to send command" + logit "BRI: Failed to send command" fi return $rc @@ -420,13 +471,13 @@ function increase_verbose() local rc check_pid_sigd SIG_VAL=`kill -l SIGRTMIN` - echo "signal: $SIG_VAL" + logit "signal: $SIG_VAL" eval "kill -SIGRTMIN $(pidof $sigd)" rc=$? if [ $rc -eq 0 ]; then - echo "BRI: Verbosity increased" + logit "BRI: Verbosity increased" else - echo "BRI: Failed to send command" + logit "BRI: Failed to send command" fi return $rc @@ -439,9 +490,9 @@ function decrease_verbose() eval "kill -SIGRTMIN+1 $(pidof $sigd)" rc=$? if [ $rc -eq 0 ]; then - echo "BRI: Verbosity decreased" + logit "BRI: Verbosity decreased" else - echo "BRI: Failed to send command" + logit "BRI: Failed to send command" fi return $rc @@ -454,9 +505,9 @@ function show_calls() eval "kill -SIGRTMIN+3 $(pidof $sigd) " rc=$? if [ $rc -eq 0 ]; then - echo "BRI: Show calls" + logit "BRI: Show calls" else - echo "BRI: Failed to send command" + logit "BRI: Failed to send command" fi return $rc @@ -469,9 +520,9 @@ function show_spans() eval "kill -SIGRTMIN+4 $(pidof $sigd)" rc=$? if [ $rc -eq 0 ]; then - echo "BRI: Show spans" + logit "BRI/PRI: Show spans" else - echo "BRI: Failed to send command" + logit "BRI/PRI: Failed to send command" fi return $rc @@ -492,14 +543,106 @@ function parse_args() done } +read_smg_conf () +{ + + WAN_HOME=/etc/wanpipe + WAN_CONF_DIR=$WAN_HOME + META_SMG_CONF=$WAN_HOME/smg.rc + + # Read meta-configuration file. + + if [ -f $META_SMG_CONF ] + then . $META_SMG_CONF + else + logit " $META_SMG_CONF not found !!!!" + return 1 + fi + return 0 +} + +function init_smg_conf() +{ +SMG_BOOT= +SANGOMA_PRID= +SANGOMA_BRID= +SANGOMA_SS7ISUP= +SANGOMA_MEDIA_GATEWAY= +SANGOMA_SS7BOOST= +SANGOMA_SAFE_MODE= +} +###################### +#main start here +###################### parse_args +init_smg_conf +read_smg_conf +#Set sigd +if [ "$SANGOMA_PRID" = "YES" ]; then + sigd="sangoma_prid" +elif [ "$SANGOMA_BRID" = "YES" ]; then + sigd="sangoma_brid" +elif [ "$SANGOMA_SS7ISUP" = "YES" ]; then + sigd="sangoma_isupd" +elif [ "$SANGOMA_SS7BOOST" = "YES" ]; then + sigd="ss7boost" +else + logit "Failed to specify sigd!!! check $META_SMG_CONF " + exit 1 +fi + + + +if [ $SANGOMA_MEDIA_GATEWAY = "NO" ]; then + xtraargs="sigdonly" +fi +if [ $sigd = "ss7boost" ] || [ $sigd = "sangoma_isupd" ]; then + sigd_safe_args="-i" + sigd_bg_args="" + SIG_LOG=/var/log/messages + + eval "type $sigd 2> /dev/null > /dev/null" + if [ $? -ne 0 ]; then + export PATH=$PATH:/usr/local/ss7box + fi + eval "type $sigd 2> /dev/null > /dev/null" + if [ $? -ne 0 ]; then + echo + logit "Error $sigd is not found" + echo + exit 1 + fi + + SS7=1; +elif [ $sigd = "sangoma_brid" ]; then + sigd_safe_args="" + sigd_bg_args="-bg" + BRI=1; +elif [ $sigd = "sangoma_prid" ]; then + sigd_safe_args="" + sigd_bg_args="-bg" + PRI=1; +else + usage + exit 1 +fi + +while [ ! -z "$3" ]; +do + args=$args"$3 " + shift +done ret=0 +if [ "$cmd" = "start" ] && [ "$SANGOMA_SAFE_MODE" = "YES" ]; then + cmd="safe_start" +fi + if [ "$cmd" = "start" ]; then - start_all - ret=$? + start_all + ret=$? if [ $ret -ne 0 ]; then stop_all fi @@ -516,14 +659,17 @@ elif [ "$cmd" = "stop" ]; then ret=$? elif [ "$cmd" = "restart" ]; then stop_all + if [ "$SANGOMA_SAFE_MODE" = "YES" ]; then + use_safe=1 + fi start_all + ret=$? if [ $ret -ne 0 ]; then stop_all fi - ret=$? else if [ $SS7 -eq 1 ]; then - echo "Error: Unsupported command $cmd for SS7" + logit "Error: Unsupported command $cmd for SS7" echo usage exit 1 diff --git a/ssmg/sangoma_mgd.trunk/.svn/text-base/smg_ctrl_bri.svn-base b/ssmg/sangoma_mgd.trunk/.svn/text-base/smg_ctrl_bri.svn-base deleted file mode 100644 index 1edd6d3..0000000 --- a/ssmg/sangoma_mgd.trunk/.svn/text-base/smg_ctrl_bri.svn-base +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -cmd=$1; - -while [ ! -z "$2" ]; -do - args=$args"$2 " - shift -done - -__smg_ctrl_common $cmd sangoma_brid $args -ret=$? - -exit $ret - - diff --git a/ssmg/sangoma_mgd.trunk/.svn/text-base/smg_ctrl_pri.svn-base b/ssmg/sangoma_mgd.trunk/.svn/text-base/smg_ctrl_pri.svn-base deleted file mode 100644 index d26708a..0000000 --- a/ssmg/sangoma_mgd.trunk/.svn/text-base/smg_ctrl_pri.svn-base +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -cmd=$1; - -while [ ! -z "$2" ]; -do - args=$args"$2 " - shift -done - -__smg_ctrl_common $cmd sangoma_prid $args -ret=$? - -exit $ret - - diff --git a/ssmg/sangoma_mgd.trunk/.svn/text-base/smg_ctrl_ss7.svn-base b/ssmg/sangoma_mgd.trunk/.svn/text-base/smg_ctrl_ss7.svn-base deleted file mode 100644 index 8b086a4..0000000 --- a/ssmg/sangoma_mgd.trunk/.svn/text-base/smg_ctrl_ss7.svn-base +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -cmd=$1; - -while [ ! -z "$2" ]; -do - args=$args"$2 " - shift -done - -__smg_ctrl_common $cmd sangoma_isupd $args -ret=$? - -exit $ret - - diff --git a/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.10.tmp b/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.10.tmp new file mode 100644 index 0000000..2e24471 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.10.tmp @@ -0,0 +1,740 @@ +/********************************************************************************* + * sangoma_mgd.h -- Sangoma Media Gateway Daemon for Sangoma/Wanpipe Cards + * + * Copyright 05-07, Nenad Corbic + * Anthony Minessale II + * + * This program is free software, distributed under the terms of + * the GNU General Public License + * + * =============================================*/ + +#ifndef __SANGOMA_MGD_H_ +#define __SANGOMA_MGD_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sangoma_mgd_common.h" +#include "call_signal.h" +#include "sangoma_mgd_memdbg.h" + +#ifdef __LINUX__ +#include +#endif + + +#include "call_signal.h" +#include "g711.h" +#include "sangoma_mgd_memdbg.h" +#include "libteletone.h" +#include "switch_buffer.h" +#include "list.h" + +#define USE_LOG_THREAD 1 +#define USE_SYSLOG 1 +#define CODEC_LAW_DEFAULT 1 + +#define WOOMERA_MAX_CHAN 31 + +#define SMG_SESSION_NAME_SZ 100 +#define SMG_CHAN_NAME_SZ 20 + +#define PIDFILE "/var/run/sangoma_mgd.pid" +#define PIDFILE_UNIT "/var/run/sangoma_mgd_unit.pid" + +#define WOOMERA_MAX_MEDIA_PORTS 899 + +#define CORE_EVENT_LEN 512 +#define WOOMERA_STRLEN 256 +#define WOOMERA_ARRAY_LEN 50 +#define WOOMERA_BODYLEN 2048 +#define WOOMERA_MIN_MEDIA_PORT 9000 +#define WOOMERA_MAX_MEDIA_PORT (WOOMERA_MIN_MEDIA_PORT + WOOMERA_MAX_MEDIA_PORTS) +#define WOOMERA_HARD_TIMEOUT 0 +#define WOOMERA_LINE_SEPERATOR "\r\n" +#define WOOMERA_RECORD_SEPERATOR "\r\n\r\n" +#define WOOMERA_DEBUG_PREFIX "[DEBUG] " +#define WOOMERA_DEBUG_LINE "------------------------------------------------------------------------------------------------" + + + + +#define STDERR fileno(stderr) +#define MAXPENDING 500 +#define MGD_STACK_SIZE 1024 * 240 + +#define SMG_SOCKET_EVENT_TIMEOUT 0 + +typedef enum { + WFLAG_RUNNING = (1 << 0), + WFLAG_LISTENING = (1 << 1), + WFLAG_MASTER_DEV = (1 << 2), + WFLAG_EVENT = (1 << 3), + WFLAG_MALLOC = (1 << 4), + WFLAG_MEDIA_RUNNING = (1 << 5), + WFLAG_MEDIA_END = (1 << 6), + WFLAG_MONITOR_RUNNING = (1 << 7), + WFLAG_HANGUP = (1 << 8), + WFLAG_ANSWER = (1 << 9), + WFLAG_MEDIA_TDM_RUNNING = (1 << 10), + WFLAG_HANGUP_ACK = (1 << 11), + WFLAG_HANGUP_NACK_ACK = (1 << 12), + WFLAG_WAIT_FOR_NACK_ACK = (1 << 13), /* Wait flag used to test reception of an NACK */ + WFLAG_WAIT_FOR_STOPPED_ACK = (1 << 14), /* Wait flag used to test reception of an ACK */ + WFLAG_RAW_MEDIA_STARTED = (1 << 15), /* Media has started on this channel */ + WFLAG_CALL_ACKED = (1 << 16), /* Woomera side rx accept so CALL ACK was Sent */ + WFLAG_WAIT_FOR_NACK_ACK_SENT = (1 << 17), /* Call START NACK was sent out on this channel */ + WFLAG_WAIT_FOR_STOPPED_ACK_SENT = (1 << 18), /* Call STOP was sent out on this channel */ + WFLAG_SYSTEM_RESET = (1 << 19), /* Initial System Reset Condition no calls allowed */ + WFLAG_WAIT_FOR_ACK_TIMEOUT = (1 << 20), /* Timeout flag indicating that incoming ACK or NACK timedout */ +} WFLAGS; + +enum { + + SMG_LOG_ALL = 0, + SMG_LOG_PROD = 1, + SMG_LOG_BOOST = 2, + SMG_LOG_WOOMERA = 3, + SMG_LOG_DEBUG_CALL = 4, + SMG_LOG_DEBUG_MISC = 5, + SMG_LOG_DEBUG_6 = 6, + SMG_LOG_DEBUG_7 = 7, + SMG_LOG_DEBUG_8 = 8, + SMG_LOG_DEBUG_9 = 9, + SMG_LOG_DEBUG_10 = 10 +}; + + +#define woomera_print_flags(woomera,level) \ + log_printf(level,woomera->log,"%s: WFLAG_RUNNING = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_RUNNING));\ + log_printf(level,woomera->log,"%s: WFLAG_LISTENING = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_LISTENING));\ + log_printf(level,woomera->log,"%s: WFLAG_MASTER_DEV = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_MASTER_DEV));\ + log_printf(level,woomera->log,"%s: WFLAG_EVENT = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_EVENT));\ + log_printf(level,woomera->log,"%s: WFLAG_MALLOC = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_MALLOC));\ + log_printf(level,woomera->log,"%s: WFLAG_MEDIA_RUNNING = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_MEDIA_RUNNING));\ + log_printf(level,woomera->log,"%s: WFLAG_MEDIA_END = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_MEDIA_END));\ + log_printf(level,woomera->log,"%s: WFLAG_MONITOR_RUNNING = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_MONITOR_RUNNING));\ + log_printf(level,woomera->log,"%s: WFLAG_HANGUP = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_HANGUP));\ + log_printf(level,woomera->log,"%s: WFLAG_ANSWER = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_ANSWER));\ + log_printf(level,woomera->log,"%s: WFLAG_MEDIA_TDM_RUNNING = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_MEDIA_TDM_RUNNING));\ + log_printf(level,woomera->log,"%s: WFLAG_HANGUP_ACK = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_HANGUP_ACK));\ + log_printf(level,woomera->log,"%s: WFLAG_HANGUP_NACK_ACK = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_HANGUP_NACK_ACK));\ + log_printf(level,woomera->log,"%s: WFLAG_WAIT_FOR_NACK_ACK = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_WAIT_FOR_NACK_ACK));\ + log_printf(level,woomera->log,"%s: WFLAG_WAIT_FOR_STOPPED_ACK = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_WAIT_FOR_STOPPED_ACK));\ + log_printf(level,woomera->log,"%s: WFLAG_RAW_MEDIA_STARTED = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_RAW_MEDIA_STARTED));\ + log_printf(level,woomera->log,"%s: WFLAG_CALL_ACKED = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_CALL_ACKED));\ + log_printf(level,woomera->log,"%s: WFLAG_WAIT_FOR_NACK_ACK_SENT = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_WAIT_FOR_NACK_ACK_SENT));\ + log_printf(level,woomera->log,"%s: WFLAG_WAIT_FOR_STOPPED_ACK_SENT = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_WAIT_FOR_STOPPED_ACK_SENT));\ + log_printf(level,woomera->log,"%s: WFLAG_SYSTEM_RESET = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_SYSTEM_RESET));\ + log_printf(level,woomera->log,"%s: WFLAG_WAIT_FOR_ACK_TIMEOUT = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_WAIT_FOR_ACK_TIMEOUT));\ + + +typedef enum { + MFLAG_EXISTS = (1 << 0), + MFLAG_CONTENT = (1 << 1), +} MFLAGS; + +typedef enum { + EVENT_FREE_DATA = 1, + EVENT_KEEP_DATA = 0 +} event_args; + + +void __log_printf(int level, FILE *fp, char *file, const char *func, int line, char *fmt, ...); + +#define ysleep(usec) sched_yield() ; usleep(usec); +#define log_printf(level, fp, fmt, ...) __log_printf(level, fp, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__) + + +#define woomera_test_flag(p,flag) ({ \ + ((p)->flags & (flag)); \ + }) + +#define _woomera_set_flag(p,flag) do { \ + ((p)->flags |= (flag)); \ + } while (0) + +#define _woomera_clear_flag(p,flag) do { \ + ((p)->flags &= ~(flag)); \ + } while (0) + +#define woomera_set_flag(p,flag) do { \ + pthread_mutex_lock(&(p)->flags_lock); \ + ((p)->flags |= (flag)); \ + pthread_mutex_unlock(&(p)->flags_lock); \ + } while (0) + +#define woomera_clear_flag(p,flag) do { \ + pthread_mutex_lock(&(p)->flags_lock); \ + ((p)->flags &= ~(flag)); \ + pthread_mutex_unlock(&(p)->flags_lock); \ + } while (0) + +#define woomera_copy_flags(dest,src,flagz) do { \ + pthread_mutex_lock(&(p)->flags_lock); \ + (dest)->flags &= ~(flagz); \ + (dest)->flags |= ((src)->flags & (flagz)); \ + pthread_mutex_unlock(&(p)->flags_lock); \ + } while (0) + + +struct media_session { + int udp_sock; + int sangoma_sock; + char *ip; + int port; + time_t started; + time_t answered; + pthread_t thread; + int socket; + struct woomera_interface *woomera; + struct sockaddr_in local_addr; + struct sockaddr_in remote_addr; + struct hostent remote_hp; + struct hostent local_hp; + int skip_read_frames; + int skip_write_frames; + int hw_coding; + int hw_dtmf; + int udp_sync_cnt; + +#ifdef WP_HPTDM_API + hp_tdm_api_chan_t *tdmchan; +#endif + + teletone_dtmf_detect_state_t dtmf_detect; + teletone_generation_session_t tone_session; + switch_buffer_t *dtmf_buffer; + unsigned char oob_disable; +}; + +struct woomera_message { + char callid[WOOMERA_STRLEN]; + char command[WOOMERA_STRLEN]; + char command_args[WOOMERA_STRLEN]; + char names[WOOMERA_STRLEN][WOOMERA_ARRAY_LEN]; + char values[WOOMERA_STRLEN][WOOMERA_ARRAY_LEN]; + char body[WOOMERA_BODYLEN]; + uint32_t flags; + pthread_mutex_t flags_lock; + int last; + struct woomera_message *next; +}; + +struct woomera_event { + char *data; + uint32_t flags; + struct woomera_event *next; +}; + +struct woomera_listener { + struct woomera_interface *woomera; + struct woomera_listener *next; +}; + +struct woomera_interface { + int socket; + char *raw; + char *interface; + time_t timeout; + struct sockaddr_in addr; + struct media_session *ms; + pthread_mutex_t queue_lock; + pthread_mutex_t ms_lock; + pthread_mutex_t dtmf_lock; + struct woomera_event *event_queue; + struct woomera_event *incoming_event_queue; + pthread_t thread; + uint32_t flags; + pthread_mutex_t flags_lock; + int debug; + int call_id; + FILE *log; + pthread_mutex_t vlock; + int index; + int index_hold; + int span; + int chan; + int trunk_group; + int call_count; + int q931_rel_cause_tosig; + int q931_rel_cause_topbx; + int loop_tdm; + char session[SMG_SESSION_NAME_SZ]; + int check_digits; /* set to 1 when session comes up */ + int bearer_cap; + struct woomera_interface *next; +}; + +struct woomera_session { + struct woomera_interface *dev; + char session[SMG_SESSION_NAME_SZ]; + char digits[MAX_DIALED_DIGITS+1]; + int digits_len; + int bearer_cap; + int clients; + unsigned char media_used; + pthread_mutex_t media_lock; +}; + +#define CORE_TANK_LEN CORE_MAX_CHAN_PER_SPAN*CORE_MAX_SPANS + +struct woomera_server { + struct woomera_session process_table[CORE_MAX_SPANS][CORE_MAX_CHAN_PER_SPAN]; + struct woomera_interface *holding_tank[CORE_TANK_LEN]; + int holding_tank_index; + struct woomera_interface master_connection; + pthread_mutex_t listen_lock; + pthread_mutex_t ht_lock; + pthread_mutex_t process_lock; + pthread_mutex_t digits_lock; + pthread_mutex_t media_udp_port_lock; + pthread_mutex_t thread_count_lock; + call_signal_connection_t mcon; + call_signal_connection_t mconp; + struct woomera_listener *listeners; + char media_ip[WOOMERA_STRLEN]; + int port; + int next_media_port; + int base_media_port; + int max_media_port; + int debug; + int panic; + int thread_count; + char *logfile_path; + FILE *log; + char boost_local_ip[25]; + int boost_local_port; + char boost_remote_ip[25]; + int boost_remote_port; + pthread_t monitor_thread; + char *config_file; + int max_calls; + int call_count; + uint32_t out_tx_test; + uint32_t rxgain; + uint32_t txgain; + uint32_t hw_coding; + uint32_t loop_trace; + uint32_t hungup_waiting; + int all_ckt_gap; + int all_ckt_busy; + struct timeval all_ckt_busy_time; + struct timeval restart_timeout; + int dtmf_on; + int dtmf_off; + int dtmf_intr_ch; + int dtmf_size; + int strip_cid_non_digits; + int call_timeout; +}; + +extern struct woomera_server server; + +struct woomera_config { + FILE *file; + char *path; + char category[256]; + char buf[1024]; + int lineno; +}; + + +static inline void smg_get_current_priority(int *policy, int *priority) +{ + struct sched_param param; + pthread_getschedparam(pthread_self(), policy, ¶m); + *priority = param.sched_priority; + return; +} + +static inline int smg_calc_elapsed(struct timeval *started, struct timeval *ended) +{ + return (((ended->tv_sec * 1000) + ended->tv_usec / 1000) - + ((started->tv_sec * 1000) + started->tv_usec / 1000)); +} + +static inline int smg_check_all_busy(void) +{ + struct timeval ended; + int elapsed; + + if (server.all_ckt_gap) { + return server.all_ckt_gap; + } + + if (server.all_ckt_busy==0) { + return 0; + } + + gettimeofday(&ended,NULL); + elapsed = smg_calc_elapsed(&server.all_ckt_busy_time,&ended); + + /* seconds elapsed */ + if (elapsed > server.all_ckt_busy) { + server.all_ckt_busy=0; + return 0; + } else { + return 1; + } + +#if 0 + + if (server.all_ckt_busy > 50) { + /* When in GAP mode wait 10s */ + return server.all_ckt_busy; + } + + --server.all_ckt_busy; + if (server.all_ckt_busy < 0) { + server.all_ckt_busy=0; + } + + return server.all_ckt_busy; +#endif +} + + +static inline void smg_all_ckt_busy(void) +{ + + if (server.call_count*10 < 1500) { + server.all_ckt_busy+=1500; + } else { + server.all_ckt_busy+=server.call_count*15; + } + + if (server.all_ckt_busy > 10000) { + server.all_ckt_busy = 10000; + } + +#if 0 + if (server.all_ckt_busy >= 5) { + server.all_ckt_busy=10; + } else if (server.all_ckt_busy >= 10) { + server.all_ckt_busy=15; + } else if (server.all_ckt_busy == 0) { + server.all_ckt_busy=5; + } +#endif + gettimeofday(&server.all_ckt_busy_time,NULL); +} + +static inline void smg_all_ckt_gap(void) +{ + server.all_ckt_gap=1; +} + +static inline void smg_clear_ckt_gap(void) +{ + server.all_ckt_gap=0; + gettimeofday(&server.all_ckt_busy_time,NULL); +} + + +static inline int smg_validate_span_chan(int span, int chan) +{ + if (span < 0 || span > max_spans) { + return -1; + } + + if (chan < 0 || chan > WOOMERA_MAX_CHAN) { + return -1; + } + + return 0; +} + + +static inline void close_socket(int *sp) +{ + if (*sp > -1) { + close(*sp); + *sp = -1; + } +} + +static inline FILE *safe_fopen(char *path, char *flags) +{ + char buf[512] = ""; + + if (readlink(path, buf, sizeof(buf)) > 0) { + fprintf(stderr, "Symlinks not allowed! [%s] != [%s]\n", buf, path); + return NULL; + } + + return fopen(path, flags); + +} + +static inline int get_pid_from_file(char *path) +{ + FILE *tmp; + int pid; + int err; + + if (!(tmp = safe_fopen(path, "r"))) { + return 0; + } else { + err=fscanf(tmp, "%d", &pid); + fclose(tmp); + tmp = NULL; + } + + return pid; +} + + +static inline int woomera_open_file(struct woomera_config *cfg, char *path) +{ + FILE *f; + + if (!(f = fopen(path, "r"))) { + log_printf(SMG_LOG_ALL, stderr, "Cannot open file %s\n", path); + return 0; + } + + memset(cfg, 0, sizeof(*cfg)); + cfg->file = f; + cfg->path = path; + return 1; + +} + + +static inline void woomera_close_file(struct woomera_config *cfg) +{ + + if (cfg->file) { + fclose(cfg->file); + } + + memset(cfg, 0, sizeof(*cfg)); +} + + +static inline void woomera_message_init (struct woomera_message *wmsg) +{ + memset (wmsg,0,sizeof(struct woomera_message)); + pthread_mutex_init(&wmsg->flags_lock, NULL); +} + +static inline void woomera_message_clear (struct woomera_message *wmsg) +{ + pthread_mutex_destroy(&wmsg->flags_lock); +} + + +static inline void woomera_set_raw(struct woomera_interface *woomera, char *raw) +{ + char *oldraw=woomera->raw; + + if (raw) { + woomera->raw = smg_strdup(raw); + } else { + woomera->raw = NULL; + } + + if (oldraw) { + smg_free(oldraw); + } +} + +static inline struct media_session * woomera_get_ms(struct woomera_interface *woomera) +{ + struct media_session *ms; + pthread_mutex_lock(&woomera->ms_lock); + ms=woomera->ms; + pthread_mutex_unlock(&woomera->ms_lock); + return ms; +} + +static inline void woomera_set_ms(struct woomera_interface *woomera,struct media_session *ms) +{ + pthread_mutex_lock(&woomera->ms_lock); + woomera->ms=ms; + pthread_mutex_unlock(&woomera->ms_lock); + return; +} + +static inline void woomera_set_interface(struct woomera_interface *woomera, char *interface) +{ + char *iface = woomera->interface; + + if (interface) { + woomera->interface = smg_strdup(interface); + } else { + woomera->interface = NULL; + } + + if (iface) { + smg_free(iface); + } +} + +static inline void woomera_set_cause_tosig(struct woomera_interface *woomera, int cause) +{ + if (!cause) { + cause=SIGBOOST_RELEASE_CAUSE_NORMAL; + } + + woomera->q931_rel_cause_tosig=cause; +} + +static inline void woomera_set_cause_topbx(struct woomera_interface *woomera, int cause) +{ + + if (!cause) { + cause=SIGBOOST_RELEASE_CAUSE_NORMAL; + } + + if (cause == SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY) { + cause=34; + } + + woomera->q931_rel_cause_topbx=cause; + +} + + +static inline struct woomera_event *new_woomera_event(void) +{ + struct woomera_event *event = NULL; + + if ((event = smg_malloc(sizeof(*event)))) { + memset(event, 0, sizeof(*event)); + _woomera_set_flag(event, WFLAG_MALLOC); + } + + return event; +} + + +static inline void destroy_woomera_event_data(struct woomera_event *event) +{ + if (event->data) { + smg_free (event->data); + event->data=NULL; + } +} + +static inline void destroy_woomera_event(struct woomera_event **event, event_args free_data) +{ + if (free_data) { + smg_free ((*event)->data); + } + + (*event)->data=NULL; + if (woomera_test_flag((*event), WFLAG_MALLOC)) { + smg_free (*event); + *event = NULL; + } +} + + +/* disable nagle's algorythm */ +static inline void no_nagle(int socket) +{ + int flag = 1; + setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); +} + + +static inline void remove_end_of_digits_char(unsigned char *s) +{ + unsigned char *p; + for (p = s; *p; p++) { + if (*p == 'F' || *p > 'f') { + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Removing a non-numeric character [%c]!\n", *p); + *p = '\0'; + break; + } + } +} + +static inline void validate_number(unsigned char *s) +{ + unsigned char *p; + for (p = s; *p; p++) { + if (*p < 48 || *p > 57) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Encountered a non-numeric character [%c]!\n", *p); + *p = '\0'; + break; + } + } +} + +static inline int woomera_check_running(struct woomera_interface *woomera) +{ + if (woomera_test_flag(woomera, WFLAG_HANGUP) || + !woomera_test_flag(woomera, WFLAG_RUNNING) || + woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + + return 0; + } + + return 1; +} + +static inline int open_span_chan (unsigned char span, unsigned char chan) +{ + int fd = -1; +#ifndef LIBSANGOMA_VERSION + fd = sangoma_open_tdmapi_span_chan(span, chan); +#else + if (chan == 24) { + pthread_mutex_lock(&server.process_table[span][chan].media_lock); + if(server.process_table[span][chan].media_used > 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical Error: channel already opened [w%ig%i]\n", span, chan); + } else { + server.process_table[span][chan].media_used++; + + fd = __sangoma_open_api_span_chan(span, chan); + } + pthread_mutex_unlock(&server.process_table[span][chan].media_lock); + } else { + fd = sangoma_open_api_span_chan(span, chan); + } +#endif + return fd; +} + +static inline void close_span_chan (int *socket, unsigned char span, unsigned char chan) +{ + if (chan == 24) { + pthread_mutex_lock(&server.process_table[span][chan].media_lock); + if(server.process_table[span][chan].media_used > 0) { + server.process_table[span][chan].media_used--; + } + close_socket(socket); + pthread_mutex_unlock(&server.process_table[span][chan].media_lock); + } else { + close_socket(socket); + } +} + +extern int smg_log_init(void); +extern void smg_log_cleanup(void); +#endif + diff --git a/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.11.tmp b/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.11.tmp new file mode 100644 index 0000000..cbff623 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.11.tmp @@ -0,0 +1,5970 @@ +/********************************************************************************* + * sangoma_mgd.c -- Sangoma Media Gateway Daemon for Sangoma/Wanpipe Cards + * + * Copyright 05-09, Nenad Corbic + * Anthony Minessale II + * + * This program is free software, distributed under the terms of + * the GNU General Public License + * + * ============================================= + * + * v1.54 Nenad Corbic + * Bug added in 1.51 release causing call on channel 31 to fail. + * + * v1.53 Nenad Corbic + * Added progress message + * + * v1.52 David Yat Sin + * Changed sangoma_open_span_chan to __sangoma_span_chan + * to enabled shared used of file descriptors when using + * with PRI in NFAS mode + * + * v1.51 David Yat Sin + * MAX_SPANS increased to 32. + * Fix for server.process_table declared incorrectly + * + * v1.50 Nenad Corbic + * Logic to support multiple woomera clients hanging up the + * channel. This feature now supprorts woomera loadbalancing + * with extension check on the client end. Example, two Asterisk + * systems setup where Asterisk with correct extension gets the + * call. + * Changed log levels: Loglevel 1 = production + * Loglevel 2 = Boost TX/RX EVENTS + * Loglevel 3 = Woomera RX Messages + * Loglevel 4 = call setup debugging + * Loglevel 5 = extra debugging + * Loglevel 10 = full debugging + * + * v1.49 Nenad Corbic + * Removed tv from sigboost to make it binary compatible + * Updated release cause on double use return code. + * + * v1.48 Nenad Corbic + * Konrad Hammel + * Jun 30 2009 + * Added feature to disable/enable HWEC on channels that are + * passing a call with a "data" only transfer capability + * + * v1.47 Nenad Corbic + * Apr 30 2009 + * Updated hangup woomera events. Each event must + * be followed by a woomera error code 200=ok >299=no ok. + * This update fixes the chan_woomera socket write + * warnings. + * + * v1.46 Nenad Corbic + * Mar 27 2009 + * Major updates on socket handling. A bug was introducted + * when cmm and trunk was merged. + * Added configuration print in the logs. + * + * v1.45 Nenad Corbic + * Mar 19 2009 + * Outbound call timeout defaulted to 300 seconds. + * Timeout on answer instead of accept. + * + * v1.44 Nenad Corbic + * Mar 19 2009 + * Adjustable boost size. Added boost version check. + * + * v1.43 David Yat Sin + * Feb 25 2009 + * Merged CMM and Trunk + * + * v1.42 Nenad Corbic + * Jan 26 2008 + * Call Bearer Capability + * BugFix: Hangup NACK was sent out with invalid cause 0 + * + * v1.41 Nenad Corbic + * Jan 12 2008 + * Fixed the NACK with cause 0 bug. + * Added cause 19 on call timeout. + * Added configuration option call_timeout to timeout + * outbound calls. + * + * v1.40 Nenad Corbic + * Dec 10 2008 + * Check for Rx call overrun. + * In unlikely case sangoma_mgd goes out of + * sync with boost. + * + * v1.39 Nenad Corbic + * Sep 17 2008 + * Updated for Unit Test + * Bug fix in Loop Logic, possible race + * between media and woomera thread on loop end. + * + * v1.38 Nenad Corbic + * Sep 5 2008 + * Added a Double use of setup id logic. + * Currently double use call gets dropped. + * + * v1.37 Nenad Corbic + * Sep 2 2008 + * Bug fix in REMOVE LOOP logic continued + * The woomera->sock in loop logic was set to 0. + * Closing it failed the next call. + * + * v1.36 Nenad Corbic + * Aug 28 2008 + * Bug fix in REMOVE LOOP logic + * Remove F from incoming calls by default + * Check for errno on poll() + * Do not delay in closing socket on media down. + * + * v1.35cmm Nenad Corbic + * Jul 28 2008 + * Bug fixes on ACK Timeout and trunk release + * Added a thread logger + * Increased max spans to 32 + * + * v1.34cmm Nenad Corbic + * Jul 24 2008 + * Clean up the setup id on CALL STOP ACK + * + * v1.33cmm Nenad Corbic + * Jul 24 2008 + * The CALL STOP timeout function had a bug + * resulting in false blocking of tank ids + * due to STOP ACK Timeouts. + * + * v1.33 Nenad Corbic + * Jul 18 2008 + * Added UDP Sequencing to check for dropped frames + * Should only be used for debugging. + * + * v1.32 David Yat Sin + * Jul 17 2008 + * Support for d-channel incoming digit + * passthrough to Asterisk + * + * v1.32cmm Nenad Corbic + * Jun 03 2008 + * Implemented new Restart ACK Policy + * Split the Event packet into 2 types + * + * v1.31cmm Nenad Corbic + * Apr 04 2008 + * New CMM Restart procedure for boost + * Block TRUNK ID on ACK Timeout + * + * v1.31 David Yat Sin + * Apr 29 2008 + * Support for HW DTMF events. + * + * v1.30 Nenad Corbic + * Feb 08 2008 + * The fix in v1.26 causes double stop. + * I took out the v1.26 fix and added + * a big warning if that condition is + * ever reached. + * + * v1.29 Nenad Corbic + * Feb 07 2008 + * Added strip_cid_non_digits option + * + * v1.28 Nenad Corbic + * Feb 06 2008 + * Fixed a memory leak in clone event + * function. Added memory check subsystem + * + * v1.27 Nenad Corbic + * Jan 24 2008 + * Fixed a memory leak on incoming calls + * Removed the use of server listener which + * was not used + * + * v1.26 Nenad Corbic + * Jan 18 2008 + * Fixed hangup after invalid Answer or Ack Session + * Can cause double use of setup id - now fixed + * Update on autoacm on accept check for acked. + * + * v1.25 Nenad Corbic + * Dec 31 2007 + * Removed UDP Resync it can cause skb_over errors. + * Moved RDNIS message to higher debug level + * + * v1.24 Nenad Corbic + * Nov 30 2007 + * Bug fix on return code on ALL ckt busy + * + * v1.23 Nenad Corbic + * Bug fix on socket open. Check for retun code >= 0 + * + * v1.22 Nenad Corbic + * Nov 27 2007 + * Updated DTMF Tx function + * Fixed - dtmf tx without voice + * Fxied - dtmf clipping. + * + * v1.21 Nenad Corbic + * Nov 25 2007 + * Major unit testing of each state + * Numerous bug fixes for non autoacm mode. + * Changed "Channel-Name" to tg/cic + * Added compile option WANPIPE_CHAN_NAME to change Asterisk channel + * name of chan_woomera.so. So one can use Dial(SS7/g1/${EXTE}) + * instead of WOOMERA (for example) + * + * v1.20 Nenad Corbic + * Added option for Auto ACM response mode. + * + * v1.19 Nenad Corbic + * Configurable DTMF + * Bug fix in release codes (all ckt busy) + * + * v1.18 Nenad Corbic + * Added new rel cause support based on + * digits instead of strings. + * + * v1.17 Nenad Corbic + * Added session support + * + * v1.16 Nenad Corbic + * Added hwec disable on loop ccr + * + * v1.15 Nenad Corbic + * Updated DTMF Locking + * Added delay between digits + * + * v1.14 Nenad Corbic + * Updated DTMF Library + * Fixed DTMF synchronization + * + * v1.13 Nenad Corbic + * Woomera OPAL Dialect + * Added Congestion control + * Added SCTP + * Added priority ISUP queue + * Fixed presentation + * + * v1.12 Nenad Corbic + * Fixed CCR + * Removed socket shutdown on end call. + * Let Media thread shutodwn sockets. + * + * v1.11 Nenad Corbic + * Fixed Remote asterisk/woomera connection + * Increased socket timeouts + * + * v1.10 Nenad Corbic + * Added Woomera OPAL dialect. + * Start montor thread in priority + * + * v1.9 Nenad Corbic + * Added Loop mode for ccr + * Added remote debug enable + * Fixed syslog logging. + * + * v1.8 Nenad Corbic + * Added a ccr loop mode for each channel. + * Boost can set any channel in loop mode + * + * v1.7 Nenad Corbic + * Pass trunk group number to incoming call + * chan woomera will use it to append to context + * name. Added presentation feature. + * + * v1.6 Nenad Corbic + * Use only ALAW and MLAW not SLIN. + * This reduces the load quite a bit. + * Send out ALAW/ULAW format on HELLO message. + * RxTx Gain is done now in chan_woomera. + * + * v1.5 Nenad Corbic + * Bug fix in START_NACK_ACK handling. + * When we receive START_NACK we must alwasy pull tank before + * we send out NACK_ACK this way we will not try to send NACK + * ourself. + *********************************************************************************/ + +#include "sangoma_mgd.h" +#include "sangoma_mgd_memdbg.h" +#include "q931_cause.h" +#include "smg_capabilities.h" + +int pipe_fd[2]; + +#ifdef CODEC_LAW_DEFAULT +static uint32_t codec_sample=8; +#else +static uint32_t codec_sample=16; +#endif + +static char ps_progname[]="sangoma_mgd"; + +static struct woomera_interface woomera_dead_dev; + +struct woomera_server server; + +#if 0 +#define DOTRACE +#endif + + +#define SMG_VERSION "v1.54" + +/* enable early media */ +#if 1 +#define WOOMERA_EARLY_MEDIA 1 +#endif + + +#define SMG_DTMF_ON 60 +#define SMG_DTMF_OFF 10 +#define SMG_DTMF_RATE 8000 +#define SMG_DEFAULT_CALL_TIMEOUT 300 + +#if 0 +#define MEDIA_SOCK_SHUTDOWN 1 +#endif + +#ifdef DOTRACE +static int tc = 0; +#endif + +#if 0 +#warning "NENAD: HPTDM API" +#define WP_HPTDM_API 1 +#else +#undef WP_HPTDM_API +#endif + +#ifdef WP_HPTDM_API +hp_tdm_api_span_t *hptdmspan[WOOMERA_MAX_SPAN]; +#endif + +#if 0 +#define SMG_DROP_SEQ 1 +#warning "SMG Debug feature Drop Seq Enabled" +static int drop_seq=0; +#else +#undef SMG_DROP_SEQ +#endif + + +#if 0 +#define SMG_NO_MEDIA +#warning "SMG No Media Defined" +#else +#undef SMG_NO_MEDIA +#endif + +const char WELCOME_TEXT[] = +"================================================================================\n" +"Sangoma Media Gateway Daemon v1.54 \n" +"\n" +"TDM Signal Media Gateway for Sangoma/Wanpipe Cards\n" +"Copyright 2005, 2006, 2007 \n" +"Nenad Corbic , Anthony Minessale II \n" +"This program is free software, distributed under the terms of\n" +"the GNU General Public License\n" +"================================================================================\n" +""; + + + +static int coredump=1; +static int autoacm=0; + +/* FIXME: Should be done in cfg file */ +#if defined(BRI_PROT) +int max_spans=WOOMERA_BRI_MAX_SPAN; +int max_chans=WOOMERA_BRI_MAX_CHAN; +#else +/* PRI_PROT uses these defines as well */ +int max_spans=WOOMERA_MAX_SPAN; +int max_chans=WOOMERA_MAX_CHAN; +#endif + +static int launch_media_tdm_thread(struct woomera_interface *woomera); +static int launch_woomera_thread(struct woomera_interface *woomera); +static void woomera_check_digits (struct woomera_interface *woomera); +static struct woomera_interface *alloc_woomera(void); +static void handle_event_dtmf(struct woomera_interface *woomera, unsigned char dtmf_digit); + +q931_cause_to_str_array_t q931_cause_to_str_array[255]; +bearer_cap_to_str_array_t bearer_cap_to_str_array[255]; +uil1p_to_str_array_t uil1p_to_str_array[255]; + +static int isup_exec_command(int span, int chan, int id, int cmd, int cause) +{ + short_signal_event_t oevent; + int retry=5; + + call_signal_event_init((short_signal_event_t*)&oevent, cmd, chan, span); + oevent.release_cause = cause; + + if (id >= 0) { + oevent.call_setup_id = id; + } +isup_exec_cmd_retry: + if (call_signal_connection_write(&server.mcon, (call_signal_event_t*)&oevent) < 0){ + + --retry; + if (retry <= 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket: %s\n", + strerror(errno)); + return -1; + } else { + log_printf(SMG_LOG_ALL, server.log, + "System Warning: Failed to tx on ISUP socket: %s :retry %i\n", + strerror(errno),retry); + } + + goto isup_exec_cmd_retry; + } + + return 0; +} + +static int isup_exec_event(call_signal_event_t *event) +{ + int retry=5; + +isup_exec_cmd_retry: + if (call_signal_connection_write(&server.mcon, event) < 0){ + + --retry; + if (retry <= 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket: %s\n", + strerror(errno)); + return -1; + } else { + log_printf(SMG_LOG_ALL, server.log, + "System Warning: Failed to tx on ISUP socket: %s :retry %i\n", + strerror(errno),retry); + } + + goto isup_exec_cmd_retry; + } + + return 0; +} + + +static int isup_exec_commandp(int span, int chan, int id, int cmd, int cause) +{ + short_signal_event_t oevent; + int retry=5; + + call_signal_event_init(&oevent, cmd, chan, span); + oevent.release_cause = cause; + + if (id >= 0) { + oevent.call_setup_id = id; + } +isup_exec_cmd_retry: + if (call_signal_connection_writep(&server.mconp, (call_signal_event_t*)&oevent) < 0){ + + --retry; + if (retry <= 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket: %s\n", + strerror(errno)); + return -1; + } else { + log_printf(SMG_LOG_ALL, server.log, + "System Warning: Failed to tx on ISUP socket: %s :retry %i\n", + strerror(errno),retry); + } + + goto isup_exec_cmd_retry; + } + + return 0; +} + + +static int get_span_chan_from_interface(char* interface, int *span_ptr, int *chan_ptr) +{ + int span, chan; + + if (sscanf(interface, "w%dg%d", &span, &chan) == 2) { + if (smg_validate_span_chan(span,chan) != 0) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "WOOMERA Warning invalid span chan in interface %s\n", + interface); + return -1; + } + + *span_ptr = span; + *chan_ptr = chan; + return 0; + } + log_printf(SMG_LOG_ALL, server.log, "ERROR: Failed to get span chan from interface:[%s]\n", interface, span, chan); + return -1; +} + +static int socket_printf(int socket, char *fmt, ...) +{ + char *data; + int ret = 0; + va_list ap; + + if (socket < 0) { + return -1; + } + + va_start(ap, fmt); +#ifdef SOLARIS + data = (char *) smg_malloc(2048); + vsnprintf(data, 2048, fmt, ap); +#else + ret = vasprintf(&data, fmt, ap); +#endif + va_end(ap); + if (ret == -1) { + fprintf(stderr, "Memory Error\n"); + log_printf(SMG_LOG_ALL, server.log, "Crtical ERROR: Memory Error!\n"); + } else { + int err; + int len = strlen(data); + err=send(socket, data, strlen(data), 0); + if (err != strlen(data)) { + log_printf(SMG_LOG_DEBUG_8, server.log, "ERROR: Failed to send data to woomera socket(%i): err=%i len=%d %s\n", + socket,err,len,strerror(errno)); + ret = err; + } else { + ret = 0; + } + + free(data); + } + + return ret; +} + + + +static int woomera_next_pair(struct woomera_config *cfg, char **var, char **val) +{ + int ret = 0; + char *p, *end; + + *var = *val = NULL; + + for(;;) { + cfg->lineno++; + + if (!fgets(cfg->buf, sizeof(cfg->buf), cfg->file)) { + ret = 0; + break; + } + + *var = cfg->buf; + + if (**var == '[' && (end = strchr(*var, ']'))) { + *end = '\0'; + (*var)++; + strncpy(cfg->category, *var, sizeof(cfg->category) - 1); + continue; + } + + if (**var == '#' || **var == '\n' || **var == '\r') { + continue; + } + + if ((end = strchr(*var, '#'))) { + *end = '\0'; + end--; + } else if ((end = strchr(*var, '\n'))) { + if (*end - 1 == '\r') { + end--; + } + *end = '\0'; + } + + p = *var; + while ((*p == ' ' || *p == '\t') && p != end) { + *p = '\0'; + p++; + } + *var = p; + + if (!(*val = strchr(*var, '='))) { + ret = -1; + log_printf(SMG_LOG_ALL, 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; + +} + + +#if 0 +static void woomera_set_span_chan(struct woomera_interface *woomera, int span, int chan) +{ + pthread_mutex_lock(&woomera->vlock); + woomera->span = span; + woomera->chan = chan; + pthread_mutex_unlock(&woomera->vlock); + +} +#endif + + +static struct woomera_event *new_woomera_event_printf(struct woomera_event *ebuf, char *fmt, ...) +{ + struct woomera_event *event = NULL; + int ret = 0; + va_list ap; + + if (ebuf) { + event = ebuf; + } else if (!(event = new_woomera_event())) { + log_printf(SMG_LOG_ALL, server.log, "Memory Error queuing event!\n"); + return NULL; + } else { + return NULL; + } + + va_start(ap, fmt); +#ifdef SOLARIS + event->data = (char *) smg_malloc(2048); + vsnprintf(event->data, 2048, fmt, ap); +#else + ret = smg_vasprintf(&event->data, fmt, ap); +#endif + va_end(ap); + if (ret == -1) { + log_printf(SMG_LOG_ALL, server.log, "Memory Error queuing event!\n"); + destroy_woomera_event(&event, EVENT_FREE_DATA); + return NULL; + } + + return event; + +} + +static struct woomera_event *woomera_clone_event(struct woomera_event *event) +{ + struct woomera_event *clone; + + if (!(clone = new_woomera_event())) { + return NULL; + } + + /* This overwrites the MALLOC in clone causing a memory leak */ + memcpy(clone, event, sizeof(*event)); + + /* We must set the malloc flag back so that this event + * will be deleted */ + _woomera_set_flag(clone, WFLAG_MALLOC); + clone->next = NULL; + clone->data = smg_strdup(event->data); + + return clone; +} + +static void enqueue_event(struct woomera_interface *woomera, + struct woomera_event *event, + event_args free_data) +{ + struct woomera_event *ptr, *clone = NULL; + + assert(woomera != NULL); + assert(event != NULL); + + if (!(clone = woomera_clone_event(event))) { + log_printf(SMG_LOG_ALL, server.log, "Error Cloning Event\n"); + return; + } + + pthread_mutex_lock(&woomera->queue_lock); + + for (ptr = woomera->event_queue; ptr && ptr->next ; ptr = ptr->next); + + if (ptr) { + ptr->next = clone; + } else { + woomera->event_queue = clone; + } + + pthread_mutex_unlock(&woomera->queue_lock); + + woomera_set_flag(woomera, WFLAG_EVENT); + + if (free_data && event->data) { + /* The event has been duplicated, the original data + * should be freed */ + smg_free(event->data); + event->data=NULL; + } +} + +static char *dequeue_event(struct woomera_interface *woomera) +{ + struct woomera_event *event; + char *data = NULL; + + if (!woomera) { + return NULL; + } + + pthread_mutex_lock(&woomera->queue_lock); + if (woomera->event_queue) { + event = woomera->event_queue; + woomera->event_queue = event->next; + data = event->data; + pthread_mutex_unlock(&woomera->queue_lock); + + destroy_woomera_event(&event, EVENT_KEEP_DATA); + return data; + } + pthread_mutex_unlock(&woomera->queue_lock); + + return data; +} + + +static int enqueue_event_on_listeners(struct woomera_event *event) +{ + struct woomera_listener *ptr; + int x = 0; + + assert(event != NULL); + + pthread_mutex_lock(&server.listen_lock); + for (ptr = server.listeners ; ptr ; ptr = ptr->next) { + enqueue_event(ptr->woomera, event, EVENT_KEEP_DATA); + x++; + } + pthread_mutex_unlock(&server.listen_lock); + + return x; +} + + +static void del_listener(struct woomera_interface *woomera) +{ + struct woomera_listener *ptr, *last = NULL; + + pthread_mutex_lock(&server.listen_lock); + for (ptr = server.listeners ; ptr ; ptr = ptr->next) { + if (ptr->woomera == woomera) { + if (last) { + last->next = ptr->next; + } else { + server.listeners = ptr->next; + } + smg_free(ptr); + break; + } + last = ptr; + } + pthread_mutex_unlock(&server.listen_lock); +} + +static void add_listener(struct woomera_interface *woomera) +{ + struct woomera_listener *new; + + pthread_mutex_lock(&server.listen_lock); + + if ((new = smg_malloc(sizeof(*new)))) { + memset(new, 0, sizeof(*new)); + new->woomera = woomera; + new->next = server.listeners; + server.listeners = new; + } else { + log_printf(SMG_LOG_ALL, server.log, "Memory Error adding listener!\n"); + } + + pthread_mutex_unlock(&server.listen_lock); +} + + + +static int wanpipe_send_dtmf(struct woomera_interface *woomera, char *digits) +{ + struct media_session *ms = woomera_get_ms(woomera); + char *cur = NULL; + int wrote = 0; + int err; + + if (!ms) { + return -EINVAL; + } + + if (!ms->dtmf_buffer) { + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Allocate DTMF Buffer...."); + + err=switch_buffer_create_dynamic(&ms->dtmf_buffer, 1024, server.dtmf_size, 0); + + if (err != 0) { + log_printf(SMG_LOG_ALL, woomera->log, "Failed to allocate DTMF Buffer!\n"); + return -ENOMEM; + } else { + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "SUCCESS!\n"); + } + + } + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Sending DTMF %s\n",digits); + for (cur = digits; *cur; cur++) { + if ((wrote = teletone_mux_tones(&ms->tone_session, + &ms->tone_session.TONES[(int)*cur]))) { + + pthread_mutex_lock(&woomera->dtmf_lock); + + err=switch_buffer_write(ms->dtmf_buffer, ms->tone_session.buffer, wrote * 2); + + pthread_mutex_unlock(&woomera->dtmf_lock); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Sending DTMF %s Wrote=%i (err=%i)\n", + digits,wrote*2,err); + } else { + log_printf(SMG_LOG_ALL, woomera->log, "Error: Sending DTMF %s (err=%i)\n", + digits,wrote); + } + } + + ms->skip_read_frames = 200; + return 0; +} + +static struct woomera_interface *alloc_woomera(void) +{ + struct woomera_interface *woomera = NULL; + + if ((woomera = smg_malloc(sizeof(struct woomera_interface)))) { + + memset(woomera, 0, sizeof(struct woomera_interface)); + + woomera->chan = -1; + woomera->span = -1; + woomera->log = server.log; + woomera->debug = server.debug; + woomera->call_id = 1; + woomera->event_queue = NULL; + woomera->q931_rel_cause_topbx=SIGBOOST_RELEASE_CAUSE_NORMAL; + woomera->q931_rel_cause_tosig=SIGBOOST_RELEASE_CAUSE_NORMAL; + + woomera_set_interface(woomera, "w-1g-1"); + + + + } + + return woomera; + +} + + +static struct woomera_interface *new_woomera_interface(int socket, struct sockaddr_in *sock_addr, int len) +{ + struct woomera_interface *woomera = NULL; + + if (socket < 0) { + log_printf(SMG_LOG_ALL, server.log, "Critical: Invalid Socket on new interface!\n"); + return NULL; + } + + if ((woomera = alloc_woomera())) { + if (socket >= 0) { + no_nagle(socket); + woomera->socket = socket; + } + + if (sock_addr && len) { + memcpy(&woomera->addr, sock_addr, len); + } + } + + return woomera; + +} + +static char *woomera_message_header(struct woomera_message *wmsg, char *key) +{ + int x = 0; + char *value = NULL; + + for (x = 0 ; x < wmsg->last ; x++) { + if (!strcasecmp(wmsg->names[x], key)) { + value = wmsg->values[x]; + break; + } + } + + return value; +} + + +#define SMG_DECODE_POLL_ERRORS(err) \ + (err & POLLERR) ? "POLLERR" : \ + (err & POLLHUP) ? "POLLHUP" : \ + (err & POLLNVAL) ? "POLLNVAL" : "UNKNOWN" + +static int waitfor_socket(int fd, int timeout, int flags, int *flags_out) +{ + struct pollfd pfds[1]; + int res; + int errflags = (POLLERR | POLLHUP | POLLNVAL); + +waitfor_socket_tryagain: + + memset(&pfds[0], 0, sizeof(pfds[0])); + + pfds[0].fd = fd; + pfds[0].events = flags | errflags; + + res = poll(pfds, 1, timeout); + + if (res == 0) { + return res; + } + + if (res > 0) { + res=-1; + *flags_out = pfds[0].revents; + if (pfds[0].revents & errflags) { + res=-1; +#if 0 + log_printf(SMG_LOG_DEBUG_10,server.log, "Wait for socket Error in revents 0x%X %s Error=%s!\n", + pfds[0].revents,strerror(errno),SMG_DECODE_POLL_ERRORS(pfds[0].revents)); +#endif + return res; + } else { + if (pfds[0].revents & flags) { + res=1; + } else { + log_printf(SMG_LOG_ALL,server.log, "Wait for socket invalid poll event in revents 0x%X Error=%s Errno=%s !\n", + pfds[0].revents,SMG_DECODE_POLL_ERRORS(pfds[0].revents),strerror(errno)); + } + } + } else { + if ((errno == EINTR || errno == EAGAIN)) { + goto waitfor_socket_tryagain; + } + log_printf(SMG_LOG_ALL,server.log, "Wait for socket error!\n"); + } + + return res; +} + + +static int waitfor_2sockets(int fda, int fdb, char *a, char *b, int timeout) +{ + struct pollfd pfds[2]; + int res = 0; + int errflags = (POLLERR | POLLHUP | POLLNVAL); + + if (fda < 0 || fdb < 0) { + return -1; + } + + +waitfor_2sockets_tryagain: + + *a=0; + *b=0; + + + memset(pfds, 0, sizeof(pfds)); + + pfds[0].fd = fda; + pfds[1].fd = fdb; + pfds[0].events = POLLIN | errflags; + pfds[1].events = POLLIN | errflags; + + + + res = poll(pfds, 2, timeout); + + if (res > 0) { + res = 1; + if ((pfds[0].revents & errflags) || (pfds[1].revents & errflags)) { + res = -1; + } else { + if ((pfds[0].revents & POLLIN)) { + *a=1; + res++; + } + if ((pfds[1].revents & POLLIN)) { + *b=1; + res++; + } + } + + if (res == 1) { + /* No event found what to do */ + res=-1; + } + } else if (res < 0) { + + if (errno == EINTR || errno == EAGAIN) { + goto waitfor_2sockets_tryagain; + } + + } + + return res; +} + + +static struct media_session *media_session_new(struct woomera_interface *woomera) +{ + struct media_session *ms = NULL; + int x; + char *p; + int span,chan; + + span=woomera->span; + chan=woomera->chan; + + log_printf(SMG_LOG_DEBUG_CALL, server.log,"Starting new MEDIA session [%s] [%s]\n", + woomera->interface,woomera->raw?woomera->raw:"N/A"); + + if ((ms = smg_malloc(sizeof(struct media_session)))) { + memset(ms, 0, sizeof(struct media_session)); + + if (woomera->loop_tdm != 1) { + for(x = 0; x < strlen(woomera->raw) ; x++) { + if (woomera->raw[x] == ':') { + break; + } + if (woomera->raw[x] == '/') { + break; + } + } + + ms->ip = smg_strndup(woomera->raw, x); + time(&ms->started); + p = woomera->raw + (x+1); + ms->port = atoi(p); + } + + time(&ms->started); + woomera_set_ms(woomera,ms); + ms->woomera = woomera; + + /* Setup artificial DTMF stuff */ + memset(&ms->tone_session, 0, sizeof(ms->tone_session)); + if (teletone_init_session(&ms->tone_session, 0, NULL, NULL)) { + log_printf(SMG_LOG_ALL, server.log, "ERROR: Failed to initialize TONE [w%ig%i]!\n", + span+1,chan+1); + } + + ms->tone_session.rate = SMG_DTMF_RATE; + ms->tone_session.duration = server.dtmf_on * (ms->tone_session.rate / 1000); + ms->tone_session.wait = server.dtmf_off * (ms->tone_session.rate / 1000); + + teletone_dtmf_detect_init (&ms->dtmf_detect, SMG_DTMF_RATE); + + } else { + log_printf(SMG_LOG_ALL, server.log, "ERROR: Memory Alloc Failed [w%ig%i]!\n", + span+1,chan+1); + } + + return ms; +} + +static void media_session_free(struct media_session *ms) +{ + if (ms->ip) { + smg_free(ms->ip); + } + + teletone_destroy_session(&ms->tone_session); + switch_buffer_destroy(&ms->dtmf_buffer); + + ms->woomera = NULL; + + smg_free(ms); +} + + +static int create_udp_socket(struct media_session *ms, 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; + + log_printf(SMG_LOG_DEBUG_9,server.log,"LocalIP %s:%d IP %s:%d \n",local_ip, local_port, ip, port); + + memset(&ms->remote_hp, 0, sizeof(ms->remote_hp)); + memset(&ms->local_hp, 0, sizeof(ms->local_hp)); + if ((ms->socket = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) { + gethostbyname_r(ip, &ms->remote_hp, buf, sizeof(buf), &result, &err); + gethostbyname_r(local_ip, &ms->local_hp, local_buf, sizeof(local_buf), &local_result, &err); + if (result && local_result) { + ms->remote_addr.sin_family = ms->remote_hp.h_addrtype; + memcpy((char *) &ms->remote_addr.sin_addr.s_addr, ms->remote_hp.h_addr_list[0], ms->remote_hp.h_length); + ms->remote_addr.sin_port = htons(port); + + ms->local_addr.sin_family = ms->local_hp.h_addrtype; + memcpy((char *) &ms->local_addr.sin_addr.s_addr, ms->local_hp.h_addr_list[0], ms->local_hp.h_length); + ms->local_addr.sin_port = htons(local_port); + + rc = bind(ms->socket, (struct sockaddr *) &ms->local_addr, sizeof(ms->local_addr)); + if (rc < 0) { + close(ms->socket); + ms->socket = -1; + + log_printf(SMG_LOG_DEBUG_9,server.log, + "Failed to bind LocalIP %s:%d IP %s:%d (%s)\n", + local_ip, local_port, ip, port,strerror(errno)); + } + + /* OK */ + + } else { + log_printf(SMG_LOG_ALL,server.log, + "Failed to get hostbyname LocalIP %s:%d IP %s:%d (%s)\n", + local_ip, local_port, ip, port,strerror(errno)); + } + } else { + log_printf(SMG_LOG_ALL,server.log, + "Failed to create/allocate UDP socket\n"); + } + + return ms->socket; +} + +static int next_media_port(void) +{ + int port; + + pthread_mutex_lock(&server.media_udp_port_lock); + port = ++server.next_media_port; + if (port > server.max_media_port) { + server.next_media_port = server.base_media_port; + port = server.base_media_port; + } + pthread_mutex_unlock(&server.media_udp_port_lock); + + return port; +} + + + +static int woomera_dtmf_transmit(struct media_session *ms, int mtu) +{ + struct woomera_interface *woomera = ms->woomera; + int bread; + unsigned char dtmf[1024]; + unsigned char dtmf_law[1024]; + sangoma_api_hdr_t hdrframe; + int i; + int slin_len = mtu*2; + short *data; + int used; + int res; + int err; + int txdtmf=0; + int flags_out; + memset(&hdrframe,0,sizeof(hdrframe)); + + if (!ms->dtmf_buffer) { + return -1; + } + + for (;;) { + + if ((used=switch_buffer_inuse(ms->dtmf_buffer)) <= 0) { + break; + } + + res = waitfor_socket(ms->sangoma_sock, -1, POLLOUT, &flags_out); + if (res <= 0) { + break; + } + +#ifdef CODEC_LAW_DEFAULT + + pthread_mutex_lock(&woomera->dtmf_lock); + if ((used=switch_buffer_inuse(ms->dtmf_buffer)) <= 0) { + pthread_mutex_unlock(&woomera->dtmf_lock); + break; + } + + bread = switch_buffer_read(ms->dtmf_buffer, dtmf, slin_len); + pthread_mutex_unlock(&woomera->dtmf_lock); + + if (bread <= 0) { + break; + } + + log_printf(SMG_LOG_DEBUG_MISC,woomera->log,"%s: Write DTMF Got %d bytes MTU=%i Coding=%i Used=%i\n", + woomera->interface,bread,mtu,ms->hw_coding,used); + + data=(short*)dtmf; + for (i=0;ihw_coding) { + /* ALAW */ + dtmf_law[i] = linear_to_alaw((int)data[i]); + } else { + /* ULAW */ + dtmf_law[i] = linear_to_ulaw((int)data[i]); + } + } + + err=sangoma_sendmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + dtmf_law, mtu, 0); + + if (err != mtu) { + log_printf(SMG_LOG_ALL, woomera->log, "Error: Failed to TX to TDM API on DTMF (err=%i mtu=%i)!\n",err,mtu); + } + + txdtmf++; + ms->skip_write_frames++; +#else +... + pthread_mutex_lock(&woomera->dtmf_lock); + bread = switch_buffer_read(ms->dtmf_buffer, dtmf, mtu); + pthread_mutex_unlock(&woomera->dtmf_lock); + + log_printf(SMG_LOG_DEBUG_MISC,woomera->log,"%s: Write DTMF Got %d bytes\n", + woomera->interface,bread); + + sangoma_sendmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + dtmf, mtu, 0); + txdtmf++; + ms->skip_write_frames++; +#endif + + } + + if (txdtmf) { + return 0; + } else { + return -1; + } +} + +static void media_loop_run(struct media_session *ms) +{ + struct woomera_interface *woomera = ms->woomera; + int sangoma_frame_len = 160; + int errs=0; + int res=0; + wanpipe_tdm_api_t tdm_api; + unsigned char circuit_frame[1024]; + char filename[100]; + FILE *filed=NULL; + int loops=0,flags_out=0; + int open_cnt = 0; + + open_cnt=0; + + sangoma_api_hdr_t hdrframe; + memset(&hdrframe,0,sizeof(hdrframe)); + memset(circuit_frame,0,sizeof(circuit_frame)); + +retry_loop: + ms->sangoma_sock = open_span_chan(woomera->span+1, woomera->chan+1); + + log_printf(SMG_LOG_PROD, server.log, "Media Loop Started %s fd=%i\n", + woomera->interface,ms->sangoma_sock); + + if (ms->sangoma_sock < 0) { + errs++; + if (errs < 5) { + usleep(500000); + goto retry_loop; + } + log_printf(SMG_LOG_ALL, server.log, "WANPIPE MEDIA Socket Error (%s) if=[%s] [w%ig%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); + + } else { + errs=0; + + if (sangoma_tdm_set_codec(ms->sangoma_sock, &tdm_api, WP_NONE) < 0 ) { + errs++; + } + + if (sangoma_tdm_flush_bufs(ms->sangoma_sock, &tdm_api)) { + errs++; + } + + if (sangoma_tdm_set_usr_period(ms->sangoma_sock, &tdm_api, 20) < 0 ) { + errs++; + } + + sangoma_frame_len = sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); + + sangoma_tdm_disable_hwec(ms->sangoma_sock,&tdm_api); + ms->oob_disable = 0; +#ifdef LIBSANGOMA_VERSION + open_cnt = sangoma_get_open_cnt(ms->sangoma_sock, &tdm_api); + if (open_cnt > 1) { + ms->oob_disable = 1; + } +#endif + } + + if (errs) { + log_printf(SMG_LOG_ALL, server.log, "Media Loop: failed to open tdm device %s\n", + woomera->interface); + return; + } + + if (server.loop_trace) { + sprintf(filename,"/smg/w%ig%i-loop.trace",woomera->span+1,woomera->chan+1); + unlink(filename); + filed = safe_fopen(filename, "w"); + } + + while ( woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + !woomera_test_flag(woomera, WFLAG_MEDIA_END) && + ((res = waitfor_socket(ms->sangoma_sock, 1000, POLLIN, &flags_out)) >= 0)) { + + if (res == SMG_SOCKET_EVENT_TIMEOUT) { + //log_printf(SMG_LOG_DEBUG_8, server.log, "%s: TDM UDP Timeout !!!\n", + // woomera->interface); + /* NENAD Timeout thus just continue */ + continue; + } + + res = sangoma_readmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + circuit_frame, + sizeof(circuit_frame), 0); + if (res < 0) { + log_printf(SMG_LOG_ALL, server.log, "TDM Loop ReadMsg Error: %s\n", + strerror(errno), woomera->interface); + break; + } + + if (server.loop_trace && filed != NULL) { + int i; + for (i=0;isangoma_sock, + &hdrframe, + sizeof(hdrframe), + circuit_frame, + res, 0); + + res=0; + + loops++; + } + + + if (res < 0) { + if (!woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + log_printf(SMG_LOG_ALL, server.log, "Media Loop: socket error %s (fd=%i) %s\n", + woomera->interface, ms->sangoma_sock, strerror(errno)); + } + } + + + if (server.loop_trace && filed != NULL) { + fclose(filed); + } + + sangoma_tdm_enable_hwec(ms->sangoma_sock,&tdm_api); + + close_span_chan(&ms->sangoma_sock, woomera->span+1, woomera->chan+1); + + if (loops < 1) { + log_printf(SMG_LOG_ALL, server.log, "Media Loop FAILED %s Master=%i MediaEnd=%i Loops=%i\n", + woomera->interface, + woomera_test_flag(&server.master_connection, WFLAG_RUNNING), + woomera_test_flag(woomera, WFLAG_MEDIA_END),loops); + } else { + log_printf(SMG_LOG_PROD, server.log, "Media Loop PASSED %s Master=%i MediaEnd=%i Loops=%i\n", + woomera->interface, + woomera_test_flag(&server.master_connection, WFLAG_RUNNING), + woomera_test_flag(woomera, WFLAG_MEDIA_END),loops); + } + + return; + +} + + + + +#ifdef WP_HPTDM_API +static int media_rx_ready(void *p, unsigned char *data, int len) +{ + struct media_session *ms = (struct media_session *)p; + + if (ms->udp_sock < 0) { + return -1; + } + + return sendto(ms->udp_sock, + data,len, 0, + (struct sockaddr *) &ms->remote_addr, + sizeof(ms->remote_addr)); + + +} +#endif + +static void *media_thread_run(void *obj) +{ + struct media_session *ms = obj; + struct woomera_interface *woomera = ms->woomera; + int sangoma_frame_len = 160; + int local_port, x = 0, errs = 0, res = 0, packet_len = 0; + //int udp_cnt=0; + struct woomera_event wevent; + wanpipe_tdm_api_t tdm_api; + FILE *tx_fd=NULL; + int sock_timeout=200; + int hwec_reenable=0; + int open_cnt = 0; + + open_cnt=0; + + if (woomera_test_flag(woomera, WFLAG_MEDIA_END) || + !woomera->interface || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "MEDIA session for [%s] Cancelled! (ptr=%p)\n", + woomera->interface,woomera); + /* In this case the call will be closed via woomera_thread_run + * function. And the process table will be cleard there */ + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_MEDIA_RUNNING); + media_session_free(ms); + pthread_exit(NULL); + return NULL; + } + + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA session for [%s] started (ptr=%p loop=%i)\n", + woomera->interface,woomera,woomera->loop_tdm); + + if (woomera->loop_tdm) { + media_loop_run(ms); + ms->udp_sock=-1; + woomera_set_flag(woomera, WFLAG_HANGUP); + woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + goto media_thread_exit; + } + + for(x = 0; x < 1000 ; x++) { + local_port = next_media_port(); + if ((ms->udp_sock = create_udp_socket(ms, server.media_ip, local_port, ms->ip, ms->port)) > -1) { + break; + } + } + + /* Normal Temporary Failure */ + woomera->q931_rel_cause_topbx = 41; + + if (ms->udp_sock < 0) { + log_printf(SMG_LOG_ALL, server.log, "UDP Socket Error (%s) [%s] LocalPort=%d\n", + strerror(errno), woomera->interface, local_port); + + errs++; + } else { + int media_retry_cnt=0; +#ifdef WP_HPTDM_API + hp_tdm_api_span_t *span=hptdmspan[woomera->span+1]; + if (!span || !span->init) { + errs++; + } else { + hp_tdm_api_usr_callback_t usr_callback; + memset(&usr_callback,0,sizeof(usr_callback)); + usr_callback.p = ms; + usr_callback.rx_avail = media_rx_ready; + if (span->open_chan(span, &usr_callback, &ms->tdmchan,woomera->chan+1)) { + errs++; + /* Switch Congestion */ + woomera->q931_rel_cause_topbx = 42; + } + } +#else +media_retry: + ms->sangoma_sock = open_span_chan(woomera->span+1, woomera->chan+1); + if (ms->sangoma_sock < 0) { + + if (!woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + media_retry_cnt++; + if (media_retry_cnt < 5) { + log_printf(SMG_LOG_ALL, server.log, "WANPIPE Socket Retry [w%ig%i]\n", + woomera->span+1, woomera->chan+1); + usleep(100000); + goto media_retry; + } + + log_printf(SMG_LOG_ALL, server.log, "WANPIPE Socket Error (%s) if=[%s] [w%ig%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); + + /* Switch Congestion */ + woomera->q931_rel_cause_topbx = 42; + } + + errs++; + } else { + +# ifdef CODEC_LAW_DEFAULT + if (sangoma_tdm_set_codec(ms->sangoma_sock, &tdm_api, WP_NONE) < 0 ) { + errs++; + } +# else + if (sangoma_tdm_set_codec(ms->sangoma_sock, &tdm_api, WP_SLINEAR) < 0 ) { + errs++; + } +# endif + if (sangoma_tdm_flush_bufs(ms->sangoma_sock, &tdm_api)) { + errs++; + } + + if (sangoma_tdm_set_usr_period(ms->sangoma_sock, &tdm_api, 20) < 0 ) { + errs++; + } + +# ifdef CODEC_LAW_DEFAULT +# ifdef LIBSANGOMA_GET_HWCODING + ms->hw_coding=sangoma_tdm_get_hw_coding(ms->sangoma_sock, &tdm_api); + if (ms->hw_coding < 0) { + errs++; + } +# else +# error "libsangoma missing hwcoding feature: not up to date!" +# endif +# endif + + +# ifdef LIBSANGOMA_GET_HWDTMF + ms->hw_dtmf=sangoma_tdm_get_hw_dtmf(ms->sangoma_sock, &tdm_api); + if (ms->hw_dtmf) { + log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Supported [w%ig%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); + } else { + log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Not Supported [w%ig%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); + } +#else + ms->hw_dtmf=0; + log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Not Supported [w%ig%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); +# warning "libsangoma missing hwdtmf feature: not up to date!" + +#endif + + ms->oob_disable = 0; +#ifdef LIBSANGOMA_VERSION + open_cnt = sangoma_get_open_cnt(ms->sangoma_sock, &tdm_api); + if (open_cnt > 1) { + ms->oob_disable = 1; + } +#endif + if (!bearer_cap_is_audio(woomera->bearer_cap)) { + int err; + err=sangoma_tdm_disable_hwec(ms->sangoma_sock, &tdm_api); + if (err == 0) { + hwec_reenable=1; + log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] Disabling hwec Ok\n",woomera->interface); + } else { + log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] Disabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + } + + } + + sangoma_frame_len = sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); + + } +#endif + } + + + +#ifdef WP_HPTDM_API + /* No tdm thread */ +#else +#ifndef SMG_NO_MEDIA + if (!errs && + launch_media_tdm_thread(woomera)) { + errs++; + } +#endif +#endif + + if (errs) { + + woomera->q931_rel_cause_topbx = 42; + + } else { + + unsigned char udp_frame[4096]; + unsigned int fromlen = sizeof(struct sockaddr_in); + int flags_out=0; + + sangoma_api_hdr_t hdrframe; + memset(&hdrframe,0,sizeof(hdrframe)); + memset(udp_frame,0,sizeof(udp_frame)); +#ifdef DOTRACE + int fdin, fdout; + char path_in[512], path_out[512]; +#endif + + new_woomera_event_printf(&wevent, + "EVENT MEDIA %s AUDIO%s" + "Unique-Call-Id: %s%s" + "Raw-Audio: %s:%d%s" + "Call-ID: %s%s" + "Raw-Format: PCM-16%s" + "DTMF: %s%s" + , + woomera->interface, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + server.media_ip, + local_port, + WOOMERA_LINE_SEPERATOR, + woomera->interface, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + (ms->hw_dtmf)? "OutofBand": "InBand", + + WOOMERA_RECORD_SEPERATOR + ); + + + enqueue_event(woomera, &wevent, EVENT_FREE_DATA); + +#ifdef DOTRACE + sprintf(path_in, "/tmp/debug-in.%d.raw", tc); + sprintf(path_out, "/tmp/debug-out.%d.raw", tc++); + fdin = open(path_in, O_WRONLY | O_CREAT, O_TRUNC, 0600); + fdout = open(path_out, O_WRONLY | O_CREAT, O_TRUNC, 0600); +#endif + + if (server.out_tx_test) { + tx_fd=fopen("/smg/sound.raw","rb"); + if (!tx_fd) { + log_printf(SMG_LOG_ALL,server.log, "FAILED TO OPEN Sound file!\n"); + } + } + + + for (;;) { + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) || + woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + res=0; + break; + } + + res = waitfor_socket(ms->udp_sock, sock_timeout, POLLIN, &flags_out); + + if (res < 0) { + break; + } + +#ifdef SMG_NO_MEDIA + continue; +#endif + + if (res == 0) { + + if (woomera_dtmf_transmit(ms,sangoma_frame_len) == 0) { + sock_timeout=(sangoma_frame_len/codec_sample); + } else { + sock_timeout=200; + } + + if (ms->skip_write_frames > 0) { + ms->skip_write_frames--; + } + + log_printf(SMG_LOG_DEBUG_8, server.log, "%s: UDP Sock Timeout !!!\n", + woomera->interface); + /* NENAD Timeout thus just continue */ + continue; + } + + if ((packet_len = recvfrom(ms->udp_sock, udp_frame, sizeof(udp_frame), + MSG_DONTWAIT, (struct sockaddr *) &ms->local_addr, &fromlen)) < 1) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "UDP Recv Error: %s\n",strerror(errno)); + break; + } + + +#ifdef SMG_DROP_SEQ + if (drop_seq) { + log_printf(SMG_LOG_ALL,server.log,"Dropping TX Sequence! %i\n",drop_seq); + drop_seq--; + continue; + } +#endif + +#if 0 + log_printf(SMG_LOG_DEBUG_10, server.log, "%s: UDP Receive %i !!!\n", + woomera->interface,packet_len); +#endif + + if (packet_len > 0) { + +#if 0 +/* NC: This can cause skb_over panic must be retested */ + if (packet_len != sangoma_frame_len && ms->udp_sync_cnt <= 5) { + /* Assume that we will always receive SLINEAR here */ + sangoma_tdm_set_usr_period(ms->sangoma_sock, + &tdm_api, packet_len/codec_sample); + sangoma_frame_len = + sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); + + log_printf(SMG_LOG_DEBUG_MISC, server.log, + "%s: UDP TDM Period ReSync to Len=%i %ims (udp=%i) \n", + woomera->interface,sangoma_frame_len, + sangoma_frame_len/codec_sample,packet_len); + + + if (++ms->udp_sync_cnt >= 6) { + sangoma_tdm_set_usr_period(ms->sangoma_sock, + &tdm_api, 20); + sangoma_frame_len = + sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); + log_printf(SMG_LOG_ALL, server.log, + "%s: UDP TDM Period Force ReSync to 20ms \n", + woomera->interface); + } + + } +#endif + if (!server.out_tx_test) { + + if (woomera_dtmf_transmit(ms,sangoma_frame_len) == 0) { + sock_timeout=(sangoma_frame_len/codec_sample); + /* For sanity sake if we are doing the out test + * dont take any chances force tx udp data */ + if (ms->skip_write_frames > 0) { + ms->skip_write_frames--; + } + continue; + } else { + sock_timeout=200; + } + + if (ms->skip_write_frames > 0) { + ms->skip_write_frames--; + continue; + } + + } + + if (server.out_tx_test && tx_fd && + fread((void*)udp_frame, + sizeof(char), + packet_len,tx_fd) <= 0) { + + sangoma_get_full_cfg(ms->sangoma_sock,&tdm_api); + fclose(tx_fd); + tx_fd=NULL; + } + +#ifdef WP_HPTDM_API + if (ms->tdmchan->push) { + ms->tdmchan->push(ms->tdmchan,udp_frame,packet_len); + } +#else + + sangoma_sendmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + udp_frame, + packet_len, 0); +#endif + + } + +#if 0 + if (woomera->span == 1 && woomera->chan == 1) { + udp_cnt++; + if (udp_cnt && udp_cnt % 1000 == 0) { + log_printf(SMG_LOG_ALL, server.log, "%s: MEDIA UDP TX RX CNT %i %i\n", + woomera->interface,udp_cnt,packet_len); + } + } +#endif + } + + if (res < 0) { + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) || + woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + res=0; + } else { + log_printf(SMG_LOG_ALL, server.log, "Media Thread: socket error %s SockID=%i %s Poll=%s!\n", + woomera->interface,ms->sangoma_sock,strerror(errno),SMG_DECODE_POLL_ERRORS(flags_out)); + } + } + } + + new_woomera_event_printf(&wevent, + "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Start-Time: %ld%s" + "End-Time: %ld%s" + "Answer-Time: %ld%s" + "Call-ID: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + woomera->interface, + WOOMERA_LINE_SEPERATOR, + + woomera->session, + WOOMERA_LINE_SEPERATOR, + + time(&ms->started), + WOOMERA_LINE_SEPERATOR, + + time(NULL), + WOOMERA_LINE_SEPERATOR, + + time(&ms->answered), + WOOMERA_LINE_SEPERATOR, + + woomera->interface, + WOOMERA_LINE_SEPERATOR, + + q931_rel_to_str(woomera->q931_rel_cause_topbx), + WOOMERA_LINE_SEPERATOR, + + woomera->q931_rel_cause_topbx, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + +media_thread_exit: + + if (hwec_reenable) { + int err; + err=sangoma_tdm_enable_hwec(ms->sangoma_sock, &tdm_api); + if (err==0) { + log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] Re-enabling hwec ok\n",woomera->interface); + } else { + log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] Re-enabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + } + } + + if (woomera_test_flag(woomera, WFLAG_MEDIA_TDM_RUNNING)) { + woomera_set_flag(woomera, WFLAG_MEDIA_END); + + /* Dont wait for the other thread */ + close_socket(&ms->udp_sock); + close_span_chan(&ms->sangoma_sock, woomera->span+1, woomera->chan+1); + while(woomera_test_flag(woomera, WFLAG_MEDIA_TDM_RUNNING)) { + usleep(1000); + sched_yield(); + } + } + + + close_socket(&ms->udp_sock); + close_span_chan(&ms->sangoma_sock, woomera->span+1, woomera->chan+1); + + if (tx_fd){ + fclose(tx_fd); + tx_fd=NULL; + } + + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + + woomera_set_ms(woomera,NULL); + woomera_clear_flag(woomera, WFLAG_MEDIA_RUNNING); + + media_session_free(ms); + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA session for [%s] ended (ptr=%p)\n", + woomera->interface,woomera); + + + pthread_exit(NULL); + return NULL; +} + + + + +static void *media_tdm_thread_run(void *obj) +{ + wanpipe_tdm_api_t tdm_api; + wp_tdm_api_event_t *rx_event; + + struct media_session *ms = obj; + struct woomera_interface *woomera = ms->woomera; + int res = 0; + unsigned char circuit_frame[1024]; + sangoma_api_hdr_t hdrframe; + int flags_out; + int poll_opt = POLLIN | POLLPRI; + + memset(&hdrframe,0,sizeof(hdrframe)); + memset(circuit_frame,0,sizeof(circuit_frame)); + + memset(&tdm_api, 0, sizeof(wanpipe_tdm_api_t)); + + if (woomera_test_flag(woomera, WFLAG_MEDIA_END) || !woomera->interface) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA TDM session for [%s] Cancelled! (ptr=%p)\n", + woomera->interface,woomera); + /* In this case the call will be closed via woomera_thread_run + * function. And the process table will be cleard there */ + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + pthread_exit(NULL); + return NULL; + } + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA TDM session for [%s] started (ptr=%p)\n", + woomera->interface,woomera); + + + for (;;) { + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) || + woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + res=0; + break; + } + + if (ms->oob_disable) { + poll_opt = POLLIN; + } else { + poll_opt = POLLIN | POLLPRI; + } + res = waitfor_socket(ms->sangoma_sock, 1000, poll_opt, &flags_out); + + + if (res < 0) { + break; + } + + if (res == 0) { +#if 0 + log_printf(SMG_LOG_DEBUG_8, server.log, "%s: TDM UDP Timeout !!!\n", + woomera->interface); + /* NENAD Timeout thus just continue */ +#endif + continue; + } + + + if (flags_out & POLLIN) { + + res = sangoma_readmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + circuit_frame, + sizeof(circuit_frame), 0); + + if (res < 0) { + if (!woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + log_printf(SMG_LOG_ALL, server.log, "TDM Read Data Error: %s %s Sockid=%i\n", + woomera->interface, + strerror(errno),ms->sangoma_sock); + } + break; + } + + res = sendto(ms->udp_sock, + circuit_frame, + res, 0, + (struct sockaddr *) &ms->remote_addr, + sizeof(ms->remote_addr)); + + if (res < 0) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "UDP Sento Error: %s\n", strerror(errno)); + + } + } + + if (flags_out & POLLPRI) { + + res = sangoma_tdm_read_event(ms->sangoma_sock, &tdm_api); + if (res < 0) { + log_printf(SMG_LOG_ALL, server.log, "TDM Read Event Error: %s %s Sockid=%i\n", + woomera->interface, + strerror(errno),ms->sangoma_sock); + break; + } + + rx_event = &tdm_api.wp_tdm_cmd.event; + + switch (rx_event->wp_tdm_api_event_type){ +# ifdef LIBSANGOMA_GET_HWDTMF + case WP_TDMAPI_EVENT_DTMF: + + /* Only handle hw dtmf if hw_dtmf option is enabled */ + if (!ms->hw_dtmf) { + break; + } + + /* PORT_SOUT = 1 */ + if (rx_event->wp_tdm_api_event_dtmf_port == WAN_EC_CHANNEL_PORT_SOUT && + rx_event->wp_tdm_api_event_dtmf_type == WAN_EC_TONE_PRESENT) { + + handle_event_dtmf(woomera, rx_event->wp_tdm_api_event_dtmf_digit); + } + break; +#endif + default: + log_printf(SMG_LOG_ALL, server.log, "TDM API Unknown OOB Event %i\n", + rx_event->wp_tdm_api_event_type); + break; + } + } + +#if 0 + if (woomera->span == 1 && woomera->chan == 1) { + tdm_cnt++; + if (tdm_cnt && tdm_cnt % 1000 == 0) { + log_printf(SMG_LOG_ALL, server.log, "%s: MEDIA TDM TX RX CNT %i %i\n", + woomera->interface,tdm_cnt,res); + } + } +#endif + + } + + if (res < 0) { + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) || + woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + + /* Good reason to exit */ + + } else { + + log_printf(SMG_LOG_ALL, server.log, "Media TDM Thread: socket error %s Sockid=%i %s Woomera Flags=0x%08X Poll=%s!\n", + woomera->interface,ms->sangoma_sock,strerror(errno),woomera->flags,SMG_DECODE_POLL_ERRORS(flags_out)); + woomera_print_flags(woomera,0); + } + } + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA TDM session for [%s] ended (ptr=%p)\n", + woomera->interface,woomera); + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + + pthread_exit(NULL); + return NULL; + +} + + +/* This function must be called with process_lock + * because it modifies shared process_table */ + +static int launch_media_thread(struct woomera_interface *woomera) +{ + pthread_attr_t attr; + int result = -1; + struct media_session *ms; + + if ((ms = media_session_new(woomera))) { + result = pthread_attr_init(&attr); + //pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + //pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + woomera_set_flag(woomera, WFLAG_MEDIA_RUNNING); + result = pthread_create(&ms->thread, &attr, media_thread_run, ms); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Thread! %s\n", + __FUNCTION__,strerror(errno)); + woomera_clear_flag(woomera, WFLAG_MEDIA_RUNNING); + media_session_free(woomera->ms); + + } + pthread_attr_destroy(&attr); + + } else { + log_printf(SMG_LOG_ALL, server.log, "Failed to start new media session\n"); + } + + return result; + +} + +static int launch_media_tdm_thread(struct woomera_interface *woomera) +{ + pthread_attr_t attr; + int result = -1; + struct media_session *ms = woomera_get_ms(woomera); + + if (!ms) { + return result; + } + + result = pthread_attr_init(&attr); + //pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + //pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + woomera_set_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + result = pthread_create(&ms->thread, &attr, media_tdm_thread_run, ms); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Thread! %s\n", + __FUNCTION__,strerror(errno)); + woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + } + pthread_attr_destroy(&attr); + + return result; +} + + +static struct woomera_interface * launch_woomera_loop_thread(short_signal_event_t *event) +{ + + struct woomera_interface *woomera = NULL; + char callid[20]; + + sprintf(callid, "w%dg%d", event->span+1,event->chan+1); + + if ((woomera = alloc_woomera())) { + + woomera->chan = event->chan; + woomera->span = event->span; + woomera->log = server.log; + woomera->debug = server.debug; + woomera->call_id = 1; + woomera->event_queue = NULL; + woomera->loop_tdm=1; + woomera->socket=-1; + + } else { + log_printf(SMG_LOG_ALL, server.log, "Critical ERROR: memory/socket error\n"); + return NULL; + } + + woomera_set_interface(woomera,callid); + + pthread_mutex_lock(&server.process_lock); + server.process_table[event->span][event->chan].dev = woomera; + pthread_mutex_unlock(&server.process_lock); + + if (launch_woomera_thread(woomera)) { + pthread_mutex_lock(&server.process_lock); + server.process_table[event->span][event->chan].dev = NULL; + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + smg_free(woomera); + log_printf(SMG_LOG_ALL, server.log, "Critical ERROR: memory/socket error\n"); + return NULL; + } + + return woomera; +} + +static int woomera_message_parse(struct woomera_interface *woomera, struct woomera_message *wmsg, int timeout) +{ + char *cur, *cr, *next = NULL, *eor = NULL; + char buf[2048]; + int res = 0, bytes = 0, sanity = 0; + struct timeval started, ended; + int elapsed, loops = 0; + int failto = 0; + int packet = 0; + int flags_out = 0; + + memset(wmsg, 0, sizeof(*wmsg)); + + if (woomera->socket < 0 ) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, WOOMERA_DEBUG_PREFIX "%s Invalid Socket! %d\n", + woomera->interface,woomera->socket); + return -1; + } + + if (woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_DEBUG_9, woomera->log, WOOMERA_DEBUG_PREFIX + "%s Woomera Message parse: Call Hangup - skipping message parse !\n", + woomera->interface); + return -1; + } + + gettimeofday(&started, NULL); + memset(buf, 0, sizeof(buf)); + + if (timeout < 0) { + timeout = abs(timeout); + failto = 1; + } else if (timeout == 0) { + timeout = -1; + } + + + while (!(eor = strstr(buf, WOOMERA_RECORD_SEPERATOR))) { + if (sanity > 1000) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, WOOMERA_DEBUG_PREFIX "%s Failed Sanity Check!\n[%s]\n\n", woomera->interface, buf); + return -1; + } + + if ((res = waitfor_socket(woomera->socket, 1000, POLLIN, &flags_out) > 0)) { + + res = recv(woomera->socket, buf, sizeof(buf), MSG_PEEK); + + if (res > 1) { + packet++; + } + if (!strncmp(buf, WOOMERA_LINE_SEPERATOR, 2)) { + res = read(woomera->socket, buf, 2); + return 0; + } + if (res == 0) { + sanity++; + /* Looks Like it's time to go! */ + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_DEBUG_9, woomera->log, WOOMERA_DEBUG_PREFIX + "%s MEDIA END or HANGUP \n", woomera->interface); + return -1; + } + ysleep(1000); + continue; + + } else if (res < 0) { + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, WOOMERA_DEBUG_PREFIX + "%s error during packet retry (err=%i) Loops#%d (%s)\n", + woomera->interface, res, loops, + strerror(errno)); + return res; + } else if (loops) { + ysleep(100000); + } + } + + gettimeofday(&ended, NULL); + elapsed = (((ended.tv_sec * 1000) + ended.tv_usec / 1000) - ((started.tv_sec * 1000) + started.tv_usec / 1000)); + + if (res < 0) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, WOOMERA_DEBUG_PREFIX "%s Bad RECV\n", + woomera->interface); + return res; + } else if (res == 0) { + sanity++; + /* Looks Like it's time to go! */ + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_DEBUG_9, woomera->log, WOOMERA_DEBUG_PREFIX + "%s MEDIA END or HANGUP \n", woomera->interface); + return -1; + } + ysleep(1000); + continue; + } + + if (packet && loops > 150) { + log_printf(SMG_LOG_PROD, woomera->log, WOOMERA_DEBUG_PREFIX + "%s Timeout waiting for packet.\n", + woomera->interface); + return -1; + } + + if (timeout > 0 && (elapsed > timeout)) { + log_printf(SMG_LOG_PROD, woomera->log, WOOMERA_DEBUG_PREFIX + "%s Timeout [%d] reached\n", + woomera->interface, timeout); + return failto ? -1 : 0; + } + + if (woomera_test_flag(woomera, WFLAG_EVENT)) { + /* BRB! we have an Event to deliver....*/ + return 0; + } + + /* what're we still doing here? */ + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + !woomera_test_flag(woomera, WFLAG_RUNNING)) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, WOOMERA_DEBUG_PREFIX + "%s Woomera Message Parse: Server or Woomera not Running\n", woomera->interface); + return -1; + } + loops++; + } + + *eor = '\0'; + bytes = strlen(buf) + 4; + + memset(buf, 0, sizeof(buf)); + res = read(woomera->socket, buf, bytes); + next = buf; + + log_printf(SMG_LOG_WOOMERA, woomera->log, "%s:WOOMERA RX MSG: %s\n",woomera->interface,buf); + + while ((cur = next)) { + + if ((cr = strstr(cur, WOOMERA_LINE_SEPERATOR))) { + *cr = '\0'; + next = cr + (sizeof(WOOMERA_LINE_SEPERATOR) - 1); + if (!strcmp(next, WOOMERA_RECORD_SEPERATOR)) { + break; + } + } + if (!cur || !*cur) { + break; + } + + if (!wmsg->last) { + char *cmd, *id, *args; + woomera_set_flag(wmsg, MFLAG_EXISTS); + cmd = cur; + + if ((id = strchr(cmd, ' '))) { + *id = '\0'; + id++; + if ((args = strchr(id, ' '))) { + *args = '\0'; + args++; + strncpy(wmsg->command_args, args, sizeof(wmsg->command_args)-1); + } + strncpy(wmsg->callid, id, sizeof(wmsg->callid)-1); + } + + strncpy(wmsg->command, cmd, sizeof(wmsg->command)-1); + } else { + char *name, *val; + name = cur; + + if ((val = strchr(name, ':'))) { + *val = '\0'; + val++; + while (*val == ' ') { + *val = '\0'; + val++; + } + strncpy(wmsg->values[wmsg->last-1], val, WOOMERA_STRLEN); + } + strncpy(wmsg->names[wmsg->last-1], name, WOOMERA_STRLEN); + if (name && val && !strcasecmp(name, "content-type")) { + woomera_set_flag(wmsg, MFLAG_CONTENT); + bytes = atoi(val); + } + if (name && val && !strcasecmp(name, "content-length")) { + woomera_set_flag(wmsg, MFLAG_CONTENT); + bytes = atoi(val); + } + + + } + wmsg->last++; + } + + wmsg->last--; + + if (bytes && woomera_test_flag(wmsg, MFLAG_CONTENT)) { + int terr; + terr=read(woomera->socket, wmsg->body, + (bytes > sizeof(wmsg->body)) ? sizeof(wmsg->body) : bytes); + } + + return woomera_test_flag(wmsg, MFLAG_EXISTS); + +} + +static struct woomera_interface *pull_from_holding_tank(int index, int span , int chan) +{ + struct woomera_interface *woomera = NULL; + + if (index < 1 || index >= CORE_TANK_LEN) { + if (index != 0) { + log_printf(SMG_LOG_ALL, server.log, "%s Error on invalid TANK INDEX = %i\n", + __FUNCTION__,index); + } + return NULL; + } + + pthread_mutex_lock(&server.ht_lock); + if (server.holding_tank[index] && + server.holding_tank[index] != &woomera_dead_dev) { + woomera = server.holding_tank[index]; + + /* Block this index until the call is completed */ + server.holding_tank[index] = &woomera_dead_dev; + + woomera->index = 0; + woomera->span=span; + woomera->chan=chan; + } + pthread_mutex_unlock(&server.ht_lock); + + return woomera; +} + +static void clear_all_holding_tank(void) +{ + int i; + pthread_mutex_lock(&server.ht_lock); + for (i=0;i= CORE_TANK_LEN) { + if (index != 0) { + log_printf(SMG_LOG_ALL, server.log, "%s Error on invalid TANK INDEX = %i\n", + __FUNCTION__,index); + } + return NULL; + } + + pthread_mutex_lock(&server.ht_lock); + woomera = server.holding_tank[index]; + pthread_mutex_unlock(&server.ht_lock); + + return woomera; +} + + +static void clear_from_holding_tank(int index, struct woomera_interface *woomera) +{ + + if (index < 1 || index >= CORE_TANK_LEN) { + if (index != 0) { + log_printf(SMG_LOG_ALL, server.log, "%s Error on invalid TANK INDEX = %i\n", + __FUNCTION__,index); + } + return; + } + + pthread_mutex_lock(&server.ht_lock); + if (server.holding_tank[index] == &woomera_dead_dev) { +#if 0 + log_printf(SMG_LOG_ALL,server.log, "%s Clearing DEAD id=%i OK\n", + __FUNCTION__,index); +#endif + server.holding_tank[index] = NULL; + } else if (woomera && server.holding_tank[index] == woomera) { +#if 0 + log_printf(SMG_LOG_ALL,server.log, "%s Clearing ACTIVE Woomera id=%i OK\n", + __FUNCTION__,index); +#endif + server.holding_tank[index] = NULL; + } else if (server.holding_tank[index]) { + log_printf(SMG_LOG_ALL, server.log, "Critical Error: Holding tank index %i not cleared %p !\n", + index, server.holding_tank[index]); + } + pthread_mutex_unlock(&server.ht_lock); + + return; +} + +static struct woomera_interface *peek_from_holding_tank(int index) +{ + struct woomera_interface *woomera = NULL; + + if (index < 1 || index >= CORE_TANK_LEN) { + if (index != 0) { + log_printf(SMG_LOG_ALL, server.log, "%s Error on invalid TANK INDEX = %i\n", + __FUNCTION__,index); + } + return NULL; + } + + pthread_mutex_lock(&server.ht_lock); + if (server.holding_tank[index] && + server.holding_tank[index] != &woomera_dead_dev) { + woomera = server.holding_tank[index]; + } + pthread_mutex_unlock(&server.ht_lock); + + return woomera; +} + +static int add_to_holding_tank(struct woomera_interface *woomera) +{ + int next, i, found=0; + + pthread_mutex_lock(&server.ht_lock); + + for (i=0;i= CORE_TANK_LEN) { + next = server.holding_tank_index = 1; + } + + if (next == 0) { + log_printf(SMG_LOG_ALL, server.log, "\nCritical Error on TANK INDEX == 0\n"); + continue; + } + + if (server.holding_tank[next]) { + continue; + } + + found=1; + break; + } + + if (!found) { + /* This means all tank vales are busy + * should never happend */ + pthread_mutex_unlock(&server.ht_lock); + log_printf(SMG_LOG_ALL, server.log, "\nCritical Error failed to obtain a TANK INDEX\n"); + return 0; + } + + server.holding_tank[next] = woomera; + woomera->timeout = time(NULL) + server.call_timeout; + + pthread_mutex_unlock(&server.ht_lock); + return next; +} + + + +static void handle_event_dtmf(struct woomera_interface *woomera, unsigned char dtmf_digit) +{ + struct woomera_event wevent; + + memset(&wevent, 0, sizeof(struct woomera_event)); + + new_woomera_event_printf(&wevent, "EVENT DTMF %sUnique-Call-Id:%s%sContent-Length:%d%s%s%c%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + 1, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + dtmf_digit, + WOOMERA_RECORD_SEPERATOR); + + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + return; +} + +<<<<<<< .mine +static int handle_woomera_progress(struct woomera_interface *woomera, + struct woomera_message *wmsg) +{ + call_signal_event_t event; + int err=-1; + + memset(&event, 0, sizeof(event)); + event.event_id = SIGBOOST_EVENT_CALL_PROGRESS; + sprintf(event.isup_in_rdnis,"SMG003-EVI-2"); + event.isup_in_rdnis_size=strlen(event.isup_in_rdnis); + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s]\n", + wmsg->command, woomera->interface); + + if (!woomera_check_running(woomera)) { + socket_printf(woomera->socket, "405 PROGRESS Channel already hungup%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + if (!woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + + socket_printf(woomera->socket, "405 PROGRESS Channel not aceked%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + err=isup_exec_event(&event); + if (err) { + socket_printf(woomera->socket, + "200 %s PROGRESS OK%s" + "Unique-Call-Id: %s%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } else { + socket_printf(woomera->socket, "405 PROGRESS Boost failure%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } + + return err; +} +======= +static int handle_woomera_progress(struct woomera_interface *woomera, + struct woomera_message *wmsg) +{ + call_signal_event_t event; + int err=-1; + + memset(&event, 0, sizeof(event)); +>>>>>>> .r203 + +<<<<<<< .mine +======= + call_signal_event_init((short_signal_event_t*)&event, SIGBOOST_EVENT_CALL_PROGRESS, woomera->chan, woomera->span); + sprintf(event.isup_in_rdnis,"SMG003-EVI-2"); + event.isup_in_rdnis_size=strlen(event.isup_in_rdnis); + if (woomera->index >= 0) { + event.call_setup_id = woomera->index; + } + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s]\n", + wmsg->command, woomera->interface); + + if (!woomera_check_running(woomera)) { + socket_printf(woomera->socket, "405 PROGRESS Channel already hungup%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + if (!woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + + socket_printf(woomera->socket, "405 PROGRESS Channel not aceked%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + err=isup_exec_event(&event); + if (err == 0) { + socket_printf(woomera->socket, + "200 %s PROGRESS OK%s" + "Unique-Call-Id: %s%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } else { + socket_printf(woomera->socket, "405 PROGRESS Boost failure%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } + + return err; +} + +>>>>>>> .r203 +static int handle_woomera_media_accept_answer(struct woomera_interface *woomera, + struct woomera_message *wmsg, + int media, int answer, int accept) +{ + char *raw = woomera_message_header(wmsg, "raw-audio"); + struct woomera_event wevent; + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s]\n", + wmsg->command, woomera->interface); + + if (!woomera_check_running(woomera)) { + + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "ERROR! call was cancelled MEDIA on HANGUP or MEDIA END!\n"); + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(woomera->q931_rel_cause_topbx), + WOOMERA_LINE_SEPERATOR, + woomera->q931_rel_cause_topbx, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call already hungup!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + woomera->timeout=0; + return -1; + } + + log_printf(SMG_LOG_DEBUG_MISC, server.log,"WOOMERA: GOT %s EVENT: [%s] RAW=%s\n", + wmsg->command,wmsg->callid,raw); + + + if (raw && + woomera->raw == NULL && + !woomera_test_flag(woomera, WFLAG_RAW_MEDIA_STARTED)) { + + woomera_set_flag(woomera, WFLAG_RAW_MEDIA_STARTED); + + woomera_set_raw(woomera, raw); + + if (launch_media_thread(woomera)) { + + log_printf(SMG_LOG_DEBUG_8, server.log,"ERROR: Failed to Launch Call [%s]\n", + woomera->interface); + + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(21), + WOOMERA_LINE_SEPERATOR, + 21, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_RUNNING); + + woomera->timeout=0; + return -1; + } + } + + if (!woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + log_printf(SMG_LOG_ALL, server.log,"ERROR! Monitor Thread not running!\n"); + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + return -1; + + } else { + + if (accept) { + if (!autoacm && !woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + woomera_set_flag(woomera,WFLAG_CALL_ACKED); + isup_exec_command(woomera->span, + woomera->chan, + -1, + SIGBOOST_EVENT_CALL_START_ACK, + 0); + } + } + + if (answer) { + struct media_session *ms; + + pthread_mutex_lock(&woomera->ms_lock); + if ((ms=woomera->ms)) { + time(&woomera->ms->answered); + } + pthread_mutex_unlock(&woomera->ms_lock); + + if (ms) { + + if (!autoacm && !woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + woomera_set_flag(woomera,WFLAG_CALL_ACKED); + isup_exec_command(woomera->span, + woomera->chan, + -1, + SIGBOOST_EVENT_CALL_START_ACK, + 0); + } + + isup_exec_command(woomera->span, + woomera->chan, + -1, + SIGBOOST_EVENT_CALL_ANSWERED, + 0); + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "Sent SIGBOOST_EVENT_CALL_ANSWERED [w%dg%d]\n", + woomera->span+1,woomera->chan+1); + } else { + struct woomera_event wevent; + log_printf(SMG_LOG_ALL, server.log, + "WOOMERA ANSWER: FAILED [%s] no Media \n", + wmsg->command,wmsg->callid); + + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(21), + WOOMERA_LINE_SEPERATOR, + 21, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_RUNNING); + woomera->timeout=0; + return -1; + } + } + } + + new_woomera_event_printf(&wevent, "200 %s OK%s" + "Unique-Call-Id: %s%s", + answer ? "ANSWER" : + accept ? "ACCEPT" : "MEDIA", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + return 0; +} + +static int handle_woomera_call_start (struct woomera_interface *woomera, + struct woomera_message *wmsg) +{ + char *raw = woomera_message_header(wmsg, "raw-audio"); + call_signal_event_t event; + char *calling = woomera_message_header(wmsg, "local-number"); +#ifdef SMG_CALLING_NAME + char *calling_name = woomera_message_header(wmsg, "local-name"); +#endif + char *presentation = woomera_message_header(wmsg, "Presentation"); + char *screening = woomera_message_header(wmsg, "Screening"); + char *rdnis = woomera_message_header(wmsg, "RDNIS"); + char *bearer_cap = woomera_message_header(wmsg, "Bearer-Cap"); + char *uil1p = woomera_message_header(wmsg, "uil1p"); + char *called = wmsg->callid; + char *grp = wmsg->callid; + char *p; + int cause = 34; + int tg = 0; + int huntgroup = SIGBOOST_HUNTGRP_SEQ_ASC; + + if (smg_check_all_busy() || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ + + + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, + "405 SMG Server All Ckt Busy!%s", WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "SMG Server Full %d (ckt busy cnt=%i)\n", + server.call_count, server.all_ckt_busy); + return -1; + } + + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "New Call %d/%d\n", server.call_count, server.max_calls); + + switch(called[0]) { + case 'g': + huntgroup = SIGBOOST_HUNTGRP_SEQ_ASC; + break; + case 'G': + huntgroup = SIGBOOST_HUNTGRP_SEQ_DESC; + break; + case 'r': + huntgroup = SIGBOOST_HUNTGRP_RR_ASC; + break; + case 'R': + huntgroup = SIGBOOST_HUNTGRP_RR_DESC; + break; + default: + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, + "Warning: Failed to determine huntgroup (%s)\n", + called); + huntgroup = SIGBOOST_HUNTGRP_SEQ_ASC; + } + + if ((p = strchr(called, '/'))) { + *p = '\0'; + called = p+1; + tg = atoi(grp+1) - 1; + if (tg < 0) { + tg=0; + } + } + + woomera->trunk_group=tg; + if (raw) { + woomera_set_raw(woomera, raw); + } + + woomera->index = add_to_holding_tank(woomera); + if (woomera->index < 1) { + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, + "405 SMG Server All Tanks Busy!%s", + WOOMERA_RECORD_SEPERATOR); + log_printf(SMG_LOG_ALL, woomera->log, "Error: Call Tank Full (Call Cnt=%i)\n", + server.call_count); + return -1; + } + + + woomera->index_hold = woomera->index; + + call_signal_call_init(&event, calling, called, woomera->index); + + if (presentation) { + event.calling_number_presentation = atoi(presentation); + } else { + event.calling_number_presentation = 0; + } + + if (screening) { + event.calling_number_screening_ind = atoi(screening); + } else { + event.calling_number_screening_ind = 0; + } + + event.isup_in_rdnis_size=0; + event.isup_in_rdnis[0]=0; + + if (rdnis && strlen(rdnis) ) { + + if (strlen(rdnis) > sizeof(event.isup_in_rdnis)){ + log_printf(SMG_LOG_ALL,server.log,"Error: RDNIS Overflow (in size=%i max=%i)\n", + strlen(rdnis), sizeof(event.isup_in_rdnis)); + + } else { + + strncpy(event.isup_in_rdnis,rdnis, + sizeof(event.isup_in_rdnis)-1); + event.isup_in_rdnis_size=strlen(rdnis)+1; + log_printf(SMG_LOG_DEBUG_MISC,server.log,"RDNIS %s\n", rdnis); + } + + } + + if (bearer_cap && strlen(bearer_cap)) { + event.bearer.capability = bearer_cap_to_code(bearer_cap); + woomera->bearer_cap = event.bearer.capability; + } + + if (uil1p && strlen(uil1p)) { + event.bearer.uil1p = uil1p_to_code(uil1p); + } + +#ifdef SMG_CALLING_NAME + if (calling_name) { + strncpy((char*)event.calling_name,calling_name, + sizeof(event.calling_name)-1); + } +#endif + + event.trunk_group = tg; + event.hunt_group = huntgroup; + + if (call_signal_connection_write(&server.mcon, &event) < 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", + strerror(errno)); + + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, + "405 SMG Signalling Contestion!%s", + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + socket_printf(woomera->socket, "100 Trying%s", WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Call Called Event [Setup ID: %d] TG=%d\n", + woomera->index,tg); + + return 0; +} + + +static void interpret_command(struct woomera_interface *woomera, struct woomera_message *wmsg) +{ + int answer = 0, media = 0, accept=0; + char *unique_id; + int cause=0; + + + + if (!strcasecmp(wmsg->command, "call")) { + int err; + + if (strlen(woomera->session) != 0) { + /* Call has already been placed */ + socket_printf(woomera->socket, "400 Error Call already in progress %s", + WOOMERA_RECORD_SEPERATOR); + log_printf(SMG_LOG_ALL,server.log,"Woomera RX Call Even while call in progress!\n"); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(34), + WOOMERA_LINE_SEPERATOR, + 34, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, + "405 SMG Server All Ckt Reset!%s", WOOMERA_RECORD_SEPERATOR); + + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + err=handle_woomera_call_start(woomera,wmsg); + if (err) { + woomera_set_flag(woomera, WFLAG_HANGUP); + } + + return; + + } else if (!strcasecmp(wmsg->command, "bye") || !strcasecmp(wmsg->command, "quit")) { + char *cause = woomera_message_header(wmsg, "cause"); + char *q931cause = woomera_message_header(wmsg, "Q931-Cause-Code"); + + if (cause) { + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Bye Cause Received: [%s]\n", cause); + } + if (q931cause && atoi(q931cause)) { + woomera_set_cause_tosig(woomera,atoi(q931cause)); + } + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: Bye Received: [%s]\n", woomera->interface); + + woomera_clear_flag(woomera, WFLAG_RUNNING); + socket_printf(woomera->socket, "200 Connection closed%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return; + + } else if (!strcasecmp(wmsg->command, "listen")) { + + if (woomera_test_flag(woomera, WFLAG_LISTENING)) { + socket_printf(woomera->socket, "405 Listener already started%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } else { + + woomera_set_flag(woomera, WFLAG_LISTENING); + add_listener(woomera); + log_printf(SMG_LOG_ALL,woomera->log, "Starting Listen Device!\n"); + + socket_printf(woomera->socket, "%s", + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, "200 Listener enabled%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } + return; + + } else if ((media = !strcasecmp(wmsg->command, "debug"))) { + + int debug_level=atoi(wmsg->callid); + + if (debug_level < 10) { + server.debug=debug_level; + log_printf(SMG_LOG_ALL,server.log,"SMG Debugging set to %i (window=%i)\n",server.debug,server.mcon.txwindow); + } + + return; + } + + if (!strcasecmp(wmsg->command, "hangup")) { + char *q931cause = woomera_message_header(wmsg, "Q931-Cause-Code"); + + + if (q931cause && atoi(q931cause)) { + log_printf(SMG_LOG_DEBUG_8,server.log,"Woomera Hangup setting cause to %s %i\n", + q931cause,atoi(q931cause)); + woomera_set_cause_tosig(woomera,atoi(q931cause)); + } + + /* Continue Through */ + } + + + + unique_id = woomera_message_header(wmsg, "Unique-Call-Id"); + if (!unique_id) { + + cause=111; + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, "400 Woomera cmd without uniquie id%s" + WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_CALL,server.log,"Woomera RX Event (%s) without unique id!\n",wmsg->command); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + if (strlen(woomera->session) == 0) { + struct woomera_interface *session_woomera=NULL; + char *session=NULL; + int span, chan; + char ifname[100]; + /* If session does not exist this is an incoming call */ + sscanf(unique_id, "w%dg%d", &span, &chan); + span--; + chan--; + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, + "WOOMERA Got CMD %s Span=%d Chan=%d from session %s\n", + wmsg->command,span,chan,unique_id); + + if (smg_validate_span_chan(span,chan) != 0) { + + cause=81; + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, "404 Invalid span/chan in session%s" + WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, + "WOOMERA Warning invalid span chan in session %s %s\n", + wmsg->command,unique_id); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + pthread_mutex_lock(&server.process_lock); + session = server.process_table[span][chan].session; + session_woomera = server.process_table[span][chan].dev; + + /* This scenario is very common when we have multile clients + where multiple clients race get the incoming call */ + if (session_woomera) { + pthread_mutex_unlock(&server.process_lock); + + cause=81; + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, "404 Session not found%s" + WOOMERA_RECORD_SEPERATOR); + + + log_printf(SMG_LOG_DEBUG_8, woomera->log, "WOOMERA Error channel in use %s %s\n", + wmsg->command,unique_id); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + if (!session || strlen(session) == 0 || + strncmp(session,unique_id,sizeof(woomera->session))){ + pthread_mutex_unlock(&server.process_lock); + + /* Invalid call reference */ + cause=81; + socket_printf(woomera->socket, "event hangup %s" + "cause: %s%s" + "q931-cause-code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, "404 Invalid/Expired Session%s" + WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, + "WOOMERA Warning: Cmd=%s with invalid session %s (orig=%s)\n", + wmsg->command,unique_id,session?session:"N/A"); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + +#if 1 + if (!strcasecmp(wmsg->command, "hangup")) { + int clients=server.process_table[span][chan].clients; + if (server.process_table[span][chan].clients < 0) { + + log_printf(SMG_LOG_ALL, woomera->log, "WOOMERA CMD: Warning Clients (%i) Race condition on Hangup [w%dg%d]\n", + clients, span+1,chan+1); + + } else if (server.process_table[span][chan].clients > 1) { + server.process_table[span][chan].clients--; + pthread_mutex_unlock(&server.process_lock); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Got Hungup on Multiple Clients %i, skipping hangup [w%dg%d]\n", + clients, span+1,chan+1); + + cause=16; + socket_printf(woomera->socket, "event hangup %s" + "cause: %s%s" + "q931-cause-code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, "404 Hangup on multiple session%s" + WOOMERA_RECORD_SEPERATOR); + + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Hanging up channel [w%dg%d]\n", + span+1,chan+1); + } +#endif + + server.process_table[span][chan].dev=woomera; + strncpy(woomera->session,unique_id,sizeof(woomera->session)); + sprintf(ifname,"w%dg%d",span+1,chan+1); + woomera_set_interface(woomera, ifname); + + woomera->span=span; + woomera->chan=chan; + + /* Save bearer cap that came in on incoming call event */ + woomera->bearer_cap = server.process_table[span][chan].bearer_cap; + + /* set it to 1 so that queued digits are checked on proceed message */ + woomera->check_digits = 1; + + pthread_mutex_unlock(&server.process_lock); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA Got New If=%s Session %s\n", + woomera->interface, woomera->session); + + + } else if (strncmp(woomera->session,unique_id,sizeof(woomera->session))) { + + cause=81; + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, "404 Session Mis-match%s" + WOOMERA_RECORD_SEPERATOR); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + if (!strcasecmp(wmsg->command, "dtmf")) { + + log_printf(SMG_LOG_WOOMERA, woomera->log, + "WOOMERA CMD: DTMF Received: [%s] Digit %s Body %s\n", + woomera->interface, wmsg->command_args, wmsg->body); + + wanpipe_send_dtmf(woomera,wmsg->body); + + socket_printf(woomera->socket, "200 DTMF OK%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + + } else if (!strcasecmp(wmsg->command, "hangup")) { + + int chan = -1, span = -1; + char *cause = woomera_message_header(wmsg, "cause"); + char *q931cause = woomera_message_header(wmsg, "Q931-Cause-Code"); + + if (q931cause && atoi(q931cause)) { + woomera_set_cause_tosig(woomera,atoi(q931cause)); + } + + span=woomera->span; + chan=woomera->chan; + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Hangup Received: [%s] MEDIA EXIST Cause=%s\n", + woomera->interface,cause); + + if (smg_validate_span_chan(span,chan) != 0) { + + socket_printf(woomera->socket, "405 No Such Channel%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return; + } + + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "Hangup Received: [w%dg%d]\n", + span+1,chan+1); + + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_RUNNING); + + + socket_printf(woomera->socket, "200 HANGUP OK%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + } else if (!strcasecmp(wmsg->command, "proceed")) { + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s]\n", + wmsg->command, woomera->interface); + + socket_printf(woomera->socket, + "200 %s PROCEED OK%s" + "Unique-Call-Id: %s%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + } else if (!strcasecmp(wmsg->command, "progress")) { + + handle_woomera_progress(woomera,wmsg); + + + } else if ((media = !strcasecmp(wmsg->command, "media")) || + (answer = !strcasecmp(wmsg->command, "answer")) || + (accept = !strcasecmp(wmsg->command, "accept"))) { + + handle_woomera_media_accept_answer(woomera, wmsg, media,answer,accept); + + + } else { + log_printf(SMG_LOG_ALL, server.log,"WOOMERA INVALID EVENT: %s [%s] \n", + wmsg->command,wmsg->callid); + socket_printf(woomera->socket, "501 Command '%s' not implemented%s", + wmsg->command, WOOMERA_RECORD_SEPERATOR); + } +} + + +/* + EVENT INCOMING 1 + Remote-Address: 10.3.3.104 + Remote-Number: + Remote-Name: Anthony Minessale!8668630501 + Protocol: H.323 + User-Agent: Post Increment Woomera 1.0alpha1 (OpenH323 v1.17.2) 9/61 + H323-Call-Id: 887b1ff8-bb1f-da11-85c0-0007e98988c4 + Local-Number: 996 + Start-Time: Fri, 09 Sep 2005 12:25:14 -0400 + Local-Name: root +*/ + + +static void handle_call_answer(short_signal_event_t *event) +{ + struct woomera_interface *woomera = NULL; + int kill = 0; + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + pthread_mutex_unlock(&server.process_lock); + + if (woomera) { + char callid[80]; + struct woomera_event wevent; + + woomera->timeout = 0; + + if (!woomera->raw) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to answer call with no media!\n"); + kill++; + goto handle_call_answer_end; + } + + if (woomera_test_flag(woomera, WFLAG_ANSWER)) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to double-answer a call!\n"); + kill++; + goto handle_call_answer_end; + } + + woomera_set_flag(woomera, WFLAG_ANSWER); + + if (woomera->span != event->span || woomera->chan != event->chan) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to start media on a different channel from the one we agreed on.!\n"); + kill++; + goto handle_call_answer_end; + } + + if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to answer a dead call!\n"); + kill++; + } else { + int err; + err=0; + sprintf(callid, "w%dg%d", event->span + 1, event->chan + 1); + woomera_set_interface(woomera, callid); +#ifndef WOOMERA_EARLY_MEDIA + err=launch_media_thread(woomera); + if (err) { + log_printf(SMG_LOG_ALL, server.log,"ERROR: Failed to Launch Call [%s]\n", + woomera->interface); + + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Q931-Cause-Code: %d%s" + "Cause: %s%s", + woomera->interface, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + 21, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(21), + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + kill++; + } +#endif + + if (!kill) { + new_woomera_event_printf(&wevent, "EVENT CONNECT w%dg%d%s" + "Unique-Call-Id: %s%s", + event->span+1, + event->chan+1, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR + ); + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + } + } + } else { + log_printf(SMG_LOG_PROD, server.log, "Answer requested on non-existant session. [w%dg%d]\n", + event->span+1, event->chan+1); + kill++; + } + +handle_call_answer_end: + + if (kill) { + if (woomera) { + woomera_set_flag(woomera,WFLAG_MEDIA_END); + } else { + +/* This can casuse a double STOP + must be debugged further */ +#if 0 + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED, + SIGBOOST_RELEASE_CAUSE_NORMAL); + + log_printf(SMG_LOG_PROD, server.log, "Sent CALL STOP to Answer without session [w%dg%d]\n", + event->span+1, event->chan+1); +#endif + log_printf(SMG_LOG_ALL, server.log, "WARNING: Received Answer with no session [w%dg%d]\n", + event->span+1, event->chan+1); + } + } +} + +static void handle_call_start_ack(short_signal_event_t *event) +{ + struct woomera_interface *woomera = NULL; + struct woomera_event wevent; + int kill = 0; + + if ((woomera = peek_from_holding_tank(event->call_setup_id))) { + char callid[80]; + + if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to ack a dead call!\n"); + kill++; + } else { + int err, span, chan; + + pull_from_holding_tank(event->call_setup_id,event->span,event->chan); + sprintf(callid, "w%dg%d", event->span + 1, event->chan + 1); + + span = event->span; + chan = event->chan; + + woomera_set_flag(woomera,WFLAG_CALL_ACKED); + woomera_set_interface(woomera, callid); + + pthread_mutex_lock(&server.process_lock); + + if (server.process_table[span][chan].dev) { + struct woomera_interface *tmp_woomera = server.process_table[span][chan].dev; + woomera_set_flag(tmp_woomera,WFLAG_HANGUP); + woomera_set_flag(tmp_woomera,WFLAG_MEDIA_END); + log_printf(SMG_LOG_ALL,server.log,"Call Overrun on [w%dg%d] - Call ACK!\n", event->span+1, event->chan+1); + kill++; + } + + server.process_table[span][chan].dev = woomera; + sprintf(woomera->session,"%s-%i-%i",callid,rand(),rand()); + sprintf(server.process_table[span][chan].session,"%s-%s", + callid,woomera->session); + + + memset(server.process_table[span][chan].digits, 0, + sizeof(server.process_table[span][chan].digits)); + + server.process_table[span][chan].digits_len = 0; + + pthread_mutex_unlock(&server.process_lock); + + if (kill) { + goto woomera_call_ack_skip; + } + +#ifdef WOOMERA_EARLY_MEDIA + err=launch_media_thread(woomera); + if (err) { + log_printf(SMG_LOG_ALL, server.log,"ERROR: Failed to Launch Call [%s]\n", + woomera->interface); + + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id:%s%s" + "Q931-Cause-Code: %d%s" + "Cause: %s%s", + woomera->interface, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + 21, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(21), + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + + kill++; + } +#endif + if (!kill) { + + new_woomera_event_printf(&wevent, "EVENT PROCEED w%dg%d%s" + "Channel-Name: g%d/%d%s" + "Unique-Call-Id: %s%s", + event->span+1, + event->chan+1, + WOOMERA_LINE_SEPERATOR, + woomera->trunk_group+1, + (event->span*max_chans)+event->chan+1, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "201 Accepted%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Call Answered Event ID = %d Device = w%dg%d!\n", + event->call_setup_id,woomera->span+1,woomera->chan+1); + } + } + } else { + log_printf(SMG_LOG_PROD, server.log, + "Event (START ACK) %d referrs to a non-existant session (%d) [w%dg%d]!\n", + event->event_id, event->call_setup_id,event->span+1, event->chan+1); + kill++; + } + +woomera_call_ack_skip: + if (kill) { + if (woomera) { + woomera_set_flag(woomera,WFLAG_MEDIA_END); + } else { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED, + SIGBOOST_RELEASE_CAUSE_NORMAL); + + log_printf(SMG_LOG_PROD, server.log, "Sent CALL STOP to CALL START ACK without session [w%dg%d]\n", + event->span+1, event->chan+1); + } + } +} + +static void handle_call_start_nack(short_signal_event_t *event) +{ + struct woomera_interface *woomera = NULL; + int span=-1, chan=-1; + int ack=0; + + /* Always ACK the incoming NACK + * Send out the NACK ACK before pulling the TANK, because + * if we send after the pull, the outgoing call could send + * a message to boost with the pulled TANK value before + * we send a NACK ACK */ + + if (smg_validate_span_chan(event->span,event->chan) == 0) { + span=event->span; + chan=event->chan; + } + + if (event->call_setup_id > 0) { + woomera=peek_from_holding_tank(event->call_setup_id); + } + + if (woomera) { + + struct woomera_event wevent; + + if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_ALL, server.log, "Event CALL START NACK on hungup call [%d]!\n", + event->call_setup_id); + ack++; + } else { + + woomera_set_cause_topbx(woomera,event->release_cause); + woomera_set_flag(woomera, (WFLAG_HANGUP|WFLAG_HANGUP_NACK_ACK)); + + new_woomera_event_printf(&wevent, "EVENT HANGUP w%dg%d%s" + "Unique-Call-Id: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + event->span+1, + event->chan+1, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(woomera->q931_rel_cause_topbx), + WOOMERA_LINE_SEPERATOR, + woomera->q931_rel_cause_topbx, + WOOMERA_RECORD_SEPERATOR + ); + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + woomera_set_flag(woomera, WFLAG_HANGUP); + woomera_clear_flag(woomera, WFLAG_RUNNING); + + /* Do not ack here, let woomera thread ack it */ + ack=0; + } + + + } else if (event->call_setup_id == 0 && + smg_validate_span_chan(event->span,event->chan) == 0) { + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + + if (woomera) { + if (!woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT) && + !woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK_SENT)) { + /* Only if we are not already waiting for hangup */ + server.process_table[event->span][event->chan].dev=NULL; + } else if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + /* At this point call is already hang up */ + woomera=NULL; + } else { + /* At this point call is already hang up */ + woomera=NULL; + } + } + + memset(server.process_table[event->span][event->chan].session, + 0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + + if (woomera) { + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event START NACK on w%dg%d ptr=%p ms=%p\n", + woomera->span+1,woomera->chan+1,woomera,woomera->ms); + + if (!woomera_test_flag(woomera,WFLAG_HANGUP)){ + if (event->release_cause == SIGBOOST_CALL_SETUP_CSUPID_DBL_USE || + event->release_cause == SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY) { + woomera_set_cause_topbx(woomera,17); + } else { + woomera_set_cause_topbx(woomera,event->release_cause); + } + } + + woomera_set_flag(woomera, + (WFLAG_HANGUP|WFLAG_MEDIA_END|WFLAG_HANGUP_NACK_ACK)); + + /* Nack Ack will be sent by the woomera thread */ + ack=0; + } else { + /* Valid state when we are not in autoacm mode */ + ack++; + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event: NACK no woomera on span chan [w%dg%d]!\n", + event->span+1, event->chan+1); + } + + } else { + log_printf(SMG_LOG_ALL, server.log, + "Error: Start Nack Invalid State Should not happen [%d] [w%dg%d]!\n", + event->call_setup_id, event->span+1, event->chan+1); + ack++; + } + + if (event->release_cause == SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY) { + smg_all_ckt_busy(); + log_printf(SMG_LOG_ALL, server.log, "WARNING: All ckt busy Timeout=%i!\n",server.all_ckt_busy); + } + if (event->release_cause == SIGBOOST_CALL_SETUP_CSUPID_DBL_USE) { + log_printf(SMG_LOG_ALL, server.log, "WARNING: Double use on [w%ig%i] setup id %i!\n", + event->span+1,event->chan+1,event->call_setup_id); + } + +#warning "Ignoring CALL GAP" +#if 0 + if (event->release_cause == SIGBOOST_CALL_SETUP_NACK_AUTO_CALL_GAP) { + log_printf(SMG_LOG_ALL, server.log, "WARNING: Call Gapping Detected!\n"); + smg_all_ckt_gap(); + } +#endif + + if (ack) { + span=0; + chan=0; + if (smg_validate_span_chan(event->span,event->chan) == 0) { + span=event->span; + chan=event->chan; + } + + isup_exec_command(span, + chan, + event->call_setup_id, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + 0); + + if (!woomera) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event (NACK ACK) %d referrs to a non-existant session (%d) [w%dg%d]!\n", + event->event_id,event->call_setup_id, event->span+1, event->chan+1); + } + } +} + +static void handle_call_loop_start(short_signal_event_t *event) +{ + struct woomera_interface *woomera; + char callid[20]; + char *session; + + pthread_mutex_lock(&server.process_lock); + if (server.process_table[event->span][event->chan].dev) { + + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + 17); + + + log_printf(SMG_LOG_PROD, server.log, + "Sent (From Handle Loop START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [w%dg] ptr=%d\n", + event->span+1, event->chan+1, server.process_table[event->span][event->chan].dev); + + pthread_mutex_unlock(&server.process_lock); + return; + + } + + sprintf(callid, "w%dg%d", event->span+1,event->chan+1); + sprintf(server.process_table[event->span][event->chan].session, + "%s-%i-%i",callid,rand(),rand()); + session=server.process_table[event->span][event->chan].session; + server.process_table[event->span][event->chan].dev = NULL; + pthread_mutex_unlock(&server.process_lock); + + + woomera=launch_woomera_loop_thread(event); + if (woomera == NULL) { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + 17); + log_printf(SMG_LOG_PROD, server.log, + "Sent (From Handle Loop START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [w%dg] ptr=%d\n", + event->span+1, event->chan+1, server.process_table[event->span][event->chan].dev); + } + + woomera_set_flag(woomera,WFLAG_CALL_ACKED); + + return; +} + + +static void handle_call_start(call_signal_event_t *event) +{ + struct woomera_event wevent; + char callid[20]; + char *session; + struct woomera_interface *tmp_woomera=NULL; + int clients; + + remove_end_of_digits_char((unsigned char*)event->called_number_digits); + remove_end_of_digits_char((unsigned char*)event->calling_number_digits); + + if (server.strip_cid_non_digits) { + validate_number((unsigned char*)event->called_number_digits); + validate_number((unsigned char*)event->calling_number_digits); + } + + if (smg_validate_span_chan(event->span,event->chan)) { + log_printf(0,server.log, + "Error: invalid span=% chan=%i on incoming call [w%dg%d] - Call START!\n", + event->span+1, event->chan+1, event->span+1,event->chan+1); + return; + } + + pthread_mutex_lock(&server.process_lock); + + if ((tmp_woomera=server.process_table[event->span][event->chan].dev)) { + + woomera_set_flag(tmp_woomera,WFLAG_HANGUP); + woomera_set_flag(tmp_woomera,WFLAG_MEDIA_END); + log_printf(SMG_LOG_ALL,server.log,"Call Overrun on [w%dg%d] - Call START!\n", event->span+1, event->chan+1); + + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + 17); + + log_printf(SMG_LOG_ALL, server.log, + "Sent (From Handle START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [w%dg%d]\n", + event->span+1, event->chan+1); + + pthread_mutex_unlock(&server.process_lock); + return; + } + + sprintf(callid, "w%dg%d", event->span+1,event->chan+1); + sprintf(server.process_table[event->span][event->chan].session, + "%s-%i-%i",callid,rand(),rand()); + session=server.process_table[event->span][event->chan].session; + server.process_table[event->span][event->chan].dev = NULL; + memset(server.process_table[event->span][event->chan].digits, 0, sizeof(server.process_table[event->span][event->chan].digits)); + server.process_table[event->span][event->chan].digits_len = 0; + server.process_table[event->span][event->chan].bearer_cap = event->bearer.capability; + server.process_table[event->span][event->chan].clients=0; + pthread_mutex_unlock(&server.process_lock); + + if (autoacm) { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_ACK, + 0); + } + + + log_printf(SMG_LOG_DEBUG_8,server.log,"BEARER %i , UIL1P = %i\n", + event->bearer.capability,event->bearer.uil1p); + + if (event->bearer.uil1p == 0) { + if (server.hw_coding) { + event->bearer.uil1p=BC_IE_UIL1P_G711_ALAW; + } else { + event->bearer.uil1p=BC_IE_UIL1P_G711_ULAW; + } + } + + new_woomera_event_printf(&wevent, "EVENT INCOMING w%dg%d%s" + "Unique-Call-Id: %s%s" + "Remote-Number: %s%s" + "Remote-Name: %s%s" +#if defined(BRI_PROT) + "Protocol: BRI%s" +#else +#if defined(PRI_PROT) + "Protocol: PRI%s" +#else + "Protocol: SS7%s" +#endif +#endif + "User-Agent: sangoma_mgd%s" + "Local-Number: %s%s" + "Channel-Name: g%d/%d%s" + "Trunk-Group: %d%s" + "Presentation: %d%s" + "Screening: %d%s" + "RDNIS: %s%s" + "Bearer-Cap: %s%s" + "uil1p: %s%s" + , + event->span+1, + event->chan+1, + WOOMERA_LINE_SEPERATOR, + session, + WOOMERA_LINE_SEPERATOR, + event->calling_number_digits, + WOOMERA_LINE_SEPERATOR, + event->calling_name, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + event->called_number_digits, + WOOMERA_LINE_SEPERATOR, + event->trunk_group+1, + (event->span*max_chans)+event->chan+1, + WOOMERA_LINE_SEPERATOR, + event->trunk_group+1, + WOOMERA_LINE_SEPERATOR, + event->calling_number_presentation, + WOOMERA_LINE_SEPERATOR, + event->calling_number_screening_ind, + WOOMERA_LINE_SEPERATOR, + event->isup_in_rdnis, + WOOMERA_LINE_SEPERATOR, + bearer_cap_to_str(event->bearer.capability), + WOOMERA_LINE_SEPERATOR, + uil1p_to_str(event->bearer.uil1p), + WOOMERA_RECORD_SEPERATOR + ); + + clients=enqueue_event_on_listeners(&wevent); + if (!clients) { + + pthread_mutex_lock(&server.process_lock); + server.process_table[event->span][event->chan].dev = NULL; + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + if (autoacm) { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED, + 17); + log_printf(SMG_LOG_ALL, server.log, + "CALL INCOMING: Enqueue Error Sent SIGBOOST_EVENT_CALL_STOPPED [w%dg%d]\n", + event->span+1, event->chan+1); + + } else { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + 17); + log_printf(SMG_LOG_ALL, server.log, + "CALL INCOMING: Enqueue Error Sent SIGBOOST_EVENT_CALL_START_NACK [w%dg%d]\n", + event->span+1, event->chan+1); + + } + + } else { + server.process_table[event->span][event->chan].clients=clients; + } + + destroy_woomera_event_data(&wevent); + +} + +static void handle_incoming_digit(call_signal_event_t *event) +{ + struct woomera_interface *woomera; + int digits_len; + + if (smg_validate_span_chan(event->span,event->chan)) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event DTMF on invalid span chan [w%dg%d] !\n", + event->span+1, event->chan+1); + return; + } + + + if (!event->called_number_digits_count) { + log_printf(SMG_LOG_ALL, server.log, "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); + } + + log_printf(SMG_LOG_DEBUG_9, server.log, "Queuing incoming digits %s [w%dg%d]\n", + event->called_number_digits, + event->span+1, event->chan+1); + + pthread_mutex_lock(&server.digits_lock); + + digits_len = server.process_table[event->span][event->chan].digits_len; + + strncpy(&server.process_table[event->span][event->chan].digits[digits_len], + event->called_number_digits, + event->called_number_digits_count); + + server.process_table[event->span][event->chan].digits_len += event->called_number_digits_count; + + pthread_mutex_unlock(&server.digits_lock); + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + pthread_mutex_unlock(&server.process_lock); + if (!woomera || !strlen(woomera->session)) { + return; + } + woomera_check_digits(woomera); +} + +static void handle_gap_abate(short_signal_event_t *event) +{ + log_printf(SMG_LOG_ALL, server.log, "NOTICE: GAP Cleared!\n", + event->span+1, event->chan+1); + smg_clear_ckt_gap(); +} + +static void handle_restart(call_signal_connection_t *mcon, short_signal_event_t *event) +{ + if (!woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + log_printf(SMG_LOG_ALL, server.log,"ERROR! Monitor Thread not running!\n"); + } else { + /* Clear Reset */ + /* Tell all threads to go down */ + + log_printf(SMG_LOG_ALL, server.log, + "RESTART Received: resetting all threads\n"); + + smg_all_ckt_busy(); + woomera_set_flag(&server.master_connection, WFLAG_SYSTEM_RESET); + gettimeofday(&server.restart_timeout,NULL); + +#if 0 + sleep(5); + + clear_all_holding_tank(); + + sleep(2); + + event->event_id = SIGBOOST_EVENT_SYSTEM_RESTART_ACK; + err=call_signal_connection_write(&server.mcon, (call_signal_event_t*)event); + if (err < 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", + strerror(errno)); + } + + smg_all_ckt_busy(); + woomera_clear_flag(&server.master_connection, WFLAG_SYSTEM_RESET); +#endif + } + +} + +static void handle_restart_ack(call_signal_connection_t *mcon, short_signal_event_t *event) +{ + + /* Do not reset WFLAG_SYSTEM_RESET flag here!. The flag should + only be reset on restart from boost */ + //woomera_clear_flag(&server.master_connection, WFLAG_SYSTEM_RESET); +} + + + +static void handle_remove_loop(short_signal_event_t *event) +{ + struct woomera_interface *woomera; + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + server.process_table[event->span][event->chan].dev=NULL; + pthread_mutex_unlock(&server.process_lock); + + if (woomera) { + + woomera_set_flag(woomera, + (WFLAG_MEDIA_END|WFLAG_HANGUP)); + + /* We have to close the socket because + At this point we are release span chan */ + + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event REMOVE LOOP on w%dg%d ptr=%p ms=%p\n", + woomera->span+1,woomera->chan+1,woomera,woomera->ms); + + } +} + + +static void handle_call_stop(short_signal_event_t *event) +{ + struct woomera_interface *woomera; + int ack=0; + + woomera = NULL; + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + if (woomera) { + + if (!woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT) && + !woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK_SENT)) { + /* Only if we are not already waiting for hangup */ + //server.process_table[event->span][event->chan].dev=NULL; + + } else if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + /* At this point call is already hangup */ + woomera=NULL; + } else { + /* At this point call is already hangup */ + woomera=NULL; + } + } + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + if (woomera) { + + woomera_set_cause_topbx(woomera,event->release_cause); + + woomera_set_flag(woomera, + (WFLAG_MEDIA_END|WFLAG_HANGUP|WFLAG_HANGUP_ACK)); + + /* We have to close the socket because + At this point we are release span chan */ + + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event CALL STOP on w%dg%d ptr=%p ms=%p\n", + woomera->span+1,woomera->chan+1,woomera,woomera->ms); + + } else { + ack++; + } + + + if (ack) { + /* At this point we have already sent our STOP so its safe to ACK */ + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED_ACK, + 0); + } + + if (!woomera){ + /* This is allowed on incoming call if remote app does not answer it */ + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event CALL STOP referrs to a non-existant session [w%dg%d]!\n", + event->span+1, event->chan+1); + } +} + +static void handle_heartbeat(short_signal_event_t *event) +{ + if (!woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + log_printf(SMG_LOG_ALL, server.log,"ERROR! Monitor Thread not running!\n"); + } else { + int err=call_signal_connection_writep(&server.mconp, (call_signal_event_t*)event); + if (err < 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", + strerror(errno)); + } + } + return; +} + + +static void handle_call_stop_ack(short_signal_event_t *event) +{ + + struct woomera_interface *woomera = NULL; + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + server.process_table[event->span][event->chan].dev=NULL; + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + + if (woomera) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Stop Ack on [w%dg%d] [Setup ID: %d] [%s]!\n", + event->span+1, event->chan+1, event->call_setup_id, + woomera->interface); + + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + + } else { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event CALL_STOP_ACK(%d) referrs to a non-existant session [w%dg%d] [Setup ID: %d]!\n", + event->event_id, event->span+1, event->chan+1, event->call_setup_id); + } + + if (event->call_setup_id > 0) { + clear_from_holding_tank(event->call_setup_id, NULL); + } + + /* No need for us to do any thing here */ + return; +} + + +static void handle_call_start_nack_ack(short_signal_event_t *event) +{ + + struct woomera_interface *woomera = NULL; + + if ((woomera=pull_from_holding_tank(event->call_setup_id,-1,-1))) { + + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + + } else if (event->call_setup_id == 0 && + smg_validate_span_chan(event->span,event->chan) == 0) { + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + server.process_table[event->span][event->chan].dev=NULL; + memset(server.process_table[event->span][event->chan].session, + 0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + if (woomera) { + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + } else { + /* Possible if incoming call is NACKed due to + * woomera client being down, in this case no + * woomera thread is created */ + log_printf(SMG_LOG_DEBUG_8, server.log, + "Event NACK ACK [w%dg%d] with valid span/chan no dev!\n", + event->span+1, event->chan+1); + } + + } else { + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "Event NACK ACK referrs to a non-existant session [w%dg%d] [Setup ID: %d]!\n", + event->span+1, event->chan+1, event->call_setup_id); + } + + if (event->call_setup_id > 0) { + clear_from_holding_tank(event->call_setup_id, NULL); + } + + /* No need for us to do any thing here */ + return; +} + + +static int parse_ss7_event(call_signal_connection_t *mcon, short_signal_event_t *event) +{ + int ret = 0; + + switch(event->event_id) { + + case SIGBOOST_EVENT_CALL_START: + handle_call_start((call_signal_event_t*)event); + break; + case SIGBOOST_EVENT_CALL_STOPPED: + handle_call_stop(event); + break; + case SIGBOOST_EVENT_CALL_START_ACK: + handle_call_start_ack(event); + break; + case SIGBOOST_EVENT_CALL_START_NACK: + handle_call_start_nack(event); + break; + case SIGBOOST_EVENT_CALL_ANSWERED: + handle_call_answer(event); + break; + case SIGBOOST_EVENT_HEARTBEAT: + handle_heartbeat(event); + break; + case SIGBOOST_EVENT_CALL_START_NACK_ACK: + handle_call_start_nack_ack(event); + break; + case SIGBOOST_EVENT_CALL_STOPPED_ACK: + handle_call_stop_ack(event); + break; + case SIGBOOST_EVENT_INSERT_CHECK_LOOP: + handle_call_loop_start(event); + break; + case SIGBOOST_EVENT_SYSTEM_RESTART: + handle_restart(mcon,event); + break; + case SIGBOOST_EVENT_REMOVE_CHECK_LOOP: + handle_remove_loop(event); + break; + case SIGBOOST_EVENT_SYSTEM_RESTART_ACK: + handle_restart_ack(mcon,event); + break; + case SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE: + handle_gap_abate(event); + break; + case SIGBOOST_EVENT_DIGIT_IN: + handle_incoming_digit((call_signal_event_t*)event); + break; + default: + log_printf(SMG_LOG_ALL, server.log, "Warning no handler implemented for [%s val:%d]\n", + call_signal_event_id_name(event->event_id), event->event_id); + break; + } + + return ret; +} + + +static void *monitor_thread_run(void *obj) +{ + int ss = 0; + int policy=0,priority=0; + char a=0,b=0; + call_signal_connection_t *mcon=&server.mcon; + call_signal_connection_t *mconp=&server.mconp; + + pthread_mutex_lock(&server.thread_count_lock); + server.thread_count++; + pthread_mutex_unlock(&server.thread_count_lock); + + woomera_set_flag(&server.master_connection, WFLAG_MONITOR_RUNNING); + + if (call_signal_connection_open(mcon, + mcon->cfg.local_ip, + mcon->cfg.local_port, + mcon->cfg.remote_ip, + mcon->cfg.remote_port) < 0) { + log_printf(SMG_LOG_ALL, server.log, "Error: Opening MCON Socket [%d] %s\n", + mcon->socket,strerror(errno)); + exit(-1); + } + + if (call_signal_connection_open(mconp, + mconp->cfg.local_ip, + mconp->cfg.local_port, + mconp->cfg.remote_ip, + mconp->cfg.remote_port) < 0) { + log_printf(SMG_LOG_ALL, server.log, "Error: Opening MCONP Socket [%d] %s\n", + mconp->socket,strerror(errno)); + exit(-1); + } + + mcon->log = server.log; + mconp->log = server.log; + + isup_exec_commandp(0, + 0, + -1, + SIGBOOST_EVENT_SYSTEM_RESTART, + 0); + + woomera_set_flag(&server.master_connection, WFLAG_SYSTEM_RESET); + + smg_get_current_priority(&policy,&priority); + + log_printf(SMG_LOG_PROD, server.log, "Open udp socket [%d] [%d]\n", + mcon->socket,mconp->socket); + log_printf(SMG_LOG_PROD, server.log, "Monitor Thread Started (%i:%i)\n",policy,priority); + + + while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { +#if 0 + ss = waitfor_socket(server.mcon.socket, 1000, POLLERR | POLLIN); +#else + ss = waitfor_2sockets(mcon->socket, + mconp->socket, + &a, &b, 1000); +#endif + + if (ss > 0) { + + call_signal_event_t *event=NULL; + int i=0; + + if (b) { +mcon_retry_priority: + if ((event = call_signal_connection_readp(mconp,i))) { + log_printf(SMG_LOG_DEBUG_9, server.log, "Socket Event P [%s] \n", + call_signal_event_id_name(event->event_id)); + parse_ss7_event(mconp,(short_signal_event_t*)event); + if (++i < 10) { + goto mcon_retry_priority; + } + + } else if (errno != EAGAIN) { + ss=-1; + log_printf(SMG_LOG_ALL, server.log, + "Error: Reading from Boost P Socket! (%i) %s\n", + errno,strerror(errno)); + break; + } + } + + i=0; + + if (a) { + if ((event = call_signal_connection_read(mcon,i))) { + log_printf(SMG_LOG_DEBUG_9, server.log, "Socket Event [%s]\n", + call_signal_event_id_name(event->event_id)); + parse_ss7_event(mcon,(short_signal_event_t*)event); + + } else if (errno != EAGAIN) { + ss=-1; + log_printf(SMG_LOG_ALL, server.log, + "Error: Reading from Boost Socket! (%i) %s\n", + errno,strerror(errno)); + break; + } + } + + } + + if (ss < 0){ + log_printf(SMG_LOG_ALL, server.log, "Thread Run: Select Socket Error!\n"); + break; + } + + if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)) { + short_signal_event_t event; + struct timeval current; + int elapsed; + gettimeofday(¤t,NULL); + + elapsed=smg_calc_elapsed(&server.restart_timeout, ¤t); + if (elapsed > 5000) { + int err; + log_printf(SMG_LOG_ALL, server.log, "Reset Condition Cleared Elapsed=%i!\n",elapsed); + clear_all_holding_tank(); + memset(&event,0,sizeof(event)); + event.event_id = SIGBOOST_EVENT_SYSTEM_RESTART_ACK; + err=call_signal_connection_write(&server.mcon, (call_signal_event_t*)&event); + if (err < 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", + strerror(errno)); + } + smg_all_ckt_busy(); + woomera_clear_flag(&server.master_connection, WFLAG_SYSTEM_RESET); + } + } + + } + + log_printf(SMG_LOG_PROD, server.log, "Close udp socket [%d] [%d]\n", + mcon->socket,mconp->socket); + call_signal_connection_close(&server.mcon); + call_signal_connection_close(&server.mconp); + + pthread_mutex_lock(&server.thread_count_lock); + server.thread_count--; + pthread_mutex_unlock(&server.thread_count_lock); + + woomera_clear_flag(&server.master_connection, WFLAG_MONITOR_RUNNING); + log_printf(SMG_LOG_ALL, server.log, "Monitor Thread Ended\n"); + + return NULL; +} + +static void woomera_loop_thread_run(struct woomera_interface *woomera) +{ + int err=launch_media_thread(woomera); + if (err) { + log_printf(SMG_LOG_ALL, server.log, "Failed to start loop media thread\n"); + woomera_set_flag(woomera, + (WFLAG_HANGUP|WFLAG_MEDIA_END)); + woomera_clear_flag(woomera, WFLAG_RUNNING); + return; + } + + while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING) && + !woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) && + !woomera_test_flag(woomera, WFLAG_MEDIA_END) && + !woomera_test_flag(woomera, WFLAG_HANGUP)) { + + usleep(300000); + continue; + + } + + woomera_clear_flag(woomera, WFLAG_RUNNING); + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Woomera Session: For Loop Test exiting %s\n",woomera->interface); + + return; +} + +static void woomera_check_digits (struct woomera_interface *woomera) +{ + int span, chan; + + if (!strlen(woomera->session)) { + return; + } + + if (get_span_chan_from_interface(woomera->interface, &span, &chan) == 0) { + pthread_mutex_lock(&server.digits_lock); + if (server.process_table[span-1][chan-1].digits_len > 0) { + int i; + unsigned char digit; + + for (i=0; i < server.process_table[span-1][chan-1].digits_len; i++) { + digit = server.process_table[span-1][chan-1].digits[i]; + + handle_event_dtmf(woomera, digit); + } + + server.process_table[span-1][chan-1].digits_len = 0; + } + pthread_mutex_unlock(&server.digits_lock); + } +} + + +static void *woomera_thread_run(void *obj) +{ + struct woomera_interface *woomera = obj; + struct woomera_message wmsg; + struct woomera_event wevent; + char *event_string; + int mwi; + int err; + int policy=0, priority=0; + int span = -1, chan = -1; + + woomera_message_init(&wmsg); + + smg_get_current_priority(&policy,&priority); + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "WOOMERA session started (ptr=%p : loop=%i)(%i:%i) Index=%i\n", + woomera,woomera->loop_tdm,policy,priority, woomera->index); + + pthread_mutex_lock(&server.thread_count_lock); + server.thread_count++; + pthread_mutex_unlock(&server.thread_count_lock); + + pthread_mutex_init(&woomera->queue_lock, NULL); + pthread_mutex_init(&woomera->ms_lock, NULL); + pthread_mutex_init(&woomera->dtmf_lock, NULL); + pthread_mutex_init(&woomera->vlock, NULL); + pthread_mutex_init(&woomera->flags_lock, NULL); + + if (woomera->loop_tdm) { + /* We must set woomera socket to -1 otherwise + a valid file descriptor will get closed */ + woomera->socket = -1; + woomera_loop_thread_run(woomera); + goto woomera_session_close; + } + + err=socket_printf(woomera->socket, + "EVENT HELLO Sangoma Media Gateway%s" + "Supported-Protocols: TDM%s" + "Version: %s%s" + "Remote-Address: %s%s" + "Remote-Port: %d%s" + "Raw-Format: %s%s", + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + SMG_VERSION, WOOMERA_LINE_SEPERATOR, + inet_ntoa(woomera->addr.sin_addr), WOOMERA_LINE_SEPERATOR, + ntohs(woomera->addr.sin_port), WOOMERA_LINE_SEPERATOR, + server.hw_coding?"ALAW":"ULAW", WOOMERA_RECORD_SEPERATOR + ); + + if (err) { + log_printf(SMG_LOG_ALL, server.log, "Woomera session socket failure! (ptr=%p)\n", + woomera); + woomera_clear_flag(woomera, WFLAG_RUNNING); + goto woomera_session_close; + } + + while ( woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING) && + woomera_test_flag(woomera, WFLAG_RUNNING) && + !woomera_test_flag(woomera, WFLAG_MEDIA_END) && + !woomera_test_flag(woomera, WFLAG_HANGUP)) { + + mwi = woomera_message_parse(woomera, &wmsg, WOOMERA_HARD_TIMEOUT); + if (mwi >= 0) { + + if (mwi) { + interpret_command(woomera, &wmsg); + } else if (woomera_test_flag(woomera, WFLAG_EVENT)){ + while ((event_string = dequeue_event(woomera))) { + if (socket_printf(woomera->socket, "%s", event_string)) { + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_RUNNING); + smg_free(event_string); + log_printf(SMG_LOG_DEBUG_8, server.log, + "WOOMERA session (ptr=%p) print string error\n", + woomera); + break; + } + smg_free(event_string); + } + woomera_clear_flag(woomera, WFLAG_EVENT); + } + + if(woomera->check_digits) { + woomera_check_digits(woomera); + woomera->check_digits = 0; + } + + if (woomera->timeout > 0 && time(NULL) >= woomera->timeout) { + + /* Sent the hangup only after we sent a NACK */ + + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "WOOMERA session Call Timedout ! [%s]\n", + woomera->interface); + + /* Let the Index check below send a NACK */ + if (woomera->index) { + woomera->q931_rel_cause_tosig=19; + woomera_set_flag(woomera, WFLAG_HANGUP); + } + break; + } + + } else { + log_printf(SMG_LOG_DEBUG_MISC, server.log, "WOOMERA session (ptr=%p) [%s] READ MSG Error %i \n", + woomera,woomera->interface,mwi); + break; + } + + } + +woomera_session_close: + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "WOOMERA session (ptr=%p) is dying [%s]: SR=%d WR=%d WF=0x%04X\n", + woomera,woomera->interface, + woomera_test_flag(&server.master_connection, WFLAG_RUNNING), + woomera_test_flag(woomera, WFLAG_RUNNING), + woomera->flags); + + + if (woomera_test_flag(woomera, WFLAG_MEDIA_RUNNING)) { + woomera_set_flag(woomera, WFLAG_MEDIA_END); + while(woomera_test_flag(woomera, WFLAG_MEDIA_RUNNING)) { + usleep(100); + sched_yield(); + } + } + + + /*********************************************** + * Identify the SPAN CHAN to be used below + ***********************************************/ + + chan = woomera->chan; + span = woomera->span; + + + if (!woomera_test_flag(woomera, WFLAG_HANGUP)) { + + /* The call was not HUNGUP. This is the last check, + If the call is valid, hungup the call if the call + was never up the keep going */ + + + if (smg_validate_span_chan(span,chan) == 0) { + + if (!woomera->index) { + + if (autoacm || woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + + woomera_set_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + isup_exec_command(span, + chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED, + woomera->q931_rel_cause_tosig); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK_SENT); + + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Woomera Sent SIGBOOST_EVENT_CALL_STOPPED [w%dg%d] [%s] ptr=%p\n", + span+1, chan+1,woomera->interface,woomera); + } else { + + woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + isup_exec_command(span, + chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + woomera->q931_rel_cause_tosig); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Woomera Sent SIGBOOST_EVENT_CALL_START_NACK [w%dg%d] [%s] ptr=%p\n", + span+1, chan+1,woomera->interface,woomera); + } + } else { + log_printf(SMG_LOG_ALL, woomera->log, "Woomera Not Sent CALL STOPPED - Instead NACK [w%dg%d] [%s] ptr=%p\n", + span+1, chan+1,woomera->interface,woomera); + + } + }else{ + /* This can happend if an outgoing call times out + or gets hungup before it gets acked. Its not a + failure */ + if (!woomera->index) { + /* In this case we really failed to tx STOP */ + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "FAILED: Woomera (R) SIGBOOST_EVENT_CALL_STOPPED [w%dg%d] [%s] Index=%d ptr=%p\n", + span+1, chan+1, woomera->interface, woomera->index, woomera); + } + } + + woomera_set_flag(woomera, WFLAG_HANGUP); + + } + +woo_re_hangup: + + /* We must send a STOP ACK to boost telling it that we are done */ + if (woomera_test_flag(woomera, WFLAG_HANGUP_ACK)) { + + /* SMG received a HANGUP from boost. + We must now send back the ACK to the HANGUP. + Boost will not release channel until we + ACK the hangup */ + + if (smg_validate_span_chan(span,chan) == 0) { + + isup_exec_command(span, + chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED_ACK, + woomera->q931_rel_cause_tosig); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, + "Sent (Ack) to SIGBOOST_EVENT_CALL_STOPPED_ACK [w%dg%d] [%s] ptr=%p\n", + span+1,chan+1,woomera->interface,woomera); + + }else{ + /* This should never happen! If it does + we broke protocol */ + log_printf(SMG_LOG_ALL, woomera->log, + "FAILED: Woomera (R) SIGBOOST_EVENT_CALL_STOPPED_ACK [w%dg%d] [%s] Index=%d ptr=%p\n", + span+1, chan+1, woomera->interface, woomera->index, woomera); + } + + woomera_clear_flag(woomera, WFLAG_HANGUP_ACK); + woomera_set_flag(woomera, WFLAG_HANGUP); + } + + if (woomera_test_flag(woomera, WFLAG_HANGUP_NACK_ACK)) { + + /* SMG received a NACK from boost during call startup. + We must now send back the ACK to the NACK. + Boost will not release channel until we + ACK the NACK */ + + if (smg_validate_span_chan(span,chan) == 0) { + + isup_exec_command(span, + chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + woomera->q931_rel_cause_tosig); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, + "Sent (Nack Ack) to SIGBOOST_EVENT_CALL_START_NACK_ACK [w%dg%d] [%s] ptr=%p\n", + span+1,chan+1,woomera->interface,woomera); + + woomera->index=0; + + } else if (woomera->index) { + isup_exec_command(0, + 0, + woomera->index, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + woomera->q931_rel_cause_tosig); + + woomera->index=0; + + } else { + log_printf(SMG_LOG_ALL, woomera->log, + "FAILED: Sent (Nack Ack) SIGBOOST_EVENT_CALL_START_NACK_ACK [w%dg%d] [%s] Index=%d ptr=%p\n", + span+1, chan+1, woomera->interface, woomera->index, woomera); + } + + woomera_clear_flag(woomera, WFLAG_HANGUP_NACK_ACK); + + } + + if (woomera->index) { + + int index = woomera->index; + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Timeout: %ld%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + woomera->interface, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + woomera->timeout, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(18), + WOOMERA_LINE_SEPERATOR, + 18, + WOOMERA_RECORD_SEPERATOR + ); + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + while ((event_string = dequeue_event(woomera))) { + socket_printf(woomera->socket, "%s", event_string); + smg_free(event_string); + } + + if (peek_from_holding_tank(index)) { + + woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + isup_exec_command(0, + 0, + index, + SIGBOOST_EVENT_CALL_START_NACK, + woomera->q931_rel_cause_tosig); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT); + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, + "Sent SIGBOOST_EVENT_CALL_START_NACK [Setup ID: %d] .. WAITING FOR NACK ACK\n", + index); + } else { + log_printf(SMG_LOG_PROD, woomera->log, + "Error Failed to Sent SIGBOOST_EVENT_CALL_START_NACK [Setup ID: %d] - index stale!\n", + index); + } + } + + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK)) { + int timeout_cnt=0; + int overall_cnt=0; + + /* SMG sent NACK to boost, however we have to wait + for boost to give us the ACK back before we + release resources. */ + + while (woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK)) { + timeout_cnt++; + if (timeout_cnt > 100) { //5sec timeout + timeout_cnt=0; + overall_cnt++; + + log_printf((overall_cnt==1)?0:4, woomera->log, + "Waiting for NACK ACK [Setup ID: %d] ... \n", + woomera->index_hold); + } + + if (overall_cnt > 15) { //50sec timeout + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING)) { + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ + break; + } + + /* If ACK comes in while we wait for NACK ACK, the ACk will + clear the tank causing NACK ACK never to clear the waiting flag + in this case we abort waiting for NACK ACK and we timeout + the wait */ + if (woomera_test_flag(&server.master_connection, WFLAG_CALL_ACKED)){ + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + + usleep(50000); + sched_yield(); + } + + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + + /* If ACK came in while waiting for NACK ACK, we TIMEOUT on purpose. + The ACK has blocked the tank id and now ony NACK ACK can unblock it. + THe only problem is that we blind and have to assume that NACK ACK + will eventually come in :) */ + if (!woomera_test_flag(&server.master_connection, WFLAG_CALL_ACKED)) { + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT)) { + log_printf(SMG_LOG_ALL, woomera->log, + "Waiting for NACK ACK [Setup ID: %d] .. TIMEOUT on NACK ACK\n", + woomera->index_hold); + + } else { + log_printf(SMG_LOG_DEBUG_8, woomera->log, + "Waiting for NACK ACK [Setup ID: %d] .. GOT NACK ACK\n", + woomera->index_hold); + } + } + } + + if (woomera_test_flag(woomera, WFLAG_EVENT)){ + while ((event_string = dequeue_event(woomera))) { + socket_printf(woomera->socket, "%s", event_string); + smg_free(event_string); + } + woomera_clear_flag(woomera, WFLAG_EVENT); + } + + + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK)) { + int timeout_cnt=0; + int overall_cnt=0; + + /* SMG sent HANGUP to boost, however we have to wait + for boost to give us the ACK back before we + release resources. */ + + log_printf(SMG_LOG_DEBUG_8, woomera->log, + "Waiting for STOPPED ACK [%s] [id=%i]... \n", + woomera->interface,woomera->index_hold); + + while (woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK)) { + timeout_cnt++; + if (timeout_cnt > 100) { //5sec timeout + timeout_cnt=0; + overall_cnt++; + log_printf((overall_cnt==1)?0:4, woomera->log, + "Waiting for STOPPED ACK [%s] [id=%i] %i... \n", + woomera->interface,woomera->index_hold,overall_cnt); + } + + if (overall_cnt > 15) { //50sec + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING)) { + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + + if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ + break; + } + + usleep(50000); + sched_yield(); + } + + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT)) { + log_printf(SMG_LOG_ALL, woomera->log, + "Wait TIMEDOUT on STOPPED ACK [%s] [id=%i]... \n", + woomera->interface,woomera->index_hold); + + } else { + log_printf(SMG_LOG_DEBUG_8, woomera->log, + "Wait GOT STOPPED ACK [%s] [id=%i]... \n", + woomera->interface,woomera->index_hold); + } + } + + /***************************************************** + * We must wait for WFLAG_WAIT_FOR_STOPPED_ACK here + * so that STOP_ACK can access the woomera + *****************************************************/ + + if (smg_validate_span_chan(span,chan) == 0) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, + "WOOMERA Clearing Processs Table ... \n", + woomera->interface); + pthread_mutex_lock(&server.process_lock); + if (server.process_table[span][chan].dev == woomera){ + server.process_table[span][chan].dev = NULL; + memset(server.process_table[span][chan].session,0,SMG_SESSION_NAME_SZ); + memset(server.process_table[span][chan].digits,0, + sizeof(server.process_table[span][chan].digits)); + server.process_table[span][chan].digits_len = 0; + } + pthread_mutex_unlock(&server.process_lock); + } + +#if 0 +//Used for testing + if (1) { + int chan = woomera->chan; + int span = woomera->span; + if (smg_validate_span_chan(span,chan) == 0) { + pthread_mutex_lock(&server.process_lock); + /* This is possible in case media thread dies on startup */ + + if (server.process_table[span][chan]){ + log_printf(SMG_LOG_ALL, server.log, + "Sanity Span Chan Still in use: [w%dg%d] [%s] Index=%d ptr=%p\n", + span+1, chan+1, woomera->interface, woomera->index, woomera); + //server.process_table[span][chan] = NULL; + } + pthread_mutex_unlock(&server.process_lock); + } + } +#endif + + usleep(3000000); + + /* Sanity Check */ + if (woomera_test_flag(woomera, WFLAG_HANGUP_ACK)) { + log_printf(SMG_LOG_ALL, woomera->log, + "Woomera MISSED HANGUP ACK: Retry HANGUP ACK\n"); + goto woo_re_hangup; + } + if (woomera_test_flag(woomera, WFLAG_HANGUP_NACK_ACK)) { + log_printf(SMG_LOG_ALL, woomera->log, + "Woomera MISSED HANGUP ACK: Retry HANGUP NACK ACK\n"); + goto woo_re_hangup; + } + + + /* This is where we actually pull the index + * out of the tank. We had to keep the tank + * value until the end of the call. Tank is only + * used on outgoing calls. */ + if (woomera->index_hold >= 1) { + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT)) { + /* Replace real woomera interface with a dummy so + the tank is blocked. The real woomera interface will be + deallocated */ + pull_from_holding_tank(woomera->index_hold,-1,-1); + + if (check_tank_index(woomera->index_hold) != NULL){ + log_printf(SMG_LOG_PROD, woomera->log, "Woomera Thread: [%s] setup id %i blocked waiting for NACK or ACK\n", + woomera->interface ,woomera->index_hold); + } + } else { + clear_from_holding_tank(woomera->index_hold, woomera); + + } + woomera->index_hold=0; + } + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "Woomera Thread Finished %u\n", (unsigned long) woomera->thread); + close_socket(&woomera->socket); + woomera->socket=-1; + + /* delete queue */ + while ((event_string = dequeue_event(woomera))) { + smg_free(event_string); + } + + if (woomera_test_flag(woomera, WFLAG_LISTENING)) { + del_listener(woomera); + } + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "WOOMERA session for [%s] stopped (ptr=%p)\n", + woomera->interface,woomera); + + pthread_mutex_destroy(&woomera->queue_lock); + pthread_mutex_destroy(&woomera->ms_lock); + pthread_mutex_destroy(&woomera->dtmf_lock); + pthread_mutex_destroy(&woomera->vlock); + pthread_mutex_destroy(&woomera->flags_lock); + woomera_set_raw(woomera, NULL); + woomera_set_interface(woomera, NULL); + + woomera_message_clear(&wmsg); + + smg_free(woomera); + pthread_mutex_lock(&server.thread_count_lock); + server.call_count--; + server.thread_count--; + pthread_mutex_unlock(&server.thread_count_lock); + + pthread_exit(NULL); + return NULL; +} + + +static int launch_woomera_thread(struct woomera_interface *woomera) +{ + int result = 0; + pthread_attr_t attr; + + result = pthread_attr_init(&attr); + //pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + //pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + woomera_set_flag(woomera, WFLAG_RUNNING); + result = pthread_create(&woomera->thread, &attr, woomera_thread_run, woomera); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Woomera Thread! (%i) %s\n", + __FUNCTION__,result,strerror(errno)); + woomera_clear_flag(woomera, WFLAG_RUNNING); + } + pthread_attr_destroy(&attr); + + return result; +} + +static int launch_monitor_thread(void) +{ + pthread_attr_t attr; + int result = 0; + struct sched_param param; + + param.sched_priority = 10; + result = pthread_attr_init(&attr); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + result = pthread_attr_setschedparam (&attr, ¶m); + + log_printf(SMG_LOG_ALL,server.log,"%s: Old Priority =%i res=%i \n",__FUNCTION__, + param.sched_priority,result); + + + woomera_set_flag(&server.master_connection, WFLAG_MONITOR_RUNNING); + result = pthread_create(&server.monitor_thread, &attr, monitor_thread_run, NULL); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Thread! %s\n", + __FUNCTION__,strerror(errno)); + woomera_clear_flag(&server.master_connection, WFLAG_MONITOR_RUNNING); + } + pthread_attr_destroy(&attr); + + return result; +} + + +#ifdef WP_HPTDM_API +static void *hp_tdmapi_span_run(void *obj) +{ + hp_tdm_api_span_t *span = obj; + int err; + + log_printf(SMG_LOG_ALL,server.log,"Starting %s span!\n",span->ifname); + + while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + + if (!span->run_span) { + break; + } + + err = span->run_span(span); + if (err) { + break; + } + + } + + if (span->close_span) { + span->close_span(span); + } + + sleep(3); + log_printf(SMG_LOG_ALL,server.log,"Stopping %s span!\n",span->ifname); + + pthread_exit(NULL); +} + + +static int launch_hptdm_api_span_thread(int span) +{ + pthread_attr_t attr; + int result = 0; + struct sched_param param; + + param.sched_priority = 5; + result = pthread_attr_init(&attr); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + result = pthread_attr_setschedparam (&attr, ¶m); + + result = pthread_create(&server.monitor_thread, &attr, hp_tdmapi_span_run, &hptdmspan[span]); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Thread! %s\n", + __FUNCTION__,strerror(errno)); + } + pthread_attr_destroy(&attr); + + return result; +} +#endif + +static int configure_server(void) +{ + struct woomera_config cfg; + char *var, *val; + int cnt = 0; + + server.dtmf_intr_ch = -1; + + if (!woomera_open_file(&cfg, server.config_file)) { + log_printf(SMG_LOG_ALL, server.log, "open of %s failed\n", server.config_file); + return 0; + } + + log_printf(SMG_LOG_ALL,server.log, "\n======================= \n"); + log_printf(SMG_LOG_ALL,server.log, "Server - Configuration \n"); + log_printf(SMG_LOG_ALL,server.log, "======================= \n"); + + while (woomera_next_pair(&cfg, &var, &val)) { + if (!strcasecmp(var, "boost_local_ip")) { + strncpy(server.mcon.cfg.local_ip, val, + sizeof(server.mcon.cfg.local_ip) -1); + strncpy(server.mconp.cfg.local_ip, val, + sizeof(server.mconp.cfg.local_ip) -1); + log_printf(SMG_LOG_ALL,server.log, "Server - Boost Local IP: %s\n",val); + + cnt++; + } else if (!strcasecmp(var, "boost_local_port")) { + server.mcon.cfg.local_port = atoi(val); + server.mconp.cfg.local_port = + server.mcon.cfg.local_port+1; + log_printf(SMG_LOG_ALL,server.log, "Server - Boost Local Port: %i\n",server.mcon.cfg.local_port); + cnt++; + } else if (!strcasecmp(var, "boost_remote_ip")) { + strncpy(server.mcon.cfg.remote_ip, val, + sizeof(server.mcon.cfg.remote_ip) -1); + strncpy(server.mconp.cfg.remote_ip, val, + sizeof(server.mconp.cfg.remote_ip) -1); + log_printf(SMG_LOG_ALL,server.log, "Server - Boost Remote IP: %s\n",server.mcon.cfg.remote_ip); + cnt++; + } else if (!strcasecmp(var, "boost_remote_port")) { + server.mcon.cfg.remote_port = atoi(val); + server.mconp.cfg.remote_port = + server.mcon.cfg.remote_port+1; + log_printf(SMG_LOG_ALL,server.log, "Server - Boost Remote Port: %i\n",server.mcon.cfg.local_port); + cnt++; + } else if (!strcasecmp(var, "logfile_path")) { + if (!server.logfile_path) { + server.logfile_path = smg_strdup(val); + } + } else if (!strcasecmp(var, "woomera_port")) { + server.port = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Woomera Port: %i\n",server.port); + } else if (!strcasecmp(var, "debug_level")) { + server.debug = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Debug Level: %i\n",server.debug); + } else if (!strcasecmp(var, "out_tx_test")) { + server.out_tx_test = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Tx Media Dbg: %s\n",server.out_tx_test?"On":"Off (Default)"); + } else if (!strcasecmp(var, "loop_trace")) { + server.loop_trace = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Media Loop Trace: %s\n",server.loop_trace?"On":"Off (Default)"); + } else if (!strcasecmp(var, "rxgain")) { + server.rxgain = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Rx Gain: %d\n",server.rxgain); + } else if (!strcasecmp(var, "txgain")) { + server.txgain = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Tx Gain: %d\n",server.txgain); + } else if (!strcasecmp(var, "dtmf_on_duration")){ + server.dtmf_on = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - DTMF ON Duration: %d ms\n",server.dtmf_on); + } else if (!strcasecmp(var, "dtmf_off_duration")){ + server.dtmf_off = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - DTMF Off Duration: %d ms\n",server.dtmf_off); + } else if (!strcasecmp(var, "dtmf_inter_ch_duration")){ + server.dtmf_intr_ch = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - DTMF Spacing: %d\n",server.dtmf_intr_ch); + } else if (!strcasecmp(var, "strip_cid_non_digits")){ + server.strip_cid_non_digits = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Strip non digits: %d\n",server.strip_cid_non_digits); + } else if (!strcasecmp(var, "max_calls")) { + int max = atoi(val); + if (max > 0) { + server.max_calls = max; + log_printf(SMG_LOG_ALL,server.log, "Server - Max Active Calls: %d\n",server.max_calls); + } + } else if (!strcasecmp(var, "autoacm")) { + int max = atoi(val); + if (max >= 0) { + autoacm=max; + log_printf(SMG_LOG_ALL,server.log, "Server - Auto ACM: %s\n",autoacm?"On":"Off (Default)"); + } + } else if (!strcasecmp(var, "max_spans")) { + int max = atoi(val); + if (max > 0) { + max_spans = max; + log_printf(SMG_LOG_ALL,server.log, "Server - Max Spans: %d\n",max_spans); + } + } else if (!strcasecmp(var, "call_timeout")) { + int max = atoi(val); + if (max >= 0) { + server.call_timeout=max; + } else { + server.call_timeout=SMG_DEFAULT_CALL_TIMEOUT; + } + log_printf(SMG_LOG_ALL,server.log, "Server - Call Comp Timeout: %d s\n",server.call_timeout); + + } else if (!strcasecmp(var, "base_media_port")) { + int base = atoi(val); + if (base >= 0) { + server.base_media_port = base; + server.next_media_port = base; + server.max_media_port = server.base_media_port + WOOMERA_MAX_MEDIA_PORTS; + log_printf(SMG_LOG_ALL,server.log, "Server - Base Media Port: %d\n",server.base_media_port); + } + + } else if (!strcasecmp(var, "max_media_ports")) { + int max = atoi(val); + if (max > WOOMERA_MAX_MEDIA_PORTS) { + server.max_media_port = server.base_media_port+max; + log_printf(SMG_LOG_ALL,server.log, "Server - Max Media Port: %d\n",server.max_media_port); + } + + } else if (!strcasecmp(var, "media_ip")) { + strncpy(server.media_ip, val, sizeof(server.media_ip) -1); + log_printf(SMG_LOG_ALL,server.log, "Server - Media IP: %s\n",server.media_ip); + } else { + log_printf(SMG_LOG_ALL, server.log, "Invalid Option %s at line %d!\n", var, cfg.lineno); + } + } + + log_printf(SMG_LOG_ALL,server.log, "======================= \n\n"); + + /* Post initialize */ + if (server.dtmf_on == 0){ + server.dtmf_on=SMG_DTMF_ON; + } + if (server.dtmf_off == 0) { + server.dtmf_off=SMG_DTMF_OFF; + } + if (server.dtmf_intr_ch == -1) { + server.dtmf_intr_ch = 0; + } + server.dtmf_size=(server.dtmf_on+server.dtmf_off)*10*2; + + log_printf(SMG_LOG_ALL,server.log, "DTMF On=%i Off=%i IntrCh=%i Size=%i\n", + server.dtmf_on,server.dtmf_off,server.dtmf_intr_ch,server.dtmf_size); + + woomera_close_file(&cfg); + return cnt == 4 ? 1 : 0; +} + + + +static int main_thread(void) +{ + + struct sockaddr_in sock_addr, client_addr; + struct woomera_interface *new_woomera; + int client_sock = -1, pid = 0; + unsigned int len = 0; + FILE *tmp; + + if ((server.master_connection.socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + fprintf(stderr,"%s:%d socket() failed %s\n",__FUNCTION__,__LINE__,strerror(errno)); + return 1; + } + + memset(&sock_addr, 0, sizeof(sock_addr)); /* Zero out structure */ + sock_addr.sin_family = AF_INET; /* Internet address family */ + sock_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ + sock_addr.sin_port = htons(server.port); /* Local port */ + + /* Bind to the local address */ + if (bind(server.master_connection.socket, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) < 0) { + fprintf(stderr,"%s:%d socket() bind %i failed %s\n",__FUNCTION__,__LINE__,server.port,strerror(errno)); + log_printf(SMG_LOG_ALL, server.log, "bind(%d) failed\n", server.port); + return 1; + } + + /* Mark the socket so it will listen for incoming connections */ + if (listen(server.master_connection.socket, MAXPENDING) < 0) { + fprintf(stderr,"%s:%d socket() listen failed %s\n",__FUNCTION__,__LINE__,strerror(errno)); + log_printf(SMG_LOG_ALL, server.log, "listen() failed\n"); + return 1; + } + + if ((pid = get_pid_from_file(PIDFILE))) { + fprintf(stderr,"%s:%d get pid file failed %s\n",__FUNCTION__,__LINE__,strerror(errno)); + log_printf(SMG_LOG_ALL, stderr, "pid %d already exists.\n", pid); + exit(0); + } + + if (!(tmp = safe_fopen(PIDFILE, "w"))) { + fprintf(stderr,"%s:%d open pid file failed %s\n",__FUNCTION__,__LINE__,strerror(errno)); + log_printf(SMG_LOG_ALL, stderr, "Error creating pidfile %s\n", PIDFILE); + return 1; + } else { + fprintf(tmp, "%d", getpid()); + fclose(tmp); + tmp = NULL; + } + + no_nagle(server.master_connection.socket); + +#if 0 + if (1) { + int span,chan; + call_signal_event_t event; +#if 0 + span=1; + chan=30; + event.span=span; + event.chan=chan; + launch_woomera_loop_thread(&event); +#else + for (span=0;span<8;span++) { + for (chan=0;chan<31;chan++) { + event.span=span; + event.chan=chan; + launch_woomera_loop_thread(&event); + } + } +#endif + } +#endif + +#ifdef WP_HPTDM_API + if (1) { + int span; + for (span=0;span<16;span++) { + hptdmspan[span] = sangoma_hptdm_api_span_init(span); + if (!hptdmspan[span]) { + break; + } else { + log_printf(SMG_LOG_ALL, server.log, "HP TDM API Span: %d configured...\n", + span); + launch_hptdm_api_span_thread(span); + } + } + } +#endif + + log_printf(SMG_LOG_PROD, server.log, "Main Process Started: Woomera Ready port: %d\n", server.port); + + while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + + /* Set the size of the in-out parameter */ + len = sizeof(client_addr); + + /* Wait for a client to connect */ + if ((client_sock = accept(server.master_connection.socket, (struct sockaddr *) &client_addr, &len)) < 0) { + /* Check if we are supposed to stop */ + if(!woomera_test_flag(&server.master_connection, WFLAG_RUNNING)) { + log_printf(SMG_LOG_ALL, server.log, "accept() stopped\n"); + return 0; + } + log_printf(SMG_LOG_ALL, server.log, "accept() failed\n"); + return 1; + } + + if ((new_woomera = new_woomera_interface(client_sock, &client_addr, len))) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Starting Thread for New Connection %s:%d Sock=%d\n", + inet_ntoa(new_woomera->addr.sin_addr), + ntohs(new_woomera->addr.sin_port), + client_sock); + + pthread_mutex_lock(&server.thread_count_lock); + server.call_count++; + pthread_mutex_unlock(&server.thread_count_lock); + + + if (launch_woomera_thread(new_woomera)) { + socket_printf(new_woomera->socket, + "501 call was cancelled!%s", + WOOMERA_RECORD_SEPERATOR); + + close_socket(&new_woomera->socket); + new_woomera->socket=-1; + smg_free(new_woomera); + log_printf(SMG_LOG_ALL, server.log, "ERROR: failed to launch woomera thread\n"); + } + } else { + log_printf(SMG_LOG_ALL, server.log, "Critical ERROR: memory/socket error\n"); + } + } + + log_printf(SMG_LOG_PROD, server.log, "Main Process End\n"); + + return 0; +} + +static int do_ignore(int sig) + +{ +#ifdef SMG_DROP_SEQ + drop_seq=4; +#endif + sdla_memdbg_free(0); + return 0; +} + +static int do_shut(int sig) +{ + woomera_clear_flag(&server.master_connection, WFLAG_RUNNING); + close_socket(&server.master_connection.socket); + log_printf(SMG_LOG_PROD, server.log, "Caught SIG %d, Closing Master Socket!\n", sig); + return 0; +} + +static int sangoma_tdm_init (int span) +{ +#ifdef LIBSANGOMA_GET_HWCODING + wanpipe_tdm_api_t tdm_api; + int fd=sangoma_open_tdmapi_span(span); + if (fd < 0 ){ + return -1; + } else { + server.hw_coding=sangoma_tdm_get_hw_coding(fd,&tdm_api); + close_socket(&fd); + } +#else +#error "libsangoma missing hwcoding feature: not up to date!" +#endif + return 0; +} + +static int woomera_startup(int argc, char **argv) +{ + int x = 0, pid = 0, bg = 0; + int span_cnt, chan_cnt; + char *cfg=NULL, *debug=NULL, *arg=NULL; + + while((arg = argv[x++])) { + + if (!strcasecmp(arg, "-hup")) { + if (! (pid = get_pid_from_file(PIDFILE))) { + log_printf(SMG_LOG_ALL, stderr, "Error reading pidfile %s\n", PIDFILE); + exit(1); + } else { + log_printf(SMG_LOG_ALL, stderr, "Killing PID %d\n", pid); + kill(pid, SIGHUP); + sleep(1); + exit(0); + } + + } else if (!strcasecmp(arg, "-term") || !strcasecmp(arg, "--term")) { + if (! (pid = get_pid_from_file(PIDFILE))) { + log_printf(SMG_LOG_ALL, stderr, "Error reading pidfile %s\n", PIDFILE); + exit(1); + } else { + log_printf(SMG_LOG_ALL, stderr, "Killing PID %d\n", pid); + kill(pid, SIGTERM); + unlink(PIDFILE); + sleep(1); + exit(0); + } + + } else if (!strcasecmp(arg, "-version")) { + fprintf(stdout, "\nSangoma Media Gateway: Version %s\n\n", SMG_VERSION); + exit(0); + + } else if (!strcasecmp(arg, "-help")) { + fprintf(stdout, "%s\n%s [-help] | [ -version] | [-hup] | [-wipe] | [[-bg] | [-debug ] | [-cfg ] | [-log ]]\n\n", WELCOME_TEXT, argv[0]); + exit(0); + } else if (!strcasecmp(arg, "-wipe")) { + unlink(PIDFILE); + } else if (!strcasecmp(arg, "-bg")) { + bg = 1; + + } else if (!strcasecmp(arg, "-g")) { + coredump = 1; + + } else if (!strcasecmp(arg, "-debug")) { + if (argv[x] && *(argv[x]) != '-') { + debug = argv[x++]; + } + } else if (!strcasecmp(arg, "-cfg")) { + if (argv[x] && *(argv[x]) != '-') { + cfg = argv[x++]; + } + } else if (!strcasecmp(arg, "-log")) { + if (argv[x] && *(argv[x]) != '-') { + server.logfile_path = smg_strdup(argv[x++]); + } + } else if (*arg == '-') { + log_printf(SMG_LOG_ALL, stderr, "Unknown Option %s\n", arg); + fprintf(stdout, "%s\n%s [-help] | [-hup] | [-wipe] | [[-bg] | [-debug ] | [-cfg ] | [-log ]]\n\n", WELCOME_TEXT, argv[0]); + exit(1); + } + } + + if (1){ + int spn; + for (spn=1;spn<=WOOMERA_MAX_SPAN;spn++) { + if (sangoma_tdm_init(spn) == 0) { + break; + } + } + if (spn>WOOMERA_MAX_SPAN) { + printf("\nError: Failed to access a channel on spans 1-16\n"); + printf(" Please start Wanpipe TDM API drivers\n"); + return 0; + } + } + + if (bg && (pid = fork())) { + log_printf(SMG_LOG_ALL, stderr, "Backgrounding!\n"); + return 0; + } + + for (span_cnt = 0; span_cnt < CORE_MAX_SPANS; span_cnt++) { + for (chan_cnt = 0; chan_cnt < CORE_MAX_CHAN_PER_SPAN; chan_cnt++) { + pthread_mutex_init(&server.process_table[span_cnt][chan_cnt].media_lock, NULL); + } + } + + q931_cause_setup(); + bearer_cap_setup(); + uil1p_to_str_setup(); + + server.port = 42420; + server.debug = 0; + strcpy(server.media_ip, "127.0.0.1"); + server.base_media_port = WOOMERA_MIN_MEDIA_PORT; + server.max_media_port = WOOMERA_MAX_MEDIA_PORT; + server.next_media_port = server.base_media_port; + server.log = stdout; + server.master_connection.socket = -1; + server.master_connection.event_queue = NULL; + server.config_file = cfg ? cfg : "/etc/sangoma_mgd.conf"; + pthread_mutex_init(&server.listen_lock, NULL); + pthread_mutex_init(&server.ht_lock, NULL); + pthread_mutex_init(&server.process_lock, NULL); + pthread_mutex_init(&server.digits_lock, NULL); + pthread_mutex_init(&server.media_udp_port_lock, NULL); + pthread_mutex_init(&server.thread_count_lock, NULL); + pthread_mutex_init(&server.master_connection.queue_lock, NULL); + pthread_mutex_init(&server.master_connection.flags_lock, NULL); + pthread_mutex_init(&server.mcon.lock, NULL); + server.master_connection.chan = -1; + server.master_connection.span = -1; + server.call_timeout=SMG_DEFAULT_CALL_TIMEOUT; + + if (smg_log_init()) { + printf("Error: Logger Launch Failed\n"); + fprintf(stderr, "Error: Logger Launch Failed\n"); + return 0; + } + + if (!configure_server()) { + log_printf(SMG_LOG_ALL, server.log, "configuration failed!\n"); + return 0; + } + +#ifndef USE_SYSLOG + if (server.logfile_path) { + if (!(server.log = safe_fopen(server.logfile_path, "a"))) { + log_printf(SMG_LOG_ALL, stderr, "Error setting logfile %s!\n", server.logfile_path); + server.log = stderr; + return 0; + } + } +#endif + + + if (debug) { + server.debug = atoi(debug); + } + + if (coredump) { + struct rlimit l; + memset(&l, 0, sizeof(l)); + l.rlim_cur = RLIM_INFINITY; + l.rlim_max = RLIM_INFINITY; + if (setrlimit(RLIMIT_CORE, &l)) { + log_printf(SMG_LOG_ALL, stderr, "Warning: Failed to disable core size limit: %s\n", + strerror(errno)); + } + } + +#ifdef __LINUX__ + if (geteuid() && coredump) { + if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) { + log_printf(SMG_LOG_ALL, stderr, "Warning: Failed to disable core size limit for non-root: %s\n", + strerror(errno)); + } + } +#endif + + + + (void) signal(SIGINT,(void *) do_shut); + (void) signal(SIGTERM,(void *) do_shut); + (void) signal(SIGPIPE,(void *) do_ignore); + (void) signal(SIGUSR1,(void *) do_ignore); + (void) signal(SIGHUP,(void *) do_shut); + + /* Wait for logger thread to start */ + usleep(5000); + + woomera_set_flag(&server.master_connection, WFLAG_RUNNING); + if (launch_monitor_thread()) { + woomera_clear_flag(&server.master_connection, WFLAG_RUNNING); + smg_log_cleanup(); + return 0; + } + + fprintf(stderr, "%s", WELCOME_TEXT); + + log_printf(SMG_LOG_ALL, stderr, "Woomera STARTUP Complete. [AutoACM=%i SDigit=%i]\n", + autoacm,server.strip_cid_non_digits); + return 1; +} + +static int woomera_shutdown(void) +{ + char *event_string; + int told = 0, loops = 0; + int span_cnt, chan_cnt; + + for (span_cnt = 0; span_cnt < CORE_MAX_SPANS; span_cnt++) { + for (chan_cnt = 0; chan_cnt < CORE_MAX_CHAN_PER_SPAN; chan_cnt++) { + pthread_mutex_destroy(&server.process_table[span_cnt][chan_cnt].media_lock); + } + } + + close_socket(&server.master_connection.socket); + pthread_mutex_destroy(&server.listen_lock); + pthread_mutex_destroy(&server.ht_lock); + pthread_mutex_destroy(&server.process_lock); + pthread_mutex_destroy(&server.digits_lock); + pthread_mutex_destroy(&server.media_udp_port_lock); + pthread_mutex_destroy(&server.thread_count_lock); + pthread_mutex_destroy(&server.master_connection.queue_lock); + pthread_mutex_destroy(&server.master_connection.flags_lock); + pthread_mutex_destroy(&server.mcon.lock); + woomera_clear_flag(&server.master_connection, WFLAG_RUNNING); + + + if (server.logfile_path) { + smg_free(server.logfile_path); + server.logfile_path = NULL; + } + + /* delete queue */ + while ((event_string = dequeue_event(&server.master_connection))) { + smg_free(event_string); + } + + while(server.thread_count > 0) { + loops++; + + if (loops % 1000 == 0) { + told = 0; + } + + if (loops > 10000) { + log_printf(SMG_LOG_ALL, server.log, "Red Alert! threads did not stop\n"); + assert(server.thread_count == 0); + } + + if (told != server.thread_count) { + log_printf(SMG_LOG_PROD, server.log, "Waiting For %d thread%s.\n", + server.thread_count, server.thread_count == 1 ? "" : "s"); + told = server.thread_count; + } + ysleep(10000); + } + unlink(PIDFILE); + log_printf(SMG_LOG_ALL, stderr, "Woomera SHUTDOWN Complete.\n"); + + smg_log_cleanup(); + + return 0; +} + +int main(int argc, char *argv[]) +{ + int ret = 0; + + memset(&server,0,sizeof(server)); + memset(&woomera_dead_dev,0,sizeof(woomera_dead_dev)); + + mlockall(MCL_FUTURE); + memset(&server, 0, sizeof(server)); + memset(&woomera_dead_dev, 0, sizeof(woomera_dead_dev)); + + ret=nice(-5); + + sdla_memdbg_init(); + + server.hw_coding=0; + + openlog (ps_progname ,LOG_PID, LOG_LOCAL2); + + if (! (ret = woomera_startup(argc, argv))) { + exit(0); + } + ret = main_thread(); + + woomera_shutdown(); + + sdla_memdbg_free(1); + + return ret; +} + +/** EMACS ** + * Local variables: + * mode: C + * c-basic-offset: 4 + * End: + */ + diff --git a/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.12.tmp b/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.12.tmp new file mode 100644 index 0000000..9685044 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.12.tmp @@ -0,0 +1,414 @@ +#!/bin/bash + +# ---------------------------------------------------------------------------- +# Prompt user for input. +# ---------------------------------------------------------------------------- +prompt() +{ + if test $NONINTERACTIVE; then + return 0 + fi + + echo -ne "$*" >&2 + read CMD rest + return 0 +} + +# ---------------------------------------------------------------------------- +# Get Yes/No +# ---------------------------------------------------------------------------- +getyn() +{ + if test $NONINTERACTIVE; then + return 0 + fi + + while prompt "$* (y/n) " + do case $CMD in + [yY]) return 0 + ;; + [nN]) return 1 + ;; + *) echo -e "\nPlease answer y or n" >&2 + ;; + esac + done +} + + + +home=`pwd`; + +force="false" +noss7="invalid" +pri="false" +rootdir="" +pbxdir=/usr/src/asterisk +pbxd="asterisk" +cfgdir="/etc/asterisk" +mysyslog=" " +smg_installed=0 +chan_woomera_installed=0 +no_woomera="false" + + +while [ ! -z $1 ] +do + if [ $1 = '-force' ]; then + force="force" + elif [ $1 = '-noss7' ]; then + noss7="true" + elif [ $1 = '-ss7' ]; then + noss7="flase" + elif [ $1 = '-bri' ]; then + noss7="true" + elif [ $1 = '-no_woomera' ]; then + noss7="true" + no_woomera="true" + elif [ $1 = '-pri' ]; then + noss7="true" + pri="true" + elif [ $1 = '-pbxdir' ]; then + shift + pbxdir=$1 + if [ "$pbxdir" = "" ]; then + echo "Error: Invalid -pbxdir input argument: $pbxdir!"; + exit 1 + elif [ ! -d $pbxdir ]; then + echo "Error: directory does not exist: $pbxdir!"; + exit 1 + fi + + elif [ $1 = '-rootdir' ]; then + shift + rootdir=$1 + if [ "$rootdir" = "" ]; then + echo "Error: Invalid root dir: $rootdir!"; + exit 1 + elif [ ! -d $rootdir ]; then + echo "Error: root dir does not exist: $rootdir!"; + exit 1 + fi + + else + echo "Invalid option: $1"; + echo + echo "Usage: ./install [-bri|-ss7]" + echo "Please specify if smg is being installed for BRI or SS7" + echo + exit 1 + fi + shift +done + +if [ -f $pbxdir/include/callweaver.h ]; then + echo "PBXD=callweaver" > /etc/wanpipe/pbxd + pbxd="callweaver" + cfgdir="/usr/local/etc/callweaver" +else + echo "PBXD=asterisk" > /etc/wanpipe/pbxd + pbxd="asterisk" + cfgdir="/etc/asterisk" +fi + +if [ $noss7 == 'invalid' ] && [ $pri == 'false' ]; then + echo + echo "Usage Error: ./install [-bri|-pri|-ss7]" + echo "Please specify if smg is being installed for BRI or SS7 or PRI" + echo + exit 1 +fi + +if [ $noss7 != 'true' ]; then + if [ ! -e /usr/local/ss7box/$ss7boost ]; then + echo "Error: ss7boost not found in /usr/local/ss7box dir"; + exit 1 + fi + if [ ! -e /usr/local/ss7box/$ss7boxd ]; then + echo "Error: ss7boxd not found in /usr/local/ss7box dir"; + exit 1 + fi +fi + +if [ $force = "force" ]; then + echo "Stopping SMG..." + eval "sangoma_mgd -term" + echo "OK." + echo +else + if [ -f /var/run/sangoma_mgd.pid ]; then + echo "Warning: sangoma_mgd is running!" + getyn "Would you like to stop sangoma_mgd?" + if [ $? -ne 0 ]; then + exit 1 + fi + + eval "sangoma_mgd -term" + if [ -f /var/run/sangoma_mgd.pid ];then + echo "Failed to stop sangoma_mgd" + exit 1 + fi + fi +fi + +echo +echo "Checking Syslog ...." +if [ -e /etc/syslog.conf ]; then + echo "found /etc/syslog.conf " + mysyslog=syslog +elif [ -e /etc/rsyslog.conf ] ; then + echo " found /etc/rsyslog.conf " + mysyslog='rsyslog' +else + echo " Warning : Syslog not found " + echo " This install script is optimized for RedHat/Centos/Fedora Distributions" + getyn "Would you like to proceed?" + if [ $? -ne 0 ]; then + exit 1 + fi + + mysyslog=" " + +fi + + +if [ $mysyslog != " " ]; then + eval "grep "local2.*sangoma_mgd" /etc/$mysyslog.conf" > /dev/null 2> /dev/null + if [ $? -ne 0 ]; then + eval "grep "local2" /etc/$mysyslog.conf " > /dev/null 2> /dev/null + if [ $? -eq 0 ]; then + echo + echo "Warning : local2 is already used in $mysyslog.conf" + echo + fi + echo -e "\n# Sangoma Media Gateway log" > tmp.$$ + echo -e "local2.* /var/log/sangoma_mgd.log\n" >> tmp.$$ + eval "cat /etc/$mysyslog.conf tmp.$$ > tmp1.$$" + \cp -f tmp1.$$ /etc/$mysyslog.conf + eval "/etc/init.d/$mysyslog restart" + fi + eval "grep "local3.*sangoma_bri" /etc/$mysyslog.conf" > /dev/null 2> /dev/null + if [ $? -ne 0 ]; then + eval "grep "local3" /etc/$mysyslog.conf " > /dev/null 2> /dev/null + if [ $? -eq 0 ]; then + echo + echo "Warning : local3 is already used in $mysyslog.conf" + echo + fi + echo -e "\n# Sangoma BRI Daemon (smg_bri) log" > tmp.$$ + echo -e "local3.* /var/log/sangoma_bri.log\n" >> tmp.$$ + eval "cat /etc/$mysyslog.conf tmp.$$ > tmp1.$$" + \cp -f tmp1.$$ /etc/$mysyslog.conf + eval "/etc/init.d/$mysyslog restart" + fi +fi + +if [ -f tmp1.$$ ]; then + rm -f tmp1.$$ +fi +if [ -f tmp.$$ ]; then + rm -f tmp.$$ +fi + +echo "Ok" +echo +echo "Checking logrotate ..." +eval "type logrotate" > /dev/null 2> /dev/null +if [ $? -ne 0 ]; then + echo "Error: Logrotate not found !" +fi + +if [ -e /etc/logrotate.d ] && [ -e /etc/logrotate.d/$mysyslog ]; then + + eval "grep sangoma_mgd /etc/logrotate.d/$mysyslog" > /dev/null 2> /dev/null + if [ $? -ne 0 ]; then + eval "sed -e 's/messages/messages \/var\/log\/sangoma_mgd.log/' /etc/logrotate.d/$mysyslog >tmp2.$$ 2>/dev/null" + eval "cp -f tmp2.$$ /etc/logrotate.d/$mysyslog" + eval "logrotate -f /etc/logrotate.d/$mysyslog" + if [ $? -ne 0 ]; then + echo "Error: logrotate restart failed!"; + exit 1; + fi + echo "Logrotate is being changed and restarted!" + else + echo "Logrotate is configured!" + fi + +else + echo "Error: Logrotate dir: /etc/logrotate.d not found !" +fi + +echo "OK." +echo +echo "Checking for SCTP Utilities...." +if [ ! -e /usr/include/netinet/sctp.h ] && [ ! -e /usr/include/sctp.h ]; then + echo + echo "Error: lksctp-tools-devel package missing" + echo "Please install sctp using a package manager" + echo "eg: yum install lksctp-tools-devel" + echo + exit 1 +fi +echo "OK." +echo + +echo "Checking for SCTP modules..." +eval "modprobe -l | grep \"\/sctp.ko\" >/dev/null 2>/dev/null" +if [ $? -ne 0 ]; then + if [ ! -e /proc/net/sctp ]; then + echo "Warning: Your Kernel does not support SCTP Protocol!" + echo "SCTP is needed by SMG!" + echo + echo "Please contact sangoma support!" + echo + exit 1 + fi +fi +echo "OK." +echo + + +echo "Compiling LibTeletone MGD ..." +echo +sleep 1 +cd lib/libteletone +eval "./configure > /dev/null" +echo "..." +eval "make clean > /dev/null" +eval "make > /devnull" +if [ $? -ne 0 ]; then + exit 1; +fi +make install +echo "Ok." + +cd $home + +echo +echo +echo "Compiling Sangoma MGD ..." +echo +sleep 1 +eval "make clean 2> /dev/null > /dev/null" +if [ $pri = "true" ]; then + eval "make PRI=YES" +else +if [ $noss7 = "true" ]; then + eval "make NO_SS7=YES" +else + eval "make" +fi +fi + +if [ $? -ne 0 ]; then + exit 1; +fi + +if [ $pri = "true" ]; then + eval "make INSTALLPREFIX=$rootdir PRI=YES install" +else +if [ $noss7 = "true" ]; then + #This case is currently used for bri + eval "make INSTALLPREFIX=$rootdir NO_SS7=YES install" +else + eval "make INSTALLPREFIX=$rootdir install" +fi +fi + +if [ $? -ne 0 ]; then + echo + echo " SMG Installation Failed" + echo + echo " Please contact Sangoma Support" + echo " http://wiki.sangoma.com" + echo + exit 1; +else + echo "Ok." + smg_installed=1; +fi + +if [ $no_woomera != "true" ]; then + if [ -d chan_woomera.trunk ]; then + + cd chan_woomera.trunk + + echo "Compiling Woomera Channel ..." + + if [ ! -e $pbxdir ]; then + echo + echo "Error: $pbxdir directory does not exist!" + echo " Please provide the path to Asterik or CallWeaver source." + echo " Then re-run eg: ./install -pbxdir /usr/src/asterisk " + echo + getyn "Would you like to continue installation?" + if [ $? -ne 0 ]; then + exit 1; + fi + else + eval "make clean 2> /dev/null > /dev/null" + eval "make PBXDIR=$pbxdir" + if [ $? -ne 0 ]; then + exit 1; + fi + make INSTALLPREFIX=$rootdir install + if [ $? -ne 0 ]; then + echo + echo "Error: Failed to compile chan_woomera into PBX source!" + echo + exit 1; + fi + echo "Ok." + chan_woomera_installed=1; + fi + + else + echo "Warning: chan_woomera directory does not exist!" + exit 1 + fi +fi + +if [ $smg_installed -eq 1 ]; then + echo "---------------------------------" + echo + echo " SMG Install Done" + echo + echo "--> Config: /etc/sangoma_mgd.conf" + echo "--> Start: sangoma_mgd -bg" + echo +else + echo "---------------------------------" + echo + echo " SMG Installation Failed" + echo + echo " Please contact Sangoma Support" + echo " http://wiki.sangoma.com" + echo +fi + +if [ $chan_woomera_installed -eq 1 ]; then + echo + echo " Chan Woomera Install Done" + echo + echo "--> Config: $cfgdir/woomera.conf" + echo "--> Start: start $pbxd (part of $pbxd)" + echo + echo "---------------------------------" +elif [ $no_woomera = "true" ];then + echo + echo " You chose not to install Chan Woomera" + echo +else + echo + echo " Chan Woomera Installatin Failed" + echo + echo " If you are running Asteirsk/CallWeaver on another system" + echo " ignore this message. Otherwise please contact" + echo " Sangoma Support." + echo " http://wiki.sangoma.com" + echo + echo "---------------------------------" +fi + +exit 0 + diff --git a/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.13.tmp b/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.13.tmp new file mode 100644 index 0000000..261c778 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.13.tmp @@ -0,0 +1,6038 @@ +/********************************************************************************* + * sangoma_mgd.c -- Sangoma Media Gateway Daemon for Sangoma/Wanpipe Cards + * + * Copyright 05-09, Nenad Corbic + * Anthony Minessale II + * + * This program is free software, distributed under the terms of + * the GNU General Public License + * + * ============================================= + * + * v1.58 Nenad Corbic + * Added bridge tdm to ip functionality + * + * v1.57 Nenad Corbic + * Support for woomera multiple profiles + * + * v1.56 David Yat Sin + * Changed BRI to run with HWEC in non-persist mode + * + * v1.55 Nenad Corbic + * Updated the base media port to 10000 and max media ports to 5000 + * + * v1.54 Nenad Corbic + * Bug added in 1.51 release causing call on channel 31 to fail. + * + * v1.53 Nenad Corbic + * Added progress message + * + * v1.52 David Yat Sin + * Changed sangoma_open_span_chan to __sangoma_span_chan + * to enabled shared used of file descriptors when using + * with PRI in NFAS mode + * + * v1.51 David Yat Sin + * MAX_SPANS increased to 32. + * Fix for server.process_table declared incorrectly + * + * v1.50 Nenad Corbic + * Logic to support multiple woomera clients hanging up the + * channel. This feature now supprorts woomera loadbalancing + * with extension check on the client end. Example, two Asterisk + * systems setup where Asterisk with correct extension gets the + * call. + * Changed log levels: Loglevel 1 = production + * Loglevel 2 = Boost TX/RX EVENTS + * Loglevel 3 = Woomera RX Messages + * Loglevel 4 = call setup debugging + * Loglevel 5 = extra debugging + * Loglevel 10 = full debugging + * + * v1.49 Nenad Corbic + * Removed tv from sigboost to make it binary compatible + * Updated release cause on double use return code. + * + * v1.48 Nenad Corbic + * Konrad Hammel + * Jun 30 2009 + * Added feature to disable/enable HWEC on channels that are + * passing a call with a "data" only transfer capability + * + * v1.47 Nenad Corbic + * Apr 30 2009 + * Updated hangup woomera events. Each event must + * be followed by a woomera error code 200=ok >299=no ok. + * This update fixes the chan_woomera socket write + * warnings. + * + * v1.46 Nenad Corbic + * Mar 27 2009 + * Major updates on socket handling. A bug was introducted + * when cmm and trunk was merged. + * Added configuration print in the logs. + * + * v1.45 Nenad Corbic + * Mar 19 2009 + * Outbound call timeout defaulted to 300 seconds. + * Timeout on answer instead of accept. + * + * v1.44 Nenad Corbic + * Mar 19 2009 + * Adjustable boost size. Added boost version check. + * + * v1.43 David Yat Sin + * Feb 25 2009 + * Merged CMM and Trunk + * + * v1.42 Nenad Corbic + * Jan 26 2008 + * Call Bearer Capability + * BugFix: Hangup NACK was sent out with invalid cause 0 + * + * v1.41 Nenad Corbic + * Jan 12 2008 + * Fixed the NACK with cause 0 bug. + * Added cause 19 on call timeout. + * Added configuration option call_timeout to timeout + * outbound calls. + * + * v1.40 Nenad Corbic + * Dec 10 2008 + * Check for Rx call overrun. + * In unlikely case sangoma_mgd goes out of + * sync with boost. + * + * v1.39 Nenad Corbic + * Sep 17 2008 + * Updated for Unit Test + * Bug fix in Loop Logic, possible race + * between media and woomera thread on loop end. + * + * v1.38 Nenad Corbic + * Sep 5 2008 + * Added a Double use of setup id logic. + * Currently double use call gets dropped. + * + * v1.37 Nenad Corbic + * Sep 2 2008 + * Bug fix in REMOVE LOOP logic continued + * The woomera->sock in loop logic was set to 0. + * Closing it failed the next call. + * + * v1.36 Nenad Corbic + * Aug 28 2008 + * Bug fix in REMOVE LOOP logic + * Remove F from incoming calls by default + * Check for errno on poll() + * Do not delay in closing socket on media down. + * + * v1.35cmm Nenad Corbic + * Jul 28 2008 + * Bug fixes on ACK Timeout and trunk release + * Added a thread logger + * Increased max spans to 32 + * + * v1.34cmm Nenad Corbic + * Jul 24 2008 + * Clean up the setup id on CALL STOP ACK + * + * v1.33cmm Nenad Corbic + * Jul 24 2008 + * The CALL STOP timeout function had a bug + * resulting in false blocking of tank ids + * due to STOP ACK Timeouts. + * + * v1.33 Nenad Corbic + * Jul 18 2008 + * Added UDP Sequencing to check for dropped frames + * Should only be used for debugging. + * + * v1.32 David Yat Sin + * Jul 17 2008 + * Support for d-channel incoming digit + * passthrough to Asterisk + * + * v1.32cmm Nenad Corbic + * Jun 03 2008 + * Implemented new Restart ACK Policy + * Split the Event packet into 2 types + * + * v1.31cmm Nenad Corbic + * Apr 04 2008 + * New CMM Restart procedure for boost + * Block TRUNK ID on ACK Timeout + * + * v1.31 David Yat Sin + * Apr 29 2008 + * Support for HW DTMF events. + * + * v1.30 Nenad Corbic + * Feb 08 2008 + * The fix in v1.26 causes double stop. + * I took out the v1.26 fix and added + * a big warning if that condition is + * ever reached. + * + * v1.29 Nenad Corbic + * Feb 07 2008 + * Added strip_cid_non_digits option + * + * v1.28 Nenad Corbic + * Feb 06 2008 + * Fixed a memory leak in clone event + * function. Added memory check subsystem + * + * v1.27 Nenad Corbic + * Jan 24 2008 + * Fixed a memory leak on incoming calls + * Removed the use of server listener which + * was not used + * + * v1.26 Nenad Corbic + * Jan 18 2008 + * Fixed hangup after invalid Answer or Ack Session + * Can cause double use of setup id - now fixed + * Update on autoacm on accept check for acked. + * + * v1.25 Nenad Corbic + * Dec 31 2007 + * Removed UDP Resync it can cause skb_over errors. + * Moved RDNIS message to higher debug level + * + * v1.24 Nenad Corbic + * Nov 30 2007 + * Bug fix on return code on ALL ckt busy + * + * v1.23 Nenad Corbic + * Bug fix on socket open. Check for retun code >= 0 + * + * v1.22 Nenad Corbic + * Nov 27 2007 + * Updated DTMF Tx function + * Fixed - dtmf tx without voice + * Fxied - dtmf clipping. + * + * v1.21 Nenad Corbic + * Nov 25 2007 + * Major unit testing of each state + * Numerous bug fixes for non autoacm mode. + * Changed "Channel-Name" to tg/cic + * Added compile option WANPIPE_CHAN_NAME to change Asterisk channel + * name of chan_woomera.so. So one can use Dial(SS7/g1/${EXTE}) + * instead of WOOMERA (for example) + * + * v1.20 Nenad Corbic + * Added option for Auto ACM response mode. + * + * v1.19 Nenad Corbic + * Configurable DTMF + * Bug fix in release codes (all ckt busy) + * + * v1.18 Nenad Corbic + * Added new rel cause support based on + * digits instead of strings. + * + * v1.17 Nenad Corbic + * Added session support + * + * v1.16 Nenad Corbic + * Added hwec disable on loop ccr + * + * v1.15 Nenad Corbic + * Updated DTMF Locking + * Added delay between digits + * + * v1.14 Nenad Corbic + * Updated DTMF Library + * Fixed DTMF synchronization + * + * v1.13 Nenad Corbic + * Woomera OPAL Dialect + * Added Congestion control + * Added SCTP + * Added priority ISUP queue + * Fixed presentation + * + * v1.12 Nenad Corbic + * Fixed CCR + * Removed socket shutdown on end call. + * Let Media thread shutodwn sockets. + * + * v1.11 Nenad Corbic + * Fixed Remote asterisk/woomera connection + * Increased socket timeouts + * + * v1.10 Nenad Corbic + * Added Woomera OPAL dialect. + * Start montor thread in priority + * + * v1.9 Nenad Corbic + * Added Loop mode for ccr + * Added remote debug enable + * Fixed syslog logging. + * + * v1.8 Nenad Corbic + * Added a ccr loop mode for each channel. + * Boost can set any channel in loop mode + * + * v1.7 Nenad Corbic + * Pass trunk group number to incoming call + * chan woomera will use it to append to context + * name. Added presentation feature. + * + * v1.6 Nenad Corbic + * Use only ALAW and MLAW not SLIN. + * This reduces the load quite a bit. + * Send out ALAW/ULAW format on HELLO message. + * RxTx Gain is done now in chan_woomera. + * + * v1.5 Nenad Corbic + * Bug fix in START_NACK_ACK handling. + * When we receive START_NACK we must alwasy pull tank before + * we send out NACK_ACK this way we will not try to send NACK + * ourself. + *********************************************************************************/ + +#include "sangoma_mgd.h" +#include "sangoma_mgd_memdbg.h" +#include "q931_cause.h" +#include "smg_capabilities.h" + +int pipe_fd[2]; + +#ifdef CODEC_LAW_DEFAULT +static uint32_t codec_sample=8; +#else +static uint32_t codec_sample=16; +#endif + +static char ps_progname[]="sangoma_mgd"; + +static struct woomera_interface woomera_dead_dev; + + +/* For 3.4. release always set to 1 */ +#ifdef BRI_PROT +static unsigned char tdmv_hwec_persist = 0; +#else +static unsigned char tdmv_hwec_persist = 1; +#endif +struct woomera_server server; + +struct smg_tdm_ip_bridge g_smg_ip_bridge_idx[MAX_SMG_BRIDGE]; +pthread_mutex_t g_smg_ip_bridge_lock; + +#if 0 +#define DOTRACE +#endif + + +#define SMG_VERSION "v1.58" + +/* enable early media */ +#if 1 +#define WOOMERA_EARLY_MEDIA 1 +#endif + + +#define SMG_DTMF_ON 60 +#define SMG_DTMF_OFF 10 +#define SMG_DTMF_RATE 8000 +#define SMG_DEFAULT_CALL_TIMEOUT 300 + +#if 0 +#define MEDIA_SOCK_SHUTDOWN 1 +#endif + +#ifdef DOTRACE +static int tc = 0; +#endif + +#if 0 +#warning "NENAD: HPTDM API" +#define WP_HPTDM_API 1 +#else +#undef WP_HPTDM_API +#endif + +#ifdef WP_HPTDM_API +hp_tdm_api_span_t *hptdmspan[WOOMERA_MAX_SPAN]; +#endif + +#if 0 +#define SMG_DROP_SEQ 1 +#warning "SMG Debug feature Drop Seq Enabled" +static int drop_seq=0; +#else +#undef SMG_DROP_SEQ +#endif + + +#if 0 +#define SMG_NO_MEDIA +#warning "SMG No Media Defined" +#else +#undef SMG_NO_MEDIA +#endif + +const char WELCOME_TEXT[] = +"================================================================================\n" +"Sangoma Media Gateway Daemon v1.58 \n" +"\n" +"TDM Signal Media Gateway for Sangoma/Wanpipe Cards\n" +"Copyright 2005, 2006, 2007 \n" +"Nenad Corbic , Anthony Minessale II \n" +"This program is free software, distributed under the terms of\n" +"the GNU General Public License\n" +"================================================================================\n" +""; + + + +static int coredump=1; +static int autoacm=0; + +/* FIXME: Should be done in cfg file */ +#if defined(BRI_PROT) +int max_spans=WOOMERA_BRI_MAX_SPAN; +int max_chans=WOOMERA_BRI_MAX_CHAN; +#else +/* PRI_PROT uses these defines as well */ +int max_spans=WOOMERA_MAX_SPAN; +int max_chans=WOOMERA_MAX_CHAN; +#endif + +static int launch_media_tdm_thread(struct woomera_interface *woomera); +static int launch_woomera_thread(struct woomera_interface *woomera); +static void woomera_check_digits (struct woomera_interface *woomera); +static struct woomera_interface *alloc_woomera(void); +static void handle_event_dtmf(struct woomera_interface *woomera, unsigned char dtmf_digit); + +q931_cause_to_str_array_t q931_cause_to_str_array[255]; +bearer_cap_to_str_array_t bearer_cap_to_str_array[255]; +uil1p_to_str_array_t uil1p_to_str_array[255]; + +static int isup_exec_command(int span, int chan, int id, int cmd, int cause) +{ + short_signal_event_t oevent; + int retry=5; + + call_signal_event_init((short_signal_event_t*)&oevent, cmd, chan, span); + oevent.release_cause = cause; + + if (id >= 0) { + oevent.call_setup_id = id; + } +isup_exec_cmd_retry: + if (call_signal_connection_write(&server.mcon, (call_signal_event_t*)&oevent) < 0){ + + --retry; + if (retry <= 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket: %s\n", + strerror(errno)); + return -1; + } else { + log_printf(SMG_LOG_ALL, server.log, + "System Warning: Failed to tx on ISUP socket: %s :retry %i\n", + strerror(errno),retry); + } + + goto isup_exec_cmd_retry; + } + + return 0; +} + +static int isup_exec_event(call_signal_event_t *event) +{ + int retry=5; + +isup_exec_cmd_retry: + if (call_signal_connection_write(&server.mcon, event) < 0){ + + --retry; + if (retry <= 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket: %s\n", + strerror(errno)); + return -1; + } else { + log_printf(SMG_LOG_ALL, server.log, + "System Warning: Failed to tx on ISUP socket: %s :retry %i\n", + strerror(errno),retry); + } + + goto isup_exec_cmd_retry; + } + + return 0; +} + + +static int isup_exec_commandp(int span, int chan, int id, int cmd, int cause) +{ + short_signal_event_t oevent; + int retry=5; + + call_signal_event_init(&oevent, cmd, chan, span); + oevent.release_cause = cause; + + if (id >= 0) { + oevent.call_setup_id = id; + } +isup_exec_cmd_retry: + if (call_signal_connection_writep(&server.mconp, (call_signal_event_t*)&oevent) < 0){ + + --retry; + if (retry <= 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket: %s\n", + strerror(errno)); + return -1; + } else { + log_printf(SMG_LOG_ALL, server.log, + "System Warning: Failed to tx on ISUP socket: %s :retry %i\n", + strerror(errno),retry); + } + + goto isup_exec_cmd_retry; + } + + return 0; +} + + +static int get_span_chan_from_interface(char* interface, int *span_ptr, int *chan_ptr) +{ + int span, chan; + int err; + + err=sscanf(interface, "s%dc%d", &span, &chan); + if (err!=2) { + err=sscanf(interface, "w%dg%d", &span, &chan); + } + + if (err==2) { + if (smg_validate_span_chan(span,chan) != 0) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "WOOMERA Warning invalid span chan in interface %s\n", + interface); + return -1; + } + + *span_ptr = span; + *chan_ptr = chan; + return 0; + } + log_printf(SMG_LOG_ALL, server.log, "ERROR: Failed to get span chan from interface:[%s]\n", interface, span, chan); + return -1; +} + +static int socket_printf(int socket, char *fmt, ...) +{ + char *data; + int ret = 0; + va_list ap; + + if (socket < 0) { + return -1; + } + + va_start(ap, fmt); +#ifdef SOLARIS + data = (char *) smg_malloc(2048); + vsnprintf(data, 2048, fmt, ap); +#else + ret = vasprintf(&data, fmt, ap); +#endif + va_end(ap); + if (ret == -1) { + fprintf(stderr, "Memory Error\n"); + log_printf(SMG_LOG_ALL, server.log, "Crtical ERROR: Memory Error!\n"); + } else { + int err; + int len = strlen(data); + err=send(socket, data, strlen(data), 0); + if (err != strlen(data)) { + log_printf(SMG_LOG_DEBUG_8, server.log, "ERROR: Failed to send data to woomera socket(%i): err=%i len=%d %s\n", + socket,err,len,strerror(errno)); + ret = err; + } else { + ret = 0; + } + + free(data); + } + + return ret; +} + + + +static int woomera_next_pair(struct woomera_config *cfg, char **var, char **val) +{ + int ret = 0; + char *p, *end; + + *var = *val = NULL; + + for(;;) { + cfg->lineno++; + + if (!fgets(cfg->buf, sizeof(cfg->buf), cfg->file)) { + ret = 0; + break; + } + + *var = cfg->buf; + + if (**var == '[' && (end = strchr(*var, ']'))) { + *end = '\0'; + (*var)++; + strncpy(cfg->category, *var, sizeof(cfg->category) - 1); + continue; + } + + if (**var == '#' || **var == '\n' || **var == '\r') { + continue; + } + + if ((end = strchr(*var, '#'))) { + *end = '\0'; + end--; + } else if ((end = strchr(*var, '\n'))) { + if (*end - 1 == '\r') { + end--; + } + *end = '\0'; + } + + p = *var; + while ((*p == ' ' || *p == '\t') && p != end) { + *p = '\0'; + p++; + } + *var = p; + + if (!(*val = strchr(*var, '='))) { + ret = -1; + log_printf(SMG_LOG_ALL, 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; + +} + + +#if 0 +static void woomera_set_span_chan(struct woomera_interface *woomera, int span, int chan) +{ + pthread_mutex_lock(&woomera->vlock); + woomera->span = span; + woomera->chan = chan; + pthread_mutex_unlock(&woomera->vlock); + +} +#endif + + +static struct woomera_event *new_woomera_event_printf(struct woomera_event *ebuf, char *fmt, ...) +{ + struct woomera_event *event = NULL; + int ret = 0; + va_list ap; + + if (ebuf) { + event = ebuf; + } else if (!(event = new_woomera_event())) { + log_printf(SMG_LOG_ALL, server.log, "Memory Error queuing event!\n"); + return NULL; + } else { + return NULL; + } + + va_start(ap, fmt); +#ifdef SOLARIS + event->data = (char *) smg_malloc(2048); + vsnprintf(event->data, 2048, fmt, ap); +#else + ret = smg_vasprintf(&event->data, fmt, ap); +#endif + va_end(ap); + if (ret == -1) { + log_printf(SMG_LOG_ALL, server.log, "Memory Error queuing event!\n"); + destroy_woomera_event(&event, EVENT_FREE_DATA); + return NULL; + } + + return event; + +} + +static struct woomera_event *woomera_clone_event(struct woomera_event *event) +{ + struct woomera_event *clone; + + if (!(clone = new_woomera_event())) { + return NULL; + } + + /* This overwrites the MALLOC in clone causing a memory leak */ + memcpy(clone, event, sizeof(*event)); + + /* We must set the malloc flag back so that this event + * will be deleted */ + _woomera_set_flag(clone, WFLAG_MALLOC); + clone->next = NULL; + clone->data = smg_strdup(event->data); + + return clone; +} + +static void enqueue_event(struct woomera_interface *woomera, + struct woomera_event *event, + event_args free_data) +{ + struct woomera_event *ptr, *clone = NULL; + + assert(woomera != NULL); + assert(event != NULL); + + if (!(clone = woomera_clone_event(event))) { + log_printf(SMG_LOG_ALL, server.log, "Error Cloning Event\n"); + return; + } + + pthread_mutex_lock(&woomera->queue_lock); + + for (ptr = woomera->event_queue; ptr && ptr->next ; ptr = ptr->next); + + if (ptr) { + ptr->next = clone; + } else { + woomera->event_queue = clone; + } + + pthread_mutex_unlock(&woomera->queue_lock); + + woomera_set_flag(woomera, WFLAG_EVENT); + + if (free_data && event->data) { + /* The event has been duplicated, the original data + * should be freed */ + smg_free(event->data); + event->data=NULL; + } +} + +static char *dequeue_event(struct woomera_interface *woomera) +{ + struct woomera_event *event; + char *data = NULL; + + if (!woomera) { + return NULL; + } + + pthread_mutex_lock(&woomera->queue_lock); + if (woomera->event_queue) { + event = woomera->event_queue; + woomera->event_queue = event->next; + data = event->data; + pthread_mutex_unlock(&woomera->queue_lock); + + destroy_woomera_event(&event, EVENT_KEEP_DATA); + return data; + } + pthread_mutex_unlock(&woomera->queue_lock); + + return data; +} + + +static int enqueue_event_on_listeners(struct woomera_event *event) +{ + struct woomera_listener *ptr; + int x = 0; + + assert(event != NULL); + + pthread_mutex_lock(&server.listen_lock); + for (ptr = server.listeners ; ptr ; ptr = ptr->next) { + enqueue_event(ptr->woomera, event, EVENT_KEEP_DATA); + x++; + } + pthread_mutex_unlock(&server.listen_lock); + + return x; +} + + +static void del_listener(struct woomera_interface *woomera) +{ + struct woomera_listener *ptr, *last = NULL; + + pthread_mutex_lock(&server.listen_lock); + for (ptr = server.listeners ; ptr ; ptr = ptr->next) { + if (ptr->woomera == woomera) { + if (last) { + last->next = ptr->next; + } else { + server.listeners = ptr->next; + } + smg_free(ptr); + break; + } + last = ptr; + } + pthread_mutex_unlock(&server.listen_lock); +} + +static void add_listener(struct woomera_interface *woomera) +{ + struct woomera_listener *new; + + pthread_mutex_lock(&server.listen_lock); + + if ((new = smg_malloc(sizeof(*new)))) { + memset(new, 0, sizeof(*new)); + new->woomera = woomera; + new->next = server.listeners; + server.listeners = new; + } else { + log_printf(SMG_LOG_ALL, server.log, "Memory Error adding listener!\n"); + } + + pthread_mutex_unlock(&server.listen_lock); +} + + + +static int wanpipe_send_dtmf(struct woomera_interface *woomera, char *digits) +{ + struct media_session *ms = woomera_get_ms(woomera); + char *cur = NULL; + int wrote = 0; + int err; + + if (!ms) { + return -EINVAL; + } + + if (!ms->dtmf_buffer) { + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Allocate DTMF Buffer...."); + + err=switch_buffer_create_dynamic(&ms->dtmf_buffer, 1024, server.dtmf_size, 0); + + if (err != 0) { + log_printf(SMG_LOG_ALL, woomera->log, "Failed to allocate DTMF Buffer!\n"); + return -ENOMEM; + } else { + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "SUCCESS!\n"); + } + + } + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Sending DTMF %s\n",digits); + for (cur = digits; *cur; cur++) { + if ((wrote = teletone_mux_tones(&ms->tone_session, + &ms->tone_session.TONES[(int)*cur]))) { + + pthread_mutex_lock(&woomera->dtmf_lock); + + err=switch_buffer_write(ms->dtmf_buffer, ms->tone_session.buffer, wrote * 2); + + pthread_mutex_unlock(&woomera->dtmf_lock); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Sending DTMF %s Wrote=%i (err=%i)\n", + digits,wrote*2,err); + } else { + log_printf(SMG_LOG_ALL, woomera->log, "Error: Sending DTMF %s (err=%i)\n", + digits,wrote); + } + } + + ms->skip_read_frames = 200; + return 0; +} + +static struct woomera_interface *alloc_woomera(void) +{ + struct woomera_interface *woomera = NULL; + + if ((woomera = smg_malloc(sizeof(struct woomera_interface)))) { + + memset(woomera, 0, sizeof(struct woomera_interface)); + + woomera->chan = -1; + woomera->span = -1; + woomera->log = server.log; + woomera->debug = server.debug; + woomera->call_id = 1; + woomera->event_queue = NULL; + woomera->q931_rel_cause_topbx=SIGBOOST_RELEASE_CAUSE_NORMAL; + woomera->q931_rel_cause_tosig=SIGBOOST_RELEASE_CAUSE_NORMAL; + + woomera_set_interface(woomera, "w-1g-1"); + + + + } + + return woomera; + +} + + +static struct woomera_interface *new_woomera_interface(int socket, struct sockaddr_in *sock_addr, int len) +{ + struct woomera_interface *woomera = NULL; + + if (socket < 0) { + log_printf(SMG_LOG_ALL, server.log, "Critical: Invalid Socket on new interface!\n"); + return NULL; + } + + if ((woomera = alloc_woomera())) { + if (socket >= 0) { + no_nagle(socket); + woomera->socket = socket; + } + + if (sock_addr && len) { + memcpy(&woomera->addr, sock_addr, len); + } + } + + return woomera; + +} + +static char *woomera_message_header(struct woomera_message *wmsg, char *key) +{ + int x = 0; + char *value = NULL; + + for (x = 0 ; x < wmsg->last ; x++) { + if (!strcasecmp(wmsg->names[x], key)) { + value = wmsg->values[x]; + break; + } + } + + return value; +} + + +#define SMG_DECODE_POLL_ERRORS(err) \ + (err & POLLERR) ? "POLLERR" : \ + (err & POLLHUP) ? "POLLHUP" : \ + (err & POLLNVAL) ? "POLLNVAL" : "UNKNOWN" + +static int waitfor_socket(int fd, int timeout, int flags, int *flags_out) +{ + struct pollfd pfds[1]; + int res; + int errflags = (POLLERR | POLLHUP | POLLNVAL); + +waitfor_socket_tryagain: + + memset(&pfds[0], 0, sizeof(pfds[0])); + + pfds[0].fd = fd; + pfds[0].events = flags | errflags; + + res = poll(pfds, 1, timeout); + + if (res == 0) { + return res; + } + + if (res > 0) { + res=-1; + *flags_out = pfds[0].revents; + if (pfds[0].revents & errflags) { + res=-1; +#if 0 + log_printf(SMG_LOG_DEBUG_10,server.log, "Wait for socket Error in revents 0x%X %s Error=%s!\n", + pfds[0].revents,strerror(errno),SMG_DECODE_POLL_ERRORS(pfds[0].revents)); +#endif + return res; + } else { + if (pfds[0].revents & flags) { + res=1; + } else { + log_printf(SMG_LOG_ALL,server.log, "Wait for socket invalid poll event in revents 0x%X Error=%s Errno=%s !\n", + pfds[0].revents,SMG_DECODE_POLL_ERRORS(pfds[0].revents),strerror(errno)); + } + } + } else { + if ((errno == EINTR || errno == EAGAIN)) { + goto waitfor_socket_tryagain; + } + log_printf(SMG_LOG_ALL,server.log, "Wait for socket error!\n"); + } + + return res; +} + + +int waitfor_2sockets(int fda, int fdb, char *a, char *b, int timeout) +{ + struct pollfd pfds[2]; + int res = 0; + int errflags = (POLLERR | POLLHUP | POLLNVAL); + + if (fda < 0 || fdb < 0) { + return -1; + } + + +waitfor_2sockets_tryagain: + + *a=0; + *b=0; + + + memset(pfds, 0, sizeof(pfds)); + + pfds[0].fd = fda; + pfds[1].fd = fdb; + pfds[0].events = POLLIN | errflags; + pfds[1].events = POLLIN | errflags; + + res = poll(pfds, 2, timeout); + + if (res > 0) { + res = 1; + if ((pfds[0].revents & errflags) || (pfds[1].revents & errflags)) { + res = -1; + } else { + if ((pfds[0].revents & POLLIN)) { + *a=1; + res++; + } + if ((pfds[1].revents & POLLIN)) { + *b=1; + res++; + } + } + + if (res == 1) { + /* No event found what to do */ + res=-1; + } + } else if (res < 0) { + + if (errno == EINTR || errno == EAGAIN) { + goto waitfor_2sockets_tryagain; + } + + } + + return res; +} + + +static struct media_session *media_session_new(struct woomera_interface *woomera) +{ + struct media_session *ms = NULL; + int x; + char *p; + int span,chan; + + span=woomera->span; + chan=woomera->chan; + + log_printf(SMG_LOG_DEBUG_CALL, server.log,"Starting new MEDIA session [%s] [%s]\n", + woomera->interface,woomera->raw?woomera->raw:"N/A"); + + if ((ms = smg_malloc(sizeof(struct media_session)))) { + memset(ms, 0, sizeof(struct media_session)); + + if (woomera->loop_tdm != 1) { + for(x = 0; x < strlen(woomera->raw) ; x++) { + if (woomera->raw[x] == ':') { + break; + } + if (woomera->raw[x] == '/') { + break; + } + } + + ms->ip = smg_strndup(woomera->raw, x); + time(&ms->started); + p = woomera->raw + (x+1); + ms->port = atoi(p); + } + + time(&ms->started); + woomera_set_ms(woomera,ms); + ms->woomera = woomera; + + /* Setup artificial DTMF stuff */ + memset(&ms->tone_session, 0, sizeof(ms->tone_session)); + if (teletone_init_session(&ms->tone_session, 0, NULL, NULL)) { + log_printf(SMG_LOG_ALL, server.log, "ERROR: Failed to initialize TONE [s%ic%i]!\n", + span+1,chan+1); + } + + ms->tone_session.rate = SMG_DTMF_RATE; + ms->tone_session.duration = server.dtmf_on * (ms->tone_session.rate / 1000); + ms->tone_session.wait = server.dtmf_off * (ms->tone_session.rate / 1000); + + teletone_dtmf_detect_init (&ms->dtmf_detect, SMG_DTMF_RATE); + + } else { + log_printf(SMG_LOG_ALL, server.log, "ERROR: Memory Alloc Failed [s%ic%i]!\n", + span+1,chan+1); + } + + return ms; +} + +static void media_session_free(struct media_session *ms) +{ + if (ms->ip) { + smg_free(ms->ip); + } + + teletone_destroy_session(&ms->tone_session); + switch_buffer_destroy(&ms->dtmf_buffer); + + ms->woomera = NULL; + + smg_free(ms); +} + + +static int create_udp_socket(struct media_session *ms, 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; + + log_printf(SMG_LOG_DEBUG_9,server.log,"LocalIP %s:%d IP %s:%d \n",local_ip, local_port, ip, port); + + memset(&ms->remote_hp, 0, sizeof(ms->remote_hp)); + memset(&ms->local_hp, 0, sizeof(ms->local_hp)); + if ((ms->socket = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) { + gethostbyname_r(ip, &ms->remote_hp, buf, sizeof(buf), &result, &err); + gethostbyname_r(local_ip, &ms->local_hp, local_buf, sizeof(local_buf), &local_result, &err); + if (result && local_result) { + ms->remote_addr.sin_family = ms->remote_hp.h_addrtype; + memcpy((char *) &ms->remote_addr.sin_addr.s_addr, ms->remote_hp.h_addr_list[0], ms->remote_hp.h_length); + ms->remote_addr.sin_port = htons(port); + + ms->local_addr.sin_family = ms->local_hp.h_addrtype; + memcpy((char *) &ms->local_addr.sin_addr.s_addr, ms->local_hp.h_addr_list[0], ms->local_hp.h_length); + ms->local_addr.sin_port = htons(local_port); + + rc = bind(ms->socket, (struct sockaddr *) &ms->local_addr, sizeof(ms->local_addr)); + if (rc < 0) { + close(ms->socket); + ms->socket = -1; + + log_printf(SMG_LOG_DEBUG_9,server.log, + "Failed to bind LocalIP %s:%d IP %s:%d (%s)\n", + local_ip, local_port, ip, port,strerror(errno)); + } + + /* OK */ + + } else { + log_printf(SMG_LOG_ALL,server.log, + "Failed to get hostbyname LocalIP %s:%d IP %s:%d (%s)\n", + local_ip, local_port, ip, port,strerror(errno)); + } + } else { + log_printf(SMG_LOG_ALL,server.log, + "Failed to create/allocate UDP socket\n"); + } + + return ms->socket; +} + +static int next_media_port(void) +{ + int port; + + pthread_mutex_lock(&server.media_udp_port_lock); + port = ++server.next_media_port; + if (port > server.max_media_port) { + server.next_media_port = server.base_media_port; + port = server.base_media_port; + } + pthread_mutex_unlock(&server.media_udp_port_lock); + + return port; +} + + + +static int woomera_dtmf_transmit(struct media_session *ms, int mtu) +{ + struct woomera_interface *woomera = ms->woomera; + int bread; + unsigned char dtmf[1024]; + unsigned char dtmf_law[1024]; + sangoma_api_hdr_t hdrframe; + int i; + int slin_len = mtu*2; + short *data; + int used; + int res; + int err; + int txdtmf=0; + int flags_out; + memset(&hdrframe,0,sizeof(hdrframe)); + + if (!ms->dtmf_buffer) { + return -1; + } + + for (;;) { + + if ((used=switch_buffer_inuse(ms->dtmf_buffer)) <= 0) { + break; + } + + res = waitfor_socket(ms->sangoma_sock, -1, POLLOUT, &flags_out); + if (res <= 0) { + break; + } + +#ifdef CODEC_LAW_DEFAULT + + pthread_mutex_lock(&woomera->dtmf_lock); + if ((used=switch_buffer_inuse(ms->dtmf_buffer)) <= 0) { + pthread_mutex_unlock(&woomera->dtmf_lock); + break; + } + + bread = switch_buffer_read(ms->dtmf_buffer, dtmf, slin_len); + pthread_mutex_unlock(&woomera->dtmf_lock); + + if (bread <= 0) { + break; + } + + log_printf(SMG_LOG_DEBUG_MISC,woomera->log,"%s: Write DTMF Got %d bytes MTU=%i Coding=%i Used=%i\n", + woomera->interface,bread,mtu,ms->hw_coding,used); + + data=(short*)dtmf; + for (i=0;ihw_coding) { + /* ALAW */ + dtmf_law[i] = linear_to_alaw((int)data[i]); + } else { + /* ULAW */ + dtmf_law[i] = linear_to_ulaw((int)data[i]); + } + } + + err=sangoma_sendmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + dtmf_law, mtu, 0); + + if (err != mtu) { + log_printf(SMG_LOG_ALL, woomera->log, "Error: Failed to TX to TDM API on DTMF (err=%i mtu=%i)!\n",err,mtu); + } + + txdtmf++; + ms->skip_write_frames++; +#else +... + pthread_mutex_lock(&woomera->dtmf_lock); + bread = switch_buffer_read(ms->dtmf_buffer, dtmf, mtu); + pthread_mutex_unlock(&woomera->dtmf_lock); + + log_printf(SMG_LOG_DEBUG_MISC,woomera->log,"%s: Write DTMF Got %d bytes\n", + woomera->interface,bread); + + sangoma_sendmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + dtmf, mtu, 0); + txdtmf++; + ms->skip_write_frames++; +#endif + + } + + if (txdtmf) { + return 0; + } else { + return -1; + } +} + +static void media_loop_run(struct media_session *ms) +{ + struct woomera_interface *woomera = ms->woomera; + int sangoma_frame_len = 160; + int errs=0; + int res=0; + wanpipe_tdm_api_t tdm_api; + unsigned char circuit_frame[1024]; + char filename[100]; + FILE *filed=NULL; + int loops=0,flags_out=0; + int open_cnt = 0; + + open_cnt=0; + + sangoma_api_hdr_t hdrframe; + memset(&hdrframe,0,sizeof(hdrframe)); + memset(circuit_frame,0,sizeof(circuit_frame)); + +retry_loop: + ms->sangoma_sock = open_span_chan(woomera->span+1, woomera->chan+1); + + log_printf(SMG_LOG_PROD, server.log, "Media Loop Started %s fd=%i\n", + woomera->interface,ms->sangoma_sock); + + if (ms->sangoma_sock < 0) { + errs++; + if (errs < 5) { + usleep(500000); + goto retry_loop; + } + log_printf(SMG_LOG_ALL, server.log, "WANPIPE MEDIA Socket Error (%s) if=[%s] [s%ic%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); + + } else { + errs=0; + + if (sangoma_tdm_set_codec(ms->sangoma_sock, &tdm_api, WP_NONE) < 0 ) { + errs++; + } + + if (sangoma_tdm_flush_bufs(ms->sangoma_sock, &tdm_api)) { + errs++; + } + + if (sangoma_tdm_set_usr_period(ms->sangoma_sock, &tdm_api, 20) < 0 ) { + errs++; + } + + sangoma_frame_len = sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); + + sangoma_tdm_disable_hwec(ms->sangoma_sock,&tdm_api); + ms->oob_disable = 0; +#ifdef LIBSANGOMA_VERSION + open_cnt = sangoma_get_open_cnt(ms->sangoma_sock, &tdm_api); + if (open_cnt > 1) { + ms->oob_disable = 1; + } +#endif + } + + if (errs) { + log_printf(SMG_LOG_ALL, server.log, "Media Loop: failed to open tdm device %s\n", + woomera->interface); + return; + } + + if (server.loop_trace) { + sprintf(filename,"/smg/s%ic%i-loop.trace",woomera->span+1,woomera->chan+1); + unlink(filename); + filed = safe_fopen(filename, "w"); + } + + while ( woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + !woomera_test_flag(woomera, WFLAG_MEDIA_END) && + ((res = waitfor_socket(ms->sangoma_sock, 1000, POLLIN, &flags_out)) >= 0)) { + + if (res == SMG_SOCKET_EVENT_TIMEOUT) { + //log_printf(SMG_LOG_DEBUG_8, server.log, "%s: TDM UDP Timeout !!!\n", + // woomera->interface); + /* NENAD Timeout thus just continue */ + continue; + } + + res = sangoma_readmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + circuit_frame, + sizeof(circuit_frame), 0); + if (res < 0) { + log_printf(SMG_LOG_ALL, server.log, "TDM Loop ReadMsg Error: %s\n", + strerror(errno), woomera->interface); + break; + } + + if (server.loop_trace && filed != NULL) { + int i; + for (i=0;isangoma_sock, + &hdrframe, + sizeof(hdrframe), + circuit_frame, + res, 0); + + res=0; + + loops++; + } + + + if (res < 0) { + if (!woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + log_printf(SMG_LOG_ALL, server.log, "Media Loop: socket error %s (fd=%i) %s\n", + woomera->interface, ms->sangoma_sock, strerror(errno)); + } + } + + + if (server.loop_trace && filed != NULL) { + fclose(filed); + } + + sangoma_tdm_enable_hwec(ms->sangoma_sock,&tdm_api); + + close_span_chan(&ms->sangoma_sock, woomera->span+1, woomera->chan+1); + + if (loops < 1) { + log_printf(SMG_LOG_ALL, server.log, "Media Loop FAILED %s Master=%i MediaEnd=%i Loops=%i\n", + woomera->interface, + woomera_test_flag(&server.master_connection, WFLAG_RUNNING), + woomera_test_flag(woomera, WFLAG_MEDIA_END),loops); + } else { + log_printf(SMG_LOG_PROD, server.log, "Media Loop PASSED %s Master=%i MediaEnd=%i Loops=%i\n", + woomera->interface, + woomera_test_flag(&server.master_connection, WFLAG_RUNNING), + woomera_test_flag(woomera, WFLAG_MEDIA_END),loops); + } + + return; + +} + + + + +#ifdef WP_HPTDM_API +static int media_rx_ready(void *p, unsigned char *data, int len) +{ + struct media_session *ms = (struct media_session *)p; + + if (ms->udp_sock < 0) { + return -1; + } + + return sendto(ms->udp_sock, + data,len, 0, + (struct sockaddr *) &ms->remote_addr, + sizeof(ms->remote_addr)); + + +} +#endif + +static void *media_thread_run(void *obj) +{ + struct media_session *ms = obj; + struct woomera_interface *woomera = ms->woomera; + int sangoma_frame_len = 160; + int local_port, x = 0, errs = 0, res = 0, packet_len = 0; + //int udp_cnt=0; + struct woomera_event wevent; + wanpipe_tdm_api_t tdm_api; + FILE *tx_fd=NULL; + int sock_timeout=200; + + int hwec_enabled=0, hwec_reenable=0; + + int open_cnt = 0; + + open_cnt=0; + + if (woomera_test_flag(woomera, WFLAG_MEDIA_END) || + !woomera->interface || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "MEDIA session for [%s] Cancelled! (ptr=%p)\n", + woomera->interface,woomera); + /* In this case the call will be closed via woomera_thread_run + * function. And the process table will be cleard there */ + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_MEDIA_RUNNING); + media_session_free(ms); + pthread_exit(NULL); + return NULL; + } + + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA session for [%s] started (ptr=%p loop=%i)\n", + woomera->interface,woomera,woomera->loop_tdm); + + if (woomera->loop_tdm) { + media_loop_run(ms); + ms->udp_sock=-1; + woomera_set_flag(woomera, WFLAG_HANGUP); + woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + goto media_thread_exit; + } + + for(x = 0; x < 1000 ; x++) { + local_port = next_media_port(); + if ((ms->udp_sock = create_udp_socket(ms, server.media_ip, local_port, ms->ip, ms->port)) > -1) { + break; + } + } + + /* Normal Temporary Failure */ + woomera->q931_rel_cause_topbx = 41; + + if (ms->udp_sock < 0) { + log_printf(SMG_LOG_ALL, server.log, "UDP Socket Error (%s) [%s] LocalPort=%d\n", + strerror(errno), woomera->interface, local_port); + + errs++; + } else { + int media_retry_cnt=0; +#ifdef WP_HPTDM_API + hp_tdm_api_span_t *span=hptdmspan[woomera->span+1]; + if (!span || !span->init) { + errs++; + } else { + hp_tdm_api_usr_callback_t usr_callback; + memset(&usr_callback,0,sizeof(usr_callback)); + usr_callback.p = ms; + usr_callback.rx_avail = media_rx_ready; + if (span->open_chan(span, &usr_callback, &ms->tdmchan,woomera->chan+1)) { + errs++; + /* Switch Congestion */ + woomera->q931_rel_cause_topbx = 42; + } + } +#else +media_retry: + ms->sangoma_sock = open_span_chan(woomera->span+1, woomera->chan+1); + if (ms->sangoma_sock < 0) { + + if (!woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + media_retry_cnt++; + if (media_retry_cnt < 5) { + log_printf(SMG_LOG_ALL, server.log, "WANPIPE Socket Retry [s%ic%i]\n", + woomera->span+1, woomera->chan+1); + usleep(100000); + goto media_retry; + } + + log_printf(SMG_LOG_ALL, server.log, "WANPIPE Socket Error (%s) if=[%s] [s%ic%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); + + /* Switch Congestion */ + woomera->q931_rel_cause_topbx = 42; + } + + errs++; + } else { + +# ifdef CODEC_LAW_DEFAULT + if (sangoma_tdm_set_codec(ms->sangoma_sock, &tdm_api, WP_NONE) < 0 ) { + errs++; + } +# else + if (sangoma_tdm_set_codec(ms->sangoma_sock, &tdm_api, WP_SLINEAR) < 0 ) { + errs++; + } +# endif + if (sangoma_tdm_flush_bufs(ms->sangoma_sock, &tdm_api)) { + errs++; + } + + if (sangoma_tdm_set_usr_period(ms->sangoma_sock, &tdm_api, 20) < 0 ) { + errs++; + } + +# ifdef CODEC_LAW_DEFAULT +# ifdef LIBSANGOMA_GET_HWCODING + ms->hw_coding=sangoma_tdm_get_hw_coding(ms->sangoma_sock, &tdm_api); + if (ms->hw_coding < 0) { + errs++; + } +# else +# error "libsangoma missing hwcoding feature: not up to date!" +# endif +# endif + + +# ifdef LIBSANGOMA_GET_HWDTMF + ms->hw_dtmf=sangoma_tdm_get_hw_dtmf(ms->sangoma_sock, &tdm_api); + if (ms->hw_dtmf) { + log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Supported [s%ic%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); + } else { + log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Not Supported [s%ic%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); + } +#else + ms->hw_dtmf=0; + log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Not Supported [s%ic%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); +# warning "libsangoma missing hwdtmf feature: not up to date!" + +#endif + + ms->oob_disable = 0; +#ifdef LIBSANGOMA_VERSION + open_cnt = sangoma_get_open_cnt(ms->sangoma_sock, &tdm_api); + if (open_cnt > 1) { + ms->oob_disable = 1; + } +#endif + + if (!tdmv_hwec_persist) { + // BRI cards start with HWEC in bypass disable state + if (bearer_cap_is_audio(woomera->bearer_cap)) { + int err; + err=sangoma_tdm_enable_hwec(ms->sangoma_sock, &tdm_api); + if (err == 0) { + hwec_enabled=1; + log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] Enabling hwec Ok\n",woomera->interface); + } else { + log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] Enabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + } + } + } else { + if (!bearer_cap_is_audio(woomera->bearer_cap)) { + int err; + err=sangoma_tdm_disable_hwec(ms->sangoma_sock, &tdm_api); + if (err == 0) { + hwec_reenable=1; + log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] Disabling hwec Ok\n",woomera->interface); + } else { + log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] Disabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + } + } + } + + sangoma_frame_len = sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); + + } +#endif + } + + + +#ifdef WP_HPTDM_API + /* No tdm thread */ +#else +#ifndef SMG_NO_MEDIA + if (!errs && + launch_media_tdm_thread(woomera)) { + errs++; + } +#endif +#endif + + if (errs) { + + woomera->q931_rel_cause_topbx = 42; + + } else { + + unsigned char udp_frame[4096]; + unsigned int fromlen = sizeof(struct sockaddr_in); + int flags_out=0; + + sangoma_api_hdr_t hdrframe; + memset(&hdrframe,0,sizeof(hdrframe)); + memset(udp_frame,0,sizeof(udp_frame)); +#ifdef DOTRACE + int fdin, fdout; + char path_in[512], path_out[512]; +#endif + + new_woomera_event_printf(&wevent, + "EVENT MEDIA %s AUDIO%s" + "Unique-Call-Id: %s%s" + "Raw-Audio: %s:%d%s" + "Call-ID: %s%s" + "Raw-Format: PCM-16%s" + "DTMF: %s%s" + , + woomera->interface, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + server.media_ip, + local_port, + WOOMERA_LINE_SEPERATOR, + woomera->interface, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + (ms->hw_dtmf)? "OutofBand": "InBand", + + WOOMERA_RECORD_SEPERATOR + ); + + + enqueue_event(woomera, &wevent, EVENT_FREE_DATA); + +#ifdef DOTRACE + sprintf(path_in, "/tmp/debug-in.%d.raw", tc); + sprintf(path_out, "/tmp/debug-out.%d.raw", tc++); + fdin = open(path_in, O_WRONLY | O_CREAT, O_TRUNC, 0600); + fdout = open(path_out, O_WRONLY | O_CREAT, O_TRUNC, 0600); +#endif + + if (server.out_tx_test) { + tx_fd=fopen("/smg/sound.raw","rb"); + if (!tx_fd) { + log_printf(SMG_LOG_ALL,server.log, "FAILED TO OPEN Sound file!\n"); + } + } + + + for (;;) { + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) || + woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + res=0; + break; + } + + res = waitfor_socket(ms->udp_sock, sock_timeout, POLLIN, &flags_out); + + if (res < 0) { + break; + } + +#ifdef SMG_NO_MEDIA + continue; +#endif + + if (res == 0) { + + if (woomera_dtmf_transmit(ms,sangoma_frame_len) == 0) { + sock_timeout=(sangoma_frame_len/codec_sample); + } else { + sock_timeout=200; + } + + if (ms->skip_write_frames > 0) { + ms->skip_write_frames--; + } + + log_printf(SMG_LOG_DEBUG_8, server.log, "%s: UDP Sock Timeout !!!\n", + woomera->interface); + /* NENAD Timeout thus just continue */ + continue; + } + + if ((packet_len = recvfrom(ms->udp_sock, udp_frame, sizeof(udp_frame), + MSG_DONTWAIT, (struct sockaddr *) &ms->local_addr, &fromlen)) < 1) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "UDP Recv Error: %s\n",strerror(errno)); + break; + } + + +#ifdef SMG_DROP_SEQ + if (drop_seq) { + log_printf(SMG_LOG_ALL,server.log,"Dropping TX Sequence! %i\n",drop_seq); + drop_seq--; + continue; + } +#endif + +#if 0 + log_printf(SMG_LOG_DEBUG_10, server.log, "%s: UDP Receive %i !!!\n", + woomera->interface,packet_len); +#endif + + if (packet_len > 0) { + +#if 0 +/* NC: This can cause skb_over panic must be retested */ + if (packet_len != sangoma_frame_len && ms->udp_sync_cnt <= 5) { + /* Assume that we will always receive SLINEAR here */ + sangoma_tdm_set_usr_period(ms->sangoma_sock, + &tdm_api, packet_len/codec_sample); + sangoma_frame_len = + sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); + + log_printf(SMG_LOG_DEBUG_MISC, server.log, + "%s: UDP TDM Period ReSync to Len=%i %ims (udp=%i) \n", + woomera->interface,sangoma_frame_len, + sangoma_frame_len/codec_sample,packet_len); + + + if (++ms->udp_sync_cnt >= 6) { + sangoma_tdm_set_usr_period(ms->sangoma_sock, + &tdm_api, 20); + sangoma_frame_len = + sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); + log_printf(SMG_LOG_ALL, server.log, + "%s: UDP TDM Period Force ReSync to 20ms \n", + woomera->interface); + } + + } +#endif + if (!server.out_tx_test) { + + if (woomera_dtmf_transmit(ms,sangoma_frame_len) == 0) { + sock_timeout=(sangoma_frame_len/codec_sample); + /* For sanity sake if we are doing the out test + * dont take any chances force tx udp data */ + if (ms->skip_write_frames > 0) { + ms->skip_write_frames--; + } + continue; + } else { + sock_timeout=200; + } + + if (ms->skip_write_frames > 0) { + ms->skip_write_frames--; + continue; + } + + } + + if (server.out_tx_test && tx_fd && + fread((void*)udp_frame, + sizeof(char), + packet_len,tx_fd) <= 0) { + + sangoma_get_full_cfg(ms->sangoma_sock,&tdm_api); + fclose(tx_fd); + tx_fd=NULL; + } + +#ifdef WP_HPTDM_API + if (ms->tdmchan->push) { + ms->tdmchan->push(ms->tdmchan,udp_frame,packet_len); + } +#else + + sangoma_sendmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + udp_frame, + packet_len, 0); +#endif + + } + +#if 0 + if (woomera->span == 1 && woomera->chan == 1) { + udp_cnt++; + if (udp_cnt && udp_cnt % 1000 == 0) { + log_printf(SMG_LOG_ALL, server.log, "%s: MEDIA UDP TX RX CNT %i %i\n", + woomera->interface,udp_cnt,packet_len); + } + } +#endif + } + + if (res < 0) { + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) || + woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + res=0; + } else { + log_printf(SMG_LOG_ALL, server.log, "Media Thread: socket error %s SockID=%i %s Poll=%s!\n", + woomera->interface,ms->sangoma_sock,strerror(errno),SMG_DECODE_POLL_ERRORS(flags_out)); + } + } + } + + new_woomera_event_printf(&wevent, + "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Start-Time: %ld%s" + "End-Time: %ld%s" + "Answer-Time: %ld%s" + "Call-ID: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + woomera->interface, + WOOMERA_LINE_SEPERATOR, + + woomera->session, + WOOMERA_LINE_SEPERATOR, + + time(&ms->started), + WOOMERA_LINE_SEPERATOR, + + time(NULL), + WOOMERA_LINE_SEPERATOR, + + time(&ms->answered), + WOOMERA_LINE_SEPERATOR, + + woomera->interface, + WOOMERA_LINE_SEPERATOR, + + q931_rel_to_str(woomera->q931_rel_cause_topbx), + WOOMERA_LINE_SEPERATOR, + + woomera->q931_rel_cause_topbx, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + +media_thread_exit: + + if (!tdmv_hwec_persist) { + if (hwec_enabled) { + int err; + err=sangoma_tdm_disable_hwec(ms->sangoma_sock, &tdm_api); + if (err==0) { + log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] disabling hwec ok\n",woomera->interface); + } else { + log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] disabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + } + } + } else { + if (hwec_reenable) { + int err; + err=sangoma_tdm_enable_hwec(ms->sangoma_sock, &tdm_api); + if (err==0) { + log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] Re-enabling hwec ok\n",woomera->interface); + } else { + log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] Re-enabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + } + } + } + + if (woomera_test_flag(woomera, WFLAG_MEDIA_TDM_RUNNING)) { + woomera_set_flag(woomera, WFLAG_MEDIA_END); + + /* Dont wait for the other thread */ + close_socket(&ms->udp_sock); + close_span_chan(&ms->sangoma_sock, woomera->span+1, woomera->chan+1); + while(woomera_test_flag(woomera, WFLAG_MEDIA_TDM_RUNNING)) { + usleep(1000); + sched_yield(); + } + } + + + close_socket(&ms->udp_sock); + close_span_chan(&ms->sangoma_sock, woomera->span+1, woomera->chan+1); + + if (tx_fd){ + fclose(tx_fd); + tx_fd=NULL; + } + + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + + woomera_set_ms(woomera,NULL); + woomera_clear_flag(woomera, WFLAG_MEDIA_RUNNING); + + media_session_free(ms); + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA session for [%s] ended (ptr=%p)\n", + woomera->interface,woomera); + + + pthread_exit(NULL); + return NULL; +} + + + + +static void *media_tdm_thread_run(void *obj) +{ + wanpipe_tdm_api_t tdm_api; + wp_tdm_api_event_t *rx_event; + + struct media_session *ms = obj; + struct woomera_interface *woomera = ms->woomera; + int res = 0; + unsigned char circuit_frame[1024]; + sangoma_api_hdr_t hdrframe; + int flags_out; + int poll_opt = POLLIN | POLLPRI; + + memset(&hdrframe,0,sizeof(hdrframe)); + memset(circuit_frame,0,sizeof(circuit_frame)); + + memset(&tdm_api, 0, sizeof(wanpipe_tdm_api_t)); + + if (woomera_test_flag(woomera, WFLAG_MEDIA_END) || !woomera->interface) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA TDM session for [%s] Cancelled! (ptr=%p)\n", + woomera->interface,woomera); + /* In this case the call will be closed via woomera_thread_run + * function. And the process table will be cleard there */ + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + pthread_exit(NULL); + return NULL; + } + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA TDM session for [%s] started (ptr=%p)\n", + woomera->interface,woomera); + + + for (;;) { + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) || + woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + res=0; + break; + } + + if (ms->oob_disable) { + poll_opt = POLLIN; + } else { + poll_opt = POLLIN | POLLPRI; + } + res = waitfor_socket(ms->sangoma_sock, 1000, poll_opt, &flags_out); + + + if (res < 0) { + break; + } + + if (res == 0) { +#if 0 + log_printf(SMG_LOG_DEBUG_8, server.log, "%s: TDM UDP Timeout !!!\n", + woomera->interface); + /* NENAD Timeout thus just continue */ +#endif + continue; + } + + + if (flags_out & POLLIN) { + + res = sangoma_readmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + circuit_frame, + sizeof(circuit_frame), 0); + + if (res < 0) { + if (!woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + log_printf(SMG_LOG_ALL, server.log, "TDM Read Data Error: %s %s Sockid=%i\n", + woomera->interface, + strerror(errno),ms->sangoma_sock); + } + break; + } + + res = sendto(ms->udp_sock, + circuit_frame, + res, 0, + (struct sockaddr *) &ms->remote_addr, + sizeof(ms->remote_addr)); + + if (res < 0) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "UDP Sento Error: %s\n", strerror(errno)); + + } + } + + if (flags_out & POLLPRI) { + + res = sangoma_tdm_read_event(ms->sangoma_sock, &tdm_api); + if (res < 0) { + log_printf(SMG_LOG_ALL, server.log, "TDM Read Event Error: %s %s Sockid=%i\n", + woomera->interface, + strerror(errno),ms->sangoma_sock); + break; + } + + rx_event = &tdm_api.wp_tdm_cmd.event; + + switch (rx_event->wp_tdm_api_event_type){ +# ifdef LIBSANGOMA_GET_HWDTMF + case WP_TDMAPI_EVENT_DTMF: + + /* Only handle hw dtmf if hw_dtmf option is enabled */ + if (!ms->hw_dtmf) { + break; + } + + /* PORT_SOUT = 1 */ + if (rx_event->wp_tdm_api_event_dtmf_port == WAN_EC_CHANNEL_PORT_SOUT && + rx_event->wp_tdm_api_event_dtmf_type == WAN_EC_TONE_PRESENT) { + + handle_event_dtmf(woomera, rx_event->wp_tdm_api_event_dtmf_digit); + } + break; +#endif + default: + log_printf(SMG_LOG_ALL, server.log, "TDM API Unknown OOB Event %i\n", + rx_event->wp_tdm_api_event_type); + break; + } + } + +#if 0 + if (woomera->span == 1 && woomera->chan == 1) { + tdm_cnt++; + if (tdm_cnt && tdm_cnt % 1000 == 0) { + log_printf(SMG_LOG_ALL, server.log, "%s: MEDIA TDM TX RX CNT %i %i\n", + woomera->interface,tdm_cnt,res); + } + } +#endif + + } + + if (res < 0) { + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) || + woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + + /* Good reason to exit */ + + } else { + + log_printf(SMG_LOG_ALL, server.log, "Media TDM Thread: socket error %s Sockid=%i %s Woomera Flags=0x%08X Poll=%s!\n", + woomera->interface,ms->sangoma_sock,strerror(errno),woomera->flags,SMG_DECODE_POLL_ERRORS(flags_out)); + woomera_print_flags(woomera,0); + } + } + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA TDM session for [%s] ended (ptr=%p)\n", + woomera->interface,woomera); + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + + pthread_exit(NULL); + return NULL; + +} + + +/* This function must be called with process_lock + * because it modifies shared process_table */ + +static int launch_media_thread(struct woomera_interface *woomera) +{ + pthread_attr_t attr; + int result = -1; + struct media_session *ms; + + if ((ms = media_session_new(woomera))) { + result = pthread_attr_init(&attr); + //pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + //pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + woomera_set_flag(woomera, WFLAG_MEDIA_RUNNING); + result = pthread_create(&ms->thread, &attr, media_thread_run, ms); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Thread! %s\n", + __FUNCTION__,strerror(errno)); + woomera_clear_flag(woomera, WFLAG_MEDIA_RUNNING); + media_session_free(woomera->ms); + + } + pthread_attr_destroy(&attr); + + } else { + log_printf(SMG_LOG_ALL, server.log, "Failed to start new media session\n"); + } + + return result; + +} + +static int launch_media_tdm_thread(struct woomera_interface *woomera) +{ + pthread_attr_t attr; + int result = -1; + struct media_session *ms = woomera_get_ms(woomera); + + if (!ms) { + return result; + } + + result = pthread_attr_init(&attr); + //pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + //pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + woomera_set_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + result = pthread_create(&ms->thread, &attr, media_tdm_thread_run, ms); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Thread! %s\n", + __FUNCTION__,strerror(errno)); + woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + } + pthread_attr_destroy(&attr); + + return result; +} + + +static struct woomera_interface * launch_woomera_loop_thread(short_signal_event_t *event) +{ + + struct woomera_interface *woomera = NULL; + char callid[20]; + + sprintf(callid, "s%dc%d", event->span+1,event->chan+1); + + if ((woomera = alloc_woomera())) { + + woomera->chan = event->chan; + woomera->span = event->span; + woomera->log = server.log; + woomera->debug = server.debug; + woomera->call_id = 1; + woomera->event_queue = NULL; + woomera->loop_tdm=1; + woomera->socket=-1; + + } else { + log_printf(SMG_LOG_ALL, server.log, "Critical ERROR: memory/socket error\n"); + return NULL; + } + + woomera_set_interface(woomera,callid); + + pthread_mutex_lock(&server.process_lock); + server.process_table[event->span][event->chan].dev = woomera; + pthread_mutex_unlock(&server.process_lock); + + if (launch_woomera_thread(woomera)) { + pthread_mutex_lock(&server.process_lock); + server.process_table[event->span][event->chan].dev = NULL; + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + smg_free(woomera); + log_printf(SMG_LOG_ALL, server.log, "Critical ERROR: memory/socket error\n"); + return NULL; + } + + return woomera; +} + +static int woomera_message_parse(struct woomera_interface *woomera, struct woomera_message *wmsg, int timeout) +{ + char *cur, *cr, *next = NULL, *eor = NULL; + char buf[2048]; + int res = 0, bytes = 0, sanity = 0; + struct timeval started, ended; + int elapsed, loops = 0; + int failto = 0; + int packet = 0; + int flags_out = 0; + + memset(wmsg, 0, sizeof(*wmsg)); + + if (woomera->socket < 0 ) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, WOOMERA_DEBUG_PREFIX "%s Invalid Socket! %d\n", + woomera->interface,woomera->socket); + return -1; + } + + if (woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_DEBUG_9, woomera->log, WOOMERA_DEBUG_PREFIX + "%s Woomera Message parse: Call Hangup - skipping message parse !\n", + woomera->interface); + return -1; + } + + gettimeofday(&started, NULL); + memset(buf, 0, sizeof(buf)); + + if (timeout < 0) { + timeout = abs(timeout); + failto = 1; + } else if (timeout == 0) { + timeout = -1; + } + + + while (!(eor = strstr(buf, WOOMERA_RECORD_SEPERATOR))) { + if (sanity > 1000) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, WOOMERA_DEBUG_PREFIX "%s Failed Sanity Check!\n[%s]\n\n", woomera->interface, buf); + return -1; + } + + if ((res = waitfor_socket(woomera->socket, 1000, POLLIN, &flags_out) > 0)) { + + res = recv(woomera->socket, buf, sizeof(buf), MSG_PEEK); + + if (res > 1) { + packet++; + } + if (!strncmp(buf, WOOMERA_LINE_SEPERATOR, 2)) { + res = read(woomera->socket, buf, 2); + return 0; + } + if (res == 0) { + sanity++; + /* Looks Like it's time to go! */ + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_DEBUG_9, woomera->log, WOOMERA_DEBUG_PREFIX + "%s MEDIA END or HANGUP \n", woomera->interface); + return -1; + } + ysleep(1000); + continue; + + } else if (res < 0) { + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, WOOMERA_DEBUG_PREFIX + "%s error during packet retry (err=%i) Loops#%d (%s)\n", + woomera->interface, res, loops, + strerror(errno)); + return res; + } else if (loops) { + ysleep(100000); + } + } + + gettimeofday(&ended, NULL); + elapsed = (((ended.tv_sec * 1000) + ended.tv_usec / 1000) - ((started.tv_sec * 1000) + started.tv_usec / 1000)); + + if (res < 0) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, WOOMERA_DEBUG_PREFIX "%s Bad RECV\n", + woomera->interface); + return res; + } else if (res == 0) { + sanity++; + /* Looks Like it's time to go! */ + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_DEBUG_9, woomera->log, WOOMERA_DEBUG_PREFIX + "%s MEDIA END or HANGUP \n", woomera->interface); + return -1; + } + ysleep(1000); + continue; + } + + if (packet && loops > 150) { + log_printf(SMG_LOG_PROD, woomera->log, WOOMERA_DEBUG_PREFIX + "%s Timeout waiting for packet.\n", + woomera->interface); + return -1; + } + + if (timeout > 0 && (elapsed > timeout)) { + log_printf(SMG_LOG_PROD, woomera->log, WOOMERA_DEBUG_PREFIX + "%s Timeout [%d] reached\n", + woomera->interface, timeout); + return failto ? -1 : 0; + } + + if (woomera_test_flag(woomera, WFLAG_EVENT)) { + /* BRB! we have an Event to deliver....*/ + return 0; + } + + /* what're we still doing here? */ + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + !woomera_test_flag(woomera, WFLAG_RUNNING)) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, WOOMERA_DEBUG_PREFIX + "%s Woomera Message Parse: Server or Woomera not Running\n", woomera->interface); + return -1; + } + loops++; + } + + *eor = '\0'; + bytes = strlen(buf) + 4; + + memset(buf, 0, sizeof(buf)); + res = read(woomera->socket, buf, bytes); + next = buf; + + log_printf(SMG_LOG_WOOMERA, woomera->log, "%s:WOOMERA RX MSG: %s\n",woomera->interface,buf); + + while ((cur = next)) { + + if ((cr = strstr(cur, WOOMERA_LINE_SEPERATOR))) { + *cr = '\0'; + next = cr + (sizeof(WOOMERA_LINE_SEPERATOR) - 1); + if (!strcmp(next, WOOMERA_RECORD_SEPERATOR)) { + break; + } + } + if (!cur || !*cur) { + break; + } + + if (!wmsg->last) { + char *cmd, *id, *args; + woomera_set_flag(wmsg, MFLAG_EXISTS); + cmd = cur; + + if ((id = strchr(cmd, ' '))) { + *id = '\0'; + id++; + if ((args = strchr(id, ' '))) { + *args = '\0'; + args++; + strncpy(wmsg->command_args, args, sizeof(wmsg->command_args)-1); + } + strncpy(wmsg->callid, id, sizeof(wmsg->callid)-1); + } + + strncpy(wmsg->command, cmd, sizeof(wmsg->command)-1); + } else { + char *name, *val; + name = cur; + + if ((val = strchr(name, ':'))) { + *val = '\0'; + val++; + while (*val == ' ') { + *val = '\0'; + val++; + } + strncpy(wmsg->values[wmsg->last-1], val, WOOMERA_STRLEN); + } + strncpy(wmsg->names[wmsg->last-1], name, WOOMERA_STRLEN); + if (name && val && !strcasecmp(name, "content-type")) { + woomera_set_flag(wmsg, MFLAG_CONTENT); + bytes = atoi(val); + } + if (name && val && !strcasecmp(name, "content-length")) { + woomera_set_flag(wmsg, MFLAG_CONTENT); + bytes = atoi(val); + } + + + } + wmsg->last++; + } + + wmsg->last--; + + if (bytes && woomera_test_flag(wmsg, MFLAG_CONTENT)) { + int terr; + terr=read(woomera->socket, wmsg->body, + (bytes > sizeof(wmsg->body)) ? sizeof(wmsg->body) : bytes); + } + + return woomera_test_flag(wmsg, MFLAG_EXISTS); + +} + +static struct woomera_interface *pull_from_holding_tank(int index, int span , int chan) +{ + struct woomera_interface *woomera = NULL; + + if (index < 1 || index >= CORE_TANK_LEN) { + if (index != 0) { + log_printf(SMG_LOG_ALL, server.log, "%s Error on invalid TANK INDEX = %i\n", + __FUNCTION__,index); + } + return NULL; + } + + pthread_mutex_lock(&server.ht_lock); + if (server.holding_tank[index] && + server.holding_tank[index] != &woomera_dead_dev) { + woomera = server.holding_tank[index]; + + /* Block this index until the call is completed */ + server.holding_tank[index] = &woomera_dead_dev; + + woomera->index = 0; + woomera->span=span; + woomera->chan=chan; + } + pthread_mutex_unlock(&server.ht_lock); + + return woomera; +} + +static void clear_all_holding_tank(void) +{ + int i; + pthread_mutex_lock(&server.ht_lock); + for (i=0;i= CORE_TANK_LEN) { + if (index != 0) { + log_printf(SMG_LOG_ALL, server.log, "%s Error on invalid TANK INDEX = %i\n", + __FUNCTION__,index); + } + return NULL; + } + + pthread_mutex_lock(&server.ht_lock); + woomera = server.holding_tank[index]; + pthread_mutex_unlock(&server.ht_lock); + + return woomera; +} + + +static void clear_from_holding_tank(int index, struct woomera_interface *woomera) +{ + + if (index < 1 || index >= CORE_TANK_LEN) { + if (index != 0) { + log_printf(SMG_LOG_ALL, server.log, "%s Error on invalid TANK INDEX = %i\n", + __FUNCTION__,index); + } + return; + } + + pthread_mutex_lock(&server.ht_lock); + if (server.holding_tank[index] == &woomera_dead_dev) { +#if 0 + log_printf(SMG_LOG_ALL,server.log, "%s Clearing DEAD id=%i OK\n", + __FUNCTION__,index); +#endif + server.holding_tank[index] = NULL; + } else if (woomera && server.holding_tank[index] == woomera) { +#if 0 + log_printf(SMG_LOG_ALL,server.log, "%s Clearing ACTIVE Woomera id=%i OK\n", + __FUNCTION__,index); +#endif + server.holding_tank[index] = NULL; + } else if (server.holding_tank[index]) { + log_printf(SMG_LOG_ALL, server.log, "Critical Error: Holding tank index %i not cleared %p !\n", + index, server.holding_tank[index]); + } + pthread_mutex_unlock(&server.ht_lock); + + return; +} + +static struct woomera_interface *peek_from_holding_tank(int index) +{ + struct woomera_interface *woomera = NULL; + + if (index < 1 || index >= CORE_TANK_LEN) { + if (index != 0) { + log_printf(SMG_LOG_ALL, server.log, "%s Error on invalid TANK INDEX = %i\n", + __FUNCTION__,index); + } + return NULL; + } + + pthread_mutex_lock(&server.ht_lock); + if (server.holding_tank[index] && + server.holding_tank[index] != &woomera_dead_dev) { + woomera = server.holding_tank[index]; + } + pthread_mutex_unlock(&server.ht_lock); + + return woomera; +} + +static int add_to_holding_tank(struct woomera_interface *woomera) +{ + int next, i, found=0; + + pthread_mutex_lock(&server.ht_lock); + + for (i=0;i= CORE_TANK_LEN) { + next = server.holding_tank_index = 1; + } + + if (next == 0) { + log_printf(SMG_LOG_ALL, server.log, "\nCritical Error on TANK INDEX == 0\n"); + continue; + } + + if (server.holding_tank[next]) { + continue; + } + + found=1; + break; + } + + if (!found) { + /* This means all tank vales are busy + * should never happend */ + pthread_mutex_unlock(&server.ht_lock); + log_printf(SMG_LOG_ALL, server.log, "\nCritical Error failed to obtain a TANK INDEX\n"); + return 0; + } + + server.holding_tank[next] = woomera; + woomera->timeout = time(NULL) + server.call_timeout; + + pthread_mutex_unlock(&server.ht_lock); + return next; +} + + + +static void handle_event_dtmf(struct woomera_interface *woomera, unsigned char dtmf_digit) +{ + struct woomera_event wevent; + + memset(&wevent, 0, sizeof(struct woomera_event)); + + new_woomera_event_printf(&wevent, "EVENT DTMF %sUnique-Call-Id:%s%sContent-Length:%d%s%s%c%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + 1, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + dtmf_digit, + WOOMERA_RECORD_SEPERATOR); + + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + return; +} + +static int handle_woomera_progress(struct woomera_interface *woomera, + struct woomera_message *wmsg) +{ + call_signal_event_t event; + int err=-1; + + memset(&event, 0, sizeof(event)); + + call_signal_event_init((short_signal_event_t*)&event, SIGBOOST_EVENT_CALL_PROGRESS, woomera->chan, woomera->span); + sprintf(event.isup_in_rdnis,"SMG003-EVI-2"); + event.isup_in_rdnis_size=strlen(event.isup_in_rdnis); + if (woomera->index >= 0) { + event.call_setup_id = woomera->index; + } + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s]\n", + wmsg->command, woomera->interface); + + if (!woomera_check_running(woomera)) { + socket_printf(woomera->socket, "405 PROGRESS Channel already hungup%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + if (!woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + + socket_printf(woomera->socket, "405 PROGRESS Channel not aceked%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + err=isup_exec_event(&event); + if (err == 0) { + socket_printf(woomera->socket, + "200 %s PROGRESS OK%s" + "Unique-Call-Id: %s%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } else { + socket_printf(woomera->socket, "405 PROGRESS Boost failure%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } + + return err; +} + +static int handle_woomera_media_accept_answer(struct woomera_interface *woomera, + struct woomera_message *wmsg, + int media, int answer, int accept) +{ + char *raw = woomera_message_header(wmsg, "raw-audio"); + struct woomera_event wevent; + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s]\n", + wmsg->command, woomera->interface); + + if (!woomera_check_running(woomera)) { + + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "ERROR! call was cancelled MEDIA on HANGUP or MEDIA END!\n"); + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(woomera->q931_rel_cause_topbx), + WOOMERA_LINE_SEPERATOR, + woomera->q931_rel_cause_topbx, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call already hungup!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + woomera->timeout=0; + return -1; + } + + log_printf(SMG_LOG_DEBUG_MISC, server.log,"WOOMERA: GOT %s EVENT: [%s] RAW=%s\n", + wmsg->command,wmsg->callid,raw); + + + if (raw && + woomera->raw == NULL && + !woomera_test_flag(woomera, WFLAG_RAW_MEDIA_STARTED)) { + + woomera_set_flag(woomera, WFLAG_RAW_MEDIA_STARTED); + + woomera_set_raw(woomera, raw); + + if (launch_media_thread(woomera)) { + + log_printf(SMG_LOG_DEBUG_8, server.log,"ERROR: Failed to Launch Call [%s]\n", + woomera->interface); + + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(21), + WOOMERA_LINE_SEPERATOR, + 21, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_RUNNING); + + woomera->timeout=0; + return -1; + } + } + + if (!woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + log_printf(SMG_LOG_ALL, server.log,"ERROR! Monitor Thread not running!\n"); + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + return -1; + + } else { + + if (accept) { + if (!autoacm && !woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + woomera_set_flag(woomera,WFLAG_CALL_ACKED); + isup_exec_command(woomera->span, + woomera->chan, + -1, + SIGBOOST_EVENT_CALL_START_ACK, + 0); + } + } + + if (answer) { + struct media_session *ms; + + pthread_mutex_lock(&woomera->ms_lock); + if ((ms=woomera->ms)) { + time(&woomera->ms->answered); + } + pthread_mutex_unlock(&woomera->ms_lock); + + if (ms) { + + if (!autoacm && !woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + woomera_set_flag(woomera,WFLAG_CALL_ACKED); + isup_exec_command(woomera->span, + woomera->chan, + -1, + SIGBOOST_EVENT_CALL_START_ACK, + 0); + } + + isup_exec_command(woomera->span, + woomera->chan, + -1, + SIGBOOST_EVENT_CALL_ANSWERED, + 0); + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "Sent SIGBOOST_EVENT_CALL_ANSWERED [s%dc%d]\n", + woomera->span+1,woomera->chan+1); + } else { + struct woomera_event wevent; + log_printf(SMG_LOG_ALL, server.log, + "WOOMERA ANSWER: FAILED [%s] no Media \n", + wmsg->command,wmsg->callid); + + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(21), + WOOMERA_LINE_SEPERATOR, + 21, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_RUNNING); + woomera->timeout=0; + return -1; + } + } + } + + new_woomera_event_printf(&wevent, "200 %s OK%s" + "Unique-Call-Id: %s%s", + answer ? "ANSWER" : + accept ? "ACCEPT" : "MEDIA", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + return 0; +} + +static int handle_woomera_call_start (struct woomera_interface *woomera, + struct woomera_message *wmsg) +{ + char *raw = woomera_message_header(wmsg, "raw-audio"); + call_signal_event_t event; + char *calling = woomera_message_header(wmsg, "local-number"); +#ifdef SMG_CALLING_NAME + char *calling_name = woomera_message_header(wmsg, "local-name"); +#endif + char *presentation = woomera_message_header(wmsg, "Presentation"); + char *screening = woomera_message_header(wmsg, "Screening"); + char *rdnis = woomera_message_header(wmsg, "RDNIS"); + char *bearer_cap = woomera_message_header(wmsg, "Bearer-Cap"); + char *uil1p = woomera_message_header(wmsg, "uil1p"); + char *called = wmsg->callid; + char *grp = wmsg->callid; + char *profile; + char *p; + int cause = 34; + int tg = 0; + int huntgroup = SIGBOOST_HUNTGRP_SEQ_ASC; + + if (smg_check_all_busy() || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ + + + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, + "405 SMG Server All Ckt Busy!%s", WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "SMG Server Full %d (ckt busy cnt=%i)\n", + server.call_count, server.all_ckt_busy); + return -1; + } + + + /* Remove profile name out of destiantion */ + if ((profile = strchr(called, ':'))) { + profile++; + called=profile; + grp=profile; + } + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "New Call %d/%d origdest=%s newdest=%s\n", + server.call_count, server.max_calls, wmsg->callid, called); + + switch(called[0]) { + case 'g': + huntgroup = SIGBOOST_HUNTGRP_SEQ_ASC; + break; + case 'G': + huntgroup = SIGBOOST_HUNTGRP_SEQ_DESC; + break; + case 'r': + huntgroup = SIGBOOST_HUNTGRP_RR_ASC; + break; + case 'R': + huntgroup = SIGBOOST_HUNTGRP_RR_DESC; + break; + default: + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, + "Warning: Failed to determine huntgroup (%s)\n", + called); + huntgroup = SIGBOOST_HUNTGRP_SEQ_ASC; + } + + if ((p = strchr(called, '/'))) { + *p = '\0'; + called = p+1; + tg = atoi(grp+1) - 1; + if (tg < 0) { + tg=0; + } + } + + woomera->trunk_group=tg; + if (raw) { + woomera_set_raw(woomera, raw); + } + + woomera->index = add_to_holding_tank(woomera); + if (woomera->index < 1) { + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, + "405 SMG Server All Tanks Busy!%s", + WOOMERA_RECORD_SEPERATOR); + log_printf(SMG_LOG_ALL, woomera->log, "Error: Call Tank Full (Call Cnt=%i)\n", + server.call_count); + return -1; + } + + + woomera->index_hold = woomera->index; + + call_signal_call_init(&event, calling, called, woomera->index); + + if (presentation) { + event.calling_number_presentation = atoi(presentation); + } else { + event.calling_number_presentation = 0; + } + + if (screening) { + event.calling_number_screening_ind = atoi(screening); + } else { + event.calling_number_screening_ind = 0; + } + + event.isup_in_rdnis_size=0; + event.isup_in_rdnis[0]=0; + + if (rdnis && strlen(rdnis) ) { + + if (strlen(rdnis) > sizeof(event.isup_in_rdnis)){ + log_printf(SMG_LOG_ALL,server.log,"Error: RDNIS Overflow (in size=%i max=%i)\n", + strlen(rdnis), sizeof(event.isup_in_rdnis)); + + } else { + + strncpy(event.isup_in_rdnis,rdnis, + sizeof(event.isup_in_rdnis)-1); + event.isup_in_rdnis_size=strlen(rdnis)+1; + log_printf(SMG_LOG_DEBUG_MISC,server.log,"RDNIS %s\n", rdnis); + } + + } + + if (bearer_cap && strlen(bearer_cap)) { + event.bearer.capability = bearer_cap_to_code(bearer_cap); + woomera->bearer_cap = event.bearer.capability; + } + + if (uil1p && strlen(uil1p)) { + event.bearer.uil1p = uil1p_to_code(uil1p); + } + +#ifdef SMG_CALLING_NAME + if (calling_name) { + strncpy((char*)event.calling_name,calling_name, + sizeof(event.calling_name)-1); + } +#endif + + event.trunk_group = tg; + event.hunt_group = huntgroup; + + if (call_signal_connection_write(&server.mcon, &event) < 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", + strerror(errno)); + + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, + "405 SMG Signalling Contestion!%s", + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + socket_printf(woomera->socket, "100 Trying%s", WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Call Called Event [Setup ID: %d] TG=%d\n", + woomera->index,tg); + + return 0; +} + + +static void interpret_command(struct woomera_interface *woomera, struct woomera_message *wmsg) +{ + int answer = 0, media = 0, accept=0; + char *unique_id; + int cause=0; + + + + if (!strcasecmp(wmsg->command, "call")) { + int err; + + if (strlen(woomera->session) != 0) { + /* Call has already been placed */ + socket_printf(woomera->socket, "400 Error Call already in progress %s", + WOOMERA_RECORD_SEPERATOR); + log_printf(SMG_LOG_ALL,server.log,"Woomera RX Call Even while call in progress!\n"); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(34), + WOOMERA_LINE_SEPERATOR, + 34, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, + "405 SMG Server All Ckt Reset!%s", WOOMERA_RECORD_SEPERATOR); + + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + err=handle_woomera_call_start(woomera,wmsg); + if (err) { + woomera_set_flag(woomera, WFLAG_HANGUP); + } + + return; + + } else if (!strcasecmp(wmsg->command, "bye") || !strcasecmp(wmsg->command, "quit")) { + char *cause = woomera_message_header(wmsg, "cause"); + char *q931cause = woomera_message_header(wmsg, "Q931-Cause-Code"); + + if (cause) { + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Bye Cause Received: [%s]\n", cause); + } + if (q931cause && atoi(q931cause)) { + woomera_set_cause_tosig(woomera,atoi(q931cause)); + } + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: Bye Received: [%s]\n", woomera->interface); + + woomera_clear_flag(woomera, WFLAG_RUNNING); + socket_printf(woomera->socket, "200 Connection closed%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return; + + } else if (!strcasecmp(wmsg->command, "listen")) { + + if (woomera_test_flag(woomera, WFLAG_LISTENING)) { + socket_printf(woomera->socket, "405 Listener already started%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } else { + + woomera_set_flag(woomera, WFLAG_LISTENING); + add_listener(woomera); + log_printf(SMG_LOG_ALL,woomera->log, "Starting Listen Device!\n"); + + socket_printf(woomera->socket, "%s", + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, "200 Listener enabled%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } + return; + + } else if ((media = !strcasecmp(wmsg->command, "debug"))) { + + int debug_level=atoi(wmsg->callid); + + if (debug_level < 10) { + server.debug=debug_level; + log_printf(SMG_LOG_ALL,server.log,"SMG Debugging set to %i (window=%i)\n",server.debug,server.mcon.txwindow); + } + + return; + } + + if (!strcasecmp(wmsg->command, "hangup")) { + char *q931cause = woomera_message_header(wmsg, "Q931-Cause-Code"); + + + if (q931cause && atoi(q931cause)) { + log_printf(SMG_LOG_DEBUG_8,server.log,"Woomera Hangup setting cause to %s %i\n", + q931cause,atoi(q931cause)); + woomera_set_cause_tosig(woomera,atoi(q931cause)); + } + + /* Continue Through */ + } + + + + unique_id = woomera_message_header(wmsg, "Unique-Call-Id"); + if (!unique_id) { + + cause=111; + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, "400 Woomera cmd without uniquie id%s" + WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_CALL,server.log,"Woomera RX Event (%s) without unique id!\n",wmsg->command); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + if (strlen(woomera->session) == 0) { + struct woomera_interface *session_woomera=NULL; + char *session=NULL; + int span, chan; + char ifname[100]; + /* If session does not exist this is an incoming call */ + sscanf(unique_id, "s%dc%d", &span, &chan); + span--; + chan--; + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, + "WOOMERA Got CMD %s Span=%d Chan=%d from session %s\n", + wmsg->command,span,chan,unique_id); + + if (smg_validate_span_chan(span,chan) != 0) { + + cause=81; + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, "404 Invalid span/chan in session%s" + WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, + "WOOMERA Warning invalid span chan in session %s %s\n", + wmsg->command,unique_id); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + pthread_mutex_lock(&server.process_lock); + session = server.process_table[span][chan].session; + session_woomera = server.process_table[span][chan].dev; + + /* This scenario is very common when we have multile clients + where multiple clients race get the incoming call */ + if (session_woomera) { + pthread_mutex_unlock(&server.process_lock); + + cause=81; + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, "404 Session not found%s" + WOOMERA_RECORD_SEPERATOR); + + + log_printf(SMG_LOG_DEBUG_8, woomera->log, "WOOMERA Error channel in use %s %s\n", + wmsg->command,unique_id); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + if (!session || strlen(session) == 0 || + strncmp(session,unique_id,sizeof(woomera->session))){ + pthread_mutex_unlock(&server.process_lock); + + /* Invalid call reference */ + cause=81; + socket_printf(woomera->socket, "event hangup %s" + "cause: %s%s" + "q931-cause-code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, "404 Invalid/Expired Session%s" + WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, + "WOOMERA Warning: Cmd=%s with invalid session %s (orig=%s)\n", + wmsg->command,unique_id,session?session:"N/A"); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + +#if 1 + if (!strcasecmp(wmsg->command, "hangup")) { + int clients=server.process_table[span][chan].clients; + if (server.process_table[span][chan].clients < 0) { + + log_printf(SMG_LOG_ALL, woomera->log, "WOOMERA CMD: Warning Clients (%i) Race condition on Hangup [s%dc%d]\n", + clients, span+1,chan+1); + + } else if (server.process_table[span][chan].clients > 1) { + server.process_table[span][chan].clients--; + pthread_mutex_unlock(&server.process_lock); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Got Hungup on Multiple Clients %i, skipping hangup [s%dc%d]\n", + clients, span+1,chan+1); + + cause=16; + socket_printf(woomera->socket, "event hangup %s" + "cause: %s%s" + "q931-cause-code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, "404 Hangup on multiple session%s" + WOOMERA_RECORD_SEPERATOR); + + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Hanging up channel [s%dc%d]\n", + span+1,chan+1); + } +#endif + + server.process_table[span][chan].dev=woomera; + strncpy(woomera->session,unique_id,sizeof(woomera->session)); + sprintf(ifname,"s%dc%d",span+1,chan+1); + woomera_set_interface(woomera, ifname); + + woomera->span=span; + woomera->chan=chan; + + /* Save bearer cap that came in on incoming call event */ + woomera->bearer_cap = server.process_table[span][chan].bearer_cap; + + /* set it to 1 so that queued digits are checked on proceed message */ + woomera->check_digits = 1; + + pthread_mutex_unlock(&server.process_lock); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA Got New If=%s Session %s\n", + woomera->interface, woomera->session); + + + } else if (strncmp(woomera->session,unique_id,sizeof(woomera->session))) { + + cause=81; + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, "404 Session Mis-match%s" + WOOMERA_RECORD_SEPERATOR); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + if (!strcasecmp(wmsg->command, "dtmf")) { + + log_printf(SMG_LOG_WOOMERA, woomera->log, + "WOOMERA CMD: DTMF Received: [%s] Digit %s Body %s\n", + woomera->interface, wmsg->command_args, wmsg->body); + + wanpipe_send_dtmf(woomera,wmsg->body); + + socket_printf(woomera->socket, "200 DTMF OK%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + + } else if (!strcasecmp(wmsg->command, "hangup")) { + + int chan = -1, span = -1; + char *cause = woomera_message_header(wmsg, "cause"); + char *q931cause = woomera_message_header(wmsg, "Q931-Cause-Code"); + + if (q931cause && atoi(q931cause)) { + woomera_set_cause_tosig(woomera,atoi(q931cause)); + } + + span=woomera->span; + chan=woomera->chan; + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Hangup Received: [%s] MEDIA EXIST Cause=%s\n", + woomera->interface,cause); + + if (smg_validate_span_chan(span,chan) != 0) { + + socket_printf(woomera->socket, "405 No Such Channel%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return; + } + + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "Hangup Received: [s%dc%d]\n", + span+1,chan+1); + + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_RUNNING); + + + socket_printf(woomera->socket, "200 HANGUP OK%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + } else if (!strcasecmp(wmsg->command, "proceed")) { + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s]\n", + wmsg->command, woomera->interface); + + socket_printf(woomera->socket, + "200 %s PROCEED OK%s" + "Unique-Call-Id: %s%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + } else if (!strcasecmp(wmsg->command, "progress")) { + + handle_woomera_progress(woomera,wmsg); + + + } else if ((media = !strcasecmp(wmsg->command, "media")) || + (answer = !strcasecmp(wmsg->command, "answer")) || + (accept = !strcasecmp(wmsg->command, "accept"))) { + + handle_woomera_media_accept_answer(woomera, wmsg, media,answer,accept); + + + } else { + log_printf(SMG_LOG_ALL, server.log,"WOOMERA INVALID EVENT: %s [%s] \n", + wmsg->command,wmsg->callid); + socket_printf(woomera->socket, "501 Command '%s' not implemented%s", + wmsg->command, WOOMERA_RECORD_SEPERATOR); + } +} + + +/* + EVENT INCOMING 1 + Remote-Address: 10.3.3.104 + Remote-Number: + Remote-Name: Anthony Minessale!8668630501 + Protocol: H.323 + User-Agent: Post Increment Woomera 1.0alpha1 (OpenH323 v1.17.2) 9/61 + H323-Call-Id: 887b1ff8-bb1f-da11-85c0-0007e98988c4 + Local-Number: 996 + Start-Time: Fri, 09 Sep 2005 12:25:14 -0400 + Local-Name: root +*/ + + +static void handle_call_answer(short_signal_event_t *event) +{ + struct woomera_interface *woomera = NULL; + int kill = 0; + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + pthread_mutex_unlock(&server.process_lock); + + if (woomera) { + char callid[80]; + struct woomera_event wevent; + + woomera->timeout = 0; + + if (!woomera->raw) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to answer call with no media!\n"); + kill++; + goto handle_call_answer_end; + } + + if (woomera_test_flag(woomera, WFLAG_ANSWER)) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to double-answer a call!\n"); + kill++; + goto handle_call_answer_end; + } + + woomera_set_flag(woomera, WFLAG_ANSWER); + + if (woomera->span != event->span || woomera->chan != event->chan) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to start media on a different channel from the one we agreed on.!\n"); + kill++; + goto handle_call_answer_end; + } + + if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to answer a dead call!\n"); + kill++; + } else { + int err; + err=0; + sprintf(callid, "s%dc%d", event->span + 1, event->chan + 1); + woomera_set_interface(woomera, callid); +#ifndef WOOMERA_EARLY_MEDIA + err=launch_media_thread(woomera); + if (err) { + log_printf(SMG_LOG_ALL, server.log,"ERROR: Failed to Launch Call [%s]\n", + woomera->interface); + + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Q931-Cause-Code: %d%s" + "Cause: %s%s", + woomera->interface, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + 21, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(21), + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + kill++; + } +#endif + + if (!kill) { + new_woomera_event_printf(&wevent, "EVENT CONNECT s%dc%d%s" + "Unique-Call-Id: %s%s", + event->span+1, + event->chan+1, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR + ); + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + } + } + } else { + log_printf(SMG_LOG_PROD, server.log, "Answer requested on non-existant session. [s%dc%d]\n", + event->span+1, event->chan+1); + kill++; + } + +handle_call_answer_end: + + if (kill) { + if (woomera) { + woomera_set_flag(woomera,WFLAG_MEDIA_END); + } else { + +/* This can casuse a double STOP + must be debugged further */ +#if 0 + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED, + SIGBOOST_RELEASE_CAUSE_NORMAL); + + log_printf(SMG_LOG_PROD, server.log, "Sent CALL STOP to Answer without session [s%dc%d]\n", + event->span+1, event->chan+1); +#endif + log_printf(SMG_LOG_ALL, server.log, "WARNING: Received Answer with no session [s%dc%d]\n", + event->span+1, event->chan+1); + } + } +} + +static void handle_call_start_ack(short_signal_event_t *event) +{ + struct woomera_interface *woomera = NULL; + struct woomera_event wevent; + int kill = 0; + + if ((woomera = peek_from_holding_tank(event->call_setup_id))) { + char callid[80]; + + if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to ack a dead call!\n"); + kill++; + } else { + int err, span, chan; + + pull_from_holding_tank(event->call_setup_id,event->span,event->chan); + sprintf(callid, "s%dc%d", event->span + 1, event->chan + 1); + + span = event->span; + chan = event->chan; + + woomera_set_flag(woomera,WFLAG_CALL_ACKED); + woomera_set_interface(woomera, callid); + + pthread_mutex_lock(&server.process_lock); + + if (server.process_table[span][chan].dev) { + struct woomera_interface *tmp_woomera = server.process_table[span][chan].dev; + woomera_set_flag(tmp_woomera,WFLAG_HANGUP); + woomera_set_flag(tmp_woomera,WFLAG_MEDIA_END); + log_printf(SMG_LOG_ALL,server.log,"Call Overrun on [s%dc%d] - Call ACK!\n", event->span+1, event->chan+1); + kill++; + } + + server.process_table[span][chan].dev = woomera; + sprintf(woomera->session,"%s-%i-%i",callid,rand(),rand()); + sprintf(server.process_table[span][chan].session,"%s-%s", + callid,woomera->session); + + + memset(server.process_table[span][chan].digits, 0, + sizeof(server.process_table[span][chan].digits)); + + server.process_table[span][chan].digits_len = 0; + + pthread_mutex_unlock(&server.process_lock); + + if (kill) { + goto woomera_call_ack_skip; + } + +#ifdef WOOMERA_EARLY_MEDIA + err=launch_media_thread(woomera); + if (err) { + log_printf(SMG_LOG_ALL, server.log,"ERROR: Failed to Launch Call [%s]\n", + woomera->interface); + + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id:%s%s" + "Q931-Cause-Code: %d%s" + "Cause: %s%s", + woomera->interface, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + 21, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(21), + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + + kill++; + } +#endif + if (!kill) { + + new_woomera_event_printf(&wevent, "EVENT PROCEED s%dc%d%s" + "Channel-Name: g%d/%d%s" + "Unique-Call-Id: %s%s", + event->span+1, + event->chan+1, + WOOMERA_LINE_SEPERATOR, + woomera->trunk_group+1, + (event->span*max_chans)+event->chan+1, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "201 Accepted%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Call Answered Event ID = %d Device = s%dc%d!\n", + event->call_setup_id,woomera->span+1,woomera->chan+1); + } + } + } else { + log_printf(SMG_LOG_PROD, server.log, + "Event (START ACK) %d referrs to a non-existant session (%d) [s%dc%d]!\n", + event->event_id, event->call_setup_id,event->span+1, event->chan+1); + kill++; + } + +woomera_call_ack_skip: + if (kill) { + if (woomera) { + woomera_set_flag(woomera,WFLAG_MEDIA_END); + } else { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED, + SIGBOOST_RELEASE_CAUSE_NORMAL); + + log_printf(SMG_LOG_PROD, server.log, "Sent CALL STOP to CALL START ACK without session [s%dc%d]\n", + event->span+1, event->chan+1); + } + } +} + +static void handle_call_start_nack(short_signal_event_t *event) +{ + struct woomera_interface *woomera = NULL; + int span=-1, chan=-1; + int ack=0; + + /* Always ACK the incoming NACK + * Send out the NACK ACK before pulling the TANK, because + * if we send after the pull, the outgoing call could send + * a message to boost with the pulled TANK value before + * we send a NACK ACK */ + + if (smg_validate_span_chan(event->span,event->chan) == 0) { + span=event->span; + chan=event->chan; + } + + if (event->call_setup_id > 0) { + woomera=peek_from_holding_tank(event->call_setup_id); + } + + if (woomera) { + + struct woomera_event wevent; + + if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_ALL, server.log, "Event CALL START NACK on hungup call [%d]!\n", + event->call_setup_id); + ack++; + } else { + + woomera_set_cause_topbx(woomera,event->release_cause); + woomera_set_flag(woomera, (WFLAG_HANGUP|WFLAG_HANGUP_NACK_ACK)); + + new_woomera_event_printf(&wevent, "EVENT HANGUP s%dc%d%s" + "Unique-Call-Id: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + event->span+1, + event->chan+1, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(woomera->q931_rel_cause_topbx), + WOOMERA_LINE_SEPERATOR, + woomera->q931_rel_cause_topbx, + WOOMERA_RECORD_SEPERATOR + ); + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + woomera_set_flag(woomera, WFLAG_HANGUP); + woomera_clear_flag(woomera, WFLAG_RUNNING); + + /* Do not ack here, let woomera thread ack it */ + ack=0; + } + + + } else if (event->call_setup_id == 0 && + smg_validate_span_chan(event->span,event->chan) == 0) { + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + + if (woomera) { + if (!woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT) && + !woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK_SENT)) { + /* Only if we are not already waiting for hangup */ + server.process_table[event->span][event->chan].dev=NULL; + } else if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + /* At this point call is already hang up */ + woomera=NULL; + } else { + /* At this point call is already hang up */ + woomera=NULL; + } + } + + memset(server.process_table[event->span][event->chan].session, + 0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + + if (woomera) { + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event START NACK on s%dc%d ptr=%p ms=%p\n", + woomera->span+1,woomera->chan+1,woomera,woomera->ms); + + if (!woomera_test_flag(woomera,WFLAG_HANGUP)){ + if (event->release_cause == SIGBOOST_CALL_SETUP_CSUPID_DBL_USE || + event->release_cause == SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY) { + woomera_set_cause_topbx(woomera,17); + } else { + woomera_set_cause_topbx(woomera,event->release_cause); + } + } + + woomera_set_flag(woomera, + (WFLAG_HANGUP|WFLAG_MEDIA_END|WFLAG_HANGUP_NACK_ACK)); + + /* Nack Ack will be sent by the woomera thread */ + ack=0; + } else { + /* Valid state when we are not in autoacm mode */ + ack++; + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event: NACK no woomera on span chan [s%dc%d]!\n", + event->span+1, event->chan+1); + } + + } else { + log_printf(SMG_LOG_ALL, server.log, + "Error: Start Nack Invalid State Should not happen [%d] [s%dc%d]!\n", + event->call_setup_id, event->span+1, event->chan+1); + ack++; + } + + if (event->release_cause == SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY) { + smg_all_ckt_busy(); + log_printf(SMG_LOG_ALL, server.log, "WARNING: All ckt busy Timeout=%i!\n",server.all_ckt_busy); + } + if (event->release_cause == SIGBOOST_CALL_SETUP_CSUPID_DBL_USE) { + log_printf(SMG_LOG_ALL, server.log, "WARNING: Double use on [s%ic%i] setup id %i!\n", + event->span+1,event->chan+1,event->call_setup_id); + } + +#warning "Ignoring CALL GAP" +#if 0 + if (event->release_cause == SIGBOOST_CALL_SETUP_NACK_AUTO_CALL_GAP) { + log_printf(SMG_LOG_ALL, server.log, "WARNING: Call Gapping Detected!\n"); + smg_all_ckt_gap(); + } +#endif + + if (ack) { + span=0; + chan=0; + if (smg_validate_span_chan(event->span,event->chan) == 0) { + span=event->span; + chan=event->chan; + } + + isup_exec_command(span, + chan, + event->call_setup_id, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + 0); + + if (!woomera) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event (NACK ACK) %d referrs to a non-existant session (%d) [s%dc%d]!\n", + event->event_id,event->call_setup_id, event->span+1, event->chan+1); + } + } +} + +static void handle_call_loop_start(short_signal_event_t *event) +{ + struct woomera_interface *woomera; + char callid[20]; + char *session; + + pthread_mutex_lock(&server.process_lock); + if (server.process_table[event->span][event->chan].dev) { + + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + 17); + + + log_printf(SMG_LOG_PROD, server.log, + "Sent (From Handle Loop START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [s%dc%d] ptr=%d\n", + event->span+1, event->chan+1, server.process_table[event->span][event->chan].dev); + + pthread_mutex_unlock(&server.process_lock); + return; + + } + + sprintf(callid, "s%dc%d", event->span+1,event->chan+1); + sprintf(server.process_table[event->span][event->chan].session, + "%s-%i-%i",callid,rand(),rand()); + session=server.process_table[event->span][event->chan].session; + server.process_table[event->span][event->chan].dev = NULL; + pthread_mutex_unlock(&server.process_lock); + + + woomera=launch_woomera_loop_thread(event); + if (woomera == NULL) { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + 17); + log_printf(SMG_LOG_PROD, server.log, + "Sent (From Handle Loop START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [s%dc%d] ptr=%d\n", + event->span+1, event->chan+1, server.process_table[event->span][event->chan].dev); + } + + woomera_set_flag(woomera,WFLAG_CALL_ACKED); + + return; +} + + +static void handle_call_start(call_signal_event_t *event) +{ + struct woomera_event wevent; + char callid[20]; + char *session; + struct woomera_interface *tmp_woomera=NULL; + int clients; + + remove_end_of_digits_char((unsigned char*)event->called_number_digits); + remove_end_of_digits_char((unsigned char*)event->calling_number_digits); + + if (server.strip_cid_non_digits) { + validate_number((unsigned char*)event->called_number_digits); + validate_number((unsigned char*)event->calling_number_digits); + } + + if (smg_validate_span_chan(event->span,event->chan)) { + log_printf(0,server.log, + "Error: invalid span=% chan=%i on incoming call [s%dc%d] - Call START!\n", + event->span+1, event->chan+1, event->span+1,event->chan+1); + return; + } + + pthread_mutex_lock(&server.process_lock); + + if ((tmp_woomera=server.process_table[event->span][event->chan].dev)) { + + woomera_set_flag(tmp_woomera,WFLAG_HANGUP); + woomera_set_flag(tmp_woomera,WFLAG_MEDIA_END); + log_printf(SMG_LOG_ALL,server.log,"Call Overrun on [s%dc%d] - Call START!\n", event->span+1, event->chan+1); + + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + 17); + + log_printf(SMG_LOG_ALL, server.log, + "Sent (From Handle START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [s%dc%d]\n", + event->span+1, event->chan+1); + + pthread_mutex_unlock(&server.process_lock); + return; + } + + sprintf(callid, "s%dc%d", event->span+1,event->chan+1); + sprintf(server.process_table[event->span][event->chan].session, + "%s-%i-%i",callid,rand(),rand()); + session=server.process_table[event->span][event->chan].session; + server.process_table[event->span][event->chan].dev = NULL; + memset(server.process_table[event->span][event->chan].digits, 0, sizeof(server.process_table[event->span][event->chan].digits)); + server.process_table[event->span][event->chan].digits_len = 0; + server.process_table[event->span][event->chan].bearer_cap = event->bearer.capability; + server.process_table[event->span][event->chan].clients=0; + pthread_mutex_unlock(&server.process_lock); + + if (autoacm) { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_ACK, + 0); + } + + + log_printf(SMG_LOG_DEBUG_8,server.log,"BEARER %i , UIL1P = %i\n", + event->bearer.capability,event->bearer.uil1p); + + if (event->bearer.uil1p == 0) { + if (server.hw_coding) { + event->bearer.uil1p=BC_IE_UIL1P_G711_ALAW; + } else { + event->bearer.uil1p=BC_IE_UIL1P_G711_ULAW; + } + } + + new_woomera_event_printf(&wevent, "EVENT INCOMING s%dc%d%s" + "Unique-Call-Id: %s%s" + "Remote-Number: %s%s" + "Remote-Name: %s%s" +#if defined(BRI_PROT) + "Protocol: BRI%s" +#else +#if defined(PRI_PROT) + "Protocol: PRI%s" +#else + "Protocol: SS7%s" +#endif +#endif + "User-Agent: sangoma_mgd%s" + "Local-Number: %s%s" + "Channel-Name: g%d/%d%s" + "Trunk-Group: %d%s" + "Presentation: %d%s" + "Screening: %d%s" + "RDNIS: %s%s" + "Bearer-Cap: %s%s" + "uil1p: %s%s" + , + event->span+1, + event->chan+1, + WOOMERA_LINE_SEPERATOR, + session, + WOOMERA_LINE_SEPERATOR, + event->calling_number_digits, + WOOMERA_LINE_SEPERATOR, + event->calling_name, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + event->called_number_digits, + WOOMERA_LINE_SEPERATOR, + event->trunk_group+1, + (event->span*max_chans)+event->chan+1, + WOOMERA_LINE_SEPERATOR, + event->trunk_group+1, + WOOMERA_LINE_SEPERATOR, + event->calling_number_presentation, + WOOMERA_LINE_SEPERATOR, + event->calling_number_screening_ind, + WOOMERA_LINE_SEPERATOR, + event->isup_in_rdnis, + WOOMERA_LINE_SEPERATOR, + bearer_cap_to_str(event->bearer.capability), + WOOMERA_LINE_SEPERATOR, + uil1p_to_str(event->bearer.uil1p), + WOOMERA_RECORD_SEPERATOR + ); + + clients=enqueue_event_on_listeners(&wevent); + if (!clients) { + + pthread_mutex_lock(&server.process_lock); + server.process_table[event->span][event->chan].dev = NULL; + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + if (autoacm) { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED, + 17); + log_printf(SMG_LOG_ALL, server.log, + "CALL INCOMING: Enqueue Error Sent SIGBOOST_EVENT_CALL_STOPPED [s%dc%d]\n", + event->span+1, event->chan+1); + + } else { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + 17); + log_printf(SMG_LOG_ALL, server.log, + "CALL INCOMING: Enqueue Error Sent SIGBOOST_EVENT_CALL_START_NACK [s%dc%d]\n", + event->span+1, event->chan+1); + + } + + } else { + //pthread_mutex_lock(&server.process_lock); + server.process_table[event->span][event->chan].clients = clients; + //pthread_mutex_unlock(&server.process_lock); + } + + destroy_woomera_event_data(&wevent); + +} + +static void handle_incoming_digit(call_signal_event_t *event) +{ + struct woomera_interface *woomera; + int digits_len; + + if (smg_validate_span_chan(event->span,event->chan)) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event DTMF on invalid span chan [s%dc%d] !\n", + event->span+1, event->chan+1); + return; + } + + + if (!event->called_number_digits_count) { + log_printf(SMG_LOG_ALL, server.log, "Error Incoming digit with len %s %d [s%dc%d]\n", + event->called_number_digits, + event->called_number_digits_count, + event->span+1, event->chan+1); + } + + log_printf(SMG_LOG_DEBUG_9, server.log, "Queuing incoming digits %s [s%dc%d]\n", + event->called_number_digits, + event->span+1, event->chan+1); + + pthread_mutex_lock(&server.digits_lock); + + digits_len = server.process_table[event->span][event->chan].digits_len; + + strncpy(&server.process_table[event->span][event->chan].digits[digits_len], + event->called_number_digits, + event->called_number_digits_count); + + server.process_table[event->span][event->chan].digits_len += event->called_number_digits_count; + + pthread_mutex_unlock(&server.digits_lock); + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + pthread_mutex_unlock(&server.process_lock); + if (!woomera || !strlen(woomera->session)) { + return; + } + woomera_check_digits(woomera); +} + +static void handle_gap_abate(short_signal_event_t *event) +{ + log_printf(SMG_LOG_ALL, server.log, "NOTICE: GAP Cleared!\n", + event->span+1, event->chan+1); + smg_clear_ckt_gap(); +} + +static void handle_restart(call_signal_connection_t *mcon, short_signal_event_t *event) +{ + if (!woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + log_printf(SMG_LOG_ALL, server.log,"ERROR! Monitor Thread not running!\n"); + } else { + /* Clear Reset */ + /* Tell all threads to go down */ + + log_printf(SMG_LOG_ALL, server.log, + "RESTART Received: resetting all threads\n"); + + smg_all_ckt_busy(); + woomera_set_flag(&server.master_connection, WFLAG_SYSTEM_RESET); + gettimeofday(&server.restart_timeout,NULL); + +#if 0 + sleep(5); + + clear_all_holding_tank(); + + sleep(2); + + event->event_id = SIGBOOST_EVENT_SYSTEM_RESTART_ACK; + err=call_signal_connection_write(&server.mcon, (call_signal_event_t*)event); + if (err < 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", + strerror(errno)); + } + + smg_all_ckt_busy(); + woomera_clear_flag(&server.master_connection, WFLAG_SYSTEM_RESET); +#endif + } + +} + +static void handle_restart_ack(call_signal_connection_t *mcon, short_signal_event_t *event) +{ + + /* Do not reset WFLAG_SYSTEM_RESET flag here!. The flag should + only be reset on restart from boost */ + //woomera_clear_flag(&server.master_connection, WFLAG_SYSTEM_RESET); +} + + + +static void handle_remove_loop(short_signal_event_t *event) +{ + struct woomera_interface *woomera; + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + server.process_table[event->span][event->chan].dev=NULL; + pthread_mutex_unlock(&server.process_lock); + + if (woomera) { + + woomera_set_flag(woomera, + (WFLAG_MEDIA_END|WFLAG_HANGUP)); + + /* We have to close the socket because + At this point we are release span chan */ + + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event REMOVE LOOP on s%dc%d ptr=%p ms=%p\n", + woomera->span+1,woomera->chan+1,woomera,woomera->ms); + + } +} + + +static void handle_call_stop(short_signal_event_t *event) +{ + struct woomera_interface *woomera; + int ack=0; + + woomera = NULL; + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + if (woomera) { + + if (!woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT) && + !woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK_SENT)) { + /* Only if we are not already waiting for hangup */ + //server.process_table[event->span][event->chan].dev=NULL; + + } else if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + /* At this point call is already hangup */ + woomera=NULL; + } else { + /* At this point call is already hangup */ + woomera=NULL; + } + } + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + if (woomera) { + + woomera_set_cause_topbx(woomera,event->release_cause); + + woomera_set_flag(woomera, + (WFLAG_MEDIA_END|WFLAG_HANGUP|WFLAG_HANGUP_ACK)); + + /* We have to close the socket because + At this point we are release span chan */ + + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event CALL STOP on s%dc%d ptr=%p ms=%p\n", + woomera->span+1,woomera->chan+1,woomera,woomera->ms); + + } else { + ack++; + } + + + if (ack) { + /* At this point we have already sent our STOP so its safe to ACK */ + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED_ACK, + 0); + } + + if (!woomera){ + /* This is allowed on incoming call if remote app does not answer it */ + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event CALL STOP referrs to a non-existant session [s%dc%d]!\n", + event->span+1, event->chan+1); + } +} + +static void handle_heartbeat(short_signal_event_t *event) +{ + if (!woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + log_printf(SMG_LOG_ALL, server.log,"ERROR! Monitor Thread not running!\n"); + } else { + int err=call_signal_connection_writep(&server.mconp, (call_signal_event_t*)event); + if (err < 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", + strerror(errno)); + } + } + return; +} + + +static void handle_call_stop_ack(short_signal_event_t *event) +{ + + struct woomera_interface *woomera = NULL; + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + server.process_table[event->span][event->chan].dev=NULL; + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + + if (woomera) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Stop Ack on [s%dc%d] [Setup ID: %d] [%s]!\n", + event->span+1, event->chan+1, event->call_setup_id, + woomera->interface); + + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + + } else { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event CALL_STOP_ACK(%d) referrs to a non-existant session [s%dc%d] [Setup ID: %d]!\n", + event->event_id, event->span+1, event->chan+1, event->call_setup_id); + } + + if (event->call_setup_id > 0) { + clear_from_holding_tank(event->call_setup_id, NULL); + } + + /* No need for us to do any thing here */ + return; +} + + +static void handle_call_start_nack_ack(short_signal_event_t *event) +{ + + struct woomera_interface *woomera = NULL; + + if ((woomera=pull_from_holding_tank(event->call_setup_id,-1,-1))) { + + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + + } else if (event->call_setup_id == 0 && + smg_validate_span_chan(event->span,event->chan) == 0) { + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + server.process_table[event->span][event->chan].dev=NULL; + memset(server.process_table[event->span][event->chan].session, + 0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + if (woomera) { + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + } else { + /* Possible if incoming call is NACKed due to + * woomera client being down, in this case no + * woomera thread is created */ + log_printf(SMG_LOG_DEBUG_8, server.log, + "Event NACK ACK [s%dc%d] with valid span/chan no dev!\n", + event->span+1, event->chan+1); + } + + } else { + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "Event NACK ACK referrs to a non-existant session [s%dc%d] [Setup ID: %d]!\n", + event->span+1, event->chan+1, event->call_setup_id); + } + + if (event->call_setup_id > 0) { + clear_from_holding_tank(event->call_setup_id, NULL); + } + + /* No need for us to do any thing here */ + return; +} + + +static int parse_ss7_event(call_signal_connection_t *mcon, short_signal_event_t *event) +{ + int ret = 0; + + switch(event->event_id) { + + case SIGBOOST_EVENT_CALL_START: + handle_call_start((call_signal_event_t*)event); + break; + case SIGBOOST_EVENT_CALL_STOPPED: + handle_call_stop(event); + break; + case SIGBOOST_EVENT_CALL_START_ACK: + handle_call_start_ack(event); + break; + case SIGBOOST_EVENT_CALL_START_NACK: + handle_call_start_nack(event); + break; + case SIGBOOST_EVENT_CALL_ANSWERED: + handle_call_answer(event); + break; + case SIGBOOST_EVENT_HEARTBEAT: + handle_heartbeat(event); + break; + case SIGBOOST_EVENT_CALL_START_NACK_ACK: + handle_call_start_nack_ack(event); + break; + case SIGBOOST_EVENT_CALL_STOPPED_ACK: + handle_call_stop_ack(event); + break; + case SIGBOOST_EVENT_INSERT_CHECK_LOOP: + handle_call_loop_start(event); + break; + case SIGBOOST_EVENT_SYSTEM_RESTART: + handle_restart(mcon,event); + break; + case SIGBOOST_EVENT_REMOVE_CHECK_LOOP: + handle_remove_loop(event); + break; + case SIGBOOST_EVENT_SYSTEM_RESTART_ACK: + handle_restart_ack(mcon,event); + break; + case SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE: + handle_gap_abate(event); + break; + case SIGBOOST_EVENT_DIGIT_IN: + handle_incoming_digit((call_signal_event_t*)event); + break; + default: + log_printf(SMG_LOG_ALL, server.log, "Warning no handler implemented for [%s val:%d]\n", + call_signal_event_id_name(event->event_id), event->event_id); + break; + } + + return ret; +} + + +static void *monitor_thread_run(void *obj) +{ + int ss = 0; + int policy=0,priority=0; + char a=0,b=0; + call_signal_connection_t *mcon=&server.mcon; + call_signal_connection_t *mconp=&server.mconp; + + pthread_mutex_lock(&server.thread_count_lock); + server.thread_count++; + pthread_mutex_unlock(&server.thread_count_lock); + + woomera_set_flag(&server.master_connection, WFLAG_MONITOR_RUNNING); + + if (call_signal_connection_open(mcon, + mcon->cfg.local_ip, + mcon->cfg.local_port, + mcon->cfg.remote_ip, + mcon->cfg.remote_port) < 0) { + log_printf(SMG_LOG_ALL, server.log, "Error: Opening MCON Socket [%d] %s\n", + mcon->socket,strerror(errno)); + exit(-1); + } + + if (call_signal_connection_open(mconp, + mconp->cfg.local_ip, + mconp->cfg.local_port, + mconp->cfg.remote_ip, + mconp->cfg.remote_port) < 0) { + log_printf(SMG_LOG_ALL, server.log, "Error: Opening MCONP Socket [%d] %s\n", + mconp->socket,strerror(errno)); + exit(-1); + } + + mcon->log = server.log; + mconp->log = server.log; + + isup_exec_commandp(0, + 0, + -1, + SIGBOOST_EVENT_SYSTEM_RESTART, + 0); + + woomera_set_flag(&server.master_connection, WFLAG_SYSTEM_RESET); + + smg_get_current_priority(&policy,&priority); + + log_printf(SMG_LOG_PROD, server.log, "Open udp socket [%d] [%d]\n", + mcon->socket,mconp->socket); + log_printf(SMG_LOG_PROD, server.log, "Monitor Thread Started (%i:%i)\n",policy,priority); + + + while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { +#if 0 + ss = waitfor_socket(server.mcon.socket, 1000, POLLERR | POLLIN); +#else + ss = waitfor_2sockets(mcon->socket, + mconp->socket, + &a, &b, 1000); +#endif + + if (ss > 0) { + + call_signal_event_t *event=NULL; + int i=0; + + if (b) { +mcon_retry_priority: + if ((event = call_signal_connection_readp(mconp,i))) { + log_printf(SMG_LOG_DEBUG_9, server.log, "Socket Event P [%s] \n", + call_signal_event_id_name(event->event_id)); + parse_ss7_event(mconp,(short_signal_event_t*)event); + if (++i < 10) { + goto mcon_retry_priority; + } + + } else if (errno != EAGAIN) { + ss=-1; + log_printf(SMG_LOG_ALL, server.log, + "Error: Reading from Boost P Socket! (%i) %s\n", + errno,strerror(errno)); + break; + } + } + + i=0; + + if (a) { + if ((event = call_signal_connection_read(mcon,i))) { + log_printf(SMG_LOG_DEBUG_9, server.log, "Socket Event [%s]\n", + call_signal_event_id_name(event->event_id)); + parse_ss7_event(mcon,(short_signal_event_t*)event); + + } else if (errno != EAGAIN) { + ss=-1; + log_printf(SMG_LOG_ALL, server.log, + "Error: Reading from Boost Socket! (%i) %s\n", + errno,strerror(errno)); + break; + } + } + + } + + if (ss < 0){ + log_printf(SMG_LOG_ALL, server.log, "Thread Run: Select Socket Error!\n"); + break; + } + + if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)) { + short_signal_event_t event; + struct timeval current; + int elapsed; + gettimeofday(¤t,NULL); + + elapsed=smg_calc_elapsed(&server.restart_timeout, ¤t); + if (elapsed > 5000) { + int err; + log_printf(SMG_LOG_ALL, server.log, "Reset Condition Cleared Elapsed=%i!\n",elapsed); + clear_all_holding_tank(); + memset(&event,0,sizeof(event)); + event.event_id = SIGBOOST_EVENT_SYSTEM_RESTART_ACK; + err=call_signal_connection_write(&server.mcon, (call_signal_event_t*)&event); + if (err < 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", + strerror(errno)); + } + smg_all_ckt_busy(); + woomera_clear_flag(&server.master_connection, WFLAG_SYSTEM_RESET); + } + } + + } + + log_printf(SMG_LOG_PROD, server.log, "Close udp socket [%d] [%d]\n", + mcon->socket,mconp->socket); + call_signal_connection_close(&server.mcon); + call_signal_connection_close(&server.mconp); + + pthread_mutex_lock(&server.thread_count_lock); + server.thread_count--; + pthread_mutex_unlock(&server.thread_count_lock); + + woomera_clear_flag(&server.master_connection, WFLAG_MONITOR_RUNNING); + log_printf(SMG_LOG_ALL, server.log, "Monitor Thread Ended\n"); + + return NULL; +} + +static void woomera_loop_thread_run(struct woomera_interface *woomera) +{ + int err=launch_media_thread(woomera); + if (err) { + log_printf(SMG_LOG_ALL, server.log, "Failed to start loop media thread\n"); + woomera_set_flag(woomera, + (WFLAG_HANGUP|WFLAG_MEDIA_END)); + woomera_clear_flag(woomera, WFLAG_RUNNING); + return; + } + + while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING) && + !woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) && + !woomera_test_flag(woomera, WFLAG_MEDIA_END) && + !woomera_test_flag(woomera, WFLAG_HANGUP)) { + + usleep(300000); + continue; + + } + + woomera_clear_flag(woomera, WFLAG_RUNNING); + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Woomera Session: For Loop Test exiting %s\n",woomera->interface); + + return; +} + +static void woomera_check_digits (struct woomera_interface *woomera) +{ + int span, chan; + + if (!strlen(woomera->session)) { + return; + } + + if (get_span_chan_from_interface(woomera->interface, &span, &chan) == 0) { + pthread_mutex_lock(&server.digits_lock); + if (server.process_table[span-1][chan-1].digits_len > 0) { + int i; + unsigned char digit; + + for (i=0; i < server.process_table[span-1][chan-1].digits_len; i++) { + digit = server.process_table[span-1][chan-1].digits[i]; + + handle_event_dtmf(woomera, digit); + } + + server.process_table[span-1][chan-1].digits_len = 0; + } + pthread_mutex_unlock(&server.digits_lock); + } +} + + +static void *woomera_thread_run(void *obj) +{ + struct woomera_interface *woomera = obj; + struct woomera_message wmsg; + struct woomera_event wevent; + char *event_string; + int mwi; + int err; + int policy=0, priority=0; + int span = -1, chan = -1; + + woomera_message_init(&wmsg); + + smg_get_current_priority(&policy,&priority); + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "WOOMERA session started (ptr=%p : loop=%i)(%i:%i) Index=%i\n", + woomera,woomera->loop_tdm,policy,priority, woomera->index); + + pthread_mutex_lock(&server.thread_count_lock); + server.thread_count++; + pthread_mutex_unlock(&server.thread_count_lock); + + pthread_mutex_init(&woomera->queue_lock, NULL); + pthread_mutex_init(&woomera->ms_lock, NULL); + pthread_mutex_init(&woomera->dtmf_lock, NULL); + pthread_mutex_init(&woomera->vlock, NULL); + pthread_mutex_init(&woomera->flags_lock, NULL); + + if (woomera->loop_tdm) { + /* We must set woomera socket to -1 otherwise + a valid file descriptor will get closed */ + woomera->socket = -1; + woomera_loop_thread_run(woomera); + goto woomera_session_close; + } + + err=socket_printf(woomera->socket, + "EVENT HELLO Sangoma Media Gateway%s" + "Supported-Protocols: TDM%s" + "Version: %s%s" + "Remote-Address: %s%s" + "Remote-Port: %d%s" + "Raw-Format: %s%s", + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + SMG_VERSION, WOOMERA_LINE_SEPERATOR, + inet_ntoa(woomera->addr.sin_addr), WOOMERA_LINE_SEPERATOR, + ntohs(woomera->addr.sin_port), WOOMERA_LINE_SEPERATOR, + server.hw_coding?"ALAW":"ULAW", WOOMERA_RECORD_SEPERATOR + ); + + if (err) { + log_printf(SMG_LOG_ALL, server.log, "Woomera session socket failure! (ptr=%p)\n", + woomera); + woomera_clear_flag(woomera, WFLAG_RUNNING); + goto woomera_session_close; + } + + while ( woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING) && + woomera_test_flag(woomera, WFLAG_RUNNING) && + !woomera_test_flag(woomera, WFLAG_MEDIA_END) && + !woomera_test_flag(woomera, WFLAG_HANGUP)) { + + mwi = woomera_message_parse(woomera, &wmsg, WOOMERA_HARD_TIMEOUT); + if (mwi >= 0) { + + if (mwi) { + interpret_command(woomera, &wmsg); + } else if (woomera_test_flag(woomera, WFLAG_EVENT)){ + while ((event_string = dequeue_event(woomera))) { + if (socket_printf(woomera->socket, "%s", event_string)) { + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_RUNNING); + smg_free(event_string); + log_printf(SMG_LOG_DEBUG_8, server.log, + "WOOMERA session (ptr=%p) print string error\n", + woomera); + break; + } + smg_free(event_string); + } + woomera_clear_flag(woomera, WFLAG_EVENT); + } + + if(woomera->check_digits) { + woomera_check_digits(woomera); + woomera->check_digits = 0; + } + + if (woomera->timeout > 0 && time(NULL) >= woomera->timeout) { + + /* Sent the hangup only after we sent a NACK */ + + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "WOOMERA session Call Timedout ! [%s]\n", + woomera->interface); + + /* Let the Index check below send a NACK */ + if (woomera->index) { + woomera->q931_rel_cause_tosig=19; + woomera_set_flag(woomera, WFLAG_HANGUP); + } + break; + } + + } else { + log_printf(SMG_LOG_DEBUG_MISC, server.log, "WOOMERA session (ptr=%p) [%s] READ MSG Error %i \n", + woomera,woomera->interface,mwi); + break; + } + + } + +woomera_session_close: + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "WOOMERA session (ptr=%p) is dying [%s]: SR=%d WR=%d WF=0x%04X\n", + woomera,woomera->interface, + woomera_test_flag(&server.master_connection, WFLAG_RUNNING), + woomera_test_flag(woomera, WFLAG_RUNNING), + woomera->flags); + + + if (woomera_test_flag(woomera, WFLAG_MEDIA_RUNNING)) { + woomera_set_flag(woomera, WFLAG_MEDIA_END); + while(woomera_test_flag(woomera, WFLAG_MEDIA_RUNNING)) { + usleep(100); + sched_yield(); + } + } + + + /*********************************************** + * Identify the SPAN CHAN to be used below + ***********************************************/ + + chan = woomera->chan; + span = woomera->span; + + + if (!woomera_test_flag(woomera, WFLAG_HANGUP)) { + + /* The call was not HUNGUP. This is the last check, + If the call is valid, hungup the call if the call + was never up the keep going */ + + + if (smg_validate_span_chan(span,chan) == 0) { + + if (!woomera->index) { + + if (autoacm || woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + + woomera_set_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + isup_exec_command(span, + chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED, + woomera->q931_rel_cause_tosig); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK_SENT); + + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Woomera Sent SIGBOOST_EVENT_CALL_STOPPED [s%dc%d] [%s] ptr=%p\n", + span+1, chan+1,woomera->interface,woomera); + } else { + + woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + isup_exec_command(span, + chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + woomera->q931_rel_cause_tosig); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Woomera Sent SIGBOOST_EVENT_CALL_START_NACK [s%dc%d] [%s] ptr=%p\n", + span+1, chan+1,woomera->interface,woomera); + } + } else { + log_printf(SMG_LOG_ALL, woomera->log, "Woomera Not Sent CALL STOPPED - Instead NACK [s%dc%d] [%s] ptr=%p\n", + span+1, chan+1,woomera->interface,woomera); + + } + }else{ + /* This can happend if an outgoing call times out + or gets hungup before it gets acked. Its not a + failure */ + if (!woomera->index) { + /* In this case we really failed to tx STOP */ + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "FAILED: Woomera (R) SIGBOOST_EVENT_CALL_STOPPED [s%dc%d] [%s] Index=%d ptr=%p\n", + span+1, chan+1, woomera->interface, woomera->index, woomera); + } + } + + woomera_set_flag(woomera, WFLAG_HANGUP); + + } + +woo_re_hangup: + + /* We must send a STOP ACK to boost telling it that we are done */ + if (woomera_test_flag(woomera, WFLAG_HANGUP_ACK)) { + + /* SMG received a HANGUP from boost. + We must now send back the ACK to the HANGUP. + Boost will not release channel until we + ACK the hangup */ + + if (smg_validate_span_chan(span,chan) == 0) { + + isup_exec_command(span, + chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED_ACK, + woomera->q931_rel_cause_tosig); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, + "Sent (Ack) to SIGBOOST_EVENT_CALL_STOPPED_ACK [s%dc%d] [%s] ptr=%p\n", + span+1,chan+1,woomera->interface,woomera); + + }else{ + /* This should never happen! If it does + we broke protocol */ + log_printf(SMG_LOG_ALL, woomera->log, + "FAILED: Woomera (R) SIGBOOST_EVENT_CALL_STOPPED_ACK [s%dc%d] [%s] Index=%d ptr=%p\n", + span+1, chan+1, woomera->interface, woomera->index, woomera); + } + + woomera_clear_flag(woomera, WFLAG_HANGUP_ACK); + woomera_set_flag(woomera, WFLAG_HANGUP); + } + + if (woomera_test_flag(woomera, WFLAG_HANGUP_NACK_ACK)) { + + /* SMG received a NACK from boost during call startup. + We must now send back the ACK to the NACK. + Boost will not release channel until we + ACK the NACK */ + + if (smg_validate_span_chan(span,chan) == 0) { + + isup_exec_command(span, + chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + woomera->q931_rel_cause_tosig); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, + "Sent (Nack Ack) to SIGBOOST_EVENT_CALL_START_NACK_ACK [s%dc%d] [%s] ptr=%p\n", + span+1,chan+1,woomera->interface,woomera); + + woomera->index=0; + + } else if (woomera->index) { + isup_exec_command(0, + 0, + woomera->index, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + woomera->q931_rel_cause_tosig); + + woomera->index=0; + + } else { + log_printf(SMG_LOG_ALL, woomera->log, + "FAILED: Sent (Nack Ack) SIGBOOST_EVENT_CALL_START_NACK_ACK [s%dc%d] [%s] Index=%d ptr=%p\n", + span+1, chan+1, woomera->interface, woomera->index, woomera); + } + + woomera_clear_flag(woomera, WFLAG_HANGUP_NACK_ACK); + + } + + if (woomera->index) { + + int index = woomera->index; + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Timeout: %ld%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + woomera->interface, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + woomera->timeout, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(18), + WOOMERA_LINE_SEPERATOR, + 18, + WOOMERA_RECORD_SEPERATOR + ); + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + while ((event_string = dequeue_event(woomera))) { + socket_printf(woomera->socket, "%s", event_string); + smg_free(event_string); + } + + if (peek_from_holding_tank(index)) { + + woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + isup_exec_command(0, + 0, + index, + SIGBOOST_EVENT_CALL_START_NACK, + woomera->q931_rel_cause_tosig); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT); + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, + "Sent SIGBOOST_EVENT_CALL_START_NACK [Setup ID: %d] .. WAITING FOR NACK ACK\n", + index); + } else { + log_printf(SMG_LOG_PROD, woomera->log, + "Error Failed to Sent SIGBOOST_EVENT_CALL_START_NACK [Setup ID: %d] - index stale!\n", + index); + } + } + + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK)) { + int timeout_cnt=0; + int overall_cnt=0; + + /* SMG sent NACK to boost, however we have to wait + for boost to give us the ACK back before we + release resources. */ + + while (woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK)) { + timeout_cnt++; + if (timeout_cnt > 100) { //5sec timeout + timeout_cnt=0; + overall_cnt++; + + log_printf((overall_cnt==1)?0:4, woomera->log, + "Waiting for NACK ACK [Setup ID: %d] ... \n", + woomera->index_hold); + } + + if (overall_cnt > 15) { //50sec timeout + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING)) { + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ + break; + } + + /* If ACK comes in while we wait for NACK ACK, the ACk will + clear the tank causing NACK ACK never to clear the waiting flag + in this case we abort waiting for NACK ACK and we timeout + the wait */ + if (woomera_test_flag(&server.master_connection, WFLAG_CALL_ACKED)){ + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + + usleep(50000); + sched_yield(); + } + + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + + /* If ACK came in while waiting for NACK ACK, we TIMEOUT on purpose. + The ACK has blocked the tank id and now ony NACK ACK can unblock it. + THe only problem is that we blind and have to assume that NACK ACK + will eventually come in :) */ + if (!woomera_test_flag(&server.master_connection, WFLAG_CALL_ACKED)) { + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT)) { + log_printf(SMG_LOG_ALL, woomera->log, + "Waiting for NACK ACK [Setup ID: %d] .. TIMEOUT on NACK ACK\n", + woomera->index_hold); + + } else { + log_printf(SMG_LOG_DEBUG_8, woomera->log, + "Waiting for NACK ACK [Setup ID: %d] .. GOT NACK ACK\n", + woomera->index_hold); + } + } + } + + if (woomera_test_flag(woomera, WFLAG_EVENT)){ + while ((event_string = dequeue_event(woomera))) { + socket_printf(woomera->socket, "%s", event_string); + smg_free(event_string); + } + woomera_clear_flag(woomera, WFLAG_EVENT); + } + + + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK)) { + int timeout_cnt=0; + int overall_cnt=0; + + /* SMG sent HANGUP to boost, however we have to wait + for boost to give us the ACK back before we + release resources. */ + + log_printf(SMG_LOG_DEBUG_8, woomera->log, + "Waiting for STOPPED ACK [%s] [id=%i]... \n", + woomera->interface,woomera->index_hold); + + while (woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK)) { + timeout_cnt++; + if (timeout_cnt > 100) { //5sec timeout + timeout_cnt=0; + overall_cnt++; + log_printf((overall_cnt==1)?0:4, woomera->log, + "Waiting for STOPPED ACK [%s] [id=%i] %i... \n", + woomera->interface,woomera->index_hold,overall_cnt); + } + + if (overall_cnt > 15) { //50sec + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING)) { + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + + if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ + break; + } + + usleep(50000); + sched_yield(); + } + + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT)) { + log_printf(SMG_LOG_ALL, woomera->log, + "Wait TIMEDOUT on STOPPED ACK [%s] [id=%i]... \n", + woomera->interface,woomera->index_hold); + + } else { + log_printf(SMG_LOG_DEBUG_8, woomera->log, + "Wait GOT STOPPED ACK [%s] [id=%i]... \n", + woomera->interface,woomera->index_hold); + } + } + + /***************************************************** + * We must wait for WFLAG_WAIT_FOR_STOPPED_ACK here + * so that STOP_ACK can access the woomera + *****************************************************/ + + if (smg_validate_span_chan(span,chan) == 0) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, + "WOOMERA Clearing Processs Table ... \n", + woomera->interface); + pthread_mutex_lock(&server.process_lock); + if (server.process_table[span][chan].dev == woomera){ + server.process_table[span][chan].dev = NULL; + memset(server.process_table[span][chan].session,0,SMG_SESSION_NAME_SZ); + memset(server.process_table[span][chan].digits,0, + sizeof(server.process_table[span][chan].digits)); + server.process_table[span][chan].digits_len = 0; + } + pthread_mutex_unlock(&server.process_lock); + } + +#if 0 +//Used for testing + if (1) { + int chan = woomera->chan; + int span = woomera->span; + if (smg_validate_span_chan(span,chan) == 0) { + pthread_mutex_lock(&server.process_lock); + /* This is possible in case media thread dies on startup */ + + if (server.process_table[span][chan]){ + log_printf(SMG_LOG_ALL, server.log, + "Sanity Span Chan Still in use: [s%dc%d] [%s] Index=%d ptr=%p\n", + span+1, chan+1, woomera->interface, woomera->index, woomera); + //server.process_table[span][chan] = NULL; + } + pthread_mutex_unlock(&server.process_lock); + } + } +#endif + + usleep(3000000); + + /* Sanity Check */ + if (woomera_test_flag(woomera, WFLAG_HANGUP_ACK)) { + log_printf(SMG_LOG_ALL, woomera->log, + "Woomera MISSED HANGUP ACK: Retry HANGUP ACK\n"); + goto woo_re_hangup; + } + if (woomera_test_flag(woomera, WFLAG_HANGUP_NACK_ACK)) { + log_printf(SMG_LOG_ALL, woomera->log, + "Woomera MISSED HANGUP ACK: Retry HANGUP NACK ACK\n"); + goto woo_re_hangup; + } + + + /* This is where we actually pull the index + * out of the tank. We had to keep the tank + * value until the end of the call. Tank is only + * used on outgoing calls. */ + if (woomera->index_hold >= 1) { + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT)) { + /* Replace real woomera interface with a dummy so + the tank is blocked. The real woomera interface will be + deallocated */ + pull_from_holding_tank(woomera->index_hold,-1,-1); + + if (check_tank_index(woomera->index_hold) != NULL){ + log_printf(SMG_LOG_PROD, woomera->log, "Woomera Thread: [%s] setup id %i blocked waiting for NACK or ACK\n", + woomera->interface ,woomera->index_hold); + } + } else { + clear_from_holding_tank(woomera->index_hold, woomera); + + } + woomera->index_hold=0; + } + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "Woomera Thread Finished %u\n", (unsigned long) woomera->thread); + close_socket(&woomera->socket); + woomera->socket=-1; + + /* delete queue */ + while ((event_string = dequeue_event(woomera))) { + smg_free(event_string); + } + + if (woomera_test_flag(woomera, WFLAG_LISTENING)) { + del_listener(woomera); + } + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "WOOMERA session for [%s] stopped (ptr=%p)\n", + woomera->interface,woomera); + + pthread_mutex_destroy(&woomera->queue_lock); + pthread_mutex_destroy(&woomera->ms_lock); + pthread_mutex_destroy(&woomera->dtmf_lock); + pthread_mutex_destroy(&woomera->vlock); + pthread_mutex_destroy(&woomera->flags_lock); + woomera_set_raw(woomera, NULL); + woomera_set_interface(woomera, NULL); + + woomera_message_clear(&wmsg); + + smg_free(woomera); + pthread_mutex_lock(&server.thread_count_lock); + server.call_count--; + server.thread_count--; + pthread_mutex_unlock(&server.thread_count_lock); + + pthread_exit(NULL); + return NULL; +} + + +static int launch_woomera_thread(struct woomera_interface *woomera) +{ + int result = 0; + pthread_attr_t attr; + + result = pthread_attr_init(&attr); + //pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + //pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + woomera_set_flag(woomera, WFLAG_RUNNING); + result = pthread_create(&woomera->thread, &attr, woomera_thread_run, woomera); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Woomera Thread! (%i) %s\n", + __FUNCTION__,result,strerror(errno)); + woomera_clear_flag(woomera, WFLAG_RUNNING); + } + pthread_attr_destroy(&attr); + + return result; +} + +static int launch_monitor_thread(void) +{ + pthread_attr_t attr; + int result = 0; + struct sched_param param; + + param.sched_priority = 10; + result = pthread_attr_init(&attr); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + result = pthread_attr_setschedparam (&attr, ¶m); + + log_printf(SMG_LOG_ALL,server.log,"%s: Old Priority =%i res=%i \n",__FUNCTION__, + param.sched_priority,result); + + + woomera_set_flag(&server.master_connection, WFLAG_MONITOR_RUNNING); + result = pthread_create(&server.monitor_thread, &attr, monitor_thread_run, NULL); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Thread! %s\n", + __FUNCTION__,strerror(errno)); + woomera_clear_flag(&server.master_connection, WFLAG_MONITOR_RUNNING); + } + pthread_attr_destroy(&attr); + + return result; +} + + +#ifdef WP_HPTDM_API +static void *hp_tdmapi_span_run(void *obj) +{ + hp_tdm_api_span_t *span = obj; + int err; + + log_printf(SMG_LOG_ALL,server.log,"Starting %s span!\n",span->ifname); + + while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + + if (!span->run_span) { + break; + } + + err = span->run_span(span); + if (err) { + break; + } + + } + + if (span->close_span) { + span->close_span(span); + } + + sleep(3); + log_printf(SMG_LOG_ALL,server.log,"Stopping %s span!\n",span->ifname); + + pthread_exit(NULL); +} + + +static int launch_hptdm_api_span_thread(int span) +{ + pthread_attr_t attr; + int result = 0; + struct sched_param param; + + param.sched_priority = 5; + result = pthread_attr_init(&attr); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + result = pthread_attr_setschedparam (&attr, ¶m); + + result = pthread_create(&server.monitor_thread, &attr, hp_tdmapi_span_run, &hptdmspan[span]); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Thread! %s\n", + __FUNCTION__,strerror(errno)); + } + pthread_attr_destroy(&attr); + + return result; +} +#endif + +static int configure_server(void) +{ + struct woomera_config cfg; + char *var, *val; + int cnt = 0; + struct smg_tdm_ip_bridge *ip_bridge=NULL; + + + server.dtmf_intr_ch = -1; + + if (!woomera_open_file(&cfg, server.config_file)) { + log_printf(SMG_LOG_ALL, server.log, "open of %s failed\n", server.config_file); + return 0; + } + + log_printf(SMG_LOG_ALL,server.log, "\n======================= \n"); + log_printf(SMG_LOG_ALL,server.log, "Server - Configuration \n"); + log_printf(SMG_LOG_ALL,server.log, "======================= \n"); + + while (woomera_next_pair(&cfg, &var, &val)) { + if (!strcasecmp(var, "boost_local_ip")) { + strncpy(server.mcon.cfg.local_ip, val, + sizeof(server.mcon.cfg.local_ip) -1); + strncpy(server.mconp.cfg.local_ip, val, + sizeof(server.mconp.cfg.local_ip) -1); + log_printf(SMG_LOG_ALL,server.log, "Server - Boost Local IP: %s\n",val); + + cnt++; + } else if (!strcasecmp(var, "boost_local_port")) { + server.mcon.cfg.local_port = atoi(val); + server.mconp.cfg.local_port = + server.mcon.cfg.local_port+1; + log_printf(SMG_LOG_ALL,server.log, "Server - Boost Local Port: %i\n",server.mcon.cfg.local_port); + cnt++; + } else if (!strcasecmp(var, "boost_remote_ip")) { + strncpy(server.mcon.cfg.remote_ip, val, + sizeof(server.mcon.cfg.remote_ip) -1); + strncpy(server.mconp.cfg.remote_ip, val, + sizeof(server.mconp.cfg.remote_ip) -1); + log_printf(SMG_LOG_ALL,server.log, "Server - Boost Remote IP: %s\n",server.mcon.cfg.remote_ip); + cnt++; + } else if (!strcasecmp(var, "boost_remote_port")) { + server.mcon.cfg.remote_port = atoi(val); + server.mconp.cfg.remote_port = + server.mcon.cfg.remote_port+1; + log_printf(SMG_LOG_ALL,server.log, "Server - Boost Remote Port: %i\n",server.mcon.cfg.local_port); + cnt++; + } else if (!strcasecmp(var, "logfile_path")) { + if (!server.logfile_path) { + server.logfile_path = smg_strdup(val); + } + } else if (!strcasecmp(var, "woomera_port")) { + server.port = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Woomera Port: %i\n",server.port); + } else if (!strcasecmp(var, "debug_level")) { + server.debug = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Debug Level: %i\n",server.debug); + } else if (!strcasecmp(var, "out_tx_test")) { + server.out_tx_test = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Tx Media Dbg: %s\n",server.out_tx_test?"On":"Off (Default)"); + } else if (!strcasecmp(var, "loop_trace")) { + server.loop_trace = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Media Loop Trace: %s\n",server.loop_trace?"On":"Off (Default)"); + } else if (!strcasecmp(var, "rxgain")) { + server.rxgain = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Rx Gain: %d\n",server.rxgain); + } else if (!strcasecmp(var, "txgain")) { + server.txgain = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Tx Gain: %d\n",server.txgain); + } else if (!strcasecmp(var, "dtmf_on_duration")){ + server.dtmf_on = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - DTMF ON Duration: %d ms\n",server.dtmf_on); + } else if (!strcasecmp(var, "dtmf_off_duration")){ + server.dtmf_off = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - DTMF Off Duration: %d ms\n",server.dtmf_off); + } else if (!strcasecmp(var, "dtmf_inter_ch_duration")){ + server.dtmf_intr_ch = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - DTMF Spacing: %d\n",server.dtmf_intr_ch); + } else if (!strcasecmp(var, "strip_cid_non_digits")){ + server.strip_cid_non_digits = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Strip non digits: %d\n",server.strip_cid_non_digits); + } else if (!strcasecmp(var, "max_calls")) { + int max = atoi(val); + if (max > 0) { + server.max_calls = max; + log_printf(SMG_LOG_ALL,server.log, "Server - Max Active Calls: %d\n",server.max_calls); + } + } else if (!strcasecmp(var, "autoacm")) { + int max = atoi(val); + if (max >= 0) { + autoacm=max; + log_printf(SMG_LOG_ALL,server.log, "Server - Auto ACM: %s\n",autoacm?"On":"Off (Default)"); + } + } else if (!strcasecmp(var, "max_spans")) { + int max = atoi(val); + if (max > 0) { + max_spans = max; + log_printf(SMG_LOG_ALL,server.log, "Server - Max Spans: %d\n",max_spans); + } + } else if (!strcasecmp(var, "call_timeout")) { + int max = atoi(val); + if (max >= 0) { + server.call_timeout=max; + } else { + server.call_timeout=SMG_DEFAULT_CALL_TIMEOUT; + } + log_printf(SMG_LOG_ALL,server.log, "Server - Call Comp Timeout: %d s\n",server.call_timeout); + + } else if (!strcasecmp(var, "base_media_port")) { + int base = atoi(val); + if (base >= 0) { + server.base_media_port = base; + server.next_media_port = base; + server.max_media_port = server.base_media_port + WOOMERA_MAX_MEDIA_PORTS; + log_printf(SMG_LOG_ALL,server.log, "Server - Base Media Port: %d\n",server.base_media_port); + } + + } else if (!strcasecmp(var, "max_media_ports")) { + int max = atoi(val); + if (max >= 0) { + server.max_media_port = server.base_media_port+max; + log_printf(SMG_LOG_ALL,server.log, "Server - Max Media Port: %d\n",server.max_media_port); + } + + } else if (!strcasecmp(var, "media_ip")) { + strncpy(server.media_ip, val, sizeof(server.media_ip) -1); + log_printf(SMG_LOG_ALL,server.log, "Server - Media IP: %s\n",server.media_ip); + + } else if (!strcasecmp(var, "bridge_tdm_ip")) { + int err=smg_get_ip_bridge_session(&ip_bridge); + if (err) { + log_printf(SMG_LOG_ALL, server.log, "Error failed to get free ip bridge %i!\n",err); + } else { + log_printf(SMG_LOG_ALL,server.log, "\n======================= \n"); + log_printf(SMG_LOG_ALL,server.log, "Bridge - Configuration \n"); + log_printf(SMG_LOG_ALL,server.log, "======================= \n"); + } + + } else if (!strcasecmp(var, "bridge_span")) { + int max = atoi(val); + if (max > 0 && max <= 32 && ip_bridge) { + ip_bridge->span=max; + log_printf(SMG_LOG_ALL, server.log, "Bridge Span: %i\n",ip_bridge->span); + } else { + log_printf(SMG_LOG_ALL, server.log, "Bridge Span: ERROR: Invalid Value %s\n",val); + } + } else if (!strcasecmp(var, "bridge_chan")) { + int max = atoi(val); + if (max > 0 && max < MAX_SMG_BRIDGE && ip_bridge) { + ip_bridge->chan=max; + log_printf(SMG_LOG_ALL, server.log, "Bridge Chan: %i\n",ip_bridge->chan); + } else { + log_printf(SMG_LOG_ALL, server.log, "Bridge Chan: ERROR: Invalid Value %s\n",val); + } + } else if (!strcasecmp(var, "bridge_local_ip")) { + if (ip_bridge) { + strncpy(ip_bridge->mcon.cfg.local_ip, val, + sizeof(ip_bridge->mcon.cfg.local_ip) -1); + log_printf(SMG_LOG_ALL, server.log, "Bridge Local IP: %s\n",ip_bridge->mcon.cfg.local_ip); + } + } else if (!strcasecmp(var, "bridge_remote_ip")) { + if (ip_bridge) { + strncpy(ip_bridge->mcon.cfg.remote_ip, val, + sizeof(ip_bridge->mcon.cfg.remote_ip) -1); + log_printf(SMG_LOG_ALL, server.log, "Bridge Remote IP: %s\n",ip_bridge->mcon.cfg.remote_ip); + } + } else if (!strcasecmp(var, "bridge_port")) { + int max = atoi(val); + if (max > 0 && ip_bridge) { + ip_bridge->mcon.cfg.local_port=max; + ip_bridge->mcon.cfg.remote_port=max; + log_printf(SMG_LOG_ALL, server.log, "Bridge Port: %i\n",max); + } + } else if (!strcasecmp(var, "bridge_period")) { + int max = atoi(val); + if (max > 0 && ip_bridge) { + ip_bridge->period=max; + log_printf(SMG_LOG_ALL, server.log, "Bridge Period: %ims\n",ip_bridge->period); + } + } else { + log_printf(SMG_LOG_ALL, server.log, "Invalid Option %s at line %d!\n", var, cfg.lineno); + } + } + + log_printf(SMG_LOG_ALL,server.log, "======================= \n\n"); + + /* Post initialize */ + if (server.dtmf_on == 0){ + server.dtmf_on=SMG_DTMF_ON; + } + if (server.dtmf_off == 0) { + server.dtmf_off=SMG_DTMF_OFF; + } + if (server.dtmf_intr_ch == -1) { + server.dtmf_intr_ch = 0; + } + server.dtmf_size=(server.dtmf_on+server.dtmf_off)*10*2; + + log_printf(SMG_LOG_ALL,server.log, "DTMF On=%i Off=%i IntrCh=%i Size=%i\n", + server.dtmf_on,server.dtmf_off,server.dtmf_intr_ch,server.dtmf_size); + + woomera_close_file(&cfg); + return cnt == 4 ? 1 : 0; +} + + + +static int main_thread(void) +{ + + struct sockaddr_in sock_addr, client_addr; + struct woomera_interface *new_woomera; + int client_sock = -1, pid = 0; + unsigned int len = 0; + FILE *tmp; + + if ((server.master_connection.socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + fprintf(stderr,"%s:%d socket() failed %s\n",__FUNCTION__,__LINE__,strerror(errno)); + return 1; + } + + memset(&sock_addr, 0, sizeof(sock_addr)); /* Zero out structure */ + sock_addr.sin_family = AF_INET; /* Internet address family */ + sock_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ + sock_addr.sin_port = htons(server.port); /* Local port */ + + /* Bind to the local address */ + if (bind(server.master_connection.socket, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) < 0) { + fprintf(stderr,"%s:%d socket() bind %i failed %s\n",__FUNCTION__,__LINE__,server.port,strerror(errno)); + log_printf(SMG_LOG_ALL, server.log, "bind(%d) failed\n", server.port); + return 1; + } + + /* Mark the socket so it will listen for incoming connections */ + if (listen(server.master_connection.socket, MAXPENDING) < 0) { + fprintf(stderr,"%s:%d socket() listen failed %s\n",__FUNCTION__,__LINE__,strerror(errno)); + log_printf(SMG_LOG_ALL, server.log, "listen() failed\n"); + return 1; + } + + if ((pid = get_pid_from_file(PIDFILE))) { + fprintf(stderr,"%s:%d get pid file failed %s\n",__FUNCTION__,__LINE__,strerror(errno)); + log_printf(SMG_LOG_ALL, stderr, "pid %d already exists.\n", pid); + exit(0); + } + + if (!(tmp = safe_fopen(PIDFILE, "w"))) { + fprintf(stderr,"%s:%d open pid file failed %s\n",__FUNCTION__,__LINE__,strerror(errno)); + log_printf(SMG_LOG_ALL, stderr, "Error creating pidfile %s\n", PIDFILE); + return 1; + } else { + fprintf(tmp, "%d", getpid()); + fclose(tmp); + tmp = NULL; + } + + no_nagle(server.master_connection.socket); + +#if 0 + if (1) { + int span,chan; + call_signal_event_t event; +#if 0 + span=1; + chan=30; + event.span=span; + event.chan=chan; + launch_woomera_loop_thread(&event); +#else + for (span=0;span<8;span++) { + for (chan=0;chan<31;chan++) { + event.span=span; + event.chan=chan; + launch_woomera_loop_thread(&event); + } + } +#endif + } +#endif + +#ifdef WP_HPTDM_API + if (1) { + int span; + for (span=0;span<16;span++) { + hptdmspan[span] = sangoma_hptdm_api_span_init(span); + if (!hptdmspan[span]) { + break; + } else { + log_printf(SMG_LOG_ALL, server.log, "HP TDM API Span: %d configured...\n", + span); + launch_hptdm_api_span_thread(span); + } + } + } +#endif + + log_printf(SMG_LOG_PROD, server.log, "Main Process Started: Woomera Ready port: %d\n", server.port); + + while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + + /* Set the size of the in-out parameter */ + len = sizeof(client_addr); + + /* Wait for a client to connect */ + if ((client_sock = accept(server.master_connection.socket, (struct sockaddr *) &client_addr, &len)) < 0) { + /* Check if we are supposed to stop */ + if(!woomera_test_flag(&server.master_connection, WFLAG_RUNNING)) { + log_printf(SMG_LOG_ALL, server.log, "accept() stopped\n"); + return 0; + } + log_printf(SMG_LOG_ALL, server.log, "accept() failed\n"); + return 1; + } + + if ((new_woomera = new_woomera_interface(client_sock, &client_addr, len))) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Starting Thread for New Connection %s:%d Sock=%d\n", + inet_ntoa(new_woomera->addr.sin_addr), + ntohs(new_woomera->addr.sin_port), + client_sock); + + pthread_mutex_lock(&server.thread_count_lock); + server.call_count++; + pthread_mutex_unlock(&server.thread_count_lock); + + + if (launch_woomera_thread(new_woomera)) { + socket_printf(new_woomera->socket, + "501 call was cancelled!%s", + WOOMERA_RECORD_SEPERATOR); + + close_socket(&new_woomera->socket); + new_woomera->socket=-1; + smg_free(new_woomera); + log_printf(SMG_LOG_ALL, server.log, "ERROR: failed to launch woomera thread\n"); + } + } else { + log_printf(SMG_LOG_ALL, server.log, "Critical ERROR: memory/socket error\n"); + } + } + + log_printf(SMG_LOG_PROD, server.log, "Main Process End\n"); + + return 0; +} + +static int do_ignore(int sig) + +{ +#ifdef SMG_DROP_SEQ + drop_seq=4; +#endif + sdla_memdbg_free(0); + return 0; +} + +static int do_shut(int sig) +{ + woomera_clear_flag(&server.master_connection, WFLAG_RUNNING); + close_socket(&server.master_connection.socket); + log_printf(SMG_LOG_PROD, server.log, "Caught SIG %d, Closing Master Socket!\n", sig); + return 0; +} + +static int sangoma_tdm_init (int span) +{ +#ifdef LIBSANGOMA_GET_HWCODING + wanpipe_tdm_api_t tdm_api; + int fd=sangoma_open_tdmapi_span(span); + if (fd < 0 ){ + return -1; + } else { + server.hw_coding=sangoma_tdm_get_hw_coding(fd,&tdm_api); + close_socket(&fd); + } +#else +#error "libsangoma missing hwcoding feature: not up to date!" +#endif + return 0; +} + +static int woomera_startup(int argc, char **argv) +{ + int x = 0, pid = 0, bg = 0; + int span_cnt, chan_cnt; + char *cfg=NULL, *debug=NULL, *arg=NULL; + + while((arg = argv[x++])) { + + if (!strcasecmp(arg, "-hup")) { + if (! (pid = get_pid_from_file(PIDFILE))) { + log_printf(SMG_LOG_ALL, stderr, "Error reading pidfile %s\n", PIDFILE); + exit(1); + } else { + log_printf(SMG_LOG_ALL, stderr, "Killing PID %d\n", pid); + kill(pid, SIGHUP); + sleep(1); + exit(0); + } + + } else if (!strcasecmp(arg, "-term") || !strcasecmp(arg, "--term")) { + if (! (pid = get_pid_from_file(PIDFILE))) { + log_printf(SMG_LOG_ALL, stderr, "Error reading pidfile %s\n", PIDFILE); + exit(1); + } else { + log_printf(SMG_LOG_ALL, stderr, "Killing PID %d\n", pid); + kill(pid, SIGTERM); + unlink(PIDFILE); + sleep(1); + exit(0); + } + + } else if (!strcasecmp(arg, "-version")) { + fprintf(stdout, "\nSangoma Media Gateway: Version %s\n\n", SMG_VERSION); + exit(0); + + } else if (!strcasecmp(arg, "-help")) { + fprintf(stdout, "%s\n%s [-help] | [ -version] | [-hup] | [-wipe] | [[-bg] | [-debug ] | [-cfg ] | [-log ]]\n\n", WELCOME_TEXT, argv[0]); + exit(0); + } else if (!strcasecmp(arg, "-wipe")) { + unlink(PIDFILE); + } else if (!strcasecmp(arg, "-bg")) { + bg = 1; + + } else if (!strcasecmp(arg, "-g")) { + coredump = 1; + + } else if (!strcasecmp(arg, "-debug")) { + if (argv[x] && *(argv[x]) != '-') { + debug = argv[x++]; + } + } else if (!strcasecmp(arg, "-cfg")) { + if (argv[x] && *(argv[x]) != '-') { + cfg = argv[x++]; + } + } else if (!strcasecmp(arg, "-log")) { + if (argv[x] && *(argv[x]) != '-') { + server.logfile_path = smg_strdup(argv[x++]); + } + } else if (*arg == '-') { + log_printf(SMG_LOG_ALL, stderr, "Unknown Option %s\n", arg); + fprintf(stdout, "%s\n%s [-help] | [-hup] | [-wipe] | [[-bg] | [-debug ] | [-cfg ] | [-log ]]\n\n", WELCOME_TEXT, argv[0]); + exit(1); + } + } + + if (1){ + int spn; + for (spn=1;spn<=WOOMERA_MAX_SPAN;spn++) { + if (sangoma_tdm_init(spn) == 0) { + break; + } + } + if (spn>WOOMERA_MAX_SPAN) { + printf("\nError: Failed to access a channel on spans 1-16\n"); + printf(" Please start Wanpipe TDM API drivers\n"); + return 0; + } + } + + if (bg && (pid = fork())) { + log_printf(SMG_LOG_ALL, stderr, "Backgrounding!\n"); + return 0; + } + + for (span_cnt = 0; span_cnt < CORE_MAX_SPANS; span_cnt++) { + for (chan_cnt = 0; chan_cnt < CORE_MAX_CHAN_PER_SPAN; chan_cnt++) { + pthread_mutex_init(&server.process_table[span_cnt][chan_cnt].media_lock, NULL); + } + } + + q931_cause_setup(); + bearer_cap_setup(); + uil1p_to_str_setup(); + + server.port = 42420; + server.debug = 0; + strcpy(server.media_ip, "127.0.0.1"); + server.base_media_port = WOOMERA_MIN_MEDIA_PORT; + server.max_media_port = WOOMERA_MAX_MEDIA_PORT; + server.next_media_port = server.base_media_port; + server.log = stdout; + server.master_connection.socket = -1; + server.master_connection.event_queue = NULL; + server.config_file = cfg ? cfg : "/etc/sangoma_mgd.conf"; + pthread_mutex_init(&server.listen_lock, NULL); + pthread_mutex_init(&server.ht_lock, NULL); + pthread_mutex_init(&server.process_lock, NULL); + pthread_mutex_init(&server.digits_lock, NULL); + pthread_mutex_init(&server.media_udp_port_lock, NULL); + pthread_mutex_init(&server.thread_count_lock, NULL); + pthread_mutex_init(&server.master_connection.queue_lock, NULL); + pthread_mutex_init(&server.master_connection.flags_lock, NULL); + pthread_mutex_init(&server.mcon.lock, NULL); + server.master_connection.chan = -1; + server.master_connection.span = -1; + server.call_timeout=SMG_DEFAULT_CALL_TIMEOUT; + + if (smg_log_init()) { + printf("Error: Logger Launch Failed\n"); + fprintf(stderr, "Error: Logger Launch Failed\n"); + return 0; + } + + if (!configure_server()) { + log_printf(SMG_LOG_ALL, server.log, "configuration failed!\n"); + return 0; + } + +#ifndef USE_SYSLOG + if (server.logfile_path) { + if (!(server.log = safe_fopen(server.logfile_path, "a"))) { + log_printf(SMG_LOG_ALL, stderr, "Error setting logfile %s!\n", server.logfile_path); + server.log = stderr; + return 0; + } + } +#endif + + + if (debug) { + server.debug = atoi(debug); + } + + if (coredump) { + struct rlimit l; + memset(&l, 0, sizeof(l)); + l.rlim_cur = RLIM_INFINITY; + l.rlim_max = RLIM_INFINITY; + if (setrlimit(RLIMIT_CORE, &l)) { + log_printf(SMG_LOG_ALL, stderr, "Warning: Failed to disable core size limit: %s\n", + strerror(errno)); + } + } + +#ifdef __LINUX__ + if (geteuid() && coredump) { + if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) { + log_printf(SMG_LOG_ALL, stderr, "Warning: Failed to disable core size limit for non-root: %s\n", + strerror(errno)); + } + } +#endif + + + + (void) signal(SIGINT,(void *) do_shut); + (void) signal(SIGTERM,(void *) do_shut); + (void) signal(SIGPIPE,(void *) do_ignore); + (void) signal(SIGUSR1,(void *) do_ignore); + (void) signal(SIGHUP,(void *) do_shut); + + /* Wait for logger thread to start */ + usleep(5000); + + woomera_set_flag(&server.master_connection, WFLAG_RUNNING); + + if (launch_monitor_thread()) { + woomera_clear_flag(&server.master_connection, WFLAG_RUNNING); + smg_log_cleanup(); + return 0; + } + + fprintf(stderr, "%s", WELCOME_TEXT); + + log_printf(SMG_LOG_ALL, stderr, "Woomera STARTUP Complete. [AutoACM=%i SDigit=%i]\n", + autoacm,server.strip_cid_non_digits); + return 1; +} + +static int woomera_shutdown(void) +{ + char *event_string; + int told = 0, loops = 0; + int span_cnt, chan_cnt; + + for (span_cnt = 0; span_cnt < CORE_MAX_SPANS; span_cnt++) { + for (chan_cnt = 0; chan_cnt < CORE_MAX_CHAN_PER_SPAN; chan_cnt++) { + pthread_mutex_destroy(&server.process_table[span_cnt][chan_cnt].media_lock); + } + } + + close_socket(&server.master_connection.socket); + pthread_mutex_destroy(&server.listen_lock); + pthread_mutex_destroy(&server.ht_lock); + pthread_mutex_destroy(&server.process_lock); + pthread_mutex_destroy(&server.digits_lock); + pthread_mutex_destroy(&server.media_udp_port_lock); + pthread_mutex_destroy(&server.thread_count_lock); + pthread_mutex_destroy(&server.master_connection.queue_lock); + pthread_mutex_destroy(&server.master_connection.flags_lock); + pthread_mutex_destroy(&server.mcon.lock); + woomera_clear_flag(&server.master_connection, WFLAG_RUNNING); + + + if (server.logfile_path) { + smg_free(server.logfile_path); + server.logfile_path = NULL; + } + + /* delete queue */ + while ((event_string = dequeue_event(&server.master_connection))) { + smg_free(event_string); + } + + while(server.thread_count > 0) { + loops++; + + if (loops % 1000 == 0) { + told = 0; + } + + if (loops > 10000) { + log_printf(SMG_LOG_ALL, server.log, "Red Alert! threads did not stop\n"); + assert(server.thread_count == 0); + } + + if (told != server.thread_count) { + log_printf(SMG_LOG_PROD, server.log, "Waiting For %d thread%s.\n", + server.thread_count, server.thread_count == 1 ? "" : "s"); + told = server.thread_count; + } + ysleep(10000); + } + unlink(PIDFILE); + log_printf(SMG_LOG_ALL, stderr, "Woomera SHUTDOWN Complete.\n"); + + smg_log_cleanup(); + + return 0; +} + +int main(int argc, char *argv[]) +{ + int ret = 0; + + memset(&server,0,sizeof(server)); + memset(&woomera_dead_dev,0,sizeof(woomera_dead_dev)); + + mlockall(MCL_FUTURE); + memset(&server, 0, sizeof(server)); + memset(&woomera_dead_dev, 0, sizeof(woomera_dead_dev)); + memset(&g_smg_ip_bridge_idx,0, sizeof(g_smg_ip_bridge_idx)); + + ret=nice(-5); + + sdla_memdbg_init(); + + server.hw_coding=0; + + openlog (ps_progname ,LOG_PID, LOG_LOCAL2); + + if (! (ret = woomera_startup(argc, argv))) { + exit(0); + } + + ret = smg_ip_bridge_start(); + if (ret == 0) { + ret = main_thread(); + } + + woomera_shutdown(); + + smg_ip_bridge_stop(); + sdla_memdbg_free(1); + + return ret; +} + +/** EMACS ** + * Local variables: + * mode: C + * c-basic-offset: 4 + * End: + */ + diff --git a/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.14.tmp b/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.14.tmp new file mode 100644 index 0000000..b5de3d5 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.14.tmp @@ -0,0 +1,786 @@ +/********************************************************************************* + * sangoma_mgd.h -- Sangoma Media Gateway Daemon for Sangoma/Wanpipe Cards + * + * Copyright 05-07, Nenad Corbic + * Anthony Minessale II + * + * This program is free software, distributed under the terms of + * the GNU General Public License + * + * =============================================*/ + +#ifndef __SANGOMA_MGD_H_ +#define __SANGOMA_MGD_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sangoma_mgd_common.h" +#include "call_signal.h" +#include "sangoma_mgd_memdbg.h" + +#ifdef __LINUX__ +#include +#endif + + +#include "call_signal.h" +#include "g711.h" +#include "sangoma_mgd_memdbg.h" +#include "libteletone.h" +#include "switch_buffer.h" +#include "list.h" + +#define USE_LOG_THREAD 1 +#define USE_SYSLOG 1 +#define CODEC_LAW_DEFAULT 1 + +#define WOOMERA_MAX_CHAN 31 + +#define SMG_SESSION_NAME_SZ 100 +#define SMG_CHAN_NAME_SZ 20 + +#define PIDFILE "/var/run/sangoma_mgd.pid" +#define PIDFILE_UNIT "/var/run/sangoma_mgd_unit.pid" + +#define WOOMERA_MAX_MEDIA_PORTS 5000 + +#define CORE_EVENT_LEN 512 +#define WOOMERA_STRLEN 256 +#define WOOMERA_ARRAY_LEN 50 +#define WOOMERA_BODYLEN 2048 +#define WOOMERA_MIN_MEDIA_PORT 10000 +#define WOOMERA_MAX_MEDIA_PORT (WOOMERA_MIN_MEDIA_PORT + WOOMERA_MAX_MEDIA_PORTS) +#define WOOMERA_HARD_TIMEOUT 0 +#define WOOMERA_LINE_SEPERATOR "\r\n" +#define WOOMERA_RECORD_SEPERATOR "\r\n\r\n" +#define WOOMERA_DEBUG_PREFIX "[DEBUG] " +#define WOOMERA_DEBUG_LINE "------------------------------------------------------------------------------------------------" + + + + +#define STDERR fileno(stderr) +#define MAXPENDING 500 +#define MGD_STACK_SIZE 1024 * 240 + +#define SMG_SOCKET_EVENT_TIMEOUT 0 + +typedef enum { + WFLAG_RUNNING = (1 << 0), + WFLAG_LISTENING = (1 << 1), + WFLAG_MASTER_DEV = (1 << 2), + WFLAG_EVENT = (1 << 3), + WFLAG_MALLOC = (1 << 4), + WFLAG_MEDIA_RUNNING = (1 << 5), + WFLAG_MEDIA_END = (1 << 6), + WFLAG_MONITOR_RUNNING = (1 << 7), + WFLAG_HANGUP = (1 << 8), + WFLAG_ANSWER = (1 << 9), + WFLAG_MEDIA_TDM_RUNNING = (1 << 10), + WFLAG_HANGUP_ACK = (1 << 11), + WFLAG_HANGUP_NACK_ACK = (1 << 12), + WFLAG_WAIT_FOR_NACK_ACK = (1 << 13), /* Wait flag used to test reception of an NACK */ + WFLAG_WAIT_FOR_STOPPED_ACK = (1 << 14), /* Wait flag used to test reception of an ACK */ + WFLAG_RAW_MEDIA_STARTED = (1 << 15), /* Media has started on this channel */ + WFLAG_CALL_ACKED = (1 << 16), /* Woomera side rx accept so CALL ACK was Sent */ + WFLAG_WAIT_FOR_NACK_ACK_SENT = (1 << 17), /* Call START NACK was sent out on this channel */ + WFLAG_WAIT_FOR_STOPPED_ACK_SENT = (1 << 18), /* Call STOP was sent out on this channel */ + WFLAG_SYSTEM_RESET = (1 << 19), /* Initial System Reset Condition no calls allowed */ + WFLAG_WAIT_FOR_ACK_TIMEOUT = (1 << 20), /* Timeout flag indicating that incoming ACK or NACK timedout */ +} WFLAGS; + +enum { + + SMG_LOG_ALL = 0, + SMG_LOG_PROD = 1, + SMG_LOG_BOOST = 2, + SMG_LOG_WOOMERA = 3, + SMG_LOG_DEBUG_CALL = 4, + SMG_LOG_DEBUG_MISC = 5, + SMG_LOG_DEBUG_6 = 6, + SMG_LOG_DEBUG_7 = 7, + SMG_LOG_DEBUG_8 = 8, + SMG_LOG_DEBUG_9 = 9, + SMG_LOG_DEBUG_10 = 10 +}; + + +#define woomera_print_flags(woomera,level) \ + log_printf(level,woomera->log,"%s: WFLAG_RUNNING = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_RUNNING));\ + log_printf(level,woomera->log,"%s: WFLAG_LISTENING = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_LISTENING));\ + log_printf(level,woomera->log,"%s: WFLAG_MASTER_DEV = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_MASTER_DEV));\ + log_printf(level,woomera->log,"%s: WFLAG_EVENT = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_EVENT));\ + log_printf(level,woomera->log,"%s: WFLAG_MALLOC = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_MALLOC));\ + log_printf(level,woomera->log,"%s: WFLAG_MEDIA_RUNNING = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_MEDIA_RUNNING));\ + log_printf(level,woomera->log,"%s: WFLAG_MEDIA_END = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_MEDIA_END));\ + log_printf(level,woomera->log,"%s: WFLAG_MONITOR_RUNNING = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_MONITOR_RUNNING));\ + log_printf(level,woomera->log,"%s: WFLAG_HANGUP = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_HANGUP));\ + log_printf(level,woomera->log,"%s: WFLAG_ANSWER = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_ANSWER));\ + log_printf(level,woomera->log,"%s: WFLAG_MEDIA_TDM_RUNNING = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_MEDIA_TDM_RUNNING));\ + log_printf(level,woomera->log,"%s: WFLAG_HANGUP_ACK = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_HANGUP_ACK));\ + log_printf(level,woomera->log,"%s: WFLAG_HANGUP_NACK_ACK = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_HANGUP_NACK_ACK));\ + log_printf(level,woomera->log,"%s: WFLAG_WAIT_FOR_NACK_ACK = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_WAIT_FOR_NACK_ACK));\ + log_printf(level,woomera->log,"%s: WFLAG_WAIT_FOR_STOPPED_ACK = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_WAIT_FOR_STOPPED_ACK));\ + log_printf(level,woomera->log,"%s: WFLAG_RAW_MEDIA_STARTED = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_RAW_MEDIA_STARTED));\ + log_printf(level,woomera->log,"%s: WFLAG_CALL_ACKED = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_CALL_ACKED));\ + log_printf(level,woomera->log,"%s: WFLAG_WAIT_FOR_NACK_ACK_SENT = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_WAIT_FOR_NACK_ACK_SENT));\ + log_printf(level,woomera->log,"%s: WFLAG_WAIT_FOR_STOPPED_ACK_SENT = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_WAIT_FOR_STOPPED_ACK_SENT));\ + log_printf(level,woomera->log,"%s: WFLAG_SYSTEM_RESET = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_SYSTEM_RESET));\ + log_printf(level,woomera->log,"%s: WFLAG_WAIT_FOR_ACK_TIMEOUT = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_WAIT_FOR_ACK_TIMEOUT));\ + + +typedef enum { + MFLAG_EXISTS = (1 << 0), + MFLAG_CONTENT = (1 << 1), +} MFLAGS; + +typedef enum { + EVENT_FREE_DATA = 1, + EVENT_KEEP_DATA = 0 +} event_args; + + +void __log_printf(int level, FILE *fp, char *file, const char *func, int line, char *fmt, ...); + +#define ysleep(usec) sched_yield() ; usleep(usec); +#define log_printf(level, fp, fmt, ...) __log_printf(level, fp, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__) + + +#define woomera_test_flag(p,flag) ({ \ + ((p)->flags & (flag)); \ + }) + +#define _woomera_set_flag(p,flag) do { \ + ((p)->flags |= (flag)); \ + } while (0) + +#define _woomera_clear_flag(p,flag) do { \ + ((p)->flags &= ~(flag)); \ + } while (0) + +#define woomera_set_flag(p,flag) do { \ + pthread_mutex_lock(&(p)->flags_lock); \ + ((p)->flags |= (flag)); \ + pthread_mutex_unlock(&(p)->flags_lock); \ + } while (0) + +#define woomera_clear_flag(p,flag) do { \ + pthread_mutex_lock(&(p)->flags_lock); \ + ((p)->flags &= ~(flag)); \ + pthread_mutex_unlock(&(p)->flags_lock); \ + } while (0) + +#define woomera_copy_flags(dest,src,flagz) do { \ + pthread_mutex_lock(&(p)->flags_lock); \ + (dest)->flags &= ~(flagz); \ + (dest)->flags |= ((src)->flags & (flagz)); \ + pthread_mutex_unlock(&(p)->flags_lock); \ + } while (0) + + +struct media_session { + int udp_sock; + int sangoma_sock; + char *ip; + int port; + time_t started; + time_t answered; + pthread_t thread; + int socket; + struct woomera_interface *woomera; + struct sockaddr_in local_addr; + struct sockaddr_in remote_addr; + struct hostent remote_hp; + struct hostent local_hp; + int skip_read_frames; + int skip_write_frames; + int hw_coding; + int hw_dtmf; + int udp_sync_cnt; + +#ifdef WP_HPTDM_API + hp_tdm_api_chan_t *tdmchan; +#endif + + teletone_dtmf_detect_state_t dtmf_detect; + teletone_generation_session_t tone_session; + switch_buffer_t *dtmf_buffer; + unsigned char oob_disable; +}; + +struct woomera_message { + char callid[WOOMERA_STRLEN]; + char command[WOOMERA_STRLEN]; + char command_args[WOOMERA_STRLEN]; + char names[WOOMERA_STRLEN][WOOMERA_ARRAY_LEN]; + char values[WOOMERA_STRLEN][WOOMERA_ARRAY_LEN]; + char body[WOOMERA_BODYLEN]; + uint32_t flags; + pthread_mutex_t flags_lock; + int last; + struct woomera_message *next; +}; + +struct woomera_event { + char *data; + uint32_t flags; + struct woomera_event *next; +}; + +struct woomera_listener { + struct woomera_interface *woomera; + struct woomera_listener *next; +}; + +struct woomera_interface { + int socket; + char *raw; + char *interface; + time_t timeout; + struct sockaddr_in addr; + struct media_session *ms; + pthread_mutex_t queue_lock; + pthread_mutex_t ms_lock; + pthread_mutex_t dtmf_lock; + struct woomera_event *event_queue; + struct woomera_event *incoming_event_queue; + pthread_t thread; + uint32_t flags; + pthread_mutex_t flags_lock; + int debug; + int call_id; + FILE *log; + pthread_mutex_t vlock; + int index; + int index_hold; + int span; + int chan; + int trunk_group; + int call_count; + int q931_rel_cause_tosig; + int q931_rel_cause_topbx; + int loop_tdm; + char session[SMG_SESSION_NAME_SZ]; + int check_digits; /* set to 1 when session comes up */ + int bearer_cap; + struct woomera_interface *next; +}; + +struct woomera_session { + struct woomera_interface *dev; + char session[SMG_SESSION_NAME_SZ]; + char digits[MAX_DIALED_DIGITS+1]; + int digits_len; + int bearer_cap; + int clients; + unsigned char media_used; + pthread_mutex_t media_lock; +}; + +struct smg_tdm_ip_bridge { + int init; + int end; + int span; + int chan; +#if 0 + int port; + char local_ip[25]; + char remote_ip[25]; +#endif + int period; + int tdm_fd; + call_signal_connection_t mcon; + pthread_t thread; +}; + +extern struct smg_tdm_ip_bridge g_smg_ip_bridge_idx[]; +extern pthread_mutex_t g_smg_ip_bridge_lock; + +#define MAX_SMG_BRIDGE 32 +#define CORE_TANK_LEN CORE_MAX_CHAN_PER_SPAN*CORE_MAX_SPANS + +struct woomera_server { + struct woomera_session process_table[CORE_MAX_SPANS][CORE_MAX_CHAN_PER_SPAN+1]; + struct woomera_interface *holding_tank[CORE_TANK_LEN]; + int holding_tank_index; + struct woomera_interface master_connection; + pthread_mutex_t listen_lock; + pthread_mutex_t ht_lock; + pthread_mutex_t process_lock; + pthread_mutex_t digits_lock; + pthread_mutex_t media_udp_port_lock; + pthread_mutex_t thread_count_lock; + call_signal_connection_t mcon; + call_signal_connection_t mconp; + struct woomera_listener *listeners; + char media_ip[WOOMERA_STRLEN]; + int port; + int next_media_port; + int base_media_port; + int max_media_port; + int debug; + int panic; + int thread_count; + char *logfile_path; + FILE *log; + char boost_local_ip[25]; + int boost_local_port; + char boost_remote_ip[25]; + int boost_remote_port; + pthread_t monitor_thread; + char *config_file; + int max_calls; + int call_count; + uint32_t out_tx_test; + uint32_t rxgain; + uint32_t txgain; + uint32_t hw_coding; + uint32_t loop_trace; + uint32_t hungup_waiting; + int all_ckt_gap; + int all_ckt_busy; + struct timeval all_ckt_busy_time; + struct timeval restart_timeout; + int dtmf_on; + int dtmf_off; + int dtmf_intr_ch; + int dtmf_size; + int strip_cid_non_digits; + int call_timeout; + struct smg_tdm_ip_bridge ip_bridge_idx[MAX_SMG_BRIDGE]; +}; + +extern struct woomera_server server; + +struct woomera_config { + FILE *file; + char *path; + char category[256]; + char buf[1024]; + int lineno; +}; + +static inline int smg_get_ip_bridge_session(struct smg_tdm_ip_bridge **ip_bridge) +{ + int i; + for (i=0;itv_sec * 1000) + ended->tv_usec / 1000) - + ((started->tv_sec * 1000) + started->tv_usec / 1000)); +} + +static inline int smg_check_all_busy(void) +{ + struct timeval ended; + int elapsed; + + if (server.all_ckt_gap) { + return server.all_ckt_gap; + } + + if (server.all_ckt_busy==0) { + return 0; + } + + gettimeofday(&ended,NULL); + elapsed = smg_calc_elapsed(&server.all_ckt_busy_time,&ended); + + /* seconds elapsed */ + if (elapsed > server.all_ckt_busy) { + server.all_ckt_busy=0; + return 0; + } else { + return 1; + } + +#if 0 + + if (server.all_ckt_busy > 50) { + /* When in GAP mode wait 10s */ + return server.all_ckt_busy; + } + + --server.all_ckt_busy; + if (server.all_ckt_busy < 0) { + server.all_ckt_busy=0; + } + + return server.all_ckt_busy; +#endif +} + + +static inline void smg_all_ckt_busy(void) +{ + + if (server.call_count*10 < 1500) { + server.all_ckt_busy+=1500; + } else { + server.all_ckt_busy+=server.call_count*15; + } + + if (server.all_ckt_busy > 10000) { + server.all_ckt_busy = 10000; + } + +#if 0 + if (server.all_ckt_busy >= 5) { + server.all_ckt_busy=10; + } else if (server.all_ckt_busy >= 10) { + server.all_ckt_busy=15; + } else if (server.all_ckt_busy == 0) { + server.all_ckt_busy=5; + } +#endif + gettimeofday(&server.all_ckt_busy_time,NULL); +} + +static inline void smg_all_ckt_gap(void) +{ + server.all_ckt_gap=1; +} + +static inline void smg_clear_ckt_gap(void) +{ + server.all_ckt_gap=0; + gettimeofday(&server.all_ckt_busy_time,NULL); +} + + +static inline int smg_validate_span_chan(int span, int chan) +{ + if (span < 0 || span > max_spans) { + return -1; + } + + if (chan < 0 || chan > WOOMERA_MAX_CHAN) { + return -1; + } + + return 0; +} + + +static inline void close_socket(int *sp) +{ + if (*sp > -1) { + close(*sp); + *sp = -1; + } +} + +static inline FILE *safe_fopen(char *path, char *flags) +{ + char buf[512] = ""; + + if (readlink(path, buf, sizeof(buf)) > 0) { + fprintf(stderr, "Symlinks not allowed! [%s] != [%s]\n", buf, path); + return NULL; + } + + return fopen(path, flags); + +} + +static inline int get_pid_from_file(char *path) +{ + FILE *tmp; + int pid; + int err; + + if (!(tmp = safe_fopen(path, "r"))) { + return 0; + } else { + err=fscanf(tmp, "%d", &pid); + fclose(tmp); + tmp = NULL; + } + + return pid; +} + + +static inline int woomera_open_file(struct woomera_config *cfg, char *path) +{ + FILE *f; + + if (!(f = fopen(path, "r"))) { + log_printf(SMG_LOG_ALL, stderr, "Cannot open file %s\n", path); + return 0; + } + + memset(cfg, 0, sizeof(*cfg)); + cfg->file = f; + cfg->path = path; + return 1; + +} + + +static inline void woomera_close_file(struct woomera_config *cfg) +{ + + if (cfg->file) { + fclose(cfg->file); + } + + memset(cfg, 0, sizeof(*cfg)); +} + + +static inline void woomera_message_init (struct woomera_message *wmsg) +{ + memset (wmsg,0,sizeof(struct woomera_message)); + pthread_mutex_init(&wmsg->flags_lock, NULL); +} + +static inline void woomera_message_clear (struct woomera_message *wmsg) +{ + pthread_mutex_destroy(&wmsg->flags_lock); +} + + +static inline void woomera_set_raw(struct woomera_interface *woomera, char *raw) +{ + char *oldraw=woomera->raw; + + if (raw) { + woomera->raw = smg_strdup(raw); + } else { + woomera->raw = NULL; + } + + if (oldraw) { + smg_free(oldraw); + } +} + +static inline struct media_session * woomera_get_ms(struct woomera_interface *woomera) +{ + struct media_session *ms; + pthread_mutex_lock(&woomera->ms_lock); + ms=woomera->ms; + pthread_mutex_unlock(&woomera->ms_lock); + return ms; +} + +static inline void woomera_set_ms(struct woomera_interface *woomera,struct media_session *ms) +{ + pthread_mutex_lock(&woomera->ms_lock); + woomera->ms=ms; + pthread_mutex_unlock(&woomera->ms_lock); + return; +} + +static inline void woomera_set_interface(struct woomera_interface *woomera, char *interface) +{ + char *iface = woomera->interface; + + if (interface) { + woomera->interface = smg_strdup(interface); + } else { + woomera->interface = NULL; + } + + if (iface) { + smg_free(iface); + } +} + +static inline void woomera_set_cause_tosig(struct woomera_interface *woomera, int cause) +{ + if (!cause) { + cause=SIGBOOST_RELEASE_CAUSE_NORMAL; + } + + woomera->q931_rel_cause_tosig=cause; +} + +static inline void woomera_set_cause_topbx(struct woomera_interface *woomera, int cause) +{ + + if (!cause) { + cause=SIGBOOST_RELEASE_CAUSE_NORMAL; + } + + if (cause == SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY) { + cause=34; + } + + woomera->q931_rel_cause_topbx=cause; + +} + + +static inline struct woomera_event *new_woomera_event(void) +{ + struct woomera_event *event = NULL; + + if ((event = smg_malloc(sizeof(*event)))) { + memset(event, 0, sizeof(*event)); + _woomera_set_flag(event, WFLAG_MALLOC); + } + + return event; +} + + +static inline void destroy_woomera_event_data(struct woomera_event *event) +{ + if (event->data) { + smg_free (event->data); + event->data=NULL; + } +} + +static inline void destroy_woomera_event(struct woomera_event **event, event_args free_data) +{ + if (free_data) { + smg_free ((*event)->data); + } + + (*event)->data=NULL; + if (woomera_test_flag((*event), WFLAG_MALLOC)) { + smg_free (*event); + *event = NULL; + } +} + + +/* disable nagle's algorythm */ +static inline void no_nagle(int socket) +{ + int flag = 1; + setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); +} + + +static inline void remove_end_of_digits_char(unsigned char *s) +{ + unsigned char *p; + for (p = s; *p; p++) { + if (*p == 'F' || *p > 'f') { + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Removing a non-numeric character [%c]!\n", *p); + *p = '\0'; + break; + } + } +} + +static inline void validate_number(unsigned char *s) +{ + unsigned char *p; + for (p = s; *p; p++) { + if (*p < 48 || *p > 57) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Encountered a non-numeric character [%c]!\n", *p); + *p = '\0'; + break; + } + } +} + +static inline int woomera_check_running(struct woomera_interface *woomera) +{ + if (woomera_test_flag(woomera, WFLAG_HANGUP) || + !woomera_test_flag(woomera, WFLAG_RUNNING) || + woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + + return 0; + } + + return 1; +} + +static inline int open_span_chan (unsigned char span, unsigned char chan) +{ + int fd = -1; +#ifndef LIBSANGOMA_VERSION + fd = sangoma_open_tdmapi_span_chan(span, chan); +#else + if (chan == 24) { + pthread_mutex_lock(&server.process_table[span][chan].media_lock); + if(server.process_table[span][chan].media_used > 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical Error: channel already opened [s%ic%i]\n", span, chan); + } else { + server.process_table[span][chan].media_used++; + + fd = __sangoma_open_api_span_chan(span, chan); + } + pthread_mutex_unlock(&server.process_table[span][chan].media_lock); + } else { + fd = sangoma_open_api_span_chan(span, chan); + } +#endif + return fd; +} + +static inline void close_span_chan (int *socket, unsigned char span, unsigned char chan) +{ + if (chan == 24) { + pthread_mutex_lock(&server.process_table[span][chan].media_lock); + if(server.process_table[span][chan].media_used > 0) { + server.process_table[span][chan].media_used--; + } + close_socket(socket); + pthread_mutex_unlock(&server.process_table[span][chan].media_lock); + } else { + close_socket(socket); + } +} + +extern int smg_log_init(void); +extern void smg_log_cleanup(void); +extern int smg_ip_bridge_start(void); +extern int smg_ip_bridge_stop(void); +extern int waitfor_2sockets(int fda, int fdb, char *a, char *b, int timeout); +#endif + diff --git a/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.15.tmp b/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.15.tmp new file mode 100644 index 0000000..7e6e5dd --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.15.tmp @@ -0,0 +1,6489 @@ +/********************************************************************************* + * sangoma_mgd.c -- Sangoma Media Gateway Daemon for Sangoma/Wanpipe Cards + * + * Copyright 05-09, Nenad Corbic + * Anthony Minessale II + * + * This program is free software, distributed under the terms of + * the GNU General Public License + * + * ============================================= + * + * v1.68 David Yat Sin + * Mar 18 2010 + * Media and RING bits implemented + * + * v1.67 David Yat Sin + * Mar 16 2010 + * Added sanity check for span chan on call stopped and call answered + + * v1.66 David Yat Sin + * Mar 15 2010 + * Fix for WFLAG_SYSTEM_RESET being cleared if when + * no boost msg was received from signalling daemon + * + * v1.65 David Yat Sin + * Mar 10 2010 + * Support for TON and NPI passthrough + * + * v1.64 David Yat Sin + * Feb 22 2010 + * Updated to sigboost 103 + * Added checks for hwec present + * + * v1.63 Nenad Corbic + * Jan 27 2010 + * Enabled media pass through so that + * two woomera servers pass media directly. + * + * v1.62 Konrad Hammel + * Jan 26 2010 + * Added rbs relay code + * + * v1.61 Konrad Hammel + * Jan 19 2010 + * changed all_ckt_busy to be per trunk group. + * + * v1.60 Nenad Corbic + * Jan 14 2010 + * Added media sequencing option. + * Check if server has it enabled. + * + * v1.59 Nenad Corbic + * Changed w1g1 to s1c1 + * + * v1.58 Nenad Corbic + * Added bridge tdm to ip functionality + * + * v1.57 Nenad Corbic + * Support for woomera multiple profiles + * + * v1.56 David Yat Sin + * Changed BRI to run with HWEC in non-persist mode + * + * v1.55 Nenad Corbic + * Updated the base media port to 10000 and max media ports to 5000 + * + * v1.54 Nenad Corbic + * Bug added in 1.51 release causing call on channel 31 to fail. + * + * v1.53 Nenad Corbic + * Added progress message + * + * v1.52 David Yat Sin + * Changed sangoma_open_span_chan to __sangoma_span_chan + * to enabled shared used of file descriptors when using + * with PRI in NFAS mode + * + * v1.51 David Yat Sin + * MAX_SPANS increased to 32. + * Fix for server.process_table declared incorrectly + * + * v1.50 Nenad Corbic + * Logic to support multiple woomera clients hanging up the + * channel. This feature now supprorts woomera loadbalancing + * with extension check on the client end. Example, two Asterisk + * systems setup where Asterisk with correct extension gets the + * call. + * Changed log levels: Loglevel 1 = production + * Loglevel 2 = Boost TX/RX EVENTS + * Loglevel 3 = Woomera RX Messages + * Loglevel 4 = call setup debugging + * Loglevel 5 = extra debugging + * Loglevel 10 = full debugging + * + * v1.49 Nenad Corbic + * Removed tv from sigboost to make it binary compatible + * Updated release cause on double use return code. + * + * v1.48 Nenad Corbic + * Konrad Hammel + * Jun 30 2009 + * Added feature to disable/enable HWEC on channels that are + * passing a call with a "data" only transfer capability + * + * v1.47 Nenad Corbic + * Apr 30 2009 + * Updated hangup woomera events. Each event must + * be followed by a woomera error code 200=ok >299=no ok. + * This update fixes the chan_woomera socket write + * warnings. + * + * v1.46 Nenad Corbic + * Mar 27 2009 + * Major updates on socket handling. A bug was introducted + * when cmm and trunk was merged. + * Added configuration print in the logs. + * + * v1.45 Nenad Corbic + * Mar 19 2009 + * Outbound call timeout defaulted to 300 seconds. + * Timeout on answer instead of accept. + * + * v1.44 Nenad Corbic + * Mar 19 2009 + * Adjustable boost size. Added boost version check. + * + * v1.43 David Yat Sin + * Feb 25 2009 + * Merged CMM and Trunk + * + * v1.42 Nenad Corbic + * Jan 26 2008 + * Call Bearer Capability + * BugFix: Hangup NACK was sent out with invalid cause 0 + * + * v1.41 Nenad Corbic + * Jan 12 2008 + * Fixed the NACK with cause 0 bug. + * Added cause 19 on call timeout. + * Added configuration option call_timeout to timeout + * outbound calls. + * + * v1.40 Nenad Corbic + * Dec 10 2008 + * Check for Rx call overrun. + * In unlikely case sangoma_mgd goes out of + * sync with boost. + * + * v1.39 Nenad Corbic + * Sep 17 2008 + * Updated for Unit Test + * Bug fix in Loop Logic, possible race + * between media and woomera thread on loop end. + * + * v1.38 Nenad Corbic + * Sep 5 2008 + * Added a Double use of setup id logic. + * Currently double use call gets dropped. + * + * v1.37 Nenad Corbic + * Sep 2 2008 + * Bug fix in REMOVE LOOP logic continued + * The woomera->sock in loop logic was set to 0. + * Closing it failed the next call. + * + * v1.36 Nenad Corbic + * Aug 28 2008 + * Bug fix in REMOVE LOOP logic + * Remove F from incoming calls by default + * Check for errno on poll() + * Do not delay in closing socket on media down. + * + * v1.35cmm Nenad Corbic + * Jul 28 2008 + * Bug fixes on ACK Timeout and trunk release + * Added a thread logger + * Increased max spans to 32 + * + * v1.34cmm Nenad Corbic + * Jul 24 2008 + * Clean up the setup id on CALL STOP ACK + * + * v1.33cmm Nenad Corbic + * Jul 24 2008 + * The CALL STOP timeout function had a bug + * resulting in false blocking of tank ids + * due to STOP ACK Timeouts. + * + * v1.33 Nenad Corbic + * Jul 18 2008 + * Added UDP Sequencing to check for dropped frames + * Should only be used for debugging. + * + * v1.32 David Yat Sin + * Jul 17 2008 + * Support for d-channel incoming digit + * passthrough to Asterisk + * + * v1.32cmm Nenad Corbic + * Jun 03 2008 + * Implemented new Restart ACK Policy + * Split the Event packet into 2 types + * + * v1.31cmm Nenad Corbic + * Apr 04 2008 + * New CMM Restart procedure for boost + * Block TRUNK ID on ACK Timeout + * + * v1.31 David Yat Sin + * Apr 29 2008 + * Support for HW DTMF events. + * + * v1.30 Nenad Corbic + * Feb 08 2008 + * The fix in v1.26 causes double stop. + * I took out the v1.26 fix and added + * a big warning if that condition is + * ever reached. + * + * v1.29 Nenad Corbic + * Feb 07 2008 + * Added strip_cid_non_digits option + * + * v1.28 Nenad Corbic + * Feb 06 2008 + * Fixed a memory leak in clone event + * function. Added memory check subsystem + * + * v1.27 Nenad Corbic + * Jan 24 2008 + * Fixed a memory leak on incoming calls + * Removed the use of server listener which + * was not used + * + * v1.26 Nenad Corbic + * Jan 18 2008 + * Fixed hangup after invalid Answer or Ack Session + * Can cause double use of setup id - now fixed + * Update on autoacm on accept check for acked. + * + * v1.25 Nenad Corbic + * Dec 31 2007 + * Removed UDP Resync it can cause skb_over errors. + * Moved RDNIS message to higher debug level + * + * v1.24 Nenad Corbic + * Nov 30 2007 + * Bug fix on return code on ALL ckt busy + * + * v1.23 Nenad Corbic + * Bug fix on socket open. Check for retun code >= 0 + * + * v1.22 Nenad Corbic + * Nov 27 2007 + * Updated DTMF Tx function + * Fixed - dtmf tx without voice + * Fxied - dtmf clipping. + * + * v1.21 Nenad Corbic + * Nov 25 2007 + * Major unit testing of each state + * Numerous bug fixes for non autoacm mode. + * Changed "Channel-Name" to tg/cic + * Added compile option WANPIPE_CHAN_NAME to change Asterisk channel + * name of chan_woomera.so. So one can use Dial(SS7/g1/${EXTE}) + * instead of WOOMERA (for example) + * + * v1.20 Nenad Corbic + * Added option for Auto ACM response mode. + * + * v1.19 Nenad Corbic + * Configurable DTMF + * Bug fix in release codes (all ckt busy) + * + * v1.18 Nenad Corbic + * Added new rel cause support based on + * digits instead of strings. + * + * v1.17 Nenad Corbic + * Added session support + * + * v1.16 Nenad Corbic + * Added hwec disable on loop ccr + * + * v1.15 Nenad Corbic + * Updated DTMF Locking + * Added delay between digits + * + * v1.14 Nenad Corbic + * Updated DTMF Library + * Fixed DTMF synchronization + * + * v1.13 Nenad Corbic + * Woomera OPAL Dialect + * Added Congestion control + * Added SCTP + * Added priority ISUP queue + * Fixed presentation + * + * v1.12 Nenad Corbic + * Fixed CCR + * Removed socket shutdown on end call. + * Let Media thread shutodwn sockets. + * + * v1.11 Nenad Corbic + * Fixed Remote asterisk/woomera connection + * Increased socket timeouts + * + * v1.10 Nenad Corbic + * Added Woomera OPAL dialect. + * Start montor thread in priority + * + * v1.9 Nenad Corbic + * Added Loop mode for ccr + * Added remote debug enable + * Fixed syslog logging. + * + * v1.8 Nenad Corbic + * Added a ccr loop mode for each channel. + * Boost can set any channel in loop mode + * + * v1.7 Nenad Corbic + * Pass trunk group number to incoming call + * chan woomera will use it to append to context + * name. Added presentation feature. + * + * v1.6 Nenad Corbic + * Use only ALAW and MLAW not SLIN. + * This reduces the load quite a bit. + * Send out ALAW/ULAW format on HELLO message. + * RxTx Gain is done now in chan_woomera. + * + * v1.5 Nenad Corbic + * Bug fix in START_NACK_ACK handling. + * When we receive START_NACK we must alwasy pull tank before + * we send out NACK_ACK this way we will not try to send NACK + * ourself. + *********************************************************************************/ + +#include "sangoma_mgd.h" +#include "sangoma_mgd_memdbg.h" +#include "q931_cause.h" +#include "smg_capabilities.h" + +int pipe_fd[2]; + +#ifdef CODEC_LAW_DEFAULT +static uint32_t codec_sample=8; +#else +static uint32_t codec_sample=16; +#endif + +static char ps_progname[]="sangoma_mgd"; + +static struct woomera_interface woomera_dead_dev; + + +#ifdef BRI_PROT +static unsigned char tdmv_hwec_persist = 0; +#else +static unsigned char tdmv_hwec_persist = 1; +#endif +struct woomera_server server; + +struct smg_tdm_ip_bridge g_smg_ip_bridge_idx[MAX_SMG_BRIDGE]; +pthread_mutex_t g_smg_ip_bridge_lock; + +#if 0 +#define DOTRACE +#endif + + +#define SMG_VERSION "v1.68" + +/* enable early media */ +#if 1 +#define WOOMERA_EARLY_MEDIA 1 +#endif + + +#define SMG_DTMF_ON 60 +#define SMG_DTMF_OFF 10 +#define SMG_DTMF_RATE 8000 +#define SMG_DEFAULT_CALL_TIMEOUT 300 + +#define SANGOMA_USR_PERIOD 10 + +#if 0 +#define MEDIA_SOCK_SHUTDOWN 1 +#endif + +#ifdef DOTRACE +static int tc = 0; +#endif + +#if 0 +#warning "NENAD: HPTDM API" +#define WP_HPTDM_API 1 +#else +#undef WP_HPTDM_API +#endif + +#ifdef WP_HPTDM_API +hp_tdm_api_span_t *hptdmspan[WOOMERA_MAX_SPAN]; +#endif + +#if 0 +#define SMG_DROP_SEQ 1 +#warning "SMG Debug feature Drop Seq Enabled" +static int drop_seq=0; +#else +#undef SMG_DROP_SEQ +#endif + + +#if 0 +#define SMG_NO_MEDIA +#warning "SMG No Media Defined" +#else +#undef SMG_NO_MEDIA +#endif + +const char WELCOME_TEXT[] = +"================================================================================\n" +"Sangoma Media Gateway Daemon v1.68 \n" +"\n" +"TDM Signal Media Gateway for Sangoma/Wanpipe Cards\n" +"Copyright 2005, 2006, 2007 \n" +"Nenad Corbic , Anthony Minessale II \n" +"This program is free software, distributed under the terms of\n" +"the GNU General Public License\n" +"================================================================================\n" +""; + + + +static int coredump=1; +static int autoacm=0; + +/* FIXME: Should be done in cfg file */ +#if defined(BRI_PROT) +int max_spans=WOOMERA_BRI_MAX_SPAN; +int max_chans=WOOMERA_BRI_MAX_CHAN; +#else +/* PRI_PROT uses these defines as well */ +int max_spans=WOOMERA_MAX_SPAN; +int max_chans=WOOMERA_MAX_CHAN; +#endif + +static int launch_media_tdm_thread(struct woomera_interface *woomera); +static int launch_woomera_thread(struct woomera_interface *woomera); +static void woomera_check_digits (struct woomera_interface *woomera); +static struct woomera_interface *alloc_woomera(void); +static void handle_event_dtmf(struct woomera_interface *woomera, unsigned char dtmf_digit); +static int handle_event_rbs(struct woomera_interface *woomera, unsigned char rbs_digit); +static int handle_dequeue_and_woomera_tx_event_rbs(struct woomera_interface *woomera); + +q931_cause_to_str_array_t q931_cause_to_str_array[255]; +bearer_cap_to_str_array_t bearer_cap_to_str_array[255]; +uil1p_to_str_array_t uil1p_to_str_array[255]; + +static int isup_exec_command(int span, int chan, int id, int cmd, int cause) +{ + short_signal_event_t oevent; + int retry=5; + + call_signal_event_init((short_signal_event_t*)&oevent, cmd, chan, span); + oevent.release_cause = cause; + + if (id >= 0) { + oevent.call_setup_id = id; + } +isup_exec_cmd_retry: + if (call_signal_connection_write(&server.mcon, (call_signal_event_t*)&oevent) < 0){ + + --retry; + if (retry <= 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket: %s\n", + strerror(errno)); + return -1; + } else { + log_printf(SMG_LOG_ALL, server.log, + "System Warning: Failed to tx on ISUP socket: %s :retry %i\n", + strerror(errno),retry); + } + + goto isup_exec_cmd_retry; + } + + return 0; +} + +static int isup_exec_event(call_signal_event_t *event) +{ + int retry=5; + +isup_exec_cmd_retry: + if (call_signal_connection_write(&server.mcon, event) < 0){ + + --retry; + if (retry <= 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket: %s\n", + strerror(errno)); + return -1; + } else { + log_printf(SMG_LOG_ALL, server.log, + "System Warning: Failed to tx on ISUP socket: %s :retry %i\n", + strerror(errno),retry); + } + + goto isup_exec_cmd_retry; + } + + return 0; +} + + +static int isup_exec_commandp(int span, int chan, int id, int cmd, int cause) +{ + short_signal_event_t oevent; + int retry=5; + + call_signal_event_init(&oevent, cmd, chan, span); + oevent.release_cause = cause; + + if (id >= 0) { + oevent.call_setup_id = id; + } +isup_exec_cmd_retry: + if (call_signal_connection_writep(&server.mconp, (call_signal_event_t*)&oevent) < 0){ + + --retry; + if (retry <= 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket: %s\n", + strerror(errno)); + return -1; + } else { + log_printf(SMG_LOG_ALL, server.log, + "System Warning: Failed to tx on ISUP socket: %s :retry %i\n", + strerror(errno),retry); + } + + goto isup_exec_cmd_retry; + } + + return 0; +} + + +static int get_span_chan_from_interface(char* interface, int *span_ptr, int *chan_ptr) +{ + int span, chan; + int err; + + err=sscanf(interface, "s%dc%d", &span, &chan); + if (err!=2) { + err=sscanf(interface, "w%dg%d", &span, &chan); + } + + if (err==2) { + if (smg_validate_span_chan(span,chan) != 0) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "WOOMERA Warning invalid span chan in interface %s\n", + interface); + return -1; + } + + *span_ptr = span; + *chan_ptr = chan; + return 0; + } + log_printf(SMG_LOG_ALL, server.log, "ERROR: Failed to get span chan from interface:[%s]\n", interface, span, chan); + return -1; +} + +static int socket_printf(int socket, char *fmt, ...) +{ + char *data; + int ret = 0; + va_list ap; + + if (socket < 0) { + return -1; + } + + va_start(ap, fmt); +#ifdef SOLARIS + data = (char *) smg_malloc(2048); + vsnprintf(data, 2048, fmt, ap); +#else + ret = vasprintf(&data, fmt, ap); +#endif + va_end(ap); + if (ret == -1) { + fprintf(stderr, "Memory Error\n"); + log_printf(SMG_LOG_ALL, server.log, "Crtical ERROR: Memory Error!\n"); + } else { + int err; + int len = strlen(data); + err=send(socket, data, strlen(data), 0); + if (err != strlen(data)) { + log_printf(SMG_LOG_DEBUG_8, server.log, "ERROR: Failed to send data to woomera socket(%i): err=%i len=%d %s\n", + socket,err,len,strerror(errno)); + ret = err; + } else { + ret = 0; + } + + free(data); + } + + return ret; +} + + + +static int woomera_next_pair(struct woomera_config *cfg, char **var, char **val) +{ + int ret = 0; + char *p, *end; + + *var = *val = NULL; + + for(;;) { + cfg->lineno++; + + if (!fgets(cfg->buf, sizeof(cfg->buf), cfg->file)) { + ret = 0; + break; + } + + *var = cfg->buf; + + if (**var == '[' && (end = strchr(*var, ']'))) { + *end = '\0'; + (*var)++; + strncpy(cfg->category, *var, sizeof(cfg->category) - 1); + continue; + } + + if (**var == '#' || **var == '\n' || **var == '\r') { + continue; + } + + if ((end = strchr(*var, '#'))) { + *end = '\0'; + end--; + } else if ((end = strchr(*var, '\n'))) { + if (*end - 1 == '\r') { + end--; + } + *end = '\0'; + } + + p = *var; + while ((*p == ' ' || *p == '\t') && p != end) { + *p = '\0'; + p++; + } + *var = p; + + if (!(*val = strchr(*var, '='))) { + ret = -1; + log_printf(SMG_LOG_ALL, 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; + +} + + +#if 0 +static void woomera_set_span_chan(struct woomera_interface *woomera, int span, int chan) +{ + pthread_mutex_lock(&woomera->vlock); + woomera->span = span; + woomera->chan = chan; + pthread_mutex_unlock(&woomera->vlock); + +} +#endif + + +static struct woomera_event *new_woomera_event_printf(struct woomera_event *ebuf, char *fmt, ...) +{ + struct woomera_event *event = NULL; + int ret = 0; + va_list ap; + + if (ebuf) { + event = ebuf; + } else if (!(event = new_woomera_event())) { + log_printf(SMG_LOG_ALL, server.log, "Memory Error queuing event!\n"); + return NULL; + } else { + return NULL; + } + + va_start(ap, fmt); +#ifdef SOLARIS + event->data = (char *) smg_malloc(2048); + vsnprintf(event->data, 2048, fmt, ap); +#else + ret = smg_vasprintf(&event->data, fmt, ap); +#endif + va_end(ap); + if (ret == -1) { + log_printf(SMG_LOG_ALL, server.log, "Memory Error queuing event!\n"); + destroy_woomera_event(&event, EVENT_FREE_DATA); + return NULL; + } + + return event; + +} + +static struct woomera_event *woomera_clone_event(struct woomera_event *event) +{ + struct woomera_event *clone; + + if (!(clone = new_woomera_event())) { + return NULL; + } + + /* This overwrites the MALLOC in clone causing a memory leak */ + memcpy(clone, event, sizeof(*event)); + + /* We must set the malloc flag back so that this event + * will be deleted */ + _woomera_set_flag(clone, WFLAG_MALLOC); + clone->next = NULL; + clone->data = smg_strdup(event->data); + + return clone; +} + +static void enqueue_event(struct woomera_interface *woomera, + struct woomera_event *event, + event_args free_data) +{ + struct woomera_event *ptr, *clone = NULL; + + assert(woomera != NULL); + assert(event != NULL); + + if (!(clone = woomera_clone_event(event))) { + log_printf(SMG_LOG_ALL, server.log, "Error Cloning Event\n"); + return; + } + + pthread_mutex_lock(&woomera->queue_lock); + + for (ptr = woomera->event_queue; ptr && ptr->next ; ptr = ptr->next); + + if (ptr) { + ptr->next = clone; + } else { + woomera->event_queue = clone; + } + + pthread_mutex_unlock(&woomera->queue_lock); + + woomera_set_flag(woomera, WFLAG_EVENT); + + if (free_data && event->data) { + /* The event has been duplicated, the original data + * should be freed */ + smg_free(event->data); + event->data=NULL; + } +} + +static char *dequeue_event(struct woomera_interface *woomera) +{ + struct woomera_event *event; + char *data = NULL; + + if (!woomera) { + return NULL; + } + + pthread_mutex_lock(&woomera->queue_lock); + if (woomera->event_queue) { + event = woomera->event_queue; + woomera->event_queue = event->next; + data = event->data; + pthread_mutex_unlock(&woomera->queue_lock); + + destroy_woomera_event(&event, EVENT_KEEP_DATA); + return data; + } + pthread_mutex_unlock(&woomera->queue_lock); + + return data; +} + + +static int enqueue_event_on_listeners(struct woomera_event *event) +{ + struct woomera_listener *ptr; + int x = 0; + + assert(event != NULL); + + pthread_mutex_lock(&server.listen_lock); + for (ptr = server.listeners ; ptr ; ptr = ptr->next) { + enqueue_event(ptr->woomera, event, EVENT_KEEP_DATA); + x++; + } + pthread_mutex_unlock(&server.listen_lock); + + return x; +} + + +static void del_listener(struct woomera_interface *woomera) +{ + struct woomera_listener *ptr, *last = NULL; + + pthread_mutex_lock(&server.listen_lock); + for (ptr = server.listeners ; ptr ; ptr = ptr->next) { + if (ptr->woomera == woomera) { + if (last) { + last->next = ptr->next; + } else { + server.listeners = ptr->next; + } + smg_free(ptr); + break; + } + last = ptr; + } + pthread_mutex_unlock(&server.listen_lock); +} + +static void add_listener(struct woomera_interface *woomera) +{ + struct woomera_listener *new; + + pthread_mutex_lock(&server.listen_lock); + + if ((new = smg_malloc(sizeof(*new)))) { + memset(new, 0, sizeof(*new)); + new->woomera = woomera; + new->next = server.listeners; + server.listeners = new; + } else { + log_printf(SMG_LOG_ALL, server.log, "Memory Error adding listener!\n"); + } + + pthread_mutex_unlock(&server.listen_lock); +} + +static int wanpipe_send_rbs(struct woomera_interface *woomera, char *digits) +{ + struct media_session *ms = woomera_get_ms(woomera); + int err; + wanpipe_tdm_api_t tdm_api; + unsigned int digit; + + memset(&tdm_api,0,sizeof(tdm_api)); + + if (!ms) { + log_printf(SMG_LOG_PROD, server.log, "[%s]: wanpipe_send_rbs (%X) Error no ms\n", + woomera->interface,digit); + return -EINVAL; + } + + if ((woomera->span+1) <= 0 || !server.rbs_relay[woomera->span+1]) { + log_printf(SMG_LOG_PROD, server.log, "[%s]: wanpipe_send_rbs (%X) Error rbs relay not enabled on span %i\n", + woomera->interface,digit,woomera->span+1); + return -EINVAL; + } + + err=sscanf(digits,"%x",&digit); + if (err == 1) { + log_printf(SMG_LOG_PROD, server.log, "[%s]: Transmitting RBS 0x%X\n", + woomera->interface,digit); + +#ifdef LIBSANGOMA_VERSION + err=sangoma_tdm_write_rbs(ms->sangoma_sock, &tdm_api, woomera->chan+1, digit); +#else + err=sangoma_tdm_write_rbs(ms->sangoma_sock, &tdm_api, digit); +#endif + } + + return err; +} + + +static int wanpipe_send_dtmf(struct woomera_interface *woomera, char *digits) +{ + struct media_session *ms = woomera_get_ms(woomera); + char *cur = NULL; + int wrote = 0; + int err; + + if (!ms) { + return -EINVAL; + } + + if (!ms->dtmf_buffer) { + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Allocate DTMF Buffer...."); + + err=switch_buffer_create_dynamic(&ms->dtmf_buffer, 1024, server.dtmf_size, 0); + + if (err != 0) { + log_printf(SMG_LOG_ALL, woomera->log, "Failed to allocate DTMF Buffer!\n"); + return -ENOMEM; + } else { + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "SUCCESS!\n"); + } + + } + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Sending DTMF %s\n",digits); + for (cur = digits; *cur; cur++) { + if ((wrote = teletone_mux_tones(&ms->tone_session, + &ms->tone_session.TONES[(int)*cur]))) { + + pthread_mutex_lock(&woomera->dtmf_lock); + + err=switch_buffer_write(ms->dtmf_buffer, ms->tone_session.buffer, wrote * 2); + + pthread_mutex_unlock(&woomera->dtmf_lock); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Sending DTMF %s Wrote=%i (err=%i)\n", + digits,wrote*2,err); + } else { + log_printf(SMG_LOG_ALL, woomera->log, "Error: Sending DTMF %s (err=%i)\n", + digits,wrote); + } + } + + ms->skip_read_frames = 200; + return 0; +} + +static struct woomera_interface *alloc_woomera(void) +{ + struct woomera_interface *woomera = NULL; + + if ((woomera = smg_malloc(sizeof(struct woomera_interface)))) { + + memset(woomera, 0, sizeof(struct woomera_interface)); + + woomera->chan = -1; + woomera->span = -1; + woomera->log = server.log; + woomera->debug = server.debug; + woomera->call_id = 1; + woomera->event_queue = NULL; + woomera->q931_rel_cause_topbx=SIGBOOST_RELEASE_CAUSE_NORMAL; + woomera->q931_rel_cause_tosig=SIGBOOST_RELEASE_CAUSE_NORMAL; + + woomera_set_interface(woomera, "w-1g-1"); + + + + } + + return woomera; + +} + + +static struct woomera_interface *new_woomera_interface(int socket, struct sockaddr_in *sock_addr, int len) +{ + struct woomera_interface *woomera = NULL; + + if (socket < 0) { + log_printf(SMG_LOG_ALL, server.log, "Critical: Invalid Socket on new interface!\n"); + return NULL; + } + + if ((woomera = alloc_woomera())) { + if (socket >= 0) { + no_nagle(socket); + woomera->socket = socket; + } + + if (sock_addr && len) { + memcpy(&woomera->addr, sock_addr, len); + } + } + + return woomera; + +} + +static char *woomera_message_header(struct woomera_message *wmsg, char *key) +{ + int x = 0; + char *value = NULL; + + for (x = 0 ; x < wmsg->last ; x++) { + if (!strcasecmp(wmsg->names[x], key)) { + value = wmsg->values[x]; + break; + } + } + + return value; +} + + +#define SMG_DECODE_POLL_ERRORS(err) \ + (err & POLLERR) ? "POLLERR" : \ + (err & POLLHUP) ? "POLLHUP" : \ + (err & POLLNVAL) ? "POLLNVAL" : "UNKNOWN" + +static int waitfor_socket(int fd, int timeout, int flags, int *flags_out) +{ + struct pollfd pfds[1]; + int res; + int errflags = (POLLERR | POLLHUP | POLLNVAL); + +waitfor_socket_tryagain: + + memset(&pfds[0], 0, sizeof(pfds[0])); + + pfds[0].fd = fd; + pfds[0].events = flags | errflags; + + res = poll(pfds, 1, timeout); + + if (res == 0) { + return res; + } + + if (res > 0) { + res=-1; + *flags_out = pfds[0].revents; + if (pfds[0].revents & errflags) { + res=-1; +#if 0 + log_printf(SMG_LOG_DEBUG_10,server.log, "Wait for socket Error in revents 0x%X %s Error=%s!\n", + pfds[0].revents,strerror(errno),SMG_DECODE_POLL_ERRORS(pfds[0].revents)); +#endif + return res; + } else { + if (pfds[0].revents & flags) { + res=1; + } else { + log_printf(SMG_LOG_ALL,server.log, "Wait for socket invalid poll event in revents 0x%X Error=%s Errno=%s !\n", + pfds[0].revents,SMG_DECODE_POLL_ERRORS(pfds[0].revents),strerror(errno)); + } + } + } else { + if ((errno == EINTR || errno == EAGAIN)) { + goto waitfor_socket_tryagain; + } + log_printf(SMG_LOG_ALL,server.log, "Wait for socket error!\n"); + } + + return res; +} + + +int waitfor_2sockets(int fda, int fdb, char *a, char *b, int timeout) +{ + struct pollfd pfds[2]; + int res = 0; + int errflags = (POLLERR | POLLHUP | POLLNVAL); + + if (fda < 0 || fdb < 0) { + return -1; + } + + +waitfor_2sockets_tryagain: + + *a=0; + *b=0; + + + memset(pfds, 0, sizeof(pfds)); + + pfds[0].fd = fda; + pfds[1].fd = fdb; + pfds[0].events = POLLIN | errflags; + pfds[1].events = POLLIN | errflags; + + res = poll(pfds, 2, timeout); + + if (res > 0) { + res = 1; + if ((pfds[0].revents & errflags) || (pfds[1].revents & errflags)) { + res = -1; + } else { + if ((pfds[0].revents & POLLIN)) { + *a=1; + res++; + } + if ((pfds[1].revents & POLLIN)) { + *b=1; + res++; + } + } + + if (res == 1) { + /* No event found what to do */ + res=-1; + } + } else if (res < 0) { + + if (errno == EINTR || errno == EAGAIN) { + goto waitfor_2sockets_tryagain; + } + + } + + return res; +} + +static int woomera_raw_to_ip(struct media_session *ms, char *raw) +{ + int x; + char *p; + + for(x = 0; x < strlen(raw) ; x++) { + if (raw[x] == ':') { + break; + } + if (raw[x] == '/') { + break; + } + } + + media_set_raw(ms,raw); + + if (ms->ip) { + smg_free(ms->ip); + } + ms->ip = smg_strndup(raw, x); + p = raw + (x+1); + ms->port = atoi(p); + + return 0; +} + +static struct media_session *media_session_new(struct woomera_interface *woomera) +{ + struct media_session *ms = NULL; + int span,chan; + + span=woomera->span; + chan=woomera->chan; + + log_printf(SMG_LOG_DEBUG_CALL, server.log,"Starting new MEDIA session [%s] [%s]\n", + woomera->interface,woomera->raw?woomera->raw:"N/A"); + + if ((ms = smg_malloc(sizeof(struct media_session)))) { + memset(ms, 0, sizeof(struct media_session)); + + if (woomera->loop_tdm != 1) { + + woomera_raw_to_ip(ms,woomera->raw); + time(&ms->started); + + } + + time(&ms->started); + woomera_set_ms(woomera,ms); + ms->woomera = woomera; + + /* Setup artificial DTMF stuff */ + memset(&ms->tone_session, 0, sizeof(ms->tone_session)); + if (teletone_init_session(&ms->tone_session, 0, NULL, NULL)) { + log_printf(SMG_LOG_ALL, server.log, "ERROR: Failed to initialize TONE [s%ic%i]!\n", + span+1,chan+1); + } + + ms->tone_session.rate = SMG_DTMF_RATE; + ms->tone_session.duration = server.dtmf_on * (ms->tone_session.rate / 1000); + ms->tone_session.wait = server.dtmf_off * (ms->tone_session.rate / 1000); + + teletone_dtmf_detect_init (&ms->dtmf_detect, SMG_DTMF_RATE); + + } else { + log_printf(SMG_LOG_ALL, server.log, "ERROR: Memory Alloc Failed [s%ic%i]!\n", + span+1,chan+1); + } + + return ms; +} + +static void media_session_free(struct media_session *ms) +{ + if (ms->ip) { + smg_free(ms->ip); + } + if (ms->raw) { + smg_free(ms->raw); + } + + teletone_destroy_session(&ms->tone_session); + switch_buffer_destroy(&ms->dtmf_buffer); + + ms->woomera = NULL; + + smg_free(ms); +} + +static int update_udp_socket(struct media_session *ms, char *ip, int port) +{ + struct hostent *result; + char buf[512]; + int err = 0; + + memset(&ms->remote_hp, 0, sizeof(ms->remote_hp)); + gethostbyname_r(ip, &ms->remote_hp, buf, sizeof(buf), &result, &err); + ms->remote_addr.sin_family = ms->remote_hp.h_addrtype; + memcpy((char *) &ms->remote_addr.sin_addr.s_addr, ms->remote_hp.h_addr_list[0], ms->remote_hp.h_length); + ms->remote_addr.sin_port = htons(port); + + return 0; +} + +static int create_udp_socket(struct media_session *ms, 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; + + log_printf(SMG_LOG_DEBUG_9,server.log,"LocalIP %s:%d IP %s:%d \n",local_ip, local_port, ip, port); + + memset(&ms->remote_hp, 0, sizeof(ms->remote_hp)); + memset(&ms->local_hp, 0, sizeof(ms->local_hp)); + if ((ms->socket = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) { + gethostbyname_r(ip, &ms->remote_hp, buf, sizeof(buf), &result, &err); + gethostbyname_r(local_ip, &ms->local_hp, local_buf, sizeof(local_buf), &local_result, &err); + if (result && local_result) { + ms->remote_addr.sin_family = ms->remote_hp.h_addrtype; + memcpy((char *) &ms->remote_addr.sin_addr.s_addr, ms->remote_hp.h_addr_list[0], ms->remote_hp.h_length); + ms->remote_addr.sin_port = htons(port); + + ms->local_addr.sin_family = ms->local_hp.h_addrtype; + memcpy((char *) &ms->local_addr.sin_addr.s_addr, ms->local_hp.h_addr_list[0], ms->local_hp.h_length); + ms->local_addr.sin_port = htons(local_port); + + rc = bind(ms->socket, (struct sockaddr *) &ms->local_addr, sizeof(ms->local_addr)); + if (rc < 0) { + close(ms->socket); + ms->socket = -1; + + log_printf(SMG_LOG_DEBUG_9,server.log, + "Failed to bind LocalIP %s:%d IP %s:%d (%s)\n", + local_ip, local_port, ip, port,strerror(errno)); + } + + /* OK */ + + } else { + log_printf(SMG_LOG_ALL,server.log, + "Failed to get hostbyname LocalIP %s:%d IP %s:%d (%s)\n", + local_ip, local_port, ip, port,strerror(errno)); + } + } else { + log_printf(SMG_LOG_ALL,server.log, + "Failed to create/allocate UDP socket\n"); + } + + return ms->socket; +} + +static int next_media_port(void) +{ + int port; + + pthread_mutex_lock(&server.media_udp_port_lock); + port = ++server.next_media_port; + if (port > server.max_media_port) { + server.next_media_port = server.base_media_port; + port = server.base_media_port; + } + pthread_mutex_unlock(&server.media_udp_port_lock); + + return port; +} + + + +static int woomera_dtmf_transmit(struct media_session *ms, int mtu) +{ + struct woomera_interface *woomera = ms->woomera; + int bread; + unsigned char dtmf[1024]; + unsigned char dtmf_law[1024]; + sangoma_api_hdr_t hdrframe; + int i; + int slin_len = mtu*2; + short *data; + int used; + int res; + int err; + int txdtmf=0; + int flags_out; + memset(&hdrframe,0,sizeof(hdrframe)); + + if (!ms->dtmf_buffer) { + return -1; + } + + for (;;) { + + if ((used=switch_buffer_inuse(ms->dtmf_buffer)) <= 0) { + break; + } + + res = waitfor_socket(ms->sangoma_sock, -1, POLLOUT, &flags_out); + if (res <= 0) { + break; + } + +#ifdef CODEC_LAW_DEFAULT + + pthread_mutex_lock(&woomera->dtmf_lock); + if ((used=switch_buffer_inuse(ms->dtmf_buffer)) <= 0) { + pthread_mutex_unlock(&woomera->dtmf_lock); + break; + } + + bread = switch_buffer_read(ms->dtmf_buffer, dtmf, slin_len); + pthread_mutex_unlock(&woomera->dtmf_lock); + + if (bread <= 0) { + break; + } + + log_printf(SMG_LOG_DEBUG_MISC,woomera->log,"%s: Write DTMF Got %d bytes MTU=%i Coding=%i Used=%i\n", + woomera->interface,bread,mtu,ms->hw_coding,used); + + data=(short*)dtmf; + for (i=0;ihw_coding) { + /* ALAW */ + dtmf_law[i] = linear_to_alaw((int)data[i]); + } else { + /* ULAW */ + dtmf_law[i] = linear_to_ulaw((int)data[i]); + } + } + + err=sangoma_sendmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + dtmf_law, mtu, 0); + + if (err != mtu) { + log_printf(SMG_LOG_ALL, woomera->log, "Error: Failed to TX to TDM API on DTMF (err=%i mtu=%i)!\n",err,mtu); + } + + txdtmf++; + ms->skip_write_frames++; +#else +... + pthread_mutex_lock(&woomera->dtmf_lock); + bread = switch_buffer_read(ms->dtmf_buffer, dtmf, mtu); + pthread_mutex_unlock(&woomera->dtmf_lock); + + log_printf(SMG_LOG_DEBUG_MISC,woomera->log,"%s: Write DTMF Got %d bytes\n", + woomera->interface,bread); + + sangoma_sendmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + dtmf, mtu, 0); + txdtmf++; + ms->skip_write_frames++; +#endif + + } + + if (txdtmf) { + return 0; + } else { + return -1; + } +} + +static void media_loop_run(struct media_session *ms) +{ + struct woomera_interface *woomera = ms->woomera; + int sangoma_frame_len = 160; + int errs=0; + int res=0; + wanpipe_tdm_api_t tdm_api; + unsigned char circuit_frame[1024]; + char filename[100]; + FILE *filed=NULL; + int loops=0,flags_out=0; + int open_cnt = 0; + + open_cnt=0; + + sangoma_api_hdr_t hdrframe; + memset(&hdrframe,0,sizeof(hdrframe)); + memset(circuit_frame,0,sizeof(circuit_frame)); + +retry_loop: + ms->sangoma_sock = open_span_chan(woomera->span+1, woomera->chan+1); + + log_printf(SMG_LOG_PROD, server.log, "Media Loop Started %s fd=%i\n", + woomera->interface,ms->sangoma_sock); + + if (ms->sangoma_sock < 0) { + errs++; + if (errs < 5) { + usleep(500000); + goto retry_loop; + } + log_printf(SMG_LOG_ALL, server.log, "WANPIPE MEDIA Socket Error (%s) if=[%s] [s%ic%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); + + } else { + errs=0; + + if (sangoma_tdm_set_codec(ms->sangoma_sock, &tdm_api, WP_NONE) < 0 ) { + errs++; + } + + if (sangoma_tdm_flush_bufs(ms->sangoma_sock, &tdm_api)) { + errs++; + } + + if (sangoma_tdm_set_usr_period(ms->sangoma_sock, &tdm_api, 20) < 0 ) { + errs++; + } + + sangoma_frame_len = sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); + +#ifdef LIBSANGOMA_VERSION + ms->has_hwec = sangoma_tdm_get_hw_ec(ms->sangoma_sock, &tdm_api); + if (ms->has_hwec) { + sangoma_tdm_disable_hwec(ms->sangoma_sock,&tdm_api); + } +#else + ms->has_hwec=1; + sangoma_tdm_disable_hwec(ms->sangoma_sock,&tdm_api); +#endif + ms->oob_disable = 0; +#ifdef LIBSANGOMA_VERSION + open_cnt = sangoma_get_open_cnt(ms->sangoma_sock, &tdm_api); + if (open_cnt > 1) { + ms->oob_disable = 1; + } +#endif + } + + if (errs) { + log_printf(SMG_LOG_ALL, server.log, "Media Loop: failed to open tdm device %s\n", + woomera->interface); + return; + } + + if (server.loop_trace) { + sprintf(filename,"/smg/s%ic%i-loop.trace",woomera->span+1,woomera->chan+1); + unlink(filename); + filed = safe_fopen(filename, "w"); + } + + while ( woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + !woomera_test_flag(woomera, WFLAG_MEDIA_END) && + ((res = waitfor_socket(ms->sangoma_sock, 1000, POLLIN, &flags_out)) >= 0)) { + + if (res == SMG_SOCKET_EVENT_TIMEOUT) { + //log_printf(SMG_LOG_DEBUG_8, server.log, "%s: TDM UDP Timeout !!!\n", + // woomera->interface); + /* NENAD Timeout thus just continue */ + continue; + } + + res = sangoma_readmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + circuit_frame, + sizeof(circuit_frame), 0); + if (res < 0) { + log_printf(SMG_LOG_ALL, server.log, "TDM Loop ReadMsg Error: %s\n", + strerror(errno), woomera->interface); + break; + } + + if (server.loop_trace && filed != NULL) { + int i; + for (i=0;isangoma_sock, + &hdrframe, + sizeof(hdrframe), + circuit_frame, + res, 0); + + res=0; + + loops++; + } + + + if (res < 0) { + if (!woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + log_printf(SMG_LOG_ALL, server.log, "Media Loop: socket error %s (fd=%i) %s\n", + woomera->interface, ms->sangoma_sock, strerror(errno)); + } + } + + + if (server.loop_trace && filed != NULL) { + fclose(filed); + } + + if (ms->has_hwec) { + sangoma_tdm_enable_hwec(ms->sangoma_sock,&tdm_api); + } + + close_span_chan(&ms->sangoma_sock, woomera->span+1, woomera->chan+1); + + if (loops < 1) { + log_printf(SMG_LOG_ALL, server.log, "Media Loop FAILED %s Master=%i MediaEnd=%i Loops=%i\n", + woomera->interface, + woomera_test_flag(&server.master_connection, WFLAG_RUNNING), + woomera_test_flag(woomera, WFLAG_MEDIA_END),loops); + } else { + log_printf(SMG_LOG_PROD, server.log, "Media Loop PASSED %s Master=%i MediaEnd=%i Loops=%i\n", + woomera->interface, + woomera_test_flag(&server.master_connection, WFLAG_RUNNING), + woomera_test_flag(woomera, WFLAG_MEDIA_END),loops); + } + + return; + +} + + + + +#ifdef WP_HPTDM_API +static int media_rx_ready(void *p, unsigned char *data, int len) +{ + struct media_session *ms = (struct media_session *)p; + + if (ms->udp_sock < 0) { + return -1; + } + + return sendto(ms->udp_sock, + data,len, 0, + (struct sockaddr *) &ms->remote_addr, + sizeof(ms->remote_addr)); + + +} +#endif + +static void *media_thread_run(void *obj) +{ + struct media_session *ms = obj; + struct woomera_interface *woomera = ms->woomera; + int sangoma_frame_len = 160; + int local_port, x = 0, errs = 0, res = 0, packet_len = 0; + //int udp_cnt=0; + struct woomera_event wevent; + wanpipe_tdm_api_t tdm_api; + FILE *tx_fd=NULL; + int sock_timeout=200; + + int hwec_enabled=0, hwec_reenable=0; + + int open_cnt = 0; + + open_cnt=0; + + if (woomera_test_flag(woomera, WFLAG_MEDIA_END) || + !woomera->interface || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "MEDIA session for [%s] Cancelled! (ptr=%p)\n", + woomera->interface,woomera); + /* In this case the call will be closed via woomera_thread_run + * function. And the process table will be cleard there */ + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_MEDIA_RUNNING); + media_session_free(ms); + pthread_exit(NULL); + return NULL; + } + + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA session for [%s] started (ptr=%p loop=%i)\n", + woomera->interface,woomera,woomera->loop_tdm); + + if (woomera->loop_tdm) { + media_loop_run(ms); + ms->udp_sock=-1; + woomera_set_flag(woomera, WFLAG_HANGUP); + woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + goto media_thread_exit; + } + + for(x = 0; x < 1000 ; x++) { + local_port = next_media_port(); + if ((ms->udp_sock = create_udp_socket(ms, server.media_ip, local_port, ms->ip, ms->port)) > -1) { + break; + } + } + + /* Normal Temporary Failure */ + woomera->q931_rel_cause_topbx = 41; + + if (ms->udp_sock < 0) { + log_printf(SMG_LOG_ALL, server.log, "UDP Socket Error (%s) [%s] LocalPort=%d\n", + strerror(errno), woomera->interface, local_port); + + errs++; + } else { + int media_retry_cnt=0; +#ifdef WP_HPTDM_API + hp_tdm_api_span_t *span=hptdmspan[woomera->span+1]; + if (!span || !span->init) { + errs++; + } else { + hp_tdm_api_usr_callback_t usr_callback; + memset(&usr_callback,0,sizeof(usr_callback)); + usr_callback.p = ms; + usr_callback.rx_avail = media_rx_ready; + if (span->open_chan(span, &usr_callback, &ms->tdmchan,woomera->chan+1)) { + errs++; + /* Switch Congestion */ + woomera->q931_rel_cause_topbx = 42; + } + } +#else +media_retry: + ms->sangoma_sock = open_span_chan(woomera->span+1, woomera->chan+1); + if (ms->sangoma_sock < 0) { + + if (!woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + media_retry_cnt++; + if (media_retry_cnt < 5) { + log_printf(SMG_LOG_ALL, server.log, "WANPIPE Socket Retry [s%ic%i]\n", + woomera->span+1, woomera->chan+1); + usleep(100000); + goto media_retry; + } + + log_printf(SMG_LOG_ALL, server.log, "WANPIPE Socket Error (%s) if=[%s] [s%ic%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); + + /* Switch Congestion */ + woomera->q931_rel_cause_topbx = 42; + } + + errs++; + } else { + +# ifdef CODEC_LAW_DEFAULT + if (sangoma_tdm_set_codec(ms->sangoma_sock, &tdm_api, WP_NONE) < 0 ) { + errs++; + } +# else + if (sangoma_tdm_set_codec(ms->sangoma_sock, &tdm_api, WP_SLINEAR) < 0 ) { + errs++; + } +# endif + if (sangoma_tdm_flush_bufs(ms->sangoma_sock, &tdm_api)) { + errs++; + } + + if (sangoma_tdm_set_usr_period(ms->sangoma_sock, &tdm_api, SANGOMA_USR_PERIOD) < 0 ) { + errs++; + } + +#ifdef LIBSANGOMA_VERSION + ms->has_hwec = sangoma_tdm_get_hw_ec(ms->sangoma_sock, &tdm_api); +#else + ms->has_hwec = 1; +#endif + +# ifdef CODEC_LAW_DEFAULT +# ifdef LIBSANGOMA_GET_HWCODING + ms->hw_coding=sangoma_tdm_get_hw_coding(ms->sangoma_sock, &tdm_api); + if (ms->hw_coding < 0) { + errs++; + } +# else +# error "libsangoma missing hwcoding feature: not up to date!" +# endif +# endif + + +# ifdef LIBSANGOMA_GET_HWDTMF + + if (ms->has_hwec) { + ms->hw_dtmf=sangoma_tdm_get_hw_dtmf(ms->sangoma_sock, &tdm_api); + if (ms->hw_dtmf) { + log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Supported [s%ic%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); + } else { + log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Not Supported [s%ic%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); + } + } + +#else + ms->hw_dtmf=0; + log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Not Supported [s%ic%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); +# warning "libsangoma missing hwdtmf feature: not up to date!" + +#endif + + ms->oob_disable = 0; +#ifdef LIBSANGOMA_VERSION + open_cnt = sangoma_get_open_cnt(ms->sangoma_sock, &tdm_api); + if (open_cnt > 1) { + ms->oob_disable = 1; + } +#endif + + + if (ms->has_hwec) { + if (!tdmv_hwec_persist) { + // BRI cards start with HWEC in bypass disable state + if (bearer_cap_is_audio(woomera->bearer_cap)) { + int err; + err=sangoma_tdm_enable_hwec(ms->sangoma_sock, &tdm_api); + if (err == 0) { + hwec_enabled=1; + log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] Enabling hwec Ok\n",woomera->interface); + } else { + log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] Enabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + } + } + } else { + if (!bearer_cap_is_audio(woomera->bearer_cap)) { + int err; + err=sangoma_tdm_disable_hwec(ms->sangoma_sock, &tdm_api); + if (err == 0) { + hwec_reenable=1; + log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] Disabling hwec Ok\n",woomera->interface); + } else { + log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] Disabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + } + } + } + } + + sangoma_frame_len = sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); + + if (server.rbs_relay[woomera->span+1]) { + sangoma_tdm_enable_rbs_events(ms->sangoma_sock, &tdm_api, 20); + } + } +#endif + } + + + +#ifdef WP_HPTDM_API + /* No tdm thread */ +#else +#ifndef SMG_NO_MEDIA + if (!errs && + launch_media_tdm_thread(woomera)) { + errs++; + } +#endif +#endif + + if (errs) { + + woomera->q931_rel_cause_topbx = 42; + + } else { + + unsigned char udp_frame[4096]; + unsigned int fromlen = sizeof(struct sockaddr_in); + int flags_out=0; + int rc; + unsigned int carrier=0,carrier_diff=0; + + sangoma_api_hdr_t hdrframe; + memset(&hdrframe,0,sizeof(hdrframe)); + memset(udp_frame,0,sizeof(udp_frame)); +#ifdef DOTRACE + int fdin, fdout; + char path_in[512], path_out[512]; +#endif + + new_woomera_event_printf(&wevent, + "EVENT MEDIA %s AUDIO%s" + "Unique-Call-Id: %s%s" + "Raw-Audio: %s:%d%s" + "Call-ID: %s%s" + "Raw-Format: PCM-16%s" + "DTMF: %s%s" + , + woomera->interface, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + server.media_ip, + local_port, + WOOMERA_LINE_SEPERATOR, + woomera->interface, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + (ms->hw_dtmf)? "OutofBand": "InBand", + + WOOMERA_RECORD_SEPERATOR + ); + + + enqueue_event(woomera, &wevent, EVENT_FREE_DATA); + +#ifdef DOTRACE + sprintf(path_in, "/tmp/debug-in.%d.raw", tc); + sprintf(path_out, "/tmp/debug-out.%d.raw", tc++); + fdin = open(path_in, O_WRONLY | O_CREAT, O_TRUNC, 0600); + fdout = open(path_out, O_WRONLY | O_CREAT, O_TRUNC, 0600); +#endif + + if (server.out_tx_test) { + tx_fd=fopen("/smg/sound.raw","rb"); + if (!tx_fd) { + log_printf(SMG_LOG_ALL,server.log, "FAILED TO OPEN Sound file!\n"); + } + } + + for (;;) { + if ((packet_len = recvfrom(ms->udp_sock, udp_frame, sizeof(udp_frame), + MSG_DONTWAIT, (struct sockaddr *) &ms->local_addr, &fromlen)) < 1) { + break; + } + } + + + for (;;) { + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) || + woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + res=0; + break; + } + + + res = waitfor_socket(ms->udp_sock, sock_timeout, POLLIN, &flags_out); + + if (res < 0) { + break; + } + +#ifdef SMG_NO_MEDIA + continue; +#endif + + if (res == 0) { + + if (woomera_dtmf_transmit(ms,sangoma_frame_len) == 0) { + sock_timeout=(sangoma_frame_len/codec_sample); + } else { + sock_timeout=200; + } + + if (ms->skip_write_frames > 0) { + ms->skip_write_frames--; + } + + log_printf(SMG_LOG_DEBUG_8, server.log, "%s: UDP Sock Timeout !!!\n", + woomera->interface); + /* NENAD Timeout thus just continue */ + continue; + } + + if ((packet_len = recvfrom(ms->udp_sock, udp_frame, sizeof(udp_frame), + MSG_DONTWAIT, (struct sockaddr *) &ms->local_addr, &fromlen)) < 1) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "UDP Recv Error: %s\n",strerror(errno)); + break; + } + + +#ifdef SMG_DROP_SEQ + if (drop_seq) { + log_printf(SMG_LOG_ALL,server.log,"Dropping TX Sequence! %i\n",drop_seq); + drop_seq--; + continue; + } +#endif + +#if 0 + log_printf(SMG_LOG_DEBUG_10, server.log, "%s: UDP Receive %i !!!\n", + woomera->interface,packet_len); +#endif + + if (packet_len > 0) { + + if (server.udp_seq && packet_len > 4) { + packet_len-=4; + if ( woomera->rx_udp_seq != *(unsigned int*)&udp_frame[packet_len]) { + woomera->rx_udp_seq = *(unsigned int*)&udp_frame[packet_len]; + log_printf(SMG_LOG_WOOMERA,server.log,"RX UDP SEQ=%i Expected=%i\n", + *(unsigned int*)&udp_frame[packet_len],woomera->rx_udp_seq); + + pthread_mutex_lock(&server.thread_count_lock); + server.media_rx_seq_err++; + pthread_mutex_unlock(&server.thread_count_lock); + } else { + woomera->rx_udp_seq++; + } + } + +#if 0 +/* NC: This can cause skb_over panic must be retested */ + if (packet_len != sangoma_frame_len && ms->udp_sync_cnt <= 5) { + /* Assume that we will always receive SLINEAR here */ + sangoma_tdm_set_usr_period(ms->sangoma_sock, + &tdm_api, packet_len/codec_sample); + sangoma_frame_len = + sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); + + log_printf(SMG_LOG_DEBUG_MISC, server.log, + "%s: UDP TDM Period ReSync to Len=%i %ims (udp=%i) \n", + woomera->interface,sangoma_frame_len, + sangoma_frame_len/codec_sample,packet_len); + + + if (++ms->udp_sync_cnt >= 6) { + sangoma_tdm_set_usr_period(ms->sangoma_sock, + &tdm_api, 20); + sangoma_frame_len = + sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); + log_printf(SMG_LOG_ALL, server.log, + "%s: UDP TDM Period Force ReSync to 20ms \n", + woomera->interface); + } + + } +#endif + if (!server.out_tx_test) { + + if (woomera_dtmf_transmit(ms,sangoma_frame_len) == 0) { + sock_timeout=(sangoma_frame_len/codec_sample); + /* For sanity sake if we are doing the out test + * dont take any chances force tx udp data */ + if (ms->skip_write_frames > 0) { + ms->skip_write_frames--; + } + continue; + } else { + sock_timeout=200; + } + + if (ms->skip_write_frames > 0) { + ms->skip_write_frames--; + continue; + } + + } + + if (server.out_tx_test && tx_fd && + fread((void*)udp_frame, + sizeof(char), + packet_len,tx_fd) <= 0) { + + sangoma_get_full_cfg(ms->sangoma_sock,&tdm_api); + fclose(tx_fd); + tx_fd=NULL; + } + + +#ifdef WP_HPTDM_API + if (ms->tdmchan->push) { + ms->tdmchan->push(ms->tdmchan,udp_frame,packet_len); + } +#else + + rc=sangoma_sendmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + udp_frame, + packet_len, 0); + if (rc != packet_len) { + log_printf(SMG_LOG_PROD, server.log, "Error: Sangoma Tx error [%s] len=%i (%s)\n",woomera->interface, packet_len, strerror(errno)); + pthread_mutex_lock(&server.thread_count_lock); + server.media_rx_seq_err++; + pthread_mutex_unlock(&server.thread_count_lock); + } + + sangoma_get_full_cfg(ms->sangoma_sock,&tdm_api); + if (carrier == 0) { + carrier=tdm_api.wp_tdm_cmd.stats.tx_carrier_errors; + } + if (carrier != tdm_api.wp_tdm_cmd.stats.tx_carrier_errors) { + carrier_diff+=tdm_api.wp_tdm_cmd.stats.tx_carrier_errors-carrier; + carrier=tdm_api.wp_tdm_cmd.stats.tx_carrier_errors; + log_printf(SMG_LOG_WOOMERA, server.log, "Error: Sangoma Tx carrier error [%s] cnt=%i\n", + woomera->interface, carrier_diff); + pthread_mutex_lock(&server.thread_count_lock); + server.media_rx_seq_err++; + pthread_mutex_unlock(&server.thread_count_lock); + + sangoma_sendmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + udp_frame, + packet_len, 0); + } + +#endif + + } + +#if 0 + if (woomera->span == 1 && woomera->chan == 1) { + udp_cnt++; + if (udp_cnt && udp_cnt % 1000 == 0) { + log_printf(SMG_LOG_ALL, server.log, "%s: MEDIA UDP TX RX CNT %i %i\n", + woomera->interface,udp_cnt,packet_len); + } + } +#endif + } + + if (res < 0) { + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) || + woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + res=0; + } else { + log_printf(SMG_LOG_ALL, server.log, "Media Thread: socket error %s SockID=%i %s Poll=%s!\n", + woomera->interface,ms->sangoma_sock,strerror(errno),SMG_DECODE_POLL_ERRORS(flags_out)); + } + } + } + + new_woomera_event_printf(&wevent, + "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Start-Time: %ld%s" + "End-Time: %ld%s" + "Answer-Time: %ld%s" + "Call-ID: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + woomera->interface, + WOOMERA_LINE_SEPERATOR, + + woomera->session, + WOOMERA_LINE_SEPERATOR, + + time(&ms->started), + WOOMERA_LINE_SEPERATOR, + + time(NULL), + WOOMERA_LINE_SEPERATOR, + + time(&ms->answered), + WOOMERA_LINE_SEPERATOR, + + woomera->interface, + WOOMERA_LINE_SEPERATOR, + + q931_rel_to_str(woomera->q931_rel_cause_topbx), + WOOMERA_LINE_SEPERATOR, + + woomera->q931_rel_cause_topbx, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + +media_thread_exit: + + if (ms->has_hwec) { + + if (!tdmv_hwec_persist) { + if (hwec_enabled) { + int err; + err=sangoma_tdm_disable_hwec(ms->sangoma_sock, &tdm_api); + if (err==0) { + log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] disabling hwec ok\n",woomera->interface); + } else { + log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] disabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + } + } + } else { + if (hwec_reenable) { + int err; + err=sangoma_tdm_enable_hwec(ms->sangoma_sock, &tdm_api); + if (err==0) { + log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] Re-enabling hwec ok\n",woomera->interface); + } else { + log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] Re-enabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + } + } + } + + } + + if (woomera_test_flag(woomera, WFLAG_MEDIA_TDM_RUNNING)) { + woomera_set_flag(woomera, WFLAG_MEDIA_END); + + /* Dont wait for the other thread */ + close_socket(&ms->udp_sock); + close_span_chan(&ms->sangoma_sock, woomera->span+1, woomera->chan+1); + while(woomera_test_flag(woomera, WFLAG_MEDIA_TDM_RUNNING)) { + usleep(1000); + sched_yield(); + } + } + + + close_socket(&ms->udp_sock); + close_span_chan(&ms->sangoma_sock, woomera->span+1, woomera->chan+1); + + if (tx_fd){ + fclose(tx_fd); + tx_fd=NULL; + } + + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + + woomera_set_ms(woomera,NULL); + woomera_clear_flag(woomera, WFLAG_MEDIA_RUNNING); + + media_session_free(ms); + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA session for [%s] ended (ptr=%p)\n", + woomera->interface,woomera); + + + pthread_exit(NULL); + return NULL; +} + + + + +static void *media_tdm_thread_run(void *obj) +{ + wanpipe_tdm_api_t tdm_api; + wp_tdm_api_event_t *rx_event; + + struct media_session *ms = obj; + struct woomera_interface *woomera = ms->woomera; + int res = 0; + unsigned char circuit_frame[1024]; + sangoma_api_hdr_t hdrframe; + int flags_out; + int poll_opt = POLLIN | POLLPRI; + + memset(&hdrframe,0,sizeof(hdrframe)); + memset(circuit_frame,0,sizeof(circuit_frame)); + + memset(&tdm_api, 0, sizeof(wanpipe_tdm_api_t)); + + if (woomera_test_flag(woomera, WFLAG_MEDIA_END) || !woomera->interface) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA TDM session for [%s] Cancelled! (ptr=%p)\n", + woomera->interface,woomera); + /* In this case the call will be closed via woomera_thread_run + * function. And the process table will be cleard there */ + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + pthread_exit(NULL); + return NULL; + } + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA TDM session for [%s] started (ptr=%p)\n", + woomera->interface,woomera); + + for (;;) { + res = sangoma_readmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + circuit_frame, + sizeof(circuit_frame), 0); + if (res < 0) { + break; + } + } + + for (;;) { + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) || + woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + res=0; + break; + } + + + if (ms->oob_disable) { + poll_opt = POLLIN; + } else { + poll_opt = POLLIN | POLLPRI; + } + res = waitfor_socket(ms->sangoma_sock, 1000, poll_opt, &flags_out); + + + if (res < 0) { + break; + } + + if (res == 0) { +#if 0 + log_printf(SMG_LOG_DEBUG_8, server.log, "%s: TDM UDP Timeout !!!\n", + woomera->interface); + /* NENAD Timeout thus just continue */ +#endif + continue; + } + + + if (flags_out & POLLIN) { + + if (server.rbs_relay[woomera->span+1]) { + handle_dequeue_and_woomera_tx_event_rbs(woomera); + } + + res = sangoma_readmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + circuit_frame, + sizeof(circuit_frame), 0); + + if (res < 0) { + if (!woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + log_printf(SMG_LOG_ALL, server.log, "TDM Read Data Error: %s %s Sockid=%i\n", + woomera->interface, + strerror(errno),ms->sangoma_sock); + } + break; + } + + + if (server.udp_seq) { + *(unsigned int*)&circuit_frame[res] = woomera->tx_udp_seq; + woomera->tx_udp_seq++; + res+=4; + } + + res = sendto(ms->udp_sock, + circuit_frame, + res, 0, + (struct sockaddr *) &ms->remote_addr, + sizeof(ms->remote_addr)); + + if (res < 0) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "UDP Sento Error: %s\n", strerror(errno)); + } + } + + if (flags_out & POLLPRI) { + + res = sangoma_tdm_read_event(ms->sangoma_sock, &tdm_api); + if (res < 0) { + log_printf(SMG_LOG_ALL, server.log, "TDM Read Event Error: %s %s Sockid=%i\n", + woomera->interface, + strerror(errno),ms->sangoma_sock); + break; + } + + rx_event = &tdm_api.wp_tdm_cmd.event; + + switch (rx_event->wp_tdm_api_event_type){ +# ifdef LIBSANGOMA_GET_HWDTMF + case WP_TDMAPI_EVENT_DTMF: + + /* Only handle hw dtmf if hw_dtmf option is enabled */ + if (!ms->hw_dtmf) { + break; + } + + /* PORT_SOUT = 1 */ + if (rx_event->wp_tdm_api_event_dtmf_port == WAN_EC_CHANNEL_PORT_SOUT && + rx_event->wp_tdm_api_event_dtmf_type == WAN_EC_TONE_PRESENT) { + + handle_event_dtmf(woomera, rx_event->wp_tdm_api_event_dtmf_digit); + } + break; +#endif + case WP_TDMAPI_EVENT_RBS: + log_printf(SMG_LOG_WOOMERA, server.log, "[%s] Rx RBS Event Bits=0x%02X\n", + woomera->interface, rx_event->wp_tdm_api_event_rbs_bits); + handle_event_rbs(woomera, rx_event->wp_tdm_api_event_rbs_bits); + break; + + default: + log_printf(SMG_LOG_ALL, server.log, "TDM API Unknown OOB Event %i\n", + rx_event->wp_tdm_api_event_type); + break; + } + } + +#if 0 + if (woomera->span == 1 && woomera->chan == 1) { + tdm_cnt++; + if (tdm_cnt && tdm_cnt % 1000 == 0) { + log_printf(SMG_LOG_ALL, server.log, "%s: MEDIA TDM TX RX CNT %i %i\n", + woomera->interface,tdm_cnt,res); + } + } +#endif + + } + + if (res < 0) { + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) || + woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + + /* Good reason to exit */ + + } else { + + log_printf(SMG_LOG_ALL, server.log, "Media TDM Thread: socket error %s Sockid=%i %s Woomera Flags=0x%08X Poll=%s!\n", + woomera->interface,ms->sangoma_sock,strerror(errno),woomera->flags,SMG_DECODE_POLL_ERRORS(flags_out)); + woomera_print_flags(woomera,0); + } + } + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA TDM session for [%s] ended (ptr=%p)\n", + woomera->interface,woomera); + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + + pthread_exit(NULL); + return NULL; + +} + + +/* This function must be called with process_lock + * because it modifies shared process_table */ + +static int launch_media_thread(struct woomera_interface *woomera) +{ + pthread_attr_t attr; + int result = -1; + struct media_session *ms; + + if ((ms = media_session_new(woomera))) { + result = pthread_attr_init(&attr); + //pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + //pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + woomera_set_flag(woomera, WFLAG_MEDIA_RUNNING); + result = pthread_create(&ms->thread, &attr, media_thread_run, ms); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Thread! %s\n", + __FUNCTION__,strerror(errno)); + woomera_clear_flag(woomera, WFLAG_MEDIA_RUNNING); + media_session_free(woomera->ms); + + } + pthread_attr_destroy(&attr); + + } else { + log_printf(SMG_LOG_ALL, server.log, "Failed to start new media session\n"); + } + + return result; + +} + +static int launch_media_tdm_thread(struct woomera_interface *woomera) +{ + pthread_attr_t attr; + int result = -1; + struct media_session *ms = woomera_get_ms(woomera); + + if (!ms) { + return result; + } + + result = pthread_attr_init(&attr); + //pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + //pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + woomera_set_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + result = pthread_create(&ms->thread, &attr, media_tdm_thread_run, ms); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Thread! %s\n", + __FUNCTION__,strerror(errno)); + woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + } + pthread_attr_destroy(&attr); + + return result; +} + + +static struct woomera_interface * launch_woomera_loop_thread(short_signal_event_t *event) +{ + + struct woomera_interface *woomera = NULL; + char callid[20]; + + sprintf(callid, "s%dc%d", event->span+1,event->chan+1); + + if ((woomera = alloc_woomera())) { + + woomera->chan = event->chan; + woomera->span = event->span; + woomera->log = server.log; + woomera->debug = server.debug; + woomera->call_id = 1; + woomera->event_queue = NULL; + woomera->loop_tdm=1; + woomera->socket=-1; + + } else { + log_printf(SMG_LOG_ALL, server.log, "Critical ERROR: memory/socket error\n"); + return NULL; + } + + woomera_set_interface(woomera,callid); + + pthread_mutex_lock(&server.process_lock); + server.process_table[event->span][event->chan].dev = woomera; + pthread_mutex_unlock(&server.process_lock); + + if (launch_woomera_thread(woomera)) { + pthread_mutex_lock(&server.process_lock); + server.process_table[event->span][event->chan].dev = NULL; + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + smg_free(woomera); + log_printf(SMG_LOG_ALL, server.log, "Critical ERROR: memory/socket error\n"); + return NULL; + } + + return woomera; +} + +static int woomera_message_parse(struct woomera_interface *woomera, struct woomera_message *wmsg, int timeout) +{ + char *cur, *cr, *next = NULL, *eor = NULL; + char buf[2048]; + int res = 0, bytes = 0, sanity = 0; + struct timeval started, ended; + int elapsed, loops = 0; + int failto = 0; + int packet = 0; + int flags_out = 0; + + memset(wmsg, 0, sizeof(*wmsg)); + + if (woomera->socket < 0 ) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, WOOMERA_DEBUG_PREFIX "%s Invalid Socket! %d\n", + woomera->interface,woomera->socket); + return -1; + } + + if (woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_DEBUG_9, woomera->log, WOOMERA_DEBUG_PREFIX + "%s Woomera Message parse: Call Hangup - skipping message parse !\n", + woomera->interface); + return -1; + } + + gettimeofday(&started, NULL); + memset(buf, 0, sizeof(buf)); + + if (timeout < 0) { + timeout = abs(timeout); + failto = 1; + } else if (timeout == 0) { + timeout = -1; + } + + + while (!(eor = strstr(buf, WOOMERA_RECORD_SEPERATOR))) { + if (sanity > 1000) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, WOOMERA_DEBUG_PREFIX "%s Failed Sanity Check!\n[%s]\n\n", woomera->interface, buf); + return -1; + } + + if ((res = waitfor_socket(woomera->socket, 1000, POLLIN, &flags_out) > 0)) { + + res = recv(woomera->socket, buf, sizeof(buf), MSG_PEEK); + + if (res > 1) { + packet++; + } + if (!strncmp(buf, WOOMERA_LINE_SEPERATOR, 2)) { + res = read(woomera->socket, buf, 2); + return 0; + } + if (res == 0) { + sanity++; + /* Looks Like it's time to go! */ + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_DEBUG_9, woomera->log, WOOMERA_DEBUG_PREFIX + "%s MEDIA END or HANGUP \n", woomera->interface); + return -1; + } + ysleep(1000); + continue; + + } else if (res < 0) { + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, WOOMERA_DEBUG_PREFIX + "%s error during packet retry (err=%i) Loops#%d (%s)\n", + woomera->interface, res, loops, + strerror(errno)); + return res; + } else if (loops) { + ysleep(100000); + } + } + + gettimeofday(&ended, NULL); + elapsed = (((ended.tv_sec * 1000) + ended.tv_usec / 1000) - ((started.tv_sec * 1000) + started.tv_usec / 1000)); + + if (res < 0) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, WOOMERA_DEBUG_PREFIX "%s Bad RECV\n", + woomera->interface); + return res; + } else if (res == 0) { + sanity++; + /* Looks Like it's time to go! */ + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_DEBUG_9, woomera->log, WOOMERA_DEBUG_PREFIX + "%s MEDIA END or HANGUP \n", woomera->interface); + return -1; + } + ysleep(1000); + continue; + } + + if (packet && loops > 150) { + log_printf(SMG_LOG_PROD, woomera->log, WOOMERA_DEBUG_PREFIX + "%s Timeout waiting for packet.\n", + woomera->interface); + return -1; + } + + if (timeout > 0 && (elapsed > timeout)) { + log_printf(SMG_LOG_PROD, woomera->log, WOOMERA_DEBUG_PREFIX + "%s Timeout [%d] reached\n", + woomera->interface, timeout); + return failto ? -1 : 0; + } + + if (woomera_test_flag(woomera, WFLAG_EVENT)) { + /* BRB! we have an Event to deliver....*/ + return 0; + } + + /* what're we still doing here? */ + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + !woomera_test_flag(woomera, WFLAG_RUNNING)) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, WOOMERA_DEBUG_PREFIX + "%s Woomera Message Parse: Server or Woomera not Running\n", woomera->interface); + return -1; + } + loops++; + } + + *eor = '\0'; + bytes = strlen(buf) + 4; + + memset(buf, 0, sizeof(buf)); + res = read(woomera->socket, buf, bytes); + next = buf; + + log_printf(SMG_LOG_WOOMERA, woomera->log, "%s:WOOMERA RX MSG: %s\n",woomera->interface,buf); + + while ((cur = next)) { + + if ((cr = strstr(cur, WOOMERA_LINE_SEPERATOR))) { + *cr = '\0'; + next = cr + (sizeof(WOOMERA_LINE_SEPERATOR) - 1); + if (!strcmp(next, WOOMERA_RECORD_SEPERATOR)) { + break; + } + } + if (!cur || !*cur) { + break; + } + + if (!wmsg->last) { + char *cmd, *id, *args; + woomera_set_flag(wmsg, MFLAG_EXISTS); + cmd = cur; + + if ((id = strchr(cmd, ' '))) { + *id = '\0'; + id++; + if ((args = strchr(id, ' '))) { + *args = '\0'; + args++; + strncpy(wmsg->command_args, args, sizeof(wmsg->command_args)-1); + } + strncpy(wmsg->callid, id, sizeof(wmsg->callid)-1); + } + + strncpy(wmsg->command, cmd, sizeof(wmsg->command)-1); + } else { + char *name, *val; + name = cur; + + if ((val = strchr(name, ':'))) { + *val = '\0'; + val++; + while (*val == ' ') { + *val = '\0'; + val++; + } + strncpy(wmsg->values[wmsg->last-1], val, WOOMERA_STRLEN); + } + strncpy(wmsg->names[wmsg->last-1], name, WOOMERA_STRLEN); + if (name && val && !strcasecmp(name, "content-type")) { + woomera_set_flag(wmsg, MFLAG_CONTENT); + bytes = atoi(val); + } + if (name && val && !strcasecmp(name, "content-length")) { + woomera_set_flag(wmsg, MFLAG_CONTENT); + bytes = atoi(val); + } + + + } + wmsg->last++; + } + + wmsg->last--; + + if (bytes && woomera_test_flag(wmsg, MFLAG_CONTENT)) { + int terr; + terr=read(woomera->socket, wmsg->body, + (bytes > sizeof(wmsg->body)) ? sizeof(wmsg->body) : bytes); + } + + return woomera_test_flag(wmsg, MFLAG_EXISTS); + +} + +static struct woomera_interface *pull_from_holding_tank(int index, int span , int chan) +{ + struct woomera_interface *woomera = NULL; + + if (index < 1 || index >= CORE_TANK_LEN) { + if (index != 0) { + log_printf(SMG_LOG_ALL, server.log, "%s Error on invalid TANK INDEX = %i\n", + __FUNCTION__,index); + } + return NULL; + } + + pthread_mutex_lock(&server.ht_lock); + if (server.holding_tank[index] && + server.holding_tank[index] != &woomera_dead_dev) { + woomera = server.holding_tank[index]; + + /* Block this index until the call is completed */ + server.holding_tank[index] = &woomera_dead_dev; + + woomera->index = 0; + woomera->span=span; + woomera->chan=chan; + } + pthread_mutex_unlock(&server.ht_lock); + + return woomera; +} + +static void clear_all_holding_tank(void) +{ + int i; + pthread_mutex_lock(&server.ht_lock); + for (i=0;i= CORE_TANK_LEN) { + if (index != 0) { + log_printf(SMG_LOG_ALL, server.log, "%s Error on invalid TANK INDEX = %i\n", + __FUNCTION__,index); + } + return NULL; + } + + pthread_mutex_lock(&server.ht_lock); + woomera = server.holding_tank[index]; + pthread_mutex_unlock(&server.ht_lock); + + return woomera; +} + + +static void clear_from_holding_tank(int index, struct woomera_interface *woomera) +{ + + if (index < 1 || index >= CORE_TANK_LEN) { + if (index != 0) { + log_printf(SMG_LOG_ALL, server.log, "%s Error on invalid TANK INDEX = %i\n", + __FUNCTION__,index); + } + return; + } + + pthread_mutex_lock(&server.ht_lock); + if (server.holding_tank[index] == &woomera_dead_dev) { +#if 0 + log_printf(SMG_LOG_ALL,server.log, "%s Clearing DEAD id=%i OK\n", + __FUNCTION__,index); +#endif + server.holding_tank[index] = NULL; + } else if (woomera && server.holding_tank[index] == woomera) { +#if 0 + log_printf(SMG_LOG_ALL,server.log, "%s Clearing ACTIVE Woomera id=%i OK\n", + __FUNCTION__,index); +#endif + server.holding_tank[index] = NULL; + } else if (server.holding_tank[index]) { + log_printf(SMG_LOG_ALL, server.log, "Critical Error: Holding tank index %i not cleared %p !\n", + index, server.holding_tank[index]); + } + pthread_mutex_unlock(&server.ht_lock); + + return; +} + +static struct woomera_interface *peek_from_holding_tank(int index) +{ + struct woomera_interface *woomera = NULL; + + if (index < 1 || index >= CORE_TANK_LEN) { + if (index != 0) { + log_printf(SMG_LOG_ALL, server.log, "%s Error on invalid TANK INDEX = %i\n", + __FUNCTION__,index); + } + return NULL; + } + + pthread_mutex_lock(&server.ht_lock); + if (server.holding_tank[index] && + server.holding_tank[index] != &woomera_dead_dev) { + woomera = server.holding_tank[index]; + } + pthread_mutex_unlock(&server.ht_lock); + + return woomera; +} + +static int add_to_holding_tank(struct woomera_interface *woomera) +{ + int next, i, found=0; + + pthread_mutex_lock(&server.ht_lock); + + for (i=0;i= CORE_TANK_LEN) { + next = server.holding_tank_index = 1; + } + + if (next == 0) { + log_printf(SMG_LOG_ALL, server.log, "\nCritical Error on TANK INDEX == 0\n"); + continue; + } + + if (server.holding_tank[next]) { + continue; + } + + found=1; + break; + } + + if (!found) { + /* This means all tank vales are busy + * should never happend */ + pthread_mutex_unlock(&server.ht_lock); + log_printf(SMG_LOG_ALL, server.log, "\nCritical Error failed to obtain a TANK INDEX\n"); + return 0; + } + + server.holding_tank[next] = woomera; + woomera->timeout = time(NULL) + server.call_timeout; + + pthread_mutex_unlock(&server.ht_lock); + return next; +} + + +static int handle_event_rbs(struct woomera_interface *woomera, unsigned char rbs_digit) +{ + woomera_rbs_relay_t *rbsrelay; + + if (smg_validate_span_chan(woomera->span,woomera->chan) != 0) { + log_printf(SMG_LOG_ALL, server.log, "[%s] handle_event_rbs invalid span chan\n", + woomera->interface); + return -1; + } + + if (!server.rbs_relay[woomera->span+1]) { + log_printf(SMG_LOG_ALL, server.log, "[%s] handle_event_rbs rbs_relan not enabled\n", + woomera->interface); + return -1; + } + + rbsrelay=&server.process_table[woomera->span][woomera->chan].rbs_relay; + + if (rbsrelay->rbs_bits[rbsrelay->rx_idx].init) { + log_printf(SMG_LOG_ALL, server.log, "[%s] Critical Error Rx RBS Overrun\n", + woomera->interface); + return -1; + } + + log_printf(SMG_LOG_ALL, server.log, "[%s] woomera rx queue rbs %X idx %i\n", + woomera->interface,rbs_digit, rbsrelay->rx_idx); + + rbsrelay->rbs_bits[rbsrelay->rx_idx].abcd=rbs_digit; + rbsrelay->rbs_bits[rbsrelay->rx_idx].init=500/SANGOMA_USR_PERIOD; + rbsrelay->rx_idx = ((rbsrelay->rx_idx + 1) % WOOMERA_MAX_RBS_BITS); + + return 0; +} + +static int handle_dequeue_and_woomera_tx_event_rbs(struct woomera_interface *woomera) +{ + struct woomera_event wevent; + woomera_rbs_relay_t *rbsrelay; + unsigned char abcd; + + if (smg_validate_span_chan(woomera->span,woomera->chan) != 0) { + log_printf(SMG_LOG_ALL, server.log, "[%s] handle_dequeue_and_woomera_tx_event_rbs invalid span chan\n", + woomera->interface); + return -1; + } + + if (!server.rbs_relay[woomera->span+1]) { + log_printf(SMG_LOG_ALL, server.log, "[%s] handle_dequeue_and_woomera_tx_event_rbs rbs_relan not enabled\n", + woomera->interface); + return -1; + } + + if (!woomera_test_flag(woomera,WFLAG_ANSWER)) { + return 0; + } + + rbsrelay=&server.process_table[woomera->span][woomera->chan].rbs_relay; + + if (rbsrelay->rbs_bits[rbsrelay->tx_idx].init == 0) { + return -1; + } + + if (rbsrelay->rbs_bits[rbsrelay->tx_idx].init > 1) { + rbsrelay->rbs_bits[rbsrelay->tx_idx].init--; + return -1; + } + + + abcd = rbsrelay->rbs_bits[rbsrelay->tx_idx].abcd; + rbsrelay->rbs_bits[rbsrelay->tx_idx].init=0; + + + log_printf(SMG_LOG_ALL, server.log, "[%s] woomera tx rbs %X idx %i\n", + woomera->interface,abcd, rbsrelay->tx_idx); + + rbsrelay->tx_idx = ((rbsrelay->tx_idx + 1) % WOOMERA_MAX_RBS_BITS); + + memset(&wevent, 0, sizeof(struct woomera_event)); + + new_woomera_event_printf(&wevent, "EVENT RBS %sUnique-Call-Id:%s%sContent-Length:%d%s%s%X%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + 1, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + abcd, + WOOMERA_RECORD_SEPERATOR); + + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + return 0; +} + +static void handle_event_dtmf(struct woomera_interface *woomera, unsigned char dtmf_digit) +{ + struct woomera_event wevent; + + memset(&wevent, 0, sizeof(struct woomera_event)); + + new_woomera_event_printf(&wevent, "EVENT DTMF %sUnique-Call-Id:%s%sContent-Length:%d%s%s%c%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + 1, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + dtmf_digit, + WOOMERA_RECORD_SEPERATOR); + + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + return; +} + +static int handle_woomera_progress(struct woomera_interface *woomera, + struct woomera_message *wmsg) +{ + call_signal_event_t event; + int err=-1; + + memset(&event, 0, sizeof(event)); + + call_signal_event_init((short_signal_event_t*)&event, SIGBOOST_EVENT_CALL_PROGRESS, woomera->chan, woomera->span); + sprintf(event.isup_in_rdnis,"SMG003-EVI-2"); + event.isup_in_rdnis_size=strlen(event.isup_in_rdnis); + if (woomera->index >= 0) { + event.call_setup_id = woomera->index; + } + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s]\n", + wmsg->command, woomera->interface); + + if (!woomera_check_running(woomera)) { + socket_printf(woomera->socket, "405 PROGRESS Channel already hungup%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + if (!woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + + socket_printf(woomera->socket, "405 PROGRESS Channel not aceked%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + err=isup_exec_event(&event); + if (err == 0) { + socket_printf(woomera->socket, + "200 %s PROGRESS OK%s" + "Unique-Call-Id: %s%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } else { + socket_printf(woomera->socket, "405 PROGRESS Boost failure%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } + + return err; +} + +static int handle_woomera_media_accept_answer(struct woomera_interface *woomera, + struct woomera_message *wmsg, + int media, int answer, int accept) +{ + char *raw = woomera_message_header(wmsg, "raw-audio"); + char *media_available = woomera_message_header(wmsg, "xMedia"); + char *ring_available = woomera_message_header(wmsg, "xRing"); + struct woomera_event wevent; + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s] raw=%s\n", + wmsg->command, woomera->interface, raw?raw:"N/A"); + + if (!woomera_check_running(woomera)) { + + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "ERROR! call was cancelled MEDIA on HANGUP or MEDIA END!\n"); + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(woomera->q931_rel_cause_topbx), + WOOMERA_LINE_SEPERATOR, + woomera->q931_rel_cause_topbx, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call already hungup!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + woomera->timeout=0; + return -1; + } + + log_printf(SMG_LOG_DEBUG_MISC, server.log,"WOOMERA: GOT %s EVENT: [%s] RAW=%s\n", + wmsg->command,wmsg->callid,raw); + + + if (raw && + woomera->raw == NULL && + !woomera_test_flag(woomera, WFLAG_RAW_MEDIA_STARTED)) { + + woomera_set_flag(woomera, WFLAG_RAW_MEDIA_STARTED); + + woomera_set_raw(woomera, raw); + + if (launch_media_thread(woomera)) { + + log_printf(SMG_LOG_DEBUG_8, server.log,"ERROR: Failed to Launch Call [%s]\n", + woomera->interface); + + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(21), + WOOMERA_LINE_SEPERATOR, + 21, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_RUNNING); + + woomera->timeout=0; + return -1; + } + } else if (media && raw && woomera->ms) { + if (strncmp(raw,woomera->raw,20) != 0) { + log_printf(SMG_LOG_WOOMERA, server.log, "[%s] MEDIA Address Change from %s to %s\n", + woomera->interface,woomera->raw, raw); + + pthread_mutex_lock(&woomera->ms_lock); + woomera_set_raw(woomera,raw); + woomera_raw_to_ip(woomera->ms,woomera->raw); + update_udp_socket(woomera->ms,woomera->ms->ip, woomera->ms->port); + + pthread_mutex_unlock(&woomera->ms_lock); + } + } + + if (!woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + log_printf(SMG_LOG_ALL, server.log,"ERROR! Monitor Thread not running!\n"); + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + return -1; + + } else { + + if (accept) { + if (!autoacm && !woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + short_signal_event_t event; + memset(&event, 0, sizeof(event)); + call_signal_event_init(&event, SIGBOOST_EVENT_CALL_START_ACK, woomera->chan, woomera->span); + if (media_available && (atoi(media_available) == 1)) { + event.flags |= SIGBOOST_PROGRESS_MEDIA; + } + + if (ring_available && (atoi(ring_available) == 1)) { + event.flags |= SIGBOOST_PROGRESS_RING; + } + woomera_set_flag(woomera,WFLAG_CALL_ACKED); + + isup_exec_event((call_signal_event_t*)&event); + } + } + + if (answer) { + struct media_session *ms; + + pthread_mutex_lock(&woomera->ms_lock); + if ((ms=woomera->ms)) { + time(&woomera->ms->answered); + } + pthread_mutex_unlock(&woomera->ms_lock); + + if (ms) { + + if (!autoacm && !woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + short_signal_event_t event; + memset(&event, 0, sizeof(event)); + call_signal_event_init(&event, SIGBOOST_EVENT_CALL_START_ACK, woomera->chan, woomera->span); + if (media_available && (atoi(media_available) == 1)) { + event.flags |= SIGBOOST_PROGRESS_MEDIA; + } + + if (ring_available && (atoi(ring_available) == 1)) { + event.flags |= SIGBOOST_PROGRESS_RING; + } + woomera_set_flag(woomera,WFLAG_CALL_ACKED); + + isup_exec_event((call_signal_event_t*)&event); + } + + woomera_set_flag(woomera, WFLAG_ANSWER); + + isup_exec_command(woomera->span, + woomera->chan, + -1, + SIGBOOST_EVENT_CALL_ANSWERED, + 0); + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "Sent SIGBOOST_EVENT_CALL_ANSWERED [s%dc%d]\n", + woomera->span+1,woomera->chan+1); + } else { + + struct woomera_event wevent; + log_printf(SMG_LOG_ALL, server.log, + "WOOMERA ANSWER: FAILED [%s] no Media \n", + wmsg->command,wmsg->callid); + + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(21), + WOOMERA_LINE_SEPERATOR, + 21, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_RUNNING); + woomera->timeout=0; + return -1; + } + } + } + + new_woomera_event_printf(&wevent, "200 %s OK%s" + "Unique-Call-Id: %s%s", + answer ? "ANSWER" : + accept ? "ACCEPT" : "MEDIA", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + return 0; +} + +static int handle_woomera_call_start (struct woomera_interface *woomera, + struct woomera_message *wmsg) +{ + char *raw = woomera_message_header(wmsg, "raw-audio"); + call_signal_event_t event; + char *calling = woomera_message_header(wmsg, "local-number"); +#ifdef SMG_CALLING_NAME + char *calling_name = woomera_message_header(wmsg, "local-name"); +#endif + char *presentation = woomera_message_header(wmsg, "Presentation"); + char *screening = woomera_message_header(wmsg, "Screening"); + char *rdnis = woomera_message_header(wmsg, "RDNIS"); + char *bearer_cap = woomera_message_header(wmsg, "Bearer-Cap"); + char *uil1p = woomera_message_header(wmsg, "uil1p"); + + char *called = wmsg->callid; + char *grp = wmsg->callid; + char *profile; + char *p; + int cause = 34; + int tg = 0; + int huntgroup = SIGBOOST_HUNTGRP_SEQ_ASC; + + /* Remove profile name out of destiantion */ + if ((profile = strchr(called, ':'))) { + profile++; + called=profile; + grp=profile; + } + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "New Call %d/%d origdest=%s newdest=%s\n", + server.call_count, server.max_calls, wmsg->callid, called); + + switch(called[0]) { + case 'g': + huntgroup = SIGBOOST_HUNTGRP_SEQ_ASC; + break; + case 'G': + huntgroup = SIGBOOST_HUNTGRP_SEQ_DESC; + break; + case 'r': + huntgroup = SIGBOOST_HUNTGRP_RR_ASC; + break; + case 'R': + huntgroup = SIGBOOST_HUNTGRP_RR_DESC; + break; + default: + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, + "Warning: Failed to determine huntgroup (%s)\n", + called); + huntgroup = SIGBOOST_HUNTGRP_SEQ_ASC; + } + + if ((p = strchr(called, '/'))) { + *p = '\0'; + called = p+1; + tg = atoi(grp+1) - 1; + if (tg < 0) { + tg=0; + } + } + + woomera->trunk_group=tg; + + if ( woomera->trunk_group > SMG_MAX_TG ) { + + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, + "405 SMG Server: trunk group value not valid!%s", WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "SMG Server: trunk group value not valid %d\n", + woomera->trunk_group); + + return -1; + } + + if (smg_check_all_busy(woomera->trunk_group) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ + + + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, + "405 SMG Server All Ckt Busy!%s", WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "SMG Server Full %d (ckt busy cnt=%i on tg=%d)\n", + server.call_count, server.all_ckt_busy[woomera->trunk_group],woomera->trunk_group); + return -1; + } + + + + if (raw) { + woomera_set_raw(woomera, raw); + } + + woomera->index = add_to_holding_tank(woomera); + if (woomera->index < 1) { + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, + "405 SMG Server All Tanks Busy!%s", + WOOMERA_RECORD_SEPERATOR); + log_printf(SMG_LOG_ALL, woomera->log, "Error: Call Tank Full (Call Cnt=%i)\n", + server.call_count); + return -1; + } + + + woomera->index_hold = woomera->index; + + call_signal_call_init(&event, calling, called, woomera->index); + + if (presentation) { + event.calling_number_presentation = atoi(presentation); + } else { + event.calling_number_presentation = 0; + } + + if (screening) { + event.calling_number_screening_ind = atoi(screening); + } else { + event.calling_number_screening_ind = 0; + } + + event.isup_in_rdnis_size=0; + event.isup_in_rdnis[0]=0; + + if (rdnis && strlen(rdnis) ) { + + if (strlen(rdnis) > sizeof(event.isup_in_rdnis)){ + log_printf(SMG_LOG_ALL,server.log,"Error: RDNIS Overflow (in size=%i max=%i)\n", + strlen(rdnis), sizeof(event.isup_in_rdnis)); + + } else { + + strncpy(event.isup_in_rdnis,rdnis, + sizeof(event.isup_in_rdnis)-1); + event.isup_in_rdnis_size=strlen(rdnis)+1; + log_printf(SMG_LOG_DEBUG_MISC,server.log,"RDNIS %s\n", rdnis); + } + + } + + if (bearer_cap && strlen(bearer_cap)) { + event.bearer.capability = bearer_cap_to_code(bearer_cap); + woomera->bearer_cap = event.bearer.capability; + } + + if (uil1p && strlen(uil1p)) { + event.bearer.uil1p = uil1p_to_code(uil1p); + } + +#ifdef SMG_CALLING_NAME + if (calling_name) { + strncpy((char*)event.calling_name,calling_name, + sizeof(event.calling_name)-1); + } +#endif + + event.trunk_group = tg; + event.hunt_group = huntgroup; + + set_digits_info(&event.calling.ton, woomera_message_header(wmsg, "xCallingTon")); + set_digits_info(&event.calling.npi, woomera_message_header(wmsg, "xCallingNpi")); + set_digits_info(&event.called.ton, woomera_message_header(wmsg, "xCalledTon")); + set_digits_info(&event.called.npi, woomera_message_header(wmsg, "xCalledNpi")); + set_digits_info(&event.rdnis.ton, woomera_message_header(wmsg, "xRdnisTon")); + set_digits_info(&event.rdnis.npi, woomera_message_header(wmsg, "xRdnisNpi")); + + if (call_signal_connection_write(&server.mcon, &event) < 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", + strerror(errno)); + + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, + "405 SMG Signalling Contestion!%s", + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + socket_printf(woomera->socket, "100 Trying%s", WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Call Called Event [Setup ID: %d] TG=%d\n", + woomera->index,tg); + + return 0; +} + + +static void interpret_command(struct woomera_interface *woomera, struct woomera_message *wmsg) +{ + int answer = 0, media = 0, accept=0; + char *unique_id; + int cause=0; + + + + if (!strcasecmp(wmsg->command, "call")) { + int err; + + if (strlen(woomera->session) != 0) { + /* Call has already been placed */ + socket_printf(woomera->socket, "400 Error Call already in progress %s", + WOOMERA_RECORD_SEPERATOR); + log_printf(SMG_LOG_ALL,server.log,"Woomera RX Call Even while call in progress!\n"); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(34), + WOOMERA_LINE_SEPERATOR, + 34, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, + "405 SMG Server All Ckt Reset!%s", WOOMERA_RECORD_SEPERATOR); + + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + err=handle_woomera_call_start(woomera,wmsg); + if (err) { + woomera_set_flag(woomera, WFLAG_HANGUP); + } + + return; + + } else if (!strcasecmp(wmsg->command, "bye") || !strcasecmp(wmsg->command, "quit")) { + char *cause = woomera_message_header(wmsg, "cause"); + char *q931cause = woomera_message_header(wmsg, "Q931-Cause-Code"); + + if (cause) { + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Bye Cause Received: [%s]\n", cause); + } + if (q931cause && atoi(q931cause)) { + woomera_set_cause_tosig(woomera,atoi(q931cause)); + } + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: Bye Received: [%s]\n", woomera->interface); + + woomera_clear_flag(woomera, WFLAG_RUNNING); + socket_printf(woomera->socket, "200 Connection closed%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return; + + } else if (!strcasecmp(wmsg->command, "listen")) { + + if (woomera_test_flag(woomera, WFLAG_LISTENING)) { + socket_printf(woomera->socket, "405 Listener already started%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } else { + + woomera_set_flag(woomera, WFLAG_LISTENING); + add_listener(woomera); + log_printf(SMG_LOG_ALL,woomera->log, "Starting Listen Device!\n"); + + socket_printf(woomera->socket, "%s", + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, "200 Listener enabled%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } + return; + + } else if ((media = !strcasecmp(wmsg->command, "debug"))) { + + int debug_level=atoi(wmsg->callid); + + if (debug_level < 10) { + server.debug=debug_level; + log_printf(SMG_LOG_ALL,server.log,"SMG Debugging set to %i (window=%i)\n",server.debug,server.mcon.txwindow); + } + + return; + } + + if (!strcasecmp(wmsg->command, "hangup")) { + char *q931cause = woomera_message_header(wmsg, "Q931-Cause-Code"); + + + if (q931cause && atoi(q931cause)) { + log_printf(SMG_LOG_DEBUG_8,server.log,"Woomera Hangup setting cause to %s %i\n", + q931cause,atoi(q931cause)); + woomera_set_cause_tosig(woomera,atoi(q931cause)); + } + + /* Continue Through */ + } + + + + unique_id = woomera_message_header(wmsg, "Unique-Call-Id"); + if (!unique_id) { + + cause=111; + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, "400 Woomera cmd without uniquie id%s" + WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_CALL,server.log,"Woomera RX Event (%s) without unique id!\n",wmsg->command); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + if (strlen(woomera->session) == 0) { + struct woomera_interface *session_woomera=NULL; + char *session=NULL; + int span, chan; + char ifname[100]; + int err; + + /* If session does not exist this is an incoming call */ + err=sscanf(unique_id, "s%dc%d", &span, &chan); + if (err!=2) { + err=sscanf(unique_id, "w%dg%d", &span, &chan); + } + span--; + chan--; + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, + "WOOMERA Got CMD %s Span=%d Chan=%d from session %s\n", + wmsg->command,span,chan,unique_id); + + if (smg_validate_span_chan(span,chan) != 0) { + + cause=81; + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, "404 Invalid span/chan in session%s", + WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, + "WOOMERA Warning invalid span chan in session %s %s\n", + wmsg->command,unique_id); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + pthread_mutex_lock(&server.process_lock); + session = server.process_table[span][chan].session; + session_woomera = server.process_table[span][chan].dev; + + /* This scenario is very common when we have multile clients + where multiple clients race get the incoming call */ + if (session_woomera) { + pthread_mutex_unlock(&server.process_lock); + + cause=81; + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, "404 Session not found%s" + WOOMERA_RECORD_SEPERATOR); + + + log_printf(SMG_LOG_DEBUG_8, woomera->log, "WOOMERA Error channel in use %s %s\n", + wmsg->command,unique_id); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + if (!session || strlen(session) == 0 || + strncmp(session,unique_id,sizeof(woomera->session))){ + pthread_mutex_unlock(&server.process_lock); + + /* Invalid call reference */ + cause=81; + socket_printf(woomera->socket, "event hangup %s" + "cause: %s%s" + "q931-cause-code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, "404 Invalid/Expired Session%s", + WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, + "WOOMERA Warning: Cmd=%s with invalid session %s (orig=%s)\n", + wmsg->command,unique_id,session?session:"N/A"); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + +#if 1 + if (!strcasecmp(wmsg->command, "hangup")) { + int clients=server.process_table[span][chan].clients; + if (server.process_table[span][chan].clients < 0) { + + log_printf(SMG_LOG_ALL, woomera->log, "WOOMERA CMD: Warning Clients (%i) Race condition on Hangup [s%dc%d]\n", + clients, span+1,chan+1); + + } else if (server.process_table[span][chan].clients > 1) { + server.process_table[span][chan].clients--; + pthread_mutex_unlock(&server.process_lock); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Got Hungup on Multiple Clients %i, skipping hangup [s%dc%d]\n", + clients, span+1,chan+1); + + cause=16; + socket_printf(woomera->socket, "event hangup %s" + "cause: %s%s" + "q931-cause-code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, "404 Hangup on multiple session%s" + WOOMERA_RECORD_SEPERATOR); + + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Hanging up channel [s%dc%d]\n", + span+1,chan+1); + } +#endif + + server.process_table[span][chan].dev=woomera; + strncpy(woomera->session,unique_id,sizeof(woomera->session)); + sprintf(ifname,"s%dc%d",span+1,chan+1); + woomera_set_interface(woomera, ifname); + + woomera->span=span; + woomera->chan=chan; + + /* Save bearer cap that came in on incoming call event */ + woomera->bearer_cap = server.process_table[span][chan].bearer_cap; + + /* set it to 1 so that queued digits are checked on proceed message */ + woomera->check_digits = 1; + + pthread_mutex_unlock(&server.process_lock); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA Got New If=%s Session %s\n", + woomera->interface, woomera->session); + + + } else if (strncmp(woomera->session,unique_id,sizeof(woomera->session))) { + + cause=81; + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, "404 Session Mis-match%s" + WOOMERA_RECORD_SEPERATOR); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + if (!strcasecmp(wmsg->command, "rbs")) { + + log_printf(SMG_LOG_PROD, server.log, + "WOOMERA CMD: RBS Received: [%s] Digit %s Body %s\n", + woomera->interface, wmsg->command_args, wmsg->body); + + wanpipe_send_rbs(woomera,wmsg->body); + + socket_printf(woomera->socket, "200 RBS OK%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + } else if (!strcasecmp(wmsg->command, "dtmf")) { + + log_printf(SMG_LOG_WOOMERA, woomera->log, + "WOOMERA CMD: DTMF Received: [%s] Digit %s Body %s\n", + woomera->interface, wmsg->command_args, wmsg->body); + + wanpipe_send_dtmf(woomera,wmsg->body); + + socket_printf(woomera->socket, "200 DTMF OK%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + + } else if (!strcasecmp(wmsg->command, "hangup")) { + + int chan = -1, span = -1; + char *cause = woomera_message_header(wmsg, "cause"); + char *q931cause = woomera_message_header(wmsg, "Q931-Cause-Code"); + + if (q931cause && atoi(q931cause)) { + woomera_set_cause_tosig(woomera,atoi(q931cause)); + } + + span=woomera->span; + chan=woomera->chan; + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Hangup Received: [%s] MEDIA EXIST Cause=%s\n", + woomera->interface,cause); + + if (smg_validate_span_chan(span,chan) != 0) { + + socket_printf(woomera->socket, "405 No Such Channel%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return; + } + + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "Hangup Received: [s%dc%d]\n", + span+1,chan+1); + + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_RUNNING); + + + socket_printf(woomera->socket, "200 HANGUP OK%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + } else if (!strcasecmp(wmsg->command, "proceed")) { + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s]\n", + wmsg->command, woomera->interface); + + socket_printf(woomera->socket, + "200 %s PROCEED OK%s" + "Unique-Call-Id: %s%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + } else if (!strcasecmp(wmsg->command, "progress")) { + + handle_woomera_progress(woomera,wmsg); + + + } else if ((media = !strcasecmp(wmsg->command, "media")) || + (answer = !strcasecmp(wmsg->command, "answer")) || + (accept = !strcasecmp(wmsg->command, "accept"))) { + + handle_woomera_media_accept_answer(woomera, wmsg, media,answer,accept); + + + } else { + log_printf(SMG_LOG_ALL, server.log,"WOOMERA INVALID EVENT: %s [%s] \n", + wmsg->command,wmsg->callid); + socket_printf(woomera->socket, "501 Command '%s' not implemented%s", + wmsg->command, WOOMERA_RECORD_SEPERATOR); + } +} + + +/* + EVENT INCOMING 1 + Remote-Address: 10.3.3.104 + Remote-Number: + Remote-Name: Anthony Minessale!8668630501 + Protocol: H.323 + User-Agent: Post Increment Woomera 1.0alpha1 (OpenH323 v1.17.2) 9/61 + H323-Call-Id: 887b1ff8-bb1f-da11-85c0-0007e98988c4 + Local-Number: 996 + Start-Time: Fri, 09 Sep 2005 12:25:14 -0400 + Local-Name: root +*/ + + +static void handle_call_answer(short_signal_event_t *event) +{ + struct woomera_interface *woomera = NULL; + int kill = 0; + + if (smg_validate_span_chan(event->span,event->chan)) { + log_printf(0,server.log, "Error: invalid span=% chan=%i on call answer [s%dc%d]!\n", + event->span+1, event->chan+1, event->span+1,event->chan+1); + return; + } + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + pthread_mutex_unlock(&server.process_lock); + + if (woomera) { + char callid[80]; + struct woomera_event wevent; + + woomera->timeout = 0; + + if (!woomera->raw) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to answer call with no media!\n"); + kill++; + goto handle_call_answer_end; + } + + if (woomera_test_flag(woomera, WFLAG_ANSWER)) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to double-answer a call!\n"); + kill++; + goto handle_call_answer_end; + } + + woomera_set_flag(woomera, WFLAG_ANSWER); + + if (woomera->span != event->span || woomera->chan != event->chan) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to start media on a different channel from the one we agreed on.!\n"); + kill++; + goto handle_call_answer_end; + } + + if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to answer a dead call!\n"); + kill++; + } else { + int err; + err=0; + sprintf(callid, "s%dc%d", event->span + 1, event->chan + 1); + woomera_set_interface(woomera, callid); +#ifndef WOOMERA_EARLY_MEDIA + err=launch_media_thread(woomera); + if (err) { + log_printf(SMG_LOG_ALL, server.log,"ERROR: Failed to Launch Call [%s]\n", + woomera->interface); + + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Q931-Cause-Code: %d%s" + "Cause: %s%s", + woomera->interface, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + 21, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(21), + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + kill++; + } +#endif + + if (!kill) { + new_woomera_event_printf(&wevent, "EVENT CONNECT s%dc%d%s" + "Unique-Call-Id: %s%s", + event->span+1, + event->chan+1, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR + ); + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + } + } + } else { + log_printf(SMG_LOG_PROD, server.log, "Answer requested on non-existant session. [s%dc%d]\n", + event->span+1, event->chan+1); + kill++; + } + +handle_call_answer_end: + + if (kill) { + if (woomera) { + woomera_set_flag(woomera,WFLAG_MEDIA_END); + } else { + +/* This can casuse a double STOP + must be debugged further */ +#if 0 + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED, + SIGBOOST_RELEASE_CAUSE_NORMAL); + + log_printf(SMG_LOG_PROD, server.log, "Sent CALL STOP to Answer without session [s%dc%d]\n", + event->span+1, event->chan+1); +#endif + log_printf(SMG_LOG_ALL, server.log, "WARNING: Received Answer with no session [s%dc%d]\n", + event->span+1, event->chan+1); + } + } +} + +static void handle_call_start_ack(short_signal_event_t *event) +{ + struct woomera_interface *woomera = NULL; + struct woomera_event wevent; + int kill = 0; + + if ((woomera = peek_from_holding_tank(event->call_setup_id))) { + char callid[80]; + + if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to ack a dead call!\n"); + kill++; + } else { + int err, span, chan; + + pull_from_holding_tank(event->call_setup_id,event->span,event->chan); + sprintf(callid, "s%dc%d", event->span + 1, event->chan + 1); + + span = event->span; + chan = event->chan; + + woomera_set_flag(woomera,WFLAG_CALL_ACKED); + woomera_set_interface(woomera, callid); + + pthread_mutex_lock(&server.process_lock); + + if (server.process_table[span][chan].dev) { + struct woomera_interface *tmp_woomera = server.process_table[span][chan].dev; + server.process_table[span][chan].dev = NULL; + woomera_set_flag(tmp_woomera,WFLAG_HANGUP); + woomera_set_flag(tmp_woomera,WFLAG_MEDIA_END); + if (tmp_woomera->ms && tmp_woomera->ms->sangoma_sock >= 0) { + close_span_chan(&tmp_woomera->ms->sangoma_sock, event->span+1, event->chan+1); + } + log_printf(SMG_LOG_ALL,server.log,"Call Overrun on [s%dc%d] - Call ACK!\n", event->span+1, event->chan+1); + } + + server.process_table[span][chan].dev = woomera; + sprintf(woomera->session,"%s-%i-%i",callid,rand(),rand()); + sprintf(server.process_table[span][chan].session,"%s-%s", + callid,woomera->session); + + + memset(server.process_table[span][chan].digits, 0, + sizeof(server.process_table[span][chan].digits)); + + server.process_table[span][chan].digits_len = 0; + memset(&server.process_table[event->span][event->chan].rbs_relay,0, + sizeof(server.process_table[event->span][event->chan].rbs_relay)); + pthread_mutex_unlock(&server.process_lock); + + if (kill) { + goto woomera_call_ack_skip; + } + +#ifdef WOOMERA_EARLY_MEDIA + err=launch_media_thread(woomera); + if (err) { + log_printf(SMG_LOG_ALL, server.log,"ERROR: Failed to Launch Call [%s]\n", + woomera->interface); + + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id:%s%s" + "Q931-Cause-Code: %d%s" + "Cause: %s%s", + woomera->interface, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + 21, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(21), + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + + kill++; + } +#endif + if (!kill) { + + new_woomera_event_printf(&wevent, "EVENT PROCEED s%dc%d%s" + "Channel-Name: g%d/%d%s" + "Unique-Call-Id: %s%s", + event->span+1, + event->chan+1, + WOOMERA_LINE_SEPERATOR, + woomera->trunk_group+1, + (event->span*max_chans)+event->chan+1, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "201 Accepted%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Call Answered Event ID = %d Device = s%dc%d!\n", + event->call_setup_id,woomera->span+1,woomera->chan+1); + } + } + } else { + log_printf(SMG_LOG_PROD, server.log, + "Event (START ACK) %d referrs to a non-existant session (%d) [s%dc%d]!\n", + event->event_id, event->call_setup_id,event->span+1, event->chan+1); + kill++; + } + +woomera_call_ack_skip: + if (kill) { + if (woomera) { + woomera_set_flag(woomera,WFLAG_MEDIA_END); + } else { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED, + SIGBOOST_RELEASE_CAUSE_NORMAL); + + log_printf(SMG_LOG_PROD, server.log, "Sent CALL STOP to CALL START ACK without session [s%dc%d]\n", + event->span+1, event->chan+1); + } + } +} + +static void handle_call_start_nack(short_signal_event_t *event) +{ + struct woomera_interface *woomera = NULL; + int span=-1, chan=-1; + int ack=0; + + /* Always ACK the incoming NACK + * Send out the NACK ACK before pulling the TANK, because + * if we send after the pull, the outgoing call could send + * a message to boost with the pulled TANK value before + * we send a NACK ACK */ + + if (smg_validate_span_chan(event->span,event->chan) == 0) { + span=event->span; + chan=event->chan; + } + + if (event->call_setup_id > 0) { + woomera=peek_from_holding_tank(event->call_setup_id); + } + + if (woomera) { + + struct woomera_event wevent; + + if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_ALL, server.log, "Event CALL START NACK on hungup call [%d]!\n", + event->call_setup_id); + ack++; + } else { + + woomera_set_cause_topbx(woomera,event->release_cause); + woomera_set_flag(woomera, (WFLAG_HANGUP|WFLAG_HANGUP_NACK_ACK)); + + new_woomera_event_printf(&wevent, "EVENT HANGUP s%dc%d%s" + "Unique-Call-Id: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + event->span+1, + event->chan+1, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(woomera->q931_rel_cause_topbx), + WOOMERA_LINE_SEPERATOR, + woomera->q931_rel_cause_topbx, + WOOMERA_RECORD_SEPERATOR + ); + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + woomera_set_flag(woomera, WFLAG_HANGUP); + woomera_clear_flag(woomera, WFLAG_RUNNING); + + /* Do not ack here, let woomera thread ack it */ + ack=0; + } + + + } else if (event->call_setup_id == 0 && + smg_validate_span_chan(event->span,event->chan) == 0) { + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + + if (woomera) { + if (!woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT) && + !woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK_SENT)) { + /* Only if we are not already waiting for hangup */ + server.process_table[event->span][event->chan].dev=NULL; + } else if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + /* At this point call is already hang up */ + woomera=NULL; + } else { + /* At this point call is already hang up */ + woomera=NULL; + } + } + + memset(server.process_table[event->span][event->chan].session, + 0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + + if (woomera) { + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event START NACK on s%dc%d ptr=%p ms=%p\n", + woomera->span+1,woomera->chan+1,woomera,woomera->ms); + + if (!woomera_test_flag(woomera,WFLAG_HANGUP)){ + if (event->release_cause == SIGBOOST_CALL_SETUP_CSUPID_DBL_USE || + event->release_cause == SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY) { + woomera_set_cause_topbx(woomera,17); + } else { + woomera_set_cause_topbx(woomera,event->release_cause); + } + } + + woomera_set_flag(woomera, + (WFLAG_HANGUP|WFLAG_MEDIA_END|WFLAG_HANGUP_NACK_ACK)); + + /* Nack Ack will be sent by the woomera thread */ + ack=0; + } else { + /* Valid state when we are not in autoacm mode */ + ack++; + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event: NACK no woomera on span chan [s%dc%d]!\n", + event->span+1, event->chan+1); + } + + } else { + log_printf(SMG_LOG_ALL, server.log, + "Error: Start Nack Invalid State Should not happen [%d] [s%dc%d]!\n", + event->call_setup_id, event->span+1, event->chan+1); + ack++; + } + + if (event->release_cause == SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY) { + smg_all_ckt_busy(woomera->trunk_group); + log_printf(SMG_LOG_ALL, server.log, "WARNING: All ckt busy Timeout=%i on tg=%i!\n", + server.all_ckt_busy[woomera->trunk_group], woomera->trunk_group); + } + if (event->release_cause == SIGBOOST_CALL_SETUP_CSUPID_DBL_USE) { + log_printf(SMG_LOG_ALL, server.log, "WARNING: Double use on [s%ic%i] setup id %i!\n", + event->span+1,event->chan+1,event->call_setup_id); + } + +#warning "Ignoring CALL GAP" +#if 0 + if (event->release_cause == SIGBOOST_CALL_SETUP_NACK_AUTO_CALL_GAP) { + log_printf(SMG_LOG_ALL, server.log, "WARNING: Call Gapping Detected!\n"); + smg_all_ckt_gap(woomera->trunk_group); + } +#endif + + if (ack) { + span=0; + chan=0; + if (smg_validate_span_chan(event->span,event->chan) == 0) { + span=event->span; + chan=event->chan; + } + + isup_exec_command(span, + chan, + event->call_setup_id, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + 0); + + if (!woomera) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event (NACK ACK) %d referrs to a non-existant session (%d) [s%dc%d]!\n", + event->event_id,event->call_setup_id, event->span+1, event->chan+1); + } + } +} + +static void handle_call_loop_start(short_signal_event_t *event) +{ + struct woomera_interface *woomera; + char callid[20]; + char *session; + + pthread_mutex_lock(&server.process_lock); + if (server.process_table[event->span][event->chan].dev) { + + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + 17); + + + log_printf(SMG_LOG_PROD, server.log, + "Sent (From Handle Loop START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [s%dc%d] ptr=%d\n", + event->span+1, event->chan+1, server.process_table[event->span][event->chan].dev); + + pthread_mutex_unlock(&server.process_lock); + return; + + } + + sprintf(callid, "s%dc%d", event->span+1,event->chan+1); + sprintf(server.process_table[event->span][event->chan].session, + "%s-%i-%i",callid,rand(),rand()); + session=server.process_table[event->span][event->chan].session; + server.process_table[event->span][event->chan].dev = NULL; + pthread_mutex_unlock(&server.process_lock); + + + woomera=launch_woomera_loop_thread(event); + if (woomera == NULL) { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + 17); + log_printf(SMG_LOG_PROD, server.log, + "Sent (From Handle Loop START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [s%dc%d] ptr=%d\n", + event->span+1, event->chan+1, server.process_table[event->span][event->chan].dev); + } + + woomera_set_flag(woomera,WFLAG_CALL_ACKED); + + return; +} + + +static void handle_call_start(call_signal_event_t *event) +{ + struct woomera_event wevent; + char callid[20]; + char *session; + struct woomera_interface *tmp_woomera=NULL; + int clients; + + remove_end_of_digits_char((unsigned char*)event->called.digits); + remove_end_of_digits_char((unsigned char*)event->calling.digits); + remove_end_of_digits_char((unsigned char*)event->rdnis.digits); + remove_end_of_digits_char((unsigned char*)event->custom_data); + + if (server.strip_cid_non_digits) { + validate_number((unsigned char*)event->called.digits); + validate_number((unsigned char*)event->calling.digits); + validate_number((unsigned char*)event->rdnis.digits); + } + + if (smg_validate_span_chan(event->span,event->chan)) { + log_printf(0,server.log, + "Error: invalid span=% chan=%i on incoming call [s%dc%d] - Call START!\n", + event->span+1, event->chan+1, event->span+1,event->chan+1); + return; + } + + pthread_mutex_lock(&server.process_lock); + + if ((tmp_woomera=server.process_table[event->span][event->chan].dev)) { + + server.process_table[event->span][event->chan].dev = NULL; + woomera_set_flag(tmp_woomera,WFLAG_HANGUP); + woomera_set_flag(tmp_woomera,WFLAG_MEDIA_END); + if (tmp_woomera->ms && tmp_woomera->ms->sangoma_sock >= 0) { + close_span_chan(&tmp_woomera->ms->sangoma_sock, event->span+1, event->chan+1); + } + log_printf(SMG_LOG_ALL,server.log,"Call Overrun on [s%dc%d] - Call START!\n", event->span+1, event->chan+1); + } + + sprintf(callid, "s%dc%d", event->span+1,event->chan+1); + sprintf(server.process_table[event->span][event->chan].session, + "%s-%i-%i",callid,rand(),rand()); + session=server.process_table[event->span][event->chan].session; + server.process_table[event->span][event->chan].dev = NULL; + memset(server.process_table[event->span][event->chan].digits, 0, sizeof(server.process_table[event->span][event->chan].digits)); + server.process_table[event->span][event->chan].digits_len = 0; + server.process_table[event->span][event->chan].bearer_cap = event->bearer.capability; + server.process_table[event->span][event->chan].clients=0; + memset(&server.process_table[event->span][event->chan].rbs_relay,0, + sizeof(server.process_table[event->span][event->chan].rbs_relay)); + pthread_mutex_unlock(&server.process_lock); + + if (autoacm) { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_ACK, + 0); + } + + + log_printf(SMG_LOG_DEBUG_8,server.log,"BEARER %i , UIL1P = %i\n", + event->bearer.capability,event->bearer.uil1p); + + if (event->bearer.uil1p == 0) { + if (server.hw_coding) { + event->bearer.uil1p=BC_IE_UIL1P_G711_ALAW; + } else { + event->bearer.uil1p=BC_IE_UIL1P_G711_ULAW; + } + } + + new_woomera_event_printf(&wevent, "EVENT INCOMING s%dc%d%s" + "Unique-Call-Id: %s%s" + "Remote-Number: %s%s" + "Remote-Name: %s%s" +#if defined(BRI_PROT) + "Protocol: BRI%s" +#else +#if defined(PRI_PROT) + "Protocol: PRI%s" +#else + "Protocol: SS7%s" +#endif +#endif + "User-Agent: sangoma_mgd%s" + "Local-Number: %s%s" + "Channel-Name: g%d/%d%s" + "Trunk-Group: %d%s" + "Presentation: %d%s" + "Screening: %d%s" + "RDNIS: %s%s" + "Bearer-Cap: %s%s" + "uil1p: %s%s" + "xCustom: %s%s" + , + event->span+1, + event->chan+1, + WOOMERA_LINE_SEPERATOR, + session, + WOOMERA_LINE_SEPERATOR, + event->calling.digits, + WOOMERA_LINE_SEPERATOR, + event->calling_name, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + event->called.digits, + WOOMERA_LINE_SEPERATOR, + event->trunk_group+1, + (event->span*max_chans)+event->chan+1, + WOOMERA_LINE_SEPERATOR, + event->trunk_group+1, + WOOMERA_LINE_SEPERATOR, + event->calling_number_presentation, + WOOMERA_LINE_SEPERATOR, + event->calling_number_screening_ind, + WOOMERA_LINE_SEPERATOR, + event->rdnis.digits, + WOOMERA_LINE_SEPERATOR, + bearer_cap_to_str(event->bearer.capability), + WOOMERA_LINE_SEPERATOR, + uil1p_to_str(event->bearer.uil1p), + WOOMERA_LINE_SEPERATOR, + event->custom_data, + WOOMERA_RECORD_SEPERATOR + ); + + clients=enqueue_event_on_listeners(&wevent); + if (!clients) { + + pthread_mutex_lock(&server.process_lock); + server.process_table[event->span][event->chan].dev = NULL; + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + if (autoacm) { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED, + 17); + log_printf(SMG_LOG_ALL, server.log, + "CALL INCOMING: Enqueue Error Sent SIGBOOST_EVENT_CALL_STOPPED [s%dc%d]\n", + event->span+1, event->chan+1); + + } else { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + 17); + log_printf(SMG_LOG_ALL, server.log, + "CALL INCOMING: Enqueue Error Sent SIGBOOST_EVENT_CALL_START_NACK [s%dc%d]\n", + event->span+1, event->chan+1); + + } + + } else { + //pthread_mutex_lock(&server.process_lock); + server.process_table[event->span][event->chan].clients = clients; + //pthread_mutex_unlock(&server.process_lock); + } + + destroy_woomera_event_data(&wevent); + +} + +static void handle_incoming_digit(call_signal_event_t *event) +{ + struct woomera_interface *woomera; + int digits_len; + + if (smg_validate_span_chan(event->span,event->chan)) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event DTMF on invalid span chan [s%dc%d] !\n", + event->span+1, event->chan+1); + return; + } + + + if (!event->called_number_digits_count) { + log_printf(SMG_LOG_ALL, server.log, "Error Incoming digit with len %s %d [s%dc%d]\n", + event->called_number_digits, + event->called_number_digits_count, + event->span+1, event->chan+1); + } + + log_printf(SMG_LOG_DEBUG_9, server.log, "Queuing incoming digits %s [s%dc%d]\n", + event->called_number_digits, + event->span+1, event->chan+1); + + pthread_mutex_lock(&server.digits_lock); + + digits_len = server.process_table[event->span][event->chan].digits_len; + + strncpy(&server.process_table[event->span][event->chan].digits[digits_len], + event->called_number_digits, + event->called_number_digits_count); + + server.process_table[event->span][event->chan].digits_len += event->called_number_digits_count; + + pthread_mutex_unlock(&server.digits_lock); + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + pthread_mutex_unlock(&server.process_lock); + if (!woomera || !strlen(woomera->session)) { + return; + } + woomera_check_digits(woomera); +} + +static void handle_gap_abate(short_signal_event_t *event) +{ + log_printf(SMG_LOG_ALL, server.log, "NOTICE: GAP Cleared!\n", + event->span+1, event->chan+1); + smg_clear_ckt_gap(event->trunk_group+1); +} + +static void handle_restart(call_signal_connection_t *mcon, short_signal_event_t *event) +{ + if (!woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + log_printf(SMG_LOG_ALL, server.log,"ERROR! Monitor Thread not running!\n"); + } else { + /* Clear Reset */ + /* Tell all threads to go down */ + + log_printf(SMG_LOG_ALL, server.log, + "RESTART Received: resetting all threads\n"); + + int i=0; + for ( i =0 ; i < SMG_MAX_TG ; i++ ){ + smg_all_ckt_busy(i); + } + woomera_set_flag(&server.master_connection, WFLAG_SYSTEM_RESET); + gettimeofday(&server.restart_timeout,NULL); + +#if 0 + sleep(5); + + clear_all_holding_tank(); + + sleep(2); + + event->event_id = SIGBOOST_EVENT_SYSTEM_RESTART_ACK; + err=call_signal_connection_write(&server.mcon, (call_signal_event_t*)event); + if (err < 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", + strerror(errno)); + } + + smg_all_ckt_busy(); + woomera_clear_flag(&server.master_connection, WFLAG_SYSTEM_RESET); +#endif + } + +} + +static void handle_restart_ack(call_signal_connection_t *mcon, short_signal_event_t *event) +{ + woomera_clear_flag(&server.master_connection, WFLAG_SYSTEM_NEED_RESET_ACK); + /* Do not reset WFLAG_SYSTEM_RESET flag here!. The flag should + only be reset on restart from boost */ + //woomera_clear_flag(&server.master_connection, WFLAG_SYSTEM_RESET); +} + + + +static void handle_remove_loop(short_signal_event_t *event) +{ + struct woomera_interface *woomera; + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + server.process_table[event->span][event->chan].dev=NULL; + pthread_mutex_unlock(&server.process_lock); + + if (woomera) { + + woomera_set_flag(woomera, + (WFLAG_MEDIA_END|WFLAG_HANGUP)); + + /* We have to close the socket because + At this point we are release span chan */ + + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event REMOVE LOOP on s%dc%d ptr=%p ms=%p\n", + woomera->span+1,woomera->chan+1,woomera,woomera->ms); + + } +} + + +static void handle_call_stop(short_signal_event_t *event) +{ + struct woomera_interface *woomera; + int ack=0; + + woomera = NULL; + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + if (woomera) { + + if (!woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT) && + !woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK_SENT)) { + /* Only if we are not already waiting for hangup */ + //server.process_table[event->span][event->chan].dev=NULL; + + } else if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + /* At this point call is already hangup */ + woomera=NULL; + } else { + /* At this point call is already hangup */ + woomera=NULL; + } + } + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + if (woomera) { + + woomera_set_cause_topbx(woomera,event->release_cause); + + woomera_set_flag(woomera, + (WFLAG_MEDIA_END|WFLAG_HANGUP|WFLAG_HANGUP_ACK)); + + /* We have to close the socket because + At this point we are release span chan */ + + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event CALL STOP on s%dc%d ptr=%p ms=%p\n", + woomera->span+1,woomera->chan+1,woomera,woomera->ms); + + } else { + ack++; + } + + + if (ack) { + /* At this point we have already sent our STOP so its safe to ACK */ + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED_ACK, + 0); + } + + if (!woomera){ + /* This is allowed on incoming call if remote app does not answer it */ + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event CALL STOP referrs to a non-existant session [s%dc%d]!\n", + event->span+1, event->chan+1); + } +} + +static void handle_heartbeat(short_signal_event_t *event) +{ + if (!woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + log_printf(SMG_LOG_ALL, server.log,"ERROR! Monitor Thread not running!\n"); + } else { + int err=call_signal_connection_writep(&server.mconp, (call_signal_event_t*)event); + if (err < 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", + strerror(errno)); + } + } + return; +} + + +static void handle_call_stop_ack(short_signal_event_t *event) +{ + + struct woomera_interface *woomera = NULL; + + + if (smg_validate_span_chan(event->span,event->chan)) { + log_printf(0,server.log, "Error: invalid span=% chan=%i on call stopped ack [s%dc%d]\n", + event->span+1, event->chan+1, event->span+1,event->chan+1); + return; + } + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + server.process_table[event->span][event->chan].dev=NULL; + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + + if (woomera) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Stop Ack on [s%dc%d] [Setup ID: %d] [%s]!\n", + event->span+1, event->chan+1, event->call_setup_id, + woomera->interface); + + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + + } else { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event CALL_STOP_ACK(%d) referrs to a non-existant session [s%dc%d] [Setup ID: %d]!\n", + event->event_id, event->span+1, event->chan+1, event->call_setup_id); + } + + if (event->call_setup_id > 0) { + clear_from_holding_tank(event->call_setup_id, NULL); + } + + /* No need for us to do any thing here */ + return; +} + + +static void handle_call_start_nack_ack(short_signal_event_t *event) +{ + + struct woomera_interface *woomera = NULL; + + if ((woomera=pull_from_holding_tank(event->call_setup_id,-1,-1))) { + + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + + } else if (event->call_setup_id == 0 && + smg_validate_span_chan(event->span,event->chan) == 0) { + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + server.process_table[event->span][event->chan].dev=NULL; + memset(server.process_table[event->span][event->chan].session, + 0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + if (woomera) { + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + } else { + /* Possible if incoming call is NACKed due to + * woomera client being down, in this case no + * woomera thread is created */ + log_printf(SMG_LOG_DEBUG_8, server.log, + "Event NACK ACK [s%dc%d] with valid span/chan no dev!\n", + event->span+1, event->chan+1); + } + + } else { + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "Event NACK ACK referrs to a non-existant session [s%dc%d] [Setup ID: %d]!\n", + event->span+1, event->chan+1, event->call_setup_id); + } + + if (event->call_setup_id > 0) { + clear_from_holding_tank(event->call_setup_id, NULL); + } + + /* No need for us to do any thing here */ + return; +} + + +static int parse_ss7_event(call_signal_connection_t *mcon, short_signal_event_t *event) +{ + int ret = 0; + + switch(event->event_id) { + + case SIGBOOST_EVENT_CALL_START: + handle_call_start((call_signal_event_t*)event); + break; + case SIGBOOST_EVENT_CALL_STOPPED: + handle_call_stop(event); + break; + case SIGBOOST_EVENT_CALL_START_ACK: + handle_call_start_ack(event); + break; + case SIGBOOST_EVENT_CALL_START_NACK: + handle_call_start_nack(event); + break; + case SIGBOOST_EVENT_CALL_ANSWERED: + handle_call_answer(event); + break; + case SIGBOOST_EVENT_HEARTBEAT: + handle_heartbeat(event); + break; + case SIGBOOST_EVENT_CALL_START_NACK_ACK: + handle_call_start_nack_ack(event); + break; + case SIGBOOST_EVENT_CALL_STOPPED_ACK: + handle_call_stop_ack(event); + break; + case SIGBOOST_EVENT_INSERT_CHECK_LOOP: + handle_call_loop_start(event); + break; + case SIGBOOST_EVENT_SYSTEM_RESTART: + handle_restart(mcon,event); + break; + case SIGBOOST_EVENT_REMOVE_CHECK_LOOP: + handle_remove_loop(event); + break; + case SIGBOOST_EVENT_SYSTEM_RESTART_ACK: + handle_restart_ack(mcon,event); + break; + case SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE: + handle_gap_abate(event); + break; + case SIGBOOST_EVENT_DIGIT_IN: + handle_incoming_digit((call_signal_event_t*)event); + break; + case SIGBOOST_EVENT_CALL_PROGRESS: + /* We always assume early media, so there is no need to do anything on progress */ + break; + default: + log_printf(SMG_LOG_ALL, server.log, "Warning no handler implemented for [%s val:%d]\n", + call_signal_event_id_name(event->event_id), event->event_id); + break; + } + + return ret; +} + + +static void *monitor_thread_run(void *obj) +{ + int ss = 0; + int policy=0,priority=0; + char a=0,b=0; + call_signal_connection_t *mcon=&server.mcon; + call_signal_connection_t *mconp=&server.mconp; + + pthread_mutex_lock(&server.thread_count_lock); + server.thread_count++; + pthread_mutex_unlock(&server.thread_count_lock); + + woomera_set_flag(&server.master_connection, WFLAG_MONITOR_RUNNING); + + if (call_signal_connection_open(mcon, + mcon->cfg.local_ip, + mcon->cfg.local_port, + mcon->cfg.remote_ip, + mcon->cfg.remote_port) < 0) { + log_printf(SMG_LOG_ALL, server.log, "Error: Opening MCON Socket [%d] %s\n", + mcon->socket,strerror(errno)); + exit(-1); + } + + if (call_signal_connection_open(mconp, + mconp->cfg.local_ip, + mconp->cfg.local_port, + mconp->cfg.remote_ip, + mconp->cfg.remote_port) < 0) { + log_printf(SMG_LOG_ALL, server.log, "Error: Opening MCONP Socket [%d] %s\n", + mconp->socket,strerror(errno)); + exit(-1); + } + + mcon->log = server.log; + mconp->log = server.log; + + isup_exec_commandp(0, + 0, + -1, + SIGBOOST_EVENT_SYSTEM_RESTART, + 0); + + woomera_set_flag(&server.master_connection, WFLAG_SYSTEM_NEED_RESET_ACK); + woomera_set_flag(&server.master_connection, WFLAG_SYSTEM_RESET); + + smg_get_current_priority(&policy,&priority); + + log_printf(SMG_LOG_PROD, server.log, "Open udp socket [%d] [%d]\n", + mcon->socket,mconp->socket); + log_printf(SMG_LOG_PROD, server.log, "Monitor Thread Started (%i:%i)\n",policy,priority); + + + while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { +#if 0 + ss = waitfor_socket(server.mcon.socket, 1000, POLLERR | POLLIN); +#else + ss = waitfor_2sockets(mcon->socket, + mconp->socket, + &a, &b, 1000); +#endif + + if (ss > 0) { + + call_signal_event_t *event=NULL; + int i=0; + + if (b) { +mcon_retry_priority: + if ((event = call_signal_connection_readp(mconp,i))) { + log_printf(SMG_LOG_DEBUG_9, server.log, "Socket Event P [%s] \n", + call_signal_event_id_name(event->event_id)); + parse_ss7_event(mconp,(short_signal_event_t*)event); + if (++i < 10) { + goto mcon_retry_priority; + } + + } else if (errno != EAGAIN) { + ss=-1; + log_printf(SMG_LOG_ALL, server.log, + "Error: Reading from Boost P Socket! (%i) %s\n", + errno,strerror(errno)); + break; + } + } + + i=0; + + if (a) { + if ((event = call_signal_connection_read(mcon,i))) { + log_printf(SMG_LOG_DEBUG_9, server.log, "Socket Event [%s]\n", + call_signal_event_id_name(event->event_id)); + parse_ss7_event(mcon,(short_signal_event_t*)event); + + } else if (errno != EAGAIN) { + ss=-1; + log_printf(SMG_LOG_ALL, server.log, + "Error: Reading from Boost Socket! (%i) %s\n", + errno,strerror(errno)); + break; + } + } + + } + + if (ss < 0){ + log_printf(SMG_LOG_ALL, server.log, "Thread Run: Select Socket Error!\n"); + break; + } + + if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) && + !woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_NEED_RESET_ACK)) { + short_signal_event_t event; + struct timeval current; + int elapsed; + gettimeofday(¤t,NULL); + + elapsed=smg_calc_elapsed(&server.restart_timeout, ¤t); + if (elapsed > 5000) { + int err; + log_printf(SMG_LOG_ALL, server.log, "Reset Condition Cleared Elapsed=%i!\n",elapsed); + clear_all_holding_tank(); + memset(&event,0,sizeof(event)); + event.event_id = SIGBOOST_EVENT_SYSTEM_RESTART_ACK; + err=call_signal_connection_write(&server.mcon, (call_signal_event_t*)&event); + if (err < 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", + strerror(errno)); + } + int i=0; + for ( i =0 ; i < SMG_MAX_TG ; i++ ){ + smg_all_ckt_busy(i); + } + woomera_clear_flag(&server.master_connection, WFLAG_SYSTEM_RESET); + } + } + + } + + log_printf(SMG_LOG_PROD, server.log, "Close udp socket [%d] [%d]\n", + mcon->socket,mconp->socket); + call_signal_connection_close(&server.mcon); + call_signal_connection_close(&server.mconp); + + pthread_mutex_lock(&server.thread_count_lock); + server.thread_count--; + pthread_mutex_unlock(&server.thread_count_lock); + + woomera_clear_flag(&server.master_connection, WFLAG_MONITOR_RUNNING); + log_printf(SMG_LOG_ALL, server.log, "Monitor Thread Ended\n"); + + return NULL; +} + +static void woomera_loop_thread_run(struct woomera_interface *woomera) +{ + int err=launch_media_thread(woomera); + if (err) { + log_printf(SMG_LOG_ALL, server.log, "Failed to start loop media thread\n"); + woomera_set_flag(woomera, + (WFLAG_HANGUP|WFLAG_MEDIA_END)); + woomera_clear_flag(woomera, WFLAG_RUNNING); + return; + } + + while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING) && + !woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) && + !woomera_test_flag(woomera, WFLAG_MEDIA_END) && + !woomera_test_flag(woomera, WFLAG_HANGUP)) { + + usleep(300000); + continue; + + } + + woomera_clear_flag(woomera, WFLAG_RUNNING); + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Woomera Session: For Loop Test exiting %s\n",woomera->interface); + + return; +} + +static void woomera_check_digits (struct woomera_interface *woomera) +{ + int span, chan; + + if (!strlen(woomera->session)) { + return; + } + + if (get_span_chan_from_interface(woomera->interface, &span, &chan) == 0) { + pthread_mutex_lock(&server.digits_lock); + if (server.process_table[span-1][chan-1].digits_len > 0) { + int i; + unsigned char digit; + + for (i=0; i < server.process_table[span-1][chan-1].digits_len; i++) { + digit = server.process_table[span-1][chan-1].digits[i]; + + handle_event_dtmf(woomera, digit); + } + + server.process_table[span-1][chan-1].digits_len = 0; + } + pthread_mutex_unlock(&server.digits_lock); + } +} + + +static void *woomera_thread_run(void *obj) +{ + struct woomera_interface *woomera = obj; + struct woomera_message wmsg; + struct woomera_event wevent; + char *event_string; + int mwi; + int err; + int policy=0, priority=0; + int span = -1, chan = -1; + + woomera_message_init(&wmsg); + + smg_get_current_priority(&policy,&priority); + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "WOOMERA session started (ptr=%p : loop=%i)(%i:%i) Index=%i\n", + woomera,woomera->loop_tdm,policy,priority, woomera->index); + + pthread_mutex_lock(&server.thread_count_lock); + server.thread_count++; + pthread_mutex_unlock(&server.thread_count_lock); + + pthread_mutex_init(&woomera->queue_lock, NULL); + pthread_mutex_init(&woomera->ms_lock, NULL); + pthread_mutex_init(&woomera->dtmf_lock, NULL); + pthread_mutex_init(&woomera->vlock, NULL); + pthread_mutex_init(&woomera->flags_lock, NULL); + + if (woomera->loop_tdm) { + /* We must set woomera socket to -1 otherwise + a valid file descriptor will get closed */ + woomera->socket = -1; + woomera_loop_thread_run(woomera); + goto woomera_session_close; + } + + err=socket_printf(woomera->socket, + "EVENT HELLO Sangoma Media Gateway%s" + "Supported-Protocols: TDM%s" + "Version: %s%s" + "Remote-Address: %s%s" + "Remote-Port: %d%s" + "Raw-Format: %s%s" + "xUDP-Seq: %s%s" + "xUDP-Seq-Err: %d%s" + "xNative-Bridge: %s%s", + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + SMG_VERSION, WOOMERA_LINE_SEPERATOR, + inet_ntoa(woomera->addr.sin_addr), WOOMERA_LINE_SEPERATOR, + ntohs(woomera->addr.sin_port), WOOMERA_LINE_SEPERATOR, + server.hw_coding?"ALAW":"ULAW", WOOMERA_LINE_SEPERATOR, + server.udp_seq?"Enabled":"Disabled", WOOMERA_LINE_SEPERATOR, + server.media_rx_seq_err, WOOMERA_LINE_SEPERATOR, + server.media_pass_through?"Enabled":"Disabled", WOOMERA_RECORD_SEPERATOR + ); + + if (err) { + log_printf(SMG_LOG_ALL, server.log, "Woomera session socket failure! (ptr=%p)\n", + woomera); + woomera_clear_flag(woomera, WFLAG_RUNNING); + goto woomera_session_close; + } + + while ( woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING) && + woomera_test_flag(woomera, WFLAG_RUNNING) && + !woomera_test_flag(woomera, WFLAG_MEDIA_END) && + !woomera_test_flag(woomera, WFLAG_HANGUP)) { + + mwi = woomera_message_parse(woomera, &wmsg, WOOMERA_HARD_TIMEOUT); + if (mwi >= 0) { + + if (mwi) { + interpret_command(woomera, &wmsg); + } else if (woomera_test_flag(woomera, WFLAG_EVENT)){ + while ((event_string = dequeue_event(woomera))) { + if (socket_printf(woomera->socket, "%s", event_string)) { + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_RUNNING); + smg_free(event_string); + log_printf(SMG_LOG_DEBUG_8, server.log, + "WOOMERA session (ptr=%p) print string error\n", + woomera); + break; + } + smg_free(event_string); + } + woomera_clear_flag(woomera, WFLAG_EVENT); + } + + if(woomera->check_digits) { + woomera_check_digits(woomera); + woomera->check_digits = 0; + } + + if (woomera->timeout > 0 && time(NULL) >= woomera->timeout) { + + /* Sent the hangup only after we sent a NACK */ + + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "WOOMERA session Call Timedout ! [%s]\n", + woomera->interface); + + /* Let the Index check below send a NACK */ + if (woomera->index) { + woomera->q931_rel_cause_tosig=19; + woomera_set_flag(woomera, WFLAG_HANGUP); + } + break; + } + + } else { + log_printf(SMG_LOG_DEBUG_MISC, server.log, "WOOMERA session (ptr=%p) [%s] READ MSG Error %i \n", + woomera,woomera->interface,mwi); + break; + } + + } + +woomera_session_close: + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "WOOMERA session (ptr=%p) is dying [%s]: SR=%d WR=%d WF=0x%04X\n", + woomera,woomera->interface, + woomera_test_flag(&server.master_connection, WFLAG_RUNNING), + woomera_test_flag(woomera, WFLAG_RUNNING), + woomera->flags); + + + if (woomera_test_flag(woomera, WFLAG_MEDIA_RUNNING)) { + woomera_set_flag(woomera, WFLAG_MEDIA_END); + while(woomera_test_flag(woomera, WFLAG_MEDIA_RUNNING)) { + usleep(100); + sched_yield(); + } + } + + + /*********************************************** + * Identify the SPAN CHAN to be used below + ***********************************************/ + + chan = woomera->chan; + span = woomera->span; + + + if (!woomera_test_flag(woomera, WFLAG_HANGUP)) { + + /* The call was not HUNGUP. This is the last check, + If the call is valid, hungup the call if the call + was never up the keep going */ + + + if (smg_validate_span_chan(span,chan) == 0) { + + if (!woomera->index) { + + if (autoacm || woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + + woomera_set_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + isup_exec_command(span, + chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED, + woomera->q931_rel_cause_tosig); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK_SENT); + + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Woomera Sent SIGBOOST_EVENT_CALL_STOPPED [s%dc%d] [%s] ptr=%p\n", + span+1, chan+1,woomera->interface,woomera); + } else { + + woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + isup_exec_command(span, + chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + woomera->q931_rel_cause_tosig); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Woomera Sent SIGBOOST_EVENT_CALL_START_NACK [s%dc%d] [%s] ptr=%p\n", + span+1, chan+1,woomera->interface,woomera); + } + } else { + log_printf(SMG_LOG_ALL, woomera->log, "Woomera Not Sent CALL STOPPED - Instead NACK [s%dc%d] [%s] ptr=%p\n", + span+1, chan+1,woomera->interface,woomera); + + } + }else{ + /* This can happend if an outgoing call times out + or gets hungup before it gets acked. Its not a + failure */ + if (!woomera->index) { + /* In this case we really failed to tx STOP */ + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "FAILED: Woomera (R) SIGBOOST_EVENT_CALL_STOPPED [s%dc%d] [%s] Index=%d ptr=%p\n", + span+1, chan+1, woomera->interface, woomera->index, woomera); + } + } + + woomera_set_flag(woomera, WFLAG_HANGUP); + + } + +woo_re_hangup: + + /* We must send a STOP ACK to boost telling it that we are done */ + if (woomera_test_flag(woomera, WFLAG_HANGUP_ACK)) { + + /* SMG received a HANGUP from boost. + We must now send back the ACK to the HANGUP. + Boost will not release channel until we + ACK the hangup */ + + if (smg_validate_span_chan(span,chan) == 0) { + + isup_exec_command(span, + chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED_ACK, + woomera->q931_rel_cause_tosig); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, + "Sent (Ack) to SIGBOOST_EVENT_CALL_STOPPED_ACK [s%dc%d] [%s] ptr=%p\n", + span+1,chan+1,woomera->interface,woomera); + + }else{ + /* This should never happen! If it does + we broke protocol */ + log_printf(SMG_LOG_ALL, woomera->log, + "FAILED: Woomera (R) SIGBOOST_EVENT_CALL_STOPPED_ACK [s%dc%d] [%s] Index=%d ptr=%p\n", + span+1, chan+1, woomera->interface, woomera->index, woomera); + } + + woomera_clear_flag(woomera, WFLAG_HANGUP_ACK); + woomera_set_flag(woomera, WFLAG_HANGUP); + } + + if (woomera_test_flag(woomera, WFLAG_HANGUP_NACK_ACK)) { + + /* SMG received a NACK from boost during call startup. + We must now send back the ACK to the NACK. + Boost will not release channel until we + ACK the NACK */ + + if (smg_validate_span_chan(span,chan) == 0) { + + isup_exec_command(span, + chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + woomera->q931_rel_cause_tosig); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, + "Sent (Nack Ack) to SIGBOOST_EVENT_CALL_START_NACK_ACK [s%dc%d] [%s] ptr=%p\n", + span+1,chan+1,woomera->interface,woomera); + + woomera->index=0; + + } else if (woomera->index) { + isup_exec_command(0, + 0, + woomera->index, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + woomera->q931_rel_cause_tosig); + + woomera->index=0; + + } else { + log_printf(SMG_LOG_ALL, woomera->log, + "FAILED: Sent (Nack Ack) SIGBOOST_EVENT_CALL_START_NACK_ACK [s%dc%d] [%s] Index=%d ptr=%p\n", + span+1, chan+1, woomera->interface, woomera->index, woomera); + } + + woomera_clear_flag(woomera, WFLAG_HANGUP_NACK_ACK); + + } + + if (woomera->index) { + + int index = woomera->index; + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Timeout: %ld%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + woomera->interface, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + woomera->timeout, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(18), + WOOMERA_LINE_SEPERATOR, + 18, + WOOMERA_RECORD_SEPERATOR + ); + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + while ((event_string = dequeue_event(woomera))) { + socket_printf(woomera->socket, "%s", event_string); + smg_free(event_string); + } + + if (peek_from_holding_tank(index)) { + + woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + isup_exec_command(0, + 0, + index, + SIGBOOST_EVENT_CALL_START_NACK, + woomera->q931_rel_cause_tosig); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT); + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, + "Sent SIGBOOST_EVENT_CALL_START_NACK [Setup ID: %d] .. WAITING FOR NACK ACK\n", + index); + } else { + log_printf(SMG_LOG_PROD, woomera->log, + "Error Failed to Sent SIGBOOST_EVENT_CALL_START_NACK [Setup ID: %d] - index stale!\n", + index); + } + } + + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK)) { + int timeout_cnt=0; + int overall_cnt=0; + + /* SMG sent NACK to boost, however we have to wait + for boost to give us the ACK back before we + release resources. */ + + while (woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK)) { + timeout_cnt++; + if (timeout_cnt > 100) { //5sec timeout + timeout_cnt=0; + overall_cnt++; + + log_printf((overall_cnt==1)?0:4, woomera->log, + "Waiting for NACK ACK [Setup ID: %d] ... \n", + woomera->index_hold); + } + + if (overall_cnt > 15) { //50sec timeout + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING)) { + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ + break; + } + + /* If ACK comes in while we wait for NACK ACK, the ACk will + clear the tank causing NACK ACK never to clear the waiting flag + in this case we abort waiting for NACK ACK and we timeout + the wait */ + if (woomera_test_flag(&server.master_connection, WFLAG_CALL_ACKED)){ + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + + usleep(50000); + sched_yield(); + } + + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + + /* If ACK came in while waiting for NACK ACK, we TIMEOUT on purpose. + The ACK has blocked the tank id and now ony NACK ACK can unblock it. + THe only problem is that we blind and have to assume that NACK ACK + will eventually come in :) */ + if (!woomera_test_flag(&server.master_connection, WFLAG_CALL_ACKED)) { + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT)) { + log_printf(SMG_LOG_ALL, woomera->log, + "Waiting for NACK ACK [Setup ID: %d] .. TIMEOUT on NACK ACK\n", + woomera->index_hold); + + } else { + log_printf(SMG_LOG_DEBUG_8, woomera->log, + "Waiting for NACK ACK [Setup ID: %d] .. GOT NACK ACK\n", + woomera->index_hold); + } + } + } + + if (woomera_test_flag(woomera, WFLAG_EVENT)){ + while ((event_string = dequeue_event(woomera))) { + socket_printf(woomera->socket, "%s", event_string); + smg_free(event_string); + } + woomera_clear_flag(woomera, WFLAG_EVENT); + } + + + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK)) { + int timeout_cnt=0; + int overall_cnt=0; + + /* SMG sent HANGUP to boost, however we have to wait + for boost to give us the ACK back before we + release resources. */ + + log_printf(SMG_LOG_DEBUG_8, woomera->log, + "Waiting for STOPPED ACK [%s] [id=%i]... \n", + woomera->interface,woomera->index_hold); + + while (woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK)) { + timeout_cnt++; + if (timeout_cnt > 100) { //5sec timeout + timeout_cnt=0; + overall_cnt++; + log_printf((overall_cnt==1)?0:4, woomera->log, + "Waiting for STOPPED ACK [%s] [id=%i] %i... \n", + woomera->interface,woomera->index_hold,overall_cnt); + } + + if (overall_cnt > 15) { //50sec + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING)) { + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + + if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ + break; + } + + usleep(50000); + sched_yield(); + } + + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT)) { + log_printf(SMG_LOG_ALL, woomera->log, + "Wait TIMEDOUT on STOPPED ACK [%s] [id=%i]... \n", + woomera->interface,woomera->index_hold); + + } else { + log_printf(SMG_LOG_DEBUG_8, woomera->log, + "Wait GOT STOPPED ACK [%s] [id=%i]... \n", + woomera->interface,woomera->index_hold); + } + } + + /***************************************************** + * We must wait for WFLAG_WAIT_FOR_STOPPED_ACK here + * so that STOP_ACK can access the woomera + *****************************************************/ + + if (smg_validate_span_chan(span,chan) == 0) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, + "WOOMERA Clearing Processs Table ... \n", + woomera->interface); + pthread_mutex_lock(&server.process_lock); + if (server.process_table[span][chan].dev == woomera){ + server.process_table[span][chan].dev = NULL; + memset(server.process_table[span][chan].session,0,SMG_SESSION_NAME_SZ); + memset(server.process_table[span][chan].digits,0, + sizeof(server.process_table[span][chan].digits)); + server.process_table[span][chan].digits_len = 0; + } + pthread_mutex_unlock(&server.process_lock); + } + +#if 0 +//Used for testing + if (1) { + int chan = woomera->chan; + int span = woomera->span; + if (smg_validate_span_chan(span,chan) == 0) { + pthread_mutex_lock(&server.process_lock); + /* This is possible in case media thread dies on startup */ + + if (server.process_table[span][chan]){ + log_printf(SMG_LOG_ALL, server.log, + "Sanity Span Chan Still in use: [s%dc%d] [%s] Index=%d ptr=%p\n", + span+1, chan+1, woomera->interface, woomera->index, woomera); + //server.process_table[span][chan] = NULL; + } + pthread_mutex_unlock(&server.process_lock); + } + } +#endif + + usleep(3000000); + + /* Sanity Check */ + if (woomera_test_flag(woomera, WFLAG_HANGUP_ACK)) { + log_printf(SMG_LOG_ALL, woomera->log, + "Woomera MISSED HANGUP ACK: Retry HANGUP ACK\n"); + goto woo_re_hangup; + } + if (woomera_test_flag(woomera, WFLAG_HANGUP_NACK_ACK)) { + log_printf(SMG_LOG_ALL, woomera->log, + "Woomera MISSED HANGUP ACK: Retry HANGUP NACK ACK\n"); + goto woo_re_hangup; + } + + + /* This is where we actually pull the index + * out of the tank. We had to keep the tank + * value until the end of the call. Tank is only + * used on outgoing calls. */ + if (woomera->index_hold >= 1) { + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT)) { + /* Replace real woomera interface with a dummy so + the tank is blocked. The real woomera interface will be + deallocated */ + pull_from_holding_tank(woomera->index_hold,-1,-1); + + if (check_tank_index(woomera->index_hold) != NULL){ + log_printf(SMG_LOG_PROD, woomera->log, "Woomera Thread: [%s] setup id %i blocked waiting for NACK or ACK\n", + woomera->interface ,woomera->index_hold); + } + } else { + clear_from_holding_tank(woomera->index_hold, woomera); + + } + woomera->index_hold=0; + } + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "Woomera Thread Finished %u\n", (unsigned long) woomera->thread); + close_socket(&woomera->socket); + woomera->socket=-1; + + /* delete queue */ + while ((event_string = dequeue_event(woomera))) { + smg_free(event_string); + } + + if (woomera_test_flag(woomera, WFLAG_LISTENING)) { + del_listener(woomera); + } + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "WOOMERA session for [%s] stopped (ptr=%p)\n", + woomera->interface,woomera); + + pthread_mutex_destroy(&woomera->queue_lock); + pthread_mutex_destroy(&woomera->ms_lock); + pthread_mutex_destroy(&woomera->dtmf_lock); + pthread_mutex_destroy(&woomera->vlock); + pthread_mutex_destroy(&woomera->flags_lock); + woomera_set_raw(woomera, NULL); + woomera_set_interface(woomera, NULL); + + woomera_message_clear(&wmsg); + + smg_free(woomera); + pthread_mutex_lock(&server.thread_count_lock); + server.call_count--; + server.thread_count--; + pthread_mutex_unlock(&server.thread_count_lock); + + pthread_exit(NULL); + return NULL; +} + + +static int launch_woomera_thread(struct woomera_interface *woomera) +{ + int result = 0; + pthread_attr_t attr; + + result = pthread_attr_init(&attr); + //pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + //pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + woomera_set_flag(woomera, WFLAG_RUNNING); + result = pthread_create(&woomera->thread, &attr, woomera_thread_run, woomera); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Woomera Thread! (%i) %s\n", + __FUNCTION__,result,strerror(errno)); + woomera_clear_flag(woomera, WFLAG_RUNNING); + } + pthread_attr_destroy(&attr); + + return result; +} + +static int launch_monitor_thread(void) +{ + pthread_attr_t attr; + int result = 0; + struct sched_param param; + + param.sched_priority = 10; + result = pthread_attr_init(&attr); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + result = pthread_attr_setschedparam (&attr, ¶m); + + log_printf(SMG_LOG_ALL,server.log,"%s: Old Priority =%i res=%i \n",__FUNCTION__, + param.sched_priority,result); + + + woomera_set_flag(&server.master_connection, WFLAG_MONITOR_RUNNING); + result = pthread_create(&server.monitor_thread, &attr, monitor_thread_run, NULL); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Thread! %s\n", + __FUNCTION__,strerror(errno)); + woomera_clear_flag(&server.master_connection, WFLAG_MONITOR_RUNNING); + } + pthread_attr_destroy(&attr); + + return result; +} + + +#ifdef WP_HPTDM_API +static void *hp_tdmapi_span_run(void *obj) +{ + hp_tdm_api_span_t *span = obj; + int err; + + log_printf(SMG_LOG_ALL,server.log,"Starting %s span!\n",span->ifname); + + while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + + if (!span->run_span) { + break; + } + + err = span->run_span(span); + if (err) { + break; + } + + } + + if (span->close_span) { + span->close_span(span); + } + + sleep(3); + log_printf(SMG_LOG_ALL,server.log,"Stopping %s span!\n",span->ifname); + + pthread_exit(NULL); +} + + +static int launch_hptdm_api_span_thread(int span) +{ + pthread_attr_t attr; + int result = 0; + struct sched_param param; + + param.sched_priority = 5; + result = pthread_attr_init(&attr); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + result = pthread_attr_setschedparam (&attr, ¶m); + + result = pthread_create(&server.monitor_thread, &attr, hp_tdmapi_span_run, &hptdmspan[span]); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Thread! %s\n", + __FUNCTION__,strerror(errno)); + } + pthread_attr_destroy(&attr); + + return result; +} +#endif + +static int configure_server(void) +{ + struct woomera_config cfg; + char *var, *val; + int cnt = 0; + struct smg_tdm_ip_bridge *ip_bridge=NULL; + + + server.dtmf_intr_ch = -1; + + if (!woomera_open_file(&cfg, server.config_file)) { + log_printf(SMG_LOG_ALL, server.log, "open of %s failed\n", server.config_file); + return 0; + } + + log_printf(SMG_LOG_ALL,server.log, "\n======================= \n"); + log_printf(SMG_LOG_ALL,server.log, "Server - Configuration \n"); + log_printf(SMG_LOG_ALL,server.log, "======================= \n"); + + while (woomera_next_pair(&cfg, &var, &val)) { + if (!strcasecmp(var, "boost_local_ip")) { + strncpy(server.mcon.cfg.local_ip, val, + sizeof(server.mcon.cfg.local_ip) -1); + strncpy(server.mconp.cfg.local_ip, val, + sizeof(server.mconp.cfg.local_ip) -1); + log_printf(SMG_LOG_ALL,server.log, "Server - Boost Local IP: %s\n",val); + + cnt++; + } else if (!strcasecmp(var, "boost_local_port")) { + server.mcon.cfg.local_port = atoi(val); + server.mconp.cfg.local_port = + server.mcon.cfg.local_port+1; + log_printf(SMG_LOG_ALL,server.log, "Server - Boost Local Port: %i\n",server.mcon.cfg.local_port); + cnt++; + } else if (!strcasecmp(var, "boost_remote_ip")) { + strncpy(server.mcon.cfg.remote_ip, val, + sizeof(server.mcon.cfg.remote_ip) -1); + strncpy(server.mconp.cfg.remote_ip, val, + sizeof(server.mconp.cfg.remote_ip) -1); + log_printf(SMG_LOG_ALL,server.log, "Server - Boost Remote IP: %s\n",server.mcon.cfg.remote_ip); + cnt++; + } else if (!strcasecmp(var, "boost_remote_port")) { + server.mcon.cfg.remote_port = atoi(val); + server.mconp.cfg.remote_port = + server.mcon.cfg.remote_port+1; + log_printf(SMG_LOG_ALL,server.log, "Server - Boost Remote Port: %i\n",server.mcon.cfg.local_port); + cnt++; + } else if (!strcasecmp(var, "logfile_path")) { + if (!server.logfile_path) { + server.logfile_path = smg_strdup(val); + } + } else if (!strcasecmp(var, "woomera_port")) { + server.port = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Woomera Port: %i\n",server.port); + } else if (!strcasecmp(var, "debug_level")) { + server.debug = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Debug Level: %i\n",server.debug); + } else if (!strcasecmp(var, "out_tx_test")) { + server.out_tx_test = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Tx Media Dbg: %s\n",server.out_tx_test?"On":"Off (Default)"); + } else if (!strcasecmp(var, "loop_trace")) { + server.loop_trace = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Media Loop Trace: %s\n",server.loop_trace?"On":"Off (Default)"); + } else if (!strcasecmp(var, "rxgain")) { + server.rxgain = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Rx Gain: %d\n",server.rxgain); + } else if (!strcasecmp(var, "txgain")) { + server.txgain = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Tx Gain: %d\n",server.txgain); + } else if (!strcasecmp(var, "dtmf_on_duration")){ + server.dtmf_on = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - DTMF ON Duration: %d ms\n",server.dtmf_on); + } else if (!strcasecmp(var, "dtmf_off_duration")){ + server.dtmf_off = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - DTMF Off Duration: %d ms\n",server.dtmf_off); + } else if (!strcasecmp(var, "dtmf_inter_ch_duration")){ + server.dtmf_intr_ch = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - DTMF Spacing: %d\n",server.dtmf_intr_ch); + } else if (!strcasecmp(var, "strip_cid_non_digits")){ + server.strip_cid_non_digits = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Strip non digits: %d\n",server.strip_cid_non_digits); + } else if (!strcasecmp(var, "max_calls")) { + int max = atoi(val); + if (max > 0) { + server.max_calls = max; + log_printf(SMG_LOG_ALL,server.log, "Server - Max Active Calls: %d\n",server.max_calls); + } + } else if (!strcasecmp(var, "autoacm")) { + int max = atoi(val); + if (max >= 0) { + autoacm=max; + log_printf(SMG_LOG_ALL,server.log, "Server - Auto ACM: %s\n",autoacm?"On":"Off (Default)"); + } + } else if (!strcasecmp(var, "max_spans")) { + int max = atoi(val); + if (max > 0) { + max_spans = max; + log_printf(SMG_LOG_ALL,server.log, "Server - Max Spans: %d\n",max_spans); + } + } else if (!strcasecmp(var, "call_timeout")) { + int max = atoi(val); + if (max >= 0) { + server.call_timeout=max; + } else { + server.call_timeout=SMG_DEFAULT_CALL_TIMEOUT; + } + log_printf(SMG_LOG_ALL,server.log, "Server - Call Comp Timeout: %d s\n",server.call_timeout); + + } else if (!strcasecmp(var, "base_media_port")) { + int base = atoi(val); + if (base >= 0) { + server.base_media_port = base; + server.next_media_port = base; + server.max_media_port = server.base_media_port + WOOMERA_MAX_MEDIA_PORTS; + log_printf(SMG_LOG_ALL,server.log, "Server - Base Media Port: %d\n",server.base_media_port); + } + + } else if (!strcasecmp(var, "max_media_ports")) { + int max = atoi(val); + if (max >= 0) { + server.max_media_port = server.base_media_port+max; + log_printf(SMG_LOG_ALL,server.log, "Server - Max Media Port: %d\n",server.max_media_port); + } + + } else if (!strcasecmp(var, "media_ip")) { + strncpy(server.media_ip, val, sizeof(server.media_ip) -1); + log_printf(SMG_LOG_ALL,server.log, "Server - Media IP: %s\n",server.media_ip); + + } else if (!strcasecmp(var, "udp_seq")) { + int max = atoi(val); + if (max > 0) { + server.udp_seq=max; + log_printf(SMG_LOG_ALL, server.log, "Server - UDP Seq: %s\n",server.udp_seq?"Enabled":"Disabled"); + } + + } else if (!strcasecmp(var, "media_pass_through")) { + int max = atoi(val); + if (max > 0) { + server.media_pass_through=max; + log_printf(SMG_LOG_ALL, server.log, "Server - Media Pass Through: %s\n",server.media_pass_through?"Enabled":"Disabled"); + } + + + } else if (!strcasecmp(var, "rbs_relay")) { + int max = atoi(val); + if (max > 0) { + server.rbs_relay[max]=max; + log_printf(SMG_LOG_ALL, server.log, "Server - RBS Relay Span: %d\n",max); + } + + } else if (!strcasecmp(var, "bridge_tdm_ip")) { + int err=smg_get_ip_bridge_session(&ip_bridge); + if (err) { + log_printf(SMG_LOG_ALL, server.log, "Error failed to get free ip bridge %i!\n",err); + } else { + log_printf(SMG_LOG_ALL,server.log, "\n======================= \n"); + log_printf(SMG_LOG_ALL,server.log, "Bridge - Configuration \n"); + log_printf(SMG_LOG_ALL,server.log, "======================= \n"); + } + + } else if (!strcasecmp(var, "bridge_span")) { + int max = atoi(val); + if (max > 0 && max <= 32 && ip_bridge) { + ip_bridge->span=max; + log_printf(SMG_LOG_ALL, server.log, "Bridge Span: %i\n",ip_bridge->span); + } else { + log_printf(SMG_LOG_ALL, server.log, "Bridge Span: ERROR: Invalid Value %s\n",val); + } + } else if (!strcasecmp(var, "bridge_chan")) { + int max = atoi(val); + if (max > 0 && max < MAX_SMG_BRIDGE && ip_bridge) { + ip_bridge->chan=max; + log_printf(SMG_LOG_ALL, server.log, "Bridge Chan: %i\n",ip_bridge->chan); + } else { + log_printf(SMG_LOG_ALL, server.log, "Bridge Chan: ERROR: Invalid Value %s\n",val); + } + } else if (!strcasecmp(var, "bridge_local_ip")) { + if (ip_bridge) { + strncpy(ip_bridge->mcon.cfg.local_ip, val, + sizeof(ip_bridge->mcon.cfg.local_ip) -1); + log_printf(SMG_LOG_ALL, server.log, "Bridge Local IP: %s\n",ip_bridge->mcon.cfg.local_ip); + } + } else if (!strcasecmp(var, "bridge_remote_ip")) { + if (ip_bridge) { + strncpy(ip_bridge->mcon.cfg.remote_ip, val, + sizeof(ip_bridge->mcon.cfg.remote_ip) -1); + log_printf(SMG_LOG_ALL, server.log, "Bridge Remote IP: %s\n",ip_bridge->mcon.cfg.remote_ip); + } + } else if (!strcasecmp(var, "bridge_port")) { + int max = atoi(val); + if (max > 0 && ip_bridge) { + ip_bridge->mcon.cfg.local_port=max; + ip_bridge->mcon.cfg.remote_port=max; + log_printf(SMG_LOG_ALL, server.log, "Bridge Port: %i\n",max); + } + } else if (!strcasecmp(var, "bridge_period")) { + int max = atoi(val); + if (max > 0 && ip_bridge) { + ip_bridge->period=max; + log_printf(SMG_LOG_ALL, server.log, "Bridge Period: %ims\n",ip_bridge->period); + } + } else { + log_printf(SMG_LOG_ALL, server.log, "Invalid Option %s at line %d!\n", var, cfg.lineno); + } + } + + log_printf(SMG_LOG_ALL,server.log, "======================= \n\n"); + + /* Post initialize */ + if (server.dtmf_on == 0){ + server.dtmf_on=SMG_DTMF_ON; + } + if (server.dtmf_off == 0) { + server.dtmf_off=SMG_DTMF_OFF; + } + if (server.dtmf_intr_ch == -1) { + server.dtmf_intr_ch = 0; + } + server.dtmf_size=(server.dtmf_on+server.dtmf_off)*10*2; + + log_printf(SMG_LOG_ALL,server.log, "DTMF On=%i Off=%i IntrCh=%i Size=%i\n", + server.dtmf_on,server.dtmf_off,server.dtmf_intr_ch,server.dtmf_size); + + woomera_close_file(&cfg); + return cnt == 4 ? 1 : 0; +} + + + +static int main_thread(void) +{ + + struct sockaddr_in sock_addr, client_addr; + struct woomera_interface *new_woomera; + int client_sock = -1, pid = 0; + unsigned int len = 0; + FILE *tmp; + + if ((server.master_connection.socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + fprintf(stderr,"%s:%d socket() failed %s\n",__FUNCTION__,__LINE__,strerror(errno)); + return 1; + } + + memset(&sock_addr, 0, sizeof(sock_addr)); /* Zero out structure */ + sock_addr.sin_family = AF_INET; /* Internet address family */ + sock_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ + sock_addr.sin_port = htons(server.port); /* Local port */ + + /* Bind to the local address */ + if (bind(server.master_connection.socket, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) < 0) { + fprintf(stderr,"%s:%d socket() bind %i failed %s\n",__FUNCTION__,__LINE__,server.port,strerror(errno)); + log_printf(SMG_LOG_ALL, server.log, "bind(%d) failed\n", server.port); + return 1; + } + + /* Mark the socket so it will listen for incoming connections */ + if (listen(server.master_connection.socket, MAXPENDING) < 0) { + fprintf(stderr,"%s:%d socket() listen failed %s\n",__FUNCTION__,__LINE__,strerror(errno)); + log_printf(SMG_LOG_ALL, server.log, "listen() failed\n"); + return 1; + } + + if ((pid = get_pid_from_file(PIDFILE))) { + fprintf(stderr,"%s:%d get pid file failed %s\n",__FUNCTION__,__LINE__,strerror(errno)); + log_printf(SMG_LOG_ALL, stderr, "pid %d already exists.\n", pid); + exit(0); + } + + if (!(tmp = safe_fopen(PIDFILE, "w"))) { + fprintf(stderr,"%s:%d open pid file failed %s\n",__FUNCTION__,__LINE__,strerror(errno)); + log_printf(SMG_LOG_ALL, stderr, "Error creating pidfile %s\n", PIDFILE); + return 1; + } else { + fprintf(tmp, "%d", getpid()); + fclose(tmp); + tmp = NULL; + } + + no_nagle(server.master_connection.socket); + +#if 0 + if (1) { + int span,chan; + call_signal_event_t event; +#if 0 + span=1; + chan=30; + event.span=span; + event.chan=chan; + launch_woomera_loop_thread(&event); +#else + for (span=0;span<8;span++) { + for (chan=0;chan<31;chan++) { + event.span=span; + event.chan=chan; + launch_woomera_loop_thread(&event); + } + } +#endif + } +#endif + +#ifdef WP_HPTDM_API + if (1) { + int span; + for (span=0;span<16;span++) { + hptdmspan[span] = sangoma_hptdm_api_span_init(span); + if (!hptdmspan[span]) { + break; + } else { + log_printf(SMG_LOG_ALL, server.log, "HP TDM API Span: %d configured...\n", + span); + launch_hptdm_api_span_thread(span); + } + } + } +#endif + + log_printf(SMG_LOG_PROD, server.log, "Main Process Started: Woomera Ready port: %d\n", server.port); + + while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + + /* Set the size of the in-out parameter */ + len = sizeof(client_addr); + + /* Wait for a client to connect */ + if ((client_sock = accept(server.master_connection.socket, (struct sockaddr *) &client_addr, &len)) < 0) { + /* Check if we are supposed to stop */ + if(!woomera_test_flag(&server.master_connection, WFLAG_RUNNING)) { + log_printf(SMG_LOG_ALL, server.log, "accept() stopped\n"); + return 0; + } + log_printf(SMG_LOG_ALL, server.log, "accept() failed\n"); + return 1; + } + + if ((new_woomera = new_woomera_interface(client_sock, &client_addr, len))) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Starting Thread for New Connection %s:%d Sock=%d\n", + inet_ntoa(new_woomera->addr.sin_addr), + ntohs(new_woomera->addr.sin_port), + client_sock); + + pthread_mutex_lock(&server.thread_count_lock); + server.call_count++; + pthread_mutex_unlock(&server.thread_count_lock); + + + if (launch_woomera_thread(new_woomera)) { + socket_printf(new_woomera->socket, + "501 call was cancelled!%s", + WOOMERA_RECORD_SEPERATOR); + + close_socket(&new_woomera->socket); + new_woomera->socket=-1; + smg_free(new_woomera); + log_printf(SMG_LOG_ALL, server.log, "ERROR: failed to launch woomera thread\n"); + } + } else { + log_printf(SMG_LOG_ALL, server.log, "Critical ERROR: memory/socket error\n"); + } + } + + log_printf(SMG_LOG_PROD, server.log, "Main Process End\n"); + + return 0; +} + +static int do_ignore(int sig) + +{ +#ifdef SMG_DROP_SEQ + drop_seq=4; +#endif + sdla_memdbg_free(0); + return 0; +} + +static int do_shut(int sig) +{ + woomera_clear_flag(&server.master_connection, WFLAG_RUNNING); + close_socket(&server.master_connection.socket); + log_printf(SMG_LOG_PROD, server.log, "Caught SIG %d, Closing Master Socket!\n", sig); + return 0; +} + +static int sangoma_tdm_init (int span) +{ +#ifdef LIBSANGOMA_GET_HWCODING + wanpipe_tdm_api_t tdm_api; + int fd=sangoma_open_tdmapi_span(span); + if (fd < 0 ){ + return -1; + } else { + server.hw_coding=sangoma_tdm_get_hw_coding(fd,&tdm_api); + close_socket(&fd); + } +#else +#error "libsangoma missing hwcoding feature: not up to date!" +#endif + return 0; +} + +static int woomera_startup(int argc, char **argv) +{ + int x = 0, pid = 0, bg = 0; + int span_cnt, chan_cnt; + char *cfg=NULL, *debug=NULL, *arg=NULL; + + while((arg = argv[x++])) { + + if (!strcasecmp(arg, "-hup")) { + if (! (pid = get_pid_from_file(PIDFILE))) { + log_printf(SMG_LOG_ALL, stderr, "Error reading pidfile %s\n", PIDFILE); + exit(1); + } else { + log_printf(SMG_LOG_ALL, stderr, "Killing PID %d\n", pid); + kill(pid, SIGHUP); + sleep(1); + exit(0); + } + + } else if (!strcasecmp(arg, "-term") || !strcasecmp(arg, "--term")) { + if (! (pid = get_pid_from_file(PIDFILE))) { + log_printf(SMG_LOG_ALL, stderr, "Error reading pidfile %s\n", PIDFILE); + exit(1); + } else { + log_printf(SMG_LOG_ALL, stderr, "Killing PID %d\n", pid); + kill(pid, SIGTERM); + unlink(PIDFILE); + sleep(1); + exit(0); + } + + } else if (!strcasecmp(arg, "-version")) { + fprintf(stdout, "\nSangoma Media Gateway: Version %s\n\n", SMG_VERSION); + exit(0); + + } else if (!strcasecmp(arg, "-help")) { + fprintf(stdout, "%s\n%s [-help] | [ -version] | [-hup] | [-wipe] | [[-bg] | [-debug ] | [-cfg ] | [-log ]]\n\n", WELCOME_TEXT, argv[0]); + exit(0); + } else if (!strcasecmp(arg, "-wipe")) { + unlink(PIDFILE); + } else if (!strcasecmp(arg, "-bg")) { + bg = 1; + + } else if (!strcasecmp(arg, "-g")) { + coredump = 1; + + } else if (!strcasecmp(arg, "-debug")) { + if (argv[x] && *(argv[x]) != '-') { + debug = argv[x++]; + } + } else if (!strcasecmp(arg, "-cfg")) { + if (argv[x] && *(argv[x]) != '-') { + cfg = argv[x++]; + } + } else if (!strcasecmp(arg, "-log")) { + if (argv[x] && *(argv[x]) != '-') { + server.logfile_path = smg_strdup(argv[x++]); + } + } else if (*arg == '-') { + log_printf(SMG_LOG_ALL, stderr, "Unknown Option %s\n", arg); + fprintf(stdout, "%s\n%s [-help] | [-hup] | [-wipe] | [[-bg] | [-debug ] | [-cfg ] | [-log ]]\n\n", WELCOME_TEXT, argv[0]); + exit(1); + } + } + + if (1){ + int spn; + for (spn=1;spn<=WOOMERA_MAX_SPAN;spn++) { + if (sangoma_tdm_init(spn) == 0) { + break; + } + } + if (spn>WOOMERA_MAX_SPAN) { + printf("\nError: Failed to access a channel on spans 1-16\n"); + printf(" Please start Wanpipe TDM API drivers\n"); + return 0; + } + } + + if (bg && (pid = fork())) { + log_printf(SMG_LOG_ALL, stderr, "Backgrounding!\n"); + return 0; + } + + for (span_cnt = 0; span_cnt < CORE_MAX_SPANS; span_cnt++) { + for (chan_cnt = 0; chan_cnt < CORE_MAX_CHAN_PER_SPAN; chan_cnt++) { + pthread_mutex_init(&server.process_table[span_cnt][chan_cnt].media_lock, NULL); + } + } + + q931_cause_setup(); + bearer_cap_setup(); + uil1p_to_str_setup(); + + server.port = 42420; + server.debug = 0; + strcpy(server.media_ip, "127.0.0.1"); + server.base_media_port = WOOMERA_MIN_MEDIA_PORT; + server.max_media_port = WOOMERA_MAX_MEDIA_PORT; + server.next_media_port = server.base_media_port; + server.log = stdout; + server.master_connection.socket = -1; + server.master_connection.event_queue = NULL; + server.config_file = cfg ? cfg : "/etc/sangoma_mgd.conf"; + pthread_mutex_init(&server.listen_lock, NULL); + pthread_mutex_init(&server.ht_lock, NULL); + pthread_mutex_init(&server.process_lock, NULL); + pthread_mutex_init(&server.digits_lock, NULL); + pthread_mutex_init(&server.media_udp_port_lock, NULL); + pthread_mutex_init(&server.thread_count_lock, NULL); + pthread_mutex_init(&server.master_connection.queue_lock, NULL); + pthread_mutex_init(&server.master_connection.flags_lock, NULL); + pthread_mutex_init(&server.mcon.lock, NULL); + server.master_connection.chan = -1; + server.master_connection.span = -1; + server.call_timeout=SMG_DEFAULT_CALL_TIMEOUT; + + if (smg_log_init()) { + printf("Error: Logger Launch Failed\n"); + fprintf(stderr, "Error: Logger Launch Failed\n"); + return 0; + } + + if (!configure_server()) { + log_printf(SMG_LOG_ALL, server.log, "configuration failed!\n"); + return 0; + } + +#ifndef USE_SYSLOG + if (server.logfile_path) { + if (!(server.log = safe_fopen(server.logfile_path, "a"))) { + log_printf(SMG_LOG_ALL, stderr, "Error setting logfile %s!\n", server.logfile_path); + server.log = stderr; + return 0; + } + } +#endif + + + if (debug) { + server.debug = atoi(debug); + } + + if (coredump) { + struct rlimit l; + memset(&l, 0, sizeof(l)); + l.rlim_cur = RLIM_INFINITY; + l.rlim_max = RLIM_INFINITY; + if (setrlimit(RLIMIT_CORE, &l)) { + log_printf(SMG_LOG_ALL, stderr, "Warning: Failed to disable core size limit: %s\n", + strerror(errno)); + } + } + +#ifdef __LINUX__ + if (geteuid() && coredump) { + if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) { + log_printf(SMG_LOG_ALL, stderr, "Warning: Failed to disable core size limit for non-root: %s\n", + strerror(errno)); + } + } +#endif + + + + (void) signal(SIGINT,(void *) do_shut); + (void) signal(SIGTERM,(void *) do_shut); + (void) signal(SIGPIPE,(void *) do_ignore); + (void) signal(SIGUSR1,(void *) do_ignore); + (void) signal(SIGHUP,(void *) do_shut); + + /* Wait for logger thread to start */ + usleep(5000); + + woomera_set_flag(&server.master_connection, WFLAG_RUNNING); + + if (launch_monitor_thread()) { + woomera_clear_flag(&server.master_connection, WFLAG_RUNNING); + smg_log_cleanup(); + return 0; + } + + fprintf(stderr, "%s", WELCOME_TEXT); + + log_printf(SMG_LOG_ALL, stderr, "Woomera STARTUP Complete. [AutoACM=%i SDigit=%i]\n", + autoacm,server.strip_cid_non_digits); + return 1; +} + +static int woomera_shutdown(void) +{ + char *event_string; + int told = 0, loops = 0; + int span_cnt, chan_cnt; + + for (span_cnt = 0; span_cnt < CORE_MAX_SPANS; span_cnt++) { + for (chan_cnt = 0; chan_cnt < CORE_MAX_CHAN_PER_SPAN; chan_cnt++) { + pthread_mutex_destroy(&server.process_table[span_cnt][chan_cnt].media_lock); + } + } + + close_socket(&server.master_connection.socket); + pthread_mutex_destroy(&server.listen_lock); + pthread_mutex_destroy(&server.ht_lock); + pthread_mutex_destroy(&server.process_lock); + pthread_mutex_destroy(&server.digits_lock); + pthread_mutex_destroy(&server.media_udp_port_lock); + pthread_mutex_destroy(&server.thread_count_lock); + pthread_mutex_destroy(&server.master_connection.queue_lock); + pthread_mutex_destroy(&server.master_connection.flags_lock); + pthread_mutex_destroy(&server.mcon.lock); + woomera_clear_flag(&server.master_connection, WFLAG_RUNNING); + + + if (server.logfile_path) { + smg_free(server.logfile_path); + server.logfile_path = NULL; + } + + /* delete queue */ + while ((event_string = dequeue_event(&server.master_connection))) { + smg_free(event_string); + } + + while(server.thread_count > 0) { + loops++; + + if (loops % 1000 == 0) { + told = 0; + } + + if (loops > 10000) { + log_printf(SMG_LOG_ALL, server.log, "Red Alert! threads did not stop\n"); + assert(server.thread_count == 0); + } + + if (told != server.thread_count) { + log_printf(SMG_LOG_PROD, server.log, "Waiting For %d thread%s.\n", + server.thread_count, server.thread_count == 1 ? "" : "s"); + told = server.thread_count; + } + ysleep(10000); + } + unlink(PIDFILE); + log_printf(SMG_LOG_ALL, stderr, "Woomera SHUTDOWN Complete.\n"); + + smg_log_cleanup(); + + return 0; +} + +int main(int argc, char *argv[]) +{ + int ret = 0; + + memset(&server,0,sizeof(server)); + memset(&woomera_dead_dev,0,sizeof(woomera_dead_dev)); + + mlockall(MCL_FUTURE); + memset(&server, 0, sizeof(server)); + memset(&woomera_dead_dev, 0, sizeof(woomera_dead_dev)); + memset(&g_smg_ip_bridge_idx,0, sizeof(g_smg_ip_bridge_idx)); + + ret=nice(-5); + + sdla_memdbg_init(); + + server.hw_coding=0; + + openlog (ps_progname ,LOG_PID, LOG_LOCAL2); + + if (! (ret = woomera_startup(argc, argv))) { + exit(0); + } + + ret = smg_ip_bridge_start(); + if (ret == 0) { + ret = main_thread(); + } + + woomera_shutdown(); + + smg_ip_bridge_stop(); + sdla_memdbg_free(1); + + return ret; +} + +/** EMACS ** + * Local variables: + * mode: C + * c-basic-offset: 4 + * End: + */ + diff --git a/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.5.tmp b/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.5.tmp new file mode 100644 index 0000000..2088a1e --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.5.tmp @@ -0,0 +1,170 @@ +################################################################################ +# Sangoma MGD +# +# Author: 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. +################################################################################ + +SMG_DTMF=YES + +#Default kernel directory to be overwritten by user +#Kernel version and location +ifndef KVER + KVER=$(shell uname -r) +endif +ifndef KMOD + KMOD=/lib/modules/$(KVER) +endif +ifndef KDIR + KDIR=$(KMOD)/build +endif +ifndef KINSTDIR + KINSTDIR=$(KMOD)/kernel +endif + +CC = gcc + +ifndef DESTDIR + ifdef INSTALLPREFIX + DESTDIR=$(INSTALLPREFIX) + else + DESTDIR= + endif +endif + +INCLUDES = -I ../../ssmg/libsangoma.trunk -I. -I ../../patches/kdrivers/include -I ../../patches/kdrivers/wanec/oct6100_api/include -I ../../patches/kdrivers/wanec -I/usr/local/include -I../../patches/kdrivers/include -I/usr/include/wanpipe -Ilib/libteletone/src + +CFLAGS = -D__LINUX__ -D_REENTRANT -D_GNU_SOURCE -O6 + + +ifeq "${PRI}" "YES" + CFLAGS += -DPRI_PROT -DSMG_CALLING_NAME + NO_SS7:=YES +else + ifeq "${BRI}" "YES" + CFLAGS += -DBRI_PROT -DSMG_CALLING_NAME + NO_SS7:=YES + else + ifeq "${NO_SS7}" "YES" + BRI:=YES + CFLAGS += -DBRI_PROT + endif + endif +endif + +CCFLAGS = -Wall -Wstrict-prototypes -Wmissing-prototypes -g +LDFLAGS=-L lib/libteletone/.libs -L. -L/usr/local/lib -L ../../ssmg/libsangoma.trunk/.libs -lpthread -lsangoma -lm + +#Enable memory leak subsystem +#Not to be used in production +#CFLAGS += -DSMG_MEMORY_DEBUG + + +ifeq "${SMG_DTMF}" "YES" +LDFLAGS+= -lteletone +CFLAGS+= -DSMG_DTMF_ENABLE +endif + + +all: sangoma_mgd + +libs: + $(shell cd lib/libteletone; ./configure --prefix=$(DESTDIR); cd ../../; ) + $(MAKE) -C lib/libteletone all + +switch_buffer.o: switch_buffer.c switch_buffer.h + $(CC) $(CCFLAGS) $(INCLUDES) $(CFLAGS) -c -o switch_buffer.o switch_buffer.c + +call_signal.o: call_signal.c call_signal.h + $(CC) $(CCFLAGS) $(INCLUDES) $(CFLAGS) -c -o call_signal.o call_signal.c + +sangoma_mgd_memdbg.o: sangoma_mgd_memdbg.c sangoma_mgd_memdbg.h + $(CC) $(CCFLAGS) $(INCLUDES) $(CFLAGS) -c -o sangoma_mgd_memdbg.o sangoma_mgd_memdbg.c + +sangoma_mgd_logger.o: sangoma_mgd_logger.c + $(CC) $(CCFLAGS) $(INCLUDES) $(CFLAGS) -c -o sangoma_mgd_logger.o sangoma_mgd_logger.c + +sangoma_mgd.o: sangoma_mgd.c sangoma_mgd.h sigboost.h + $(CC) $(CCFLAGS) $(INCLUDES) $(CFLAGS) -c -o sangoma_mgd.o sangoma_mgd.c + +sangoma_mgd: sangoma_mgd.o sangoma_mgd_memdbg.o sangoma_mgd_logger.o call_signal.o switch_buffer.o sigboost.h sangoma_mgd_memdbg.h + rm -fr core* + $(CC) $(CCFLAGS) $(INCLUDES) $(CFLAGS) -o sangoma_mgd sangoma_mgd.o sangoma_mgd_memdbg.o sangoma_mgd_logger.o switch_buffer.o call_signal.o $(LDFLAGS) + + + +clean: old_cleanup + find . -name '*.o' | xargs rm -f + rm -fr sangoma_mgd pritest *.o *.so *~ *core* *.so* *.a + make -C lib/libteletone clean + +distclean: clean + @echo OK + +install: all install_smg old_cleanup + +install_smg: old_cleanup + install -D -m 755 sangoma_mgd $(DESTDIR)/usr/sbin/sangoma_mgd + @if [ ! -e $(DESTDIR)/etc/sangoma_mgd.conf ]; then \ + install -D -m 755 sangoma_mgd.conf.sample $(DESTDIR)/etc/sangoma_mgd.conf; \ + fi + + install -D -m 755 ./safe_sangoma $(DESTDIR)/usr/sbin/safe_sangoma + install -D -m 755 ./smg_ctrl $(DESTDIR)/usr/sbin/smg_ctrl + +ifeq "${PRI}" "YES" + @echo "PRI control scripts installed" + @if [ ! -e $(DESTDIR)/etc/sangoma_mgd.conf ]; then \ + install -D -m 755 sangoma_mgd.conf.sample.pri $(DESTDIR)/etc/sangoma_mgd.conf; \ + fi +<<<<<<< .mine + install -D -m 755 smg.rc.pri $(DESTDIR)/etc/wanpipe/smg.rc +======= + install -D -m 755 rc/smg.rc.pri $(DESTDIR)/etc/wanpipe/smg.rc +>>>>>>> .r198 +else +ifeq "${BRI}" "YES" + @echo "BRI control scripts installed" + @if [ ! -e $(DESTDIR)/etc/sangoma_mgd.conf ]; then \ + install -D -m 755 sangoma_mgd.conf.sample.bri $(DESTDIR)/etc/sangoma_mgd.conf; \ + fi +<<<<<<< .mine + install -D -m 755 smg.rc.bri $(DESTDIR)/etc/wanpipe/smg.rc +======= + install -D -m 755 rc/smg.rc.bri $(DESTDIR)/etc/wanpipe/smg.rc +>>>>>>> .r198 +else + @echo "SS7 control scripts installed" + @if [ ! -e $(DESTDIR)/etc/sangoma_mgd.conf ]; then \ + install -D -m 755 sangoma_mgd.conf.sample.ss7 $(DESTDIR)/etc/sangoma_mgd.conf; \ + fi +<<<<<<< .mine + install -D -m 755 smg.rc.ss7 $(DESTDIR)/etc/wanpipe/smg.rc +======= + install -D -m 755 rc/smg.rc.ss7 $(DESTDIR)/etc/wanpipe/smg.rc +>>>>>>> .r198 + install -D -m 755 scripts/init.d/smgss7_init_ctrl $(DESTDIR)/etc/init.d/smgss7_init_ctrl + install -D -m 755 scripts/init.d/smgss7_init_ctrl $(DESTDIR)/usr/sbin/smgss7_init_ctrl + +endif +endif + @echo "sangoma_mgd Installed" + +old_cleanup: + ./scripts/old_cleanup.sh + + +install_all: all install_smg + +uninstall: + /bin/rm $(DESTDIR)/usr/sbin/sangoma_mgd $(DESTDIR)/etc/sangoma_mgd.conf + + + diff --git a/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.6.tmp b/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.6.tmp new file mode 100644 index 0000000..7b6d2b5 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.6.tmp @@ -0,0 +1,5882 @@ +/********************************************************************************* + * sangoma_mgd.c -- Sangoma Media Gateway Daemon for Sangoma/Wanpipe Cards + * + * Copyright 05-09, Nenad Corbic + * Anthony Minessale II + * + * This program is free software, distributed under the terms of + * the GNU General Public License + * + * ============================================= + * + * + * v1.52 David Yat Sin + * Changed sangoma_open_span_chan to __sangoma_span_chan + * to enabled shared used of file descriptors when using + * with PRI in NFAS mode + * + * v1.51 David Yat Sin + * MAX_SPANS increased to 32. + * Fix for server.process_table declared incorrectly + * + * v1.50 Nenad Corbic + * Logic to support multiple woomera clients hanging up the + * channel. This feature now supprorts woomera loadbalancing + * with extension check on the client end. Example, two Asterisk + * systems setup where Asterisk with correct extension gets the + * call. + * Changed log levels: Loglevel 1 = production + * Loglevel 2 = Boost TX/RX EVENTS + * Loglevel 3 = Woomera RX Messages + * Loglevel 4 = call setup debugging + * Loglevel 5 = extra debugging + * Loglevel 10 = full debugging + * + * v1.49 Nenad Corbic + * Removed tv from sigboost to make it binary compatible + * Updated release cause on double use return code. + * + * v1.48 Nenad Corbic + * Konrad Hammel + * Jun 30 2009 + * Added feature to disable/enable HWEC on channels that are + * passing a call with a "data" only transfer capability + * + * v1.47 Nenad Corbic + * Apr 30 2009 + * Updated hangup woomera events. Each event must + * be followed by a woomera error code 200=ok >299=no ok. + * This update fixes the chan_woomera socket write + * warnings. + * + * v1.46 Nenad Corbic + * Mar 27 2009 + * Major updates on socket handling. A bug was introducted + * when cmm and trunk was merged. + * Added configuration print in the logs. + * + * v1.45 Nenad Corbic + * Mar 19 2009 + * Outbound call timeout defaulted to 300 seconds. + * Timeout on answer instead of accept. + * + * v1.44 Nenad Corbic + * Mar 19 2009 + * Adjustable boost size. Added boost version check. + * + * v1.43 David Yat Sin + * Feb 25 2009 + * Merged CMM and Trunk + * + * v1.42 Nenad Corbic + * Jan 26 2008 + * Call Bearer Capability + * BugFix: Hangup NACK was sent out with invalid cause 0 + * + * v1.41 Nenad Corbic + * Jan 12 2008 + * Fixed the NACK with cause 0 bug. + * Added cause 19 on call timeout. + * Added configuration option call_timeout to timeout + * outbound calls. + * + * v1.40 Nenad Corbic + * Dec 10 2008 + * Check for Rx call overrun. + * In unlikely case sangoma_mgd goes out of + * sync with boost. + * + * v1.39 Nenad Corbic + * Sep 17 2008 + * Updated for Unit Test + * Bug fix in Loop Logic, possible race + * between media and woomera thread on loop end. + * + * v1.38 Nenad Corbic + * Sep 5 2008 + * Added a Double use of setup id logic. + * Currently double use call gets dropped. + * + * v1.37 Nenad Corbic + * Sep 2 2008 + * Bug fix in REMOVE LOOP logic continued + * The woomera->sock in loop logic was set to 0. + * Closing it failed the next call. + * + * v1.36 Nenad Corbic + * Aug 28 2008 + * Bug fix in REMOVE LOOP logic + * Remove F from incoming calls by default + * Check for errno on poll() + * Do not delay in closing socket on media down. + * + * v1.35cmm Nenad Corbic + * Jul 28 2008 + * Bug fixes on ACK Timeout and trunk release + * Added a thread logger + * Increased max spans to 32 + * + * v1.34cmm Nenad Corbic + * Jul 24 2008 + * Clean up the setup id on CALL STOP ACK + * + * v1.33cmm Nenad Corbic + * Jul 24 2008 + * The CALL STOP timeout function had a bug + * resulting in false blocking of tank ids + * due to STOP ACK Timeouts. + * + * v1.33 Nenad Corbic + * Jul 18 2008 + * Added UDP Sequencing to check for dropped frames + * Should only be used for debugging. + * + * v1.32 David Yat Sin + * Jul 17 2008 + * Support for d-channel incoming digit + * passthrough to Asterisk + * + * v1.32cmm Nenad Corbic + * Jun 03 2008 + * Implemented new Restart ACK Policy + * Split the Event packet into 2 types + * + * v1.31cmm Nenad Corbic + * Apr 04 2008 + * New CMM Restart procedure for boost + * Block TRUNK ID on ACK Timeout + * + * v1.31 David Yat Sin + * Apr 29 2008 + * Support for HW DTMF events. + * + * v1.30 Nenad Corbic + * Feb 08 2008 + * The fix in v1.26 causes double stop. + * I took out the v1.26 fix and added + * a big warning if that condition is + * ever reached. + * + * v1.29 Nenad Corbic + * Feb 07 2008 + * Added strip_cid_non_digits option + * + * v1.28 Nenad Corbic + * Feb 06 2008 + * Fixed a memory leak in clone event + * function. Added memory check subsystem + * + * v1.27 Nenad Corbic + * Jan 24 2008 + * Fixed a memory leak on incoming calls + * Removed the use of server listener which + * was not used + * + * v1.26 Nenad Corbic + * Jan 18 2008 + * Fixed hangup after invalid Answer or Ack Session + * Can cause double use of setup id - now fixed + * Update on autoacm on accept check for acked. + * + * v1.25 Nenad Corbic + * Dec 31 2007 + * Removed UDP Resync it can cause skb_over errors. + * Moved RDNIS message to higher debug level + * + * v1.24 Nenad Corbic + * Nov 30 2007 + * Bug fix on return code on ALL ckt busy + * + * v1.23 Nenad Corbic + * Bug fix on socket open. Check for retun code >= 0 + * + * v1.22 Nenad Corbic + * Nov 27 2007 + * Updated DTMF Tx function + * Fixed - dtmf tx without voice + * Fxied - dtmf clipping. + * + * v1.21 Nenad Corbic + * Nov 25 2007 + * Major unit testing of each state + * Numerous bug fixes for non autoacm mode. + * Changed "Channel-Name" to tg/cic + * Added compile option WANPIPE_CHAN_NAME to change Asterisk channel + * name of chan_woomera.so. So one can use Dial(SS7/g1/${EXTE}) + * instead of WOOMERA (for example) + * + * v1.20 Nenad Corbic + * Added option for Auto ACM response mode. + * + * v1.19 Nenad Corbic + * Configurable DTMF + * Bug fix in release codes (all ckt busy) + * + * v1.18 Nenad Corbic + * Added new rel cause support based on + * digits instead of strings. + * + * v1.17 Nenad Corbic + * Added session support + * + * v1.16 Nenad Corbic + * Added hwec disable on loop ccr + * + * v1.15 Nenad Corbic + * Updated DTMF Locking + * Added delay between digits + * + * v1.14 Nenad Corbic + * Updated DTMF Library + * Fixed DTMF synchronization + * + * v1.13 Nenad Corbic + * Woomera OPAL Dialect + * Added Congestion control + * Added SCTP + * Added priority ISUP queue + * Fixed presentation + * + * v1.12 Nenad Corbic + * Fixed CCR + * Removed socket shutdown on end call. + * Let Media thread shutodwn sockets. + * + * v1.11 Nenad Corbic + * Fixed Remote asterisk/woomera connection + * Increased socket timeouts + * + * v1.10 Nenad Corbic + * Added Woomera OPAL dialect. + * Start montor thread in priority + * + * v1.9 Nenad Corbic + * Added Loop mode for ccr + * Added remote debug enable + * Fixed syslog logging. + * + * v1.8 Nenad Corbic + * Added a ccr loop mode for each channel. + * Boost can set any channel in loop mode + * + * v1.7 Nenad Corbic + * Pass trunk group number to incoming call + * chan woomera will use it to append to context + * name. Added presentation feature. + * + * v1.6 Nenad Corbic + * Use only ALAW and MLAW not SLIN. + * This reduces the load quite a bit. + * Send out ALAW/ULAW format on HELLO message. + * RxTx Gain is done now in chan_woomera. + * + * v1.5 Nenad Corbic + * Bug fix in START_NACK_ACK handling. + * When we receive START_NACK we must alwasy pull tank before + * we send out NACK_ACK this way we will not try to send NACK + * ourself. + *********************************************************************************/ + +#include "sangoma_mgd.h" +#include "sangoma_mgd_memdbg.h" +#include "q931_cause.h" +#include "smg_capabilities.h" + +int pipe_fd[2]; + +#ifdef CODEC_LAW_DEFAULT +static uint32_t codec_sample=8; +#else +static uint32_t codec_sample=16; +#endif + +static char ps_progname[]="sangoma_mgd"; + +static struct woomera_interface woomera_dead_dev; + +struct woomera_server server; + +#if 0 +#define DOTRACE +#endif + + +#define SMG_VERSION "v1.52" + +/* enable early media */ +#if 1 +#define WOOMERA_EARLY_MEDIA 1 +#endif + + +#define SMG_DTMF_ON 60 +#define SMG_DTMF_OFF 10 +#define SMG_DTMF_RATE 8000 +#define SMG_DEFAULT_CALL_TIMEOUT 300 + +#if 0 +#define MEDIA_SOCK_SHUTDOWN 1 +#endif + +#ifdef DOTRACE +static int tc = 0; +#endif + +#if 0 +#warning "NENAD: HPTDM API" +#define WP_HPTDM_API 1 +#else +#undef WP_HPTDM_API +#endif + +#ifdef WP_HPTDM_API +hp_tdm_api_span_t *hptdmspan[WOOMERA_MAX_SPAN]; +#endif + +#if 0 +#define SMG_DROP_SEQ 1 +#warning "SMG Debug feature Drop Seq Enabled" +static int drop_seq=0; +#else +#undef SMG_DROP_SEQ +#endif + +const char WELCOME_TEXT[] = +"================================================================================\n" +"Sangoma Media Gateway Daemon v1.52 \n" +"\n" +"TDM Signal Media Gateway for Sangoma/Wanpipe Cards\n" +"Copyright 2005, 2006, 2007 \n" +"Nenad Corbic , Anthony Minessale II \n" +"This program is free software, distributed under the terms of\n" +"the GNU General Public License\n" +"================================================================================\n" +""; + + + +static int coredump=1; +static int autoacm=0; + +/* FIXME: Should be done in cfg file */ +#if defined(BRI_PROT) +int max_spans=WOOMERA_BRI_MAX_SPAN; +int max_chans=WOOMERA_BRI_MAX_CHAN; +#else +/* PRI_PROT uses these defines as well */ +int max_spans=WOOMERA_MAX_SPAN; +int max_chans=WOOMERA_MAX_CHAN; +#endif + +static int launch_media_tdm_thread(struct woomera_interface *woomera); +static int launch_woomera_thread(struct woomera_interface *woomera); +static void woomera_check_digits (struct woomera_interface *woomera); +static struct woomera_interface *alloc_woomera(void); +static void handle_event_dtmf(struct woomera_interface *woomera, unsigned char dtmf_digit); + +q931_cause_to_str_array_t q931_cause_to_str_array[255]; +bearer_cap_to_str_array_t bearer_cap_to_str_array[255]; +uil1p_to_str_array_t uil1p_to_str_array[255]; + +static int isup_exec_command(int span, int chan, int id, int cmd, int cause) +{ + short_signal_event_t oevent; + int retry=5; + + call_signal_event_init(&oevent, cmd, chan, span); + oevent.release_cause = cause; + + if (id >= 0) { + oevent.call_setup_id = id; + } +isup_exec_cmd_retry: + if (call_signal_connection_write(&server.mcon, (call_signal_event_t*)&oevent) < 0){ + + --retry; + if (retry <= 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket: %s\n", + strerror(errno)); + return -1; + } else { + log_printf(SMG_LOG_ALL, server.log, + "System Warning: Failed to tx on ISUP socket: %s :retry %i\n", + strerror(errno),retry); + } + + goto isup_exec_cmd_retry; + } + + return 0; +} + +static int isup_exec_event(call_signal_event_t *event) +{ + int retry=5; + +isup_exec_cmd_retry: + if (call_signal_connection_write(&server.mcon, event) < 0){ + + --retry; + if (retry <= 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket: %s\n", + strerror(errno)); + return -1; + } else { + log_printf(SMG_LOG_ALL, server.log, + "System Warning: Failed to tx on ISUP socket: %s :retry %i\n", + strerror(errno),retry); + } + + goto isup_exec_cmd_retry; + } + + return 0; +} + + +static int isup_exec_commandp(int span, int chan, int id, int cmd, int cause) +{ + short_signal_event_t oevent; + int retry=5; + + call_signal_event_init(&oevent, cmd, chan, span); + oevent.release_cause = cause; + + if (id >= 0) { + oevent.call_setup_id = id; + } +isup_exec_cmd_retry: + if (call_signal_connection_writep(&server.mconp, (call_signal_event_t*)&oevent) < 0){ + + --retry; + if (retry <= 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket: %s\n", + strerror(errno)); + return -1; + } else { + log_printf(SMG_LOG_ALL, server.log, + "System Warning: Failed to tx on ISUP socket: %s :retry %i\n", + strerror(errno),retry); + } + + goto isup_exec_cmd_retry; + } + + return 0; +} + + +static int get_span_chan_from_interface(char* interface, int *span_ptr, int *chan_ptr) +{ + int span, chan; + + if (sscanf(interface, "w%dg%d", &span, &chan) == 2) { + if (smg_validate_span_chan(span,chan) != 0) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "WOOMERA Warning invalid span chan in interface %s\n", + interface); + return -1; + } + + *span_ptr = span; + *chan_ptr = chan; + return 0; + } + log_printf(SMG_LOG_ALL, server.log, "ERROR: Failed to get span chan from interface:[%s]\n", interface, span, chan); + return -1; +} + +static int socket_printf(int socket, char *fmt, ...) +{ + char *data; + int ret = 0; + va_list ap; + + if (socket < 0) { + return -1; + } + + va_start(ap, fmt); +#ifdef SOLARIS + data = (char *) smg_malloc(2048); + vsnprintf(data, 2048, fmt, ap); +#else + ret = vasprintf(&data, fmt, ap); +#endif + va_end(ap); + if (ret == -1) { + fprintf(stderr, "Memory Error\n"); + log_printf(SMG_LOG_ALL, server.log, "Crtical ERROR: Memory Error!\n"); + } else { + int err; + int len = strlen(data); + err=send(socket, data, strlen(data), 0); + if (err != strlen(data)) { + log_printf(SMG_LOG_DEBUG_8, server.log, "ERROR: Failed to send data to woomera socket(%i): err=%i len=%d %s\n", + socket,err,len,strerror(errno)); + ret = err; + } else { + ret = 0; + } + + free(data); + } + + return ret; +} + + + +static int woomera_next_pair(struct woomera_config *cfg, char **var, char **val) +{ + int ret = 0; + char *p, *end; + + *var = *val = NULL; + + for(;;) { + cfg->lineno++; + + if (!fgets(cfg->buf, sizeof(cfg->buf), cfg->file)) { + ret = 0; + break; + } + + *var = cfg->buf; + + if (**var == '[' && (end = strchr(*var, ']'))) { + *end = '\0'; + (*var)++; + strncpy(cfg->category, *var, sizeof(cfg->category) - 1); + continue; + } + + if (**var == '#' || **var == '\n' || **var == '\r') { + continue; + } + + if ((end = strchr(*var, '#'))) { + *end = '\0'; + end--; + } else if ((end = strchr(*var, '\n'))) { + if (*end - 1 == '\r') { + end--; + } + *end = '\0'; + } + + p = *var; + while ((*p == ' ' || *p == '\t') && p != end) { + *p = '\0'; + p++; + } + *var = p; + + if (!(*val = strchr(*var, '='))) { + ret = -1; + log_printf(SMG_LOG_ALL, 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; + +} + + +#if 0 +static void woomera_set_span_chan(struct woomera_interface *woomera, int span, int chan) +{ + pthread_mutex_lock(&woomera->vlock); + woomera->span = span; + woomera->chan = chan; + pthread_mutex_unlock(&woomera->vlock); + +} +#endif + + +static struct woomera_event *new_woomera_event_printf(struct woomera_event *ebuf, char *fmt, ...) +{ + struct woomera_event *event = NULL; + int ret = 0; + va_list ap; + + if (ebuf) { + event = ebuf; + } else if (!(event = new_woomera_event())) { + log_printf(SMG_LOG_ALL, server.log, "Memory Error queuing event!\n"); + return NULL; + } else { + return NULL; + } + + va_start(ap, fmt); +#ifdef SOLARIS + event->data = (char *) smg_malloc(2048); + vsnprintf(event->data, 2048, fmt, ap); +#else + ret = smg_vasprintf(&event->data, fmt, ap); +#endif + va_end(ap); + if (ret == -1) { + log_printf(SMG_LOG_ALL, server.log, "Memory Error queuing event!\n"); + destroy_woomera_event(&event, EVENT_FREE_DATA); + return NULL; + } + + return event; + +} + +static struct woomera_event *woomera_clone_event(struct woomera_event *event) +{ + struct woomera_event *clone; + + if (!(clone = new_woomera_event())) { + return NULL; + } + + /* This overwrites the MALLOC in clone causing a memory leak */ + memcpy(clone, event, sizeof(*event)); + + /* We must set the malloc flag back so that this event + * will be deleted */ + _woomera_set_flag(clone, WFLAG_MALLOC); + clone->next = NULL; + clone->data = smg_strdup(event->data); + + return clone; +} + +static void enqueue_event(struct woomera_interface *woomera, + struct woomera_event *event, + event_args free_data) +{ + struct woomera_event *ptr, *clone = NULL; + + assert(woomera != NULL); + assert(event != NULL); + + if (!(clone = woomera_clone_event(event))) { + log_printf(SMG_LOG_ALL, server.log, "Error Cloning Event\n"); + return; + } + + pthread_mutex_lock(&woomera->queue_lock); + + for (ptr = woomera->event_queue; ptr && ptr->next ; ptr = ptr->next); + + if (ptr) { + ptr->next = clone; + } else { + woomera->event_queue = clone; + } + + pthread_mutex_unlock(&woomera->queue_lock); + + woomera_set_flag(woomera, WFLAG_EVENT); + + if (free_data && event->data) { + /* The event has been duplicated, the original data + * should be freed */ + smg_free(event->data); + event->data=NULL; + } +} + +static char *dequeue_event(struct woomera_interface *woomera) +{ + struct woomera_event *event; + char *data = NULL; + + if (!woomera) { + return NULL; + } + + pthread_mutex_lock(&woomera->queue_lock); + if (woomera->event_queue) { + event = woomera->event_queue; + woomera->event_queue = event->next; + data = event->data; + pthread_mutex_unlock(&woomera->queue_lock); + + destroy_woomera_event(&event, EVENT_KEEP_DATA); + return data; + } + pthread_mutex_unlock(&woomera->queue_lock); + + return data; +} + + +static int enqueue_event_on_listeners(struct woomera_event *event) +{ + struct woomera_listener *ptr; + int x = 0; + + assert(event != NULL); + + pthread_mutex_lock(&server.listen_lock); + for (ptr = server.listeners ; ptr ; ptr = ptr->next) { + enqueue_event(ptr->woomera, event, EVENT_KEEP_DATA); + x++; + } + pthread_mutex_unlock(&server.listen_lock); + + return x; +} + + +static void del_listener(struct woomera_interface *woomera) +{ + struct woomera_listener *ptr, *last = NULL; + + pthread_mutex_lock(&server.listen_lock); + for (ptr = server.listeners ; ptr ; ptr = ptr->next) { + if (ptr->woomera == woomera) { + if (last) { + last->next = ptr->next; + } else { + server.listeners = ptr->next; + } + smg_free(ptr); + break; + } + last = ptr; + } + pthread_mutex_unlock(&server.listen_lock); +} + +static void add_listener(struct woomera_interface *woomera) +{ + struct woomera_listener *new; + + pthread_mutex_lock(&server.listen_lock); + + if ((new = smg_malloc(sizeof(*new)))) { + memset(new, 0, sizeof(*new)); + new->woomera = woomera; + new->next = server.listeners; + server.listeners = new; + } else { + log_printf(SMG_LOG_ALL, server.log, "Memory Error adding listener!\n"); + } + + pthread_mutex_unlock(&server.listen_lock); +} + + + +static int wanpipe_send_dtmf(struct woomera_interface *woomera, char *digits) +{ + struct media_session *ms = woomera_get_ms(woomera); + char *cur = NULL; + int wrote = 0; + int err; + + if (!ms) { + return -EINVAL; + } + + if (!ms->dtmf_buffer) { + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Allocate DTMF Buffer...."); + + err=switch_buffer_create_dynamic(&ms->dtmf_buffer, 1024, server.dtmf_size, 0); + + if (err != 0) { + log_printf(SMG_LOG_ALL, woomera->log, "Failed to allocate DTMF Buffer!\n"); + return -ENOMEM; + } else { + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "SUCCESS!\n"); + } + + } + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Sending DTMF %s\n",digits); + for (cur = digits; *cur; cur++) { + if ((wrote = teletone_mux_tones(&ms->tone_session, + &ms->tone_session.TONES[(int)*cur]))) { + + pthread_mutex_lock(&woomera->dtmf_lock); + + err=switch_buffer_write(ms->dtmf_buffer, ms->tone_session.buffer, wrote * 2); + + pthread_mutex_unlock(&woomera->dtmf_lock); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Sending DTMF %s Wrote=%i (err=%i)\n", + digits,wrote*2,err); + } else { + log_printf(SMG_LOG_ALL, woomera->log, "Error: Sending DTMF %s (err=%i)\n", + digits,wrote); + } + } + + ms->skip_read_frames = 200; + return 0; +} + +static struct woomera_interface *alloc_woomera(void) +{ + struct woomera_interface *woomera = NULL; + + if ((woomera = smg_malloc(sizeof(struct woomera_interface)))) { + + memset(woomera, 0, sizeof(struct woomera_interface)); + + woomera->chan = -1; + woomera->span = -1; + woomera->log = server.log; + woomera->debug = server.debug; + woomera->call_id = 1; + woomera->event_queue = NULL; + woomera->q931_rel_cause_topbx=SIGBOOST_RELEASE_CAUSE_NORMAL; + woomera->q931_rel_cause_tosig=SIGBOOST_RELEASE_CAUSE_NORMAL; + + woomera_set_interface(woomera, "w-1g-1"); + + + + } + + return woomera; + +} + + +static struct woomera_interface *new_woomera_interface(int socket, struct sockaddr_in *sock_addr, int len) +{ + struct woomera_interface *woomera = NULL; + + if (socket < 0) { + log_printf(SMG_LOG_ALL, server.log, "Critical: Invalid Socket on new interface!\n"); + return NULL; + } + + if ((woomera = alloc_woomera())) { + if (socket >= 0) { + no_nagle(socket); + woomera->socket = socket; + } + + if (sock_addr && len) { + memcpy(&woomera->addr, sock_addr, len); + } + } + + return woomera; + +} + +static char *woomera_message_header(struct woomera_message *wmsg, char *key) +{ + int x = 0; + char *value = NULL; + + for (x = 0 ; x < wmsg->last ; x++) { + if (!strcasecmp(wmsg->names[x], key)) { + value = wmsg->values[x]; + break; + } + } + + return value; +} + + +#define SMG_DECODE_POLL_ERRORS(err) \ + (err & POLLERR) ? "POLLERR" : \ + (err & POLLHUP) ? "POLLHUP" : \ + (err & POLLNVAL) ? "POLLNVAL" : "UNKNOWN" + +static int waitfor_socket(int fd, int timeout, int flags, int *flags_out) +{ + struct pollfd pfds[1]; + int res; + int errflags = (POLLERR | POLLHUP | POLLNVAL); + +waitfor_socket_tryagain: + + memset(&pfds[0], 0, sizeof(pfds[0])); + + pfds[0].fd = fd; + pfds[0].events = flags | errflags; + + res = poll(pfds, 1, timeout); + + if (res == 0) { + return res; + } + + if (res > 0) { + res=-1; + *flags_out = pfds[0].revents; + if (pfds[0].revents & errflags) { + res=-1; +#if 0 + log_printf(SMG_LOG_DEBUG_10,server.log, "Wait for socket Error in revents 0x%X %s Error=%s!\n", + pfds[0].revents,strerror(errno),SMG_DECODE_POLL_ERRORS(pfds[0].revents)); +#endif + return res; + } else { + if (pfds[0].revents & flags) { + res=1; + } else { + log_printf(SMG_LOG_ALL,server.log, "Wait for socket invalid poll event in revents 0x%X Error=%s Errno=%s !\n", + pfds[0].revents,SMG_DECODE_POLL_ERRORS(pfds[0].revents),strerror(errno)); + } + } + } else { + if ((errno == EINTR || errno == EAGAIN)) { + goto waitfor_socket_tryagain; + } + log_printf(SMG_LOG_ALL,server.log, "Wait for socket error!\n"); + } + + return res; +} + + +static int waitfor_2sockets(int fda, int fdb, char *a, char *b, int timeout) +{ + struct pollfd pfds[2]; + int res = 0; + int errflags = (POLLERR | POLLHUP | POLLNVAL); + + if (fda < 0 || fdb < 0) { + return -1; + } + + +waitfor_2sockets_tryagain: + + *a=0; + *b=0; + + + memset(pfds, 0, sizeof(pfds)); + + pfds[0].fd = fda; + pfds[1].fd = fdb; + pfds[0].events = POLLIN | errflags; + pfds[1].events = POLLIN | errflags; + + + + res = poll(pfds, 2, timeout); + + if (res > 0) { + res = 1; + if ((pfds[0].revents & errflags) || (pfds[1].revents & errflags)) { + res = -1; + } else { + if ((pfds[0].revents & POLLIN)) { + *a=1; + res++; + } + if ((pfds[1].revents & POLLIN)) { + *b=1; + res++; + } + } + + if (res == 1) { + /* No event found what to do */ + res=-1; + } + } else if (res < 0) { + + if (errno == EINTR || errno == EAGAIN) { + goto waitfor_2sockets_tryagain; + } + + } + + return res; +} + + +static struct media_session *media_session_new(struct woomera_interface *woomera) +{ + struct media_session *ms = NULL; + int x; + char *p; + int span,chan; + + span=woomera->span; + chan=woomera->chan; + + log_printf(SMG_LOG_DEBUG_CALL, server.log,"Starting new MEDIA session [%s] [%s]\n", + woomera->interface,woomera->raw?woomera->raw:"N/A"); + + if ((ms = smg_malloc(sizeof(struct media_session)))) { + memset(ms, 0, sizeof(struct media_session)); + + if (woomera->loop_tdm != 1) { + for(x = 0; x < strlen(woomera->raw) ; x++) { + if (woomera->raw[x] == ':') { + break; + } + if (woomera->raw[x] == '/') { + break; + } + } + + ms->ip = smg_strndup(woomera->raw, x); + time(&ms->started); + p = woomera->raw + (x+1); + ms->port = atoi(p); + } + + time(&ms->started); + woomera_set_ms(woomera,ms); + ms->woomera = woomera; + + /* Setup artificial DTMF stuff */ + memset(&ms->tone_session, 0, sizeof(ms->tone_session)); + if (teletone_init_session(&ms->tone_session, 0, NULL, NULL)) { + log_printf(SMG_LOG_ALL, server.log, "ERROR: Failed to initialize TONE [w%ig%i]!\n", + span+1,chan+1); + } + + ms->tone_session.rate = SMG_DTMF_RATE; + ms->tone_session.duration = server.dtmf_on * (ms->tone_session.rate / 1000); + ms->tone_session.wait = server.dtmf_off * (ms->tone_session.rate / 1000); + + teletone_dtmf_detect_init (&ms->dtmf_detect, SMG_DTMF_RATE); + + } else { + log_printf(SMG_LOG_ALL, server.log, "ERROR: Memory Alloc Failed [w%ig%i]!\n", + span+1,chan+1); + } + + return ms; +} + +static void media_session_free(struct media_session *ms) +{ + if (ms->ip) { + smg_free(ms->ip); + } + + teletone_destroy_session(&ms->tone_session); + switch_buffer_destroy(&ms->dtmf_buffer); + + ms->woomera = NULL; + + smg_free(ms); +} + + +static int create_udp_socket(struct media_session *ms, 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; + + log_printf(SMG_LOG_DEBUG_9,server.log,"LocalIP %s:%d IP %s:%d \n",local_ip, local_port, ip, port); + + memset(&ms->remote_hp, 0, sizeof(ms->remote_hp)); + memset(&ms->local_hp, 0, sizeof(ms->local_hp)); + if ((ms->socket = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) { + gethostbyname_r(ip, &ms->remote_hp, buf, sizeof(buf), &result, &err); + gethostbyname_r(local_ip, &ms->local_hp, local_buf, sizeof(local_buf), &local_result, &err); + if (result && local_result) { + ms->remote_addr.sin_family = ms->remote_hp.h_addrtype; + memcpy((char *) &ms->remote_addr.sin_addr.s_addr, ms->remote_hp.h_addr_list[0], ms->remote_hp.h_length); + ms->remote_addr.sin_port = htons(port); + + ms->local_addr.sin_family = ms->local_hp.h_addrtype; + memcpy((char *) &ms->local_addr.sin_addr.s_addr, ms->local_hp.h_addr_list[0], ms->local_hp.h_length); + ms->local_addr.sin_port = htons(local_port); + + rc = bind(ms->socket, (struct sockaddr *) &ms->local_addr, sizeof(ms->local_addr)); + if (rc < 0) { + close(ms->socket); + ms->socket = -1; + + log_printf(SMG_LOG_DEBUG_9,server.log, + "Failed to bind LocalIP %s:%d IP %s:%d (%s)\n", + local_ip, local_port, ip, port,strerror(errno)); + } + + /* OK */ + + } else { + log_printf(SMG_LOG_ALL,server.log, + "Failed to get hostbyname LocalIP %s:%d IP %s:%d (%s)\n", + local_ip, local_port, ip, port,strerror(errno)); + } + } else { + log_printf(SMG_LOG_ALL,server.log, + "Failed to create/allocate UDP socket\n"); + } + + return ms->socket; +} + +static int next_media_port(void) +{ + int port; + + pthread_mutex_lock(&server.media_udp_port_lock); + port = ++server.next_media_port; + if (port > server.max_media_port) { + server.next_media_port = server.base_media_port; + port = server.base_media_port; + } + pthread_mutex_unlock(&server.media_udp_port_lock); + + return port; +} + + + +static int woomera_dtmf_transmit(struct media_session *ms, int mtu) +{ + struct woomera_interface *woomera = ms->woomera; + int bread; + unsigned char dtmf[1024]; + unsigned char dtmf_law[1024]; + sangoma_api_hdr_t hdrframe; + int i; + int slin_len = mtu*2; + short *data; + int used; + int res; + int err; + int txdtmf=0; + int flags_out; + memset(&hdrframe,0,sizeof(hdrframe)); + + if (!ms->dtmf_buffer) { + return -1; + } + + for (;;) { + + if ((used=switch_buffer_inuse(ms->dtmf_buffer)) <= 0) { + break; + } + + res = waitfor_socket(ms->sangoma_sock, -1, POLLOUT, &flags_out); + if (res <= 0) { + break; + } + +#ifdef CODEC_LAW_DEFAULT + + pthread_mutex_lock(&woomera->dtmf_lock); + if ((used=switch_buffer_inuse(ms->dtmf_buffer)) <= 0) { + pthread_mutex_unlock(&woomera->dtmf_lock); + break; + } + + bread = switch_buffer_read(ms->dtmf_buffer, dtmf, slin_len); + pthread_mutex_unlock(&woomera->dtmf_lock); + + if (bread <= 0) { + break; + } + + log_printf(SMG_LOG_DEBUG_MISC,woomera->log,"%s: Write DTMF Got %d bytes MTU=%i Coding=%i Used=%i\n", + woomera->interface,bread,mtu,ms->hw_coding,used); + + data=(short*)dtmf; + for (i=0;ihw_coding) { + /* ALAW */ + dtmf_law[i] = linear_to_alaw((int)data[i]); + } else { + /* ULAW */ + dtmf_law[i] = linear_to_ulaw((int)data[i]); + } + } + + err=sangoma_sendmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + dtmf_law, mtu, 0); + + if (err != mtu) { + log_printf(SMG_LOG_ALL, woomera->log, "Error: Failed to TX to TDM API on DTMF (err=%i mtu=%i)!\n",err,mtu); + } + + txdtmf++; + ms->skip_write_frames++; +#else +... + pthread_mutex_lock(&woomera->dtmf_lock); + bread = switch_buffer_read(ms->dtmf_buffer, dtmf, mtu); + pthread_mutex_unlock(&woomera->dtmf_lock); + + log_printf(SMG_LOG_DEBUG_MISC,woomera->log,"%s: Write DTMF Got %d bytes\n", + woomera->interface,bread); + + sangoma_sendmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + dtmf, mtu, 0); + txdtmf++; + ms->skip_write_frames++; +#endif + + } + + if (txdtmf) { + return 0; + } else { + return -1; + } +} + +static void media_loop_run(struct media_session *ms) +{ + struct woomera_interface *woomera = ms->woomera; + int sangoma_frame_len = 160; + int errs=0; + int res=0; + wanpipe_tdm_api_t tdm_api; + unsigned char circuit_frame[1024]; + char filename[100]; + FILE *filed=NULL; + int loops=0,flags_out=0; + int open_cnt = 0; + + open_cnt=0; + + sangoma_api_hdr_t hdrframe; + memset(&hdrframe,0,sizeof(hdrframe)); + memset(circuit_frame,0,sizeof(circuit_frame)); + +retry_loop: + ms->sangoma_sock = open_span_chan(woomera->span+1, woomera->chan+1); + + log_printf(SMG_LOG_PROD, server.log, "Media Loop Started %s fd=%i\n", + woomera->interface,ms->sangoma_sock); + + if (ms->sangoma_sock < 0) { + errs++; + if (errs < 5) { + usleep(500000); + goto retry_loop; + } + log_printf(SMG_LOG_ALL, server.log, "WANPIPE MEDIA Socket Error (%s) if=[%s] [w%ig%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); + + } else { + errs=0; + + if (sangoma_tdm_set_codec(ms->sangoma_sock, &tdm_api, WP_NONE) < 0 ) { + errs++; + } + + if (sangoma_tdm_flush_bufs(ms->sangoma_sock, &tdm_api)) { + errs++; + } + + if (sangoma_tdm_set_usr_period(ms->sangoma_sock, &tdm_api, 20) < 0 ) { + errs++; + } + + sangoma_frame_len = sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); + + sangoma_tdm_disable_hwec(ms->sangoma_sock,&tdm_api); + ms->oob_disable = 0; +#ifdef LIBSANGOMA_VERSION + open_cnt = sangoma_get_open_cnt(ms->sangoma_sock, &tdm_api); + if (open_cnt > 1) { + ms->oob_disable = 1; + } +#endif + } + + if (errs) { + log_printf(SMG_LOG_ALL, server.log, "Media Loop: failed to open tdm device %s\n", + woomera->interface); + return; + } + + if (server.loop_trace) { + sprintf(filename,"/smg/w%ig%i-loop.trace",woomera->span+1,woomera->chan+1); + unlink(filename); + filed = safe_fopen(filename, "w"); + } + + while ( woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + !woomera_test_flag(woomera, WFLAG_MEDIA_END) && + ((res = waitfor_socket(ms->sangoma_sock, 1000, POLLIN, &flags_out)) >= 0)) { + + if (res == SMG_SOCKET_EVENT_TIMEOUT) { + //log_printf(SMG_LOG_DEBUG_8, server.log, "%s: TDM UDP Timeout !!!\n", + // woomera->interface); + /* NENAD Timeout thus just continue */ + continue; + } + + res = sangoma_readmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + circuit_frame, + sizeof(circuit_frame), 0); + if (res < 0) { + log_printf(SMG_LOG_ALL, server.log, "TDM Loop ReadMsg Error: %s\n", + strerror(errno), woomera->interface); + break; + } + + if (server.loop_trace && filed != NULL) { + int i; + for (i=0;isangoma_sock, + &hdrframe, + sizeof(hdrframe), + circuit_frame, + res, 0); + + res=0; + + loops++; + } + + + if (res < 0) { + if (!woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + log_printf(SMG_LOG_ALL, server.log, "Media Loop: socket error %s (fd=%i) %s\n", + woomera->interface, ms->sangoma_sock, strerror(errno)); + } + } + + + if (server.loop_trace && filed != NULL) { + fclose(filed); + } + + sangoma_tdm_enable_hwec(ms->sangoma_sock,&tdm_api); + + close_span_chan(&ms->sangoma_sock, woomera->span+1, woomera->chan+1); + + if (loops < 1) { + log_printf(SMG_LOG_ALL, server.log, "Media Loop FAILED %s Master=%i MediaEnd=%i Loops=%i\n", + woomera->interface, + woomera_test_flag(&server.master_connection, WFLAG_RUNNING), + woomera_test_flag(woomera, WFLAG_MEDIA_END),loops); + } else { + log_printf(SMG_LOG_PROD, server.log, "Media Loop PASSED %s Master=%i MediaEnd=%i Loops=%i\n", + woomera->interface, + woomera_test_flag(&server.master_connection, WFLAG_RUNNING), + woomera_test_flag(woomera, WFLAG_MEDIA_END),loops); + } + + return; + +} + + + + +#ifdef WP_HPTDM_API +static int media_rx_ready(void *p, unsigned char *data, int len) +{ + struct media_session *ms = (struct media_session *)p; + + if (ms->udp_sock < 0) { + return -1; + } + + return sendto(ms->udp_sock, + data,len, 0, + (struct sockaddr *) &ms->remote_addr, + sizeof(ms->remote_addr)); + + +} +#endif + +static void *media_thread_run(void *obj) +{ + struct media_session *ms = obj; + struct woomera_interface *woomera = ms->woomera; + int sangoma_frame_len = 160; + int local_port, x = 0, errs = 0, res = 0, packet_len = 0; + //int udp_cnt=0; + struct woomera_event wevent; + wanpipe_tdm_api_t tdm_api; + FILE *tx_fd=NULL; + int sock_timeout=200; + int hwec_reenable=0; + int open_cnt = 0; + + open_cnt=0; + + if (woomera_test_flag(woomera, WFLAG_MEDIA_END) || + !woomera->interface || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "MEDIA session for [%s] Cancelled! (ptr=%p)\n", + woomera->interface,woomera); + /* In this case the call will be closed via woomera_thread_run + * function. And the process table will be cleard there */ + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_MEDIA_RUNNING); + media_session_free(ms); + pthread_exit(NULL); + return NULL; + } + + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA session for [%s] started (ptr=%p loop=%i)\n", + woomera->interface,woomera,woomera->loop_tdm); + + if (woomera->loop_tdm) { + media_loop_run(ms); + ms->udp_sock=-1; + woomera_set_flag(woomera, WFLAG_HANGUP); + woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + goto media_thread_exit; + } + + for(x = 0; x < 1000 ; x++) { + local_port = next_media_port(); + if ((ms->udp_sock = create_udp_socket(ms, server.media_ip, local_port, ms->ip, ms->port)) > -1) { + break; + } + } + + /* Normal Temporary Failure */ + woomera->q931_rel_cause_topbx = 41; + + if (ms->udp_sock < 0) { + log_printf(SMG_LOG_ALL, server.log, "UDP Socket Error (%s) [%s] LocalPort=%d\n", + strerror(errno), woomera->interface, local_port); + + errs++; + } else { + int media_retry_cnt=0; +#ifdef WP_HPTDM_API + hp_tdm_api_span_t *span=hptdmspan[woomera->span+1]; + if (!span || !span->init) { + errs++; + } else { + hp_tdm_api_usr_callback_t usr_callback; + memset(&usr_callback,0,sizeof(usr_callback)); + usr_callback.p = ms; + usr_callback.rx_avail = media_rx_ready; + if (span->open_chan(span, &usr_callback, &ms->tdmchan,woomera->chan+1)) { + errs++; + /* Switch Congestion */ + woomera->q931_rel_cause_topbx = 42; + } + } +#else +media_retry: + ms->sangoma_sock = open_span_chan(woomera->span+1, woomera->chan+1); + if (ms->sangoma_sock < 0) { + + if (!woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + media_retry_cnt++; + if (media_retry_cnt < 5) { + log_printf(SMG_LOG_ALL, server.log, "WANPIPE Socket Retry [w%ig%i]\n", + woomera->span+1, woomera->chan+1); + usleep(100000); + goto media_retry; + } + + log_printf(SMG_LOG_ALL, server.log, "WANPIPE Socket Error (%s) if=[%s] [w%ig%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); + + /* Switch Congestion */ + woomera->q931_rel_cause_topbx = 42; + } + + errs++; + } else { + +# ifdef CODEC_LAW_DEFAULT + if (sangoma_tdm_set_codec(ms->sangoma_sock, &tdm_api, WP_NONE) < 0 ) { + errs++; + } +# else + if (sangoma_tdm_set_codec(ms->sangoma_sock, &tdm_api, WP_SLINEAR) < 0 ) { + errs++; + } +# endif + if (sangoma_tdm_flush_bufs(ms->sangoma_sock, &tdm_api)) { + errs++; + } + + if (sangoma_tdm_set_usr_period(ms->sangoma_sock, &tdm_api, 20) < 0 ) { + errs++; + } + +# ifdef CODEC_LAW_DEFAULT +# ifdef LIBSANGOMA_GET_HWCODING + ms->hw_coding=sangoma_tdm_get_hw_coding(ms->sangoma_sock, &tdm_api); + if (ms->hw_coding < 0) { + errs++; + } +# else +# error "libsangoma missing hwcoding feature: not up to date!" +# endif +# endif + + +# ifdef LIBSANGOMA_GET_HWDTMF + ms->hw_dtmf=sangoma_tdm_get_hw_dtmf(ms->sangoma_sock, &tdm_api); + if (ms->hw_dtmf) { + log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Supported [w%ig%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); + } else { + log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Not Supported [w%ig%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); + } +#else + ms->hw_dtmf=0; + log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Not Supported [w%ig%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); +# warning "libsangoma missing hwdtmf feature: not up to date!" + +#endif + + ms->oob_disable = 0; +#ifdef LIBSANGOMA_VERSION + open_cnt = sangoma_get_open_cnt(ms->sangoma_sock, &tdm_api); + if (open_cnt > 1) { + ms->oob_disable = 1; + } +#endif + if (!bearer_cap_is_audio(woomera->bearer_cap)) { + int err; + err=sangoma_tdm_disable_hwec(ms->sangoma_sock, &tdm_api); + if (err == 0) { + hwec_reenable=1; + log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] Disabling hwec Ok\n",woomera->interface); + } else { + log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] Disabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + } + + } + + sangoma_frame_len = sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); + + } +#endif + } + + + +#ifdef WP_HPTDM_API + /* No tdm thread */ +#else + if (!errs && + launch_media_tdm_thread(woomera)) { + errs++; + } +#endif + + if (errs) { + + woomera->q931_rel_cause_topbx = 42; + + } else { + + unsigned char udp_frame[4096]; + unsigned int fromlen = sizeof(struct sockaddr_in); + int flags_out=0; + + sangoma_api_hdr_t hdrframe; + memset(&hdrframe,0,sizeof(hdrframe)); + memset(udp_frame,0,sizeof(udp_frame)); +#ifdef DOTRACE + int fdin, fdout; + char path_in[512], path_out[512]; +#endif + + new_woomera_event_printf(&wevent, + "EVENT MEDIA %s AUDIO%s" + "Unique-Call-Id: %s%s" + "Raw-Audio: %s:%d%s" + "Call-ID: %s%s" + "Raw-Format: PCM-16%s" + "DTMF: %s%s" + , + woomera->interface, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + server.media_ip, + local_port, + WOOMERA_LINE_SEPERATOR, + woomera->interface, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + (ms->hw_dtmf)? "OutofBand": "InBand", + + WOOMERA_RECORD_SEPERATOR + ); + + + enqueue_event(woomera, &wevent, EVENT_FREE_DATA); + +#ifdef DOTRACE + sprintf(path_in, "/tmp/debug-in.%d.raw", tc); + sprintf(path_out, "/tmp/debug-out.%d.raw", tc++); + fdin = open(path_in, O_WRONLY | O_CREAT, O_TRUNC, 0600); + fdout = open(path_out, O_WRONLY | O_CREAT, O_TRUNC, 0600); +#endif + + if (server.out_tx_test) { + tx_fd=fopen("/smg/sound.raw","rb"); + if (!tx_fd) { + log_printf(SMG_LOG_ALL,server.log, "FAILED TO OPEN Sound file!\n"); + } + } + + + for (;;) { + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) || + woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + res=0; + break; + } + + res = waitfor_socket(ms->udp_sock, sock_timeout, POLLIN, &flags_out); + + if (res < 0) { + break; + } + + if (res == 0) { + + if (woomera_dtmf_transmit(ms,sangoma_frame_len) == 0) { + sock_timeout=(sangoma_frame_len/codec_sample); + } else { + sock_timeout=200; + } + + if (ms->skip_write_frames > 0) { + ms->skip_write_frames--; + } + + log_printf(SMG_LOG_DEBUG_8, server.log, "%s: UDP Sock Timeout !!!\n", + woomera->interface); + /* NENAD Timeout thus just continue */ + continue; + } + + if ((packet_len = recvfrom(ms->udp_sock, udp_frame, sizeof(udp_frame), + MSG_DONTWAIT, (struct sockaddr *) &ms->local_addr, &fromlen)) < 1) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "UDP Recv Error: %s\n",strerror(errno)); + break; + } + + +#ifdef SMG_DROP_SEQ + if (drop_seq) { + log_printf(SMG_LOG_ALL,server.log,"Dropping TX Sequence! %i\n",drop_seq); + drop_seq--; + continue; + } +#endif + +#if 0 + log_printf(SMG_LOG_DEBUG_10, server.log, "%s: UDP Receive %i !!!\n", + woomera->interface,packet_len); +#endif + + if (packet_len > 0) { + +#if 0 +/* NC: This can cause skb_over panic must be retested */ + if (packet_len != sangoma_frame_len && ms->udp_sync_cnt <= 5) { + /* Assume that we will always receive SLINEAR here */ + sangoma_tdm_set_usr_period(ms->sangoma_sock, + &tdm_api, packet_len/codec_sample); + sangoma_frame_len = + sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); + + log_printf(SMG_LOG_DEBUG_MISC, server.log, + "%s: UDP TDM Period ReSync to Len=%i %ims (udp=%i) \n", + woomera->interface,sangoma_frame_len, + sangoma_frame_len/codec_sample,packet_len); + + + if (++ms->udp_sync_cnt >= 6) { + sangoma_tdm_set_usr_period(ms->sangoma_sock, + &tdm_api, 20); + sangoma_frame_len = + sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); + log_printf(SMG_LOG_ALL, server.log, + "%s: UDP TDM Period Force ReSync to 20ms \n", + woomera->interface); + } + + } +#endif + if (!server.out_tx_test) { + + if (woomera_dtmf_transmit(ms,sangoma_frame_len) == 0) { + sock_timeout=(sangoma_frame_len/codec_sample); + /* For sanity sake if we are doing the out test + * dont take any chances force tx udp data */ + if (ms->skip_write_frames > 0) { + ms->skip_write_frames--; + } + continue; + } else { + sock_timeout=200; + } + + if (ms->skip_write_frames > 0) { + ms->skip_write_frames--; + continue; + } + + } + + if (server.out_tx_test && tx_fd && + fread((void*)udp_frame, + sizeof(char), + packet_len,tx_fd) <= 0) { + + sangoma_get_full_cfg(ms->sangoma_sock,&tdm_api); + fclose(tx_fd); + tx_fd=NULL; + } + +#ifdef WP_HPTDM_API + if (ms->tdmchan->push) { + ms->tdmchan->push(ms->tdmchan,udp_frame,packet_len); + } +#else + + sangoma_sendmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + udp_frame, + packet_len, 0); +#endif + + } + +#if 0 + if (woomera->span == 1 && woomera->chan == 1) { + udp_cnt++; + if (udp_cnt && udp_cnt % 1000 == 0) { + log_printf(SMG_LOG_ALL, server.log, "%s: MEDIA UDP TX RX CNT %i %i\n", + woomera->interface,udp_cnt,packet_len); + } + } +#endif + } + + if (res < 0) { + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) || + woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + res=0; + } else { + log_printf(SMG_LOG_ALL, server.log, "Media Thread: socket error %s SockID=%i %s Poll=%s!\n", + woomera->interface,ms->sangoma_sock,strerror(errno),SMG_DECODE_POLL_ERRORS(flags_out)); + } + } + } + + new_woomera_event_printf(&wevent, + "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Start-Time: %ld%s" + "End-Time: %ld%s" + "Answer-Time: %ld%s" + "Call-ID: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + woomera->interface, + WOOMERA_LINE_SEPERATOR, + + woomera->session, + WOOMERA_LINE_SEPERATOR, + + time(&ms->started), + WOOMERA_LINE_SEPERATOR, + + time(NULL), + WOOMERA_LINE_SEPERATOR, + + time(&ms->answered), + WOOMERA_LINE_SEPERATOR, + + woomera->interface, + WOOMERA_LINE_SEPERATOR, + + q931_rel_to_str(woomera->q931_rel_cause_topbx), + WOOMERA_LINE_SEPERATOR, + + woomera->q931_rel_cause_topbx, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + +media_thread_exit: + + if (hwec_reenable) { + int err; + err=sangoma_tdm_enable_hwec(ms->sangoma_sock, &tdm_api); + if (err==0) { + log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] Re-enabling hwec ok\n",woomera->interface); + } else { + log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] Re-enabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + } + } + + if (woomera_test_flag(woomera, WFLAG_MEDIA_TDM_RUNNING)) { + woomera_set_flag(woomera, WFLAG_MEDIA_END); + + /* Dont wait for the other thread */ + close_socket(&ms->udp_sock); + close_span_chan(&ms->sangoma_sock, woomera->span+1, woomera->chan+1); + while(woomera_test_flag(woomera, WFLAG_MEDIA_TDM_RUNNING)) { + usleep(1000); + sched_yield(); + } + } + + + close_socket(&ms->udp_sock); + close_span_chan(&ms->sangoma_sock, woomera->span+1, woomera->chan+1); + + if (tx_fd){ + fclose(tx_fd); + tx_fd=NULL; + } + + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + + woomera_set_ms(woomera,NULL); + woomera_clear_flag(woomera, WFLAG_MEDIA_RUNNING); + + media_session_free(ms); + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA session for [%s] ended (ptr=%p)\n", + woomera->interface,woomera); + + + pthread_exit(NULL); + return NULL; +} + + + + +static void *media_tdm_thread_run(void *obj) +{ + wanpipe_tdm_api_t tdm_api; + wp_tdm_api_event_t *rx_event; + + struct media_session *ms = obj; + struct woomera_interface *woomera = ms->woomera; + int res = 0; + unsigned char circuit_frame[1024]; + sangoma_api_hdr_t hdrframe; + int flags_out; + int poll_opt = POLLIN | POLLPRI; + + memset(&hdrframe,0,sizeof(hdrframe)); + memset(circuit_frame,0,sizeof(circuit_frame)); + + memset(&tdm_api, 0, sizeof(wanpipe_tdm_api_t)); + + if (woomera_test_flag(woomera, WFLAG_MEDIA_END) || !woomera->interface) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA TDM session for [%s] Cancelled! (ptr=%p)\n", + woomera->interface,woomera); + /* In this case the call will be closed via woomera_thread_run + * function. And the process table will be cleard there */ + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + pthread_exit(NULL); + return NULL; + } + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA TDM session for [%s] started (ptr=%p)\n", + woomera->interface,woomera); + + + for (;;) { + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) || + woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + res=0; + break; + } + + if (ms->oob_disable) { + poll_opt = POLLIN; + } else { + poll_opt = POLLIN | POLLPRI; + } + res = waitfor_socket(ms->sangoma_sock, 1000, poll_opt, &flags_out); + + + if (res < 0) { + break; + } + + if (res == 0) { +#if 0 + log_printf(SMG_LOG_DEBUG_8, server.log, "%s: TDM UDP Timeout !!!\n", + woomera->interface); + /* NENAD Timeout thus just continue */ +#endif + continue; + } + + + if (flags_out & POLLIN) { + + res = sangoma_readmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + circuit_frame, + sizeof(circuit_frame), 0); + + if (res < 0) { + if (!woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + log_printf(SMG_LOG_ALL, server.log, "TDM Read Data Error: %s %s Sockid=%i\n", + woomera->interface, + strerror(errno),ms->sangoma_sock); + } + break; + } + + res = sendto(ms->udp_sock, + circuit_frame, + res, 0, + (struct sockaddr *) &ms->remote_addr, + sizeof(ms->remote_addr)); + + if (res < 0) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "UDP Sento Error: %s\n", strerror(errno)); + + } + } + + if (flags_out & POLLPRI) { + + res = sangoma_tdm_read_event(ms->sangoma_sock, &tdm_api); + if (res < 0) { + log_printf(SMG_LOG_ALL, server.log, "TDM Read Event Error: %s %s Sockid=%i\n", + woomera->interface, + strerror(errno),ms->sangoma_sock); + break; + } + + rx_event = &tdm_api.wp_tdm_cmd.event; + + switch (rx_event->wp_tdm_api_event_type){ +# ifdef LIBSANGOMA_GET_HWDTMF + case WP_TDMAPI_EVENT_DTMF: + + /* Only handle hw dtmf if hw_dtmf option is enabled */ + if (!ms->hw_dtmf) { + break; + } + + /* PORT_SOUT = 1 */ + if (rx_event->wp_tdm_api_event_dtmf_port == WAN_EC_CHANNEL_PORT_SOUT && + rx_event->wp_tdm_api_event_dtmf_type == WAN_EC_TONE_PRESENT) { + + handle_event_dtmf(woomera, rx_event->wp_tdm_api_event_dtmf_digit); + } + break; +#endif + default: + log_printf(SMG_LOG_ALL, server.log, "TDM API Unknown OOB Event %i\n", + rx_event->wp_tdm_api_event_type); + break; + } + } + +#if 0 + if (woomera->span == 1 && woomera->chan == 1) { + tdm_cnt++; + if (tdm_cnt && tdm_cnt % 1000 == 0) { + log_printf(SMG_LOG_ALL, server.log, "%s: MEDIA TDM TX RX CNT %i %i\n", + woomera->interface,tdm_cnt,res); + } + } +#endif + + } + + if (res < 0) { + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) || + woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + + /* Good reason to exit */ + + } else { + + log_printf(SMG_LOG_ALL, server.log, "Media TDM Thread: socket error %s Sockid=%i %s Woomera Flags=0x%08X Poll=%s!\n", + woomera->interface,ms->sangoma_sock,strerror(errno),woomera->flags,SMG_DECODE_POLL_ERRORS(flags_out)); + woomera_print_flags(woomera,0); + } + } + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA TDM session for [%s] ended (ptr=%p)\n", + woomera->interface,woomera); + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + + pthread_exit(NULL); + return NULL; + +} + + +/* This function must be called with process_lock + * because it modifies shared process_table */ + +static int launch_media_thread(struct woomera_interface *woomera) +{ + pthread_attr_t attr; + int result = -1; + struct media_session *ms; + + if ((ms = media_session_new(woomera))) { + result = pthread_attr_init(&attr); + //pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + //pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + woomera_set_flag(woomera, WFLAG_MEDIA_RUNNING); + result = pthread_create(&ms->thread, &attr, media_thread_run, ms); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Thread! %s\n", + __FUNCTION__,strerror(errno)); + woomera_clear_flag(woomera, WFLAG_MEDIA_RUNNING); + media_session_free(woomera->ms); + + } + pthread_attr_destroy(&attr); + + } else { + log_printf(SMG_LOG_ALL, server.log, "Failed to start new media session\n"); + } + + return result; + +} + +static int launch_media_tdm_thread(struct woomera_interface *woomera) +{ + pthread_attr_t attr; + int result = -1; + struct media_session *ms = woomera_get_ms(woomera); + + if (!ms) { + return result; + } + + result = pthread_attr_init(&attr); + //pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + //pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + woomera_set_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + result = pthread_create(&ms->thread, &attr, media_tdm_thread_run, ms); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Thread! %s\n", + __FUNCTION__,strerror(errno)); + woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + } + pthread_attr_destroy(&attr); + + return result; +} + + +static struct woomera_interface * launch_woomera_loop_thread(short_signal_event_t *event) +{ + + struct woomera_interface *woomera = NULL; + char callid[20]; + + sprintf(callid, "w%dg%d", event->span+1,event->chan+1); + + if ((woomera = alloc_woomera())) { + + woomera->chan = event->chan; + woomera->span = event->span; + woomera->log = server.log; + woomera->debug = server.debug; + woomera->call_id = 1; + woomera->event_queue = NULL; + woomera->loop_tdm=1; + woomera->socket=-1; + + } else { + log_printf(SMG_LOG_ALL, server.log, "Critical ERROR: memory/socket error\n"); + return NULL; + } + + woomera_set_interface(woomera,callid); + + pthread_mutex_lock(&server.process_lock); + server.process_table[event->span][event->chan].dev = woomera; + pthread_mutex_unlock(&server.process_lock); + + if (launch_woomera_thread(woomera)) { + pthread_mutex_lock(&server.process_lock); + server.process_table[event->span][event->chan].dev = NULL; + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + smg_free(woomera); + log_printf(SMG_LOG_ALL, server.log, "Critical ERROR: memory/socket error\n"); + return NULL; + } + + return woomera; +} + +static int woomera_message_parse(struct woomera_interface *woomera, struct woomera_message *wmsg, int timeout) +{ + char *cur, *cr, *next = NULL, *eor = NULL; + char buf[2048]; + int res = 0, bytes = 0, sanity = 0; + struct timeval started, ended; + int elapsed, loops = 0; + int failto = 0; + int packet = 0; + int flags_out = 0; + + memset(wmsg, 0, sizeof(*wmsg)); + + if (woomera->socket < 0 ) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, WOOMERA_DEBUG_PREFIX "%s Invalid Socket! %d\n", + woomera->interface,woomera->socket); + return -1; + } + + if (woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_DEBUG_9, woomera->log, WOOMERA_DEBUG_PREFIX + "%s Woomera Message parse: Call Hangup - skipping message parse !\n", + woomera->interface); + return -1; + } + + gettimeofday(&started, NULL); + memset(buf, 0, sizeof(buf)); + + if (timeout < 0) { + timeout = abs(timeout); + failto = 1; + } else if (timeout == 0) { + timeout = -1; + } + + + while (!(eor = strstr(buf, WOOMERA_RECORD_SEPERATOR))) { + if (sanity > 1000) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, WOOMERA_DEBUG_PREFIX "%s Failed Sanity Check!\n[%s]\n\n", woomera->interface, buf); + return -1; + } + + if ((res = waitfor_socket(woomera->socket, 1000, POLLIN, &flags_out) > 0)) { + + res = recv(woomera->socket, buf, sizeof(buf), MSG_PEEK); + + if (res > 1) { + packet++; + } + if (!strncmp(buf, WOOMERA_LINE_SEPERATOR, 2)) { + res = read(woomera->socket, buf, 2); + return 0; + } + if (res == 0) { + sanity++; + /* Looks Like it's time to go! */ + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_DEBUG_9, woomera->log, WOOMERA_DEBUG_PREFIX + "%s MEDIA END or HANGUP \n", woomera->interface); + return -1; + } + ysleep(1000); + continue; + + } else if (res < 0) { + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, WOOMERA_DEBUG_PREFIX + "%s error during packet retry (err=%i) Loops#%d (%s)\n", + woomera->interface, res, loops, + strerror(errno)); + return res; + } else if (loops) { + ysleep(100000); + } + } + + gettimeofday(&ended, NULL); + elapsed = (((ended.tv_sec * 1000) + ended.tv_usec / 1000) - ((started.tv_sec * 1000) + started.tv_usec / 1000)); + + if (res < 0) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, WOOMERA_DEBUG_PREFIX "%s Bad RECV\n", + woomera->interface); + return res; + } else if (res == 0) { + sanity++; + /* Looks Like it's time to go! */ + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_DEBUG_9, woomera->log, WOOMERA_DEBUG_PREFIX + "%s MEDIA END or HANGUP \n", woomera->interface); + return -1; + } + ysleep(1000); + continue; + } + + if (packet && loops > 150) { + log_printf(SMG_LOG_PROD, woomera->log, WOOMERA_DEBUG_PREFIX + "%s Timeout waiting for packet.\n", + woomera->interface); + return -1; + } + + if (timeout > 0 && (elapsed > timeout)) { + log_printf(SMG_LOG_PROD, woomera->log, WOOMERA_DEBUG_PREFIX + "%s Timeout [%d] reached\n", + woomera->interface, timeout); + return failto ? -1 : 0; + } + + if (woomera_test_flag(woomera, WFLAG_EVENT)) { + /* BRB! we have an Event to deliver....*/ + return 0; + } + + /* what're we still doing here? */ + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + !woomera_test_flag(woomera, WFLAG_RUNNING)) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, WOOMERA_DEBUG_PREFIX + "%s Woomera Message Parse: Server or Woomera not Running\n", woomera->interface); + return -1; + } + loops++; + } + + *eor = '\0'; + bytes = strlen(buf) + 4; + + memset(buf, 0, sizeof(buf)); + res = read(woomera->socket, buf, bytes); + next = buf; + + log_printf(SMG_LOG_WOOMERA, woomera->log, "%s:WOOMERA RX MSG: %s\n",woomera->interface,buf); + + while ((cur = next)) { + + if ((cr = strstr(cur, WOOMERA_LINE_SEPERATOR))) { + *cr = '\0'; + next = cr + (sizeof(WOOMERA_LINE_SEPERATOR) - 1); + if (!strcmp(next, WOOMERA_RECORD_SEPERATOR)) { + break; + } + } + if (!cur || !*cur) { + break; + } + + if (!wmsg->last) { + char *cmd, *id, *args; + woomera_set_flag(wmsg, MFLAG_EXISTS); + cmd = cur; + + if ((id = strchr(cmd, ' '))) { + *id = '\0'; + id++; + if ((args = strchr(id, ' '))) { + *args = '\0'; + args++; + strncpy(wmsg->command_args, args, sizeof(wmsg->command_args)-1); + } + strncpy(wmsg->callid, id, sizeof(wmsg->callid)-1); + } + + strncpy(wmsg->command, cmd, sizeof(wmsg->command)-1); + } else { + char *name, *val; + name = cur; + + if ((val = strchr(name, ':'))) { + *val = '\0'; + val++; + while (*val == ' ') { + *val = '\0'; + val++; + } + strncpy(wmsg->values[wmsg->last-1], val, WOOMERA_STRLEN); + } + strncpy(wmsg->names[wmsg->last-1], name, WOOMERA_STRLEN); + if (name && val && !strcasecmp(name, "content-type")) { + woomera_set_flag(wmsg, MFLAG_CONTENT); + bytes = atoi(val); + } + if (name && val && !strcasecmp(name, "content-length")) { + woomera_set_flag(wmsg, MFLAG_CONTENT); + bytes = atoi(val); + } + + + } + wmsg->last++; + } + + wmsg->last--; + + if (bytes && woomera_test_flag(wmsg, MFLAG_CONTENT)) { + int terr; + terr=read(woomera->socket, wmsg->body, + (bytes > sizeof(wmsg->body)) ? sizeof(wmsg->body) : bytes); + } + + return woomera_test_flag(wmsg, MFLAG_EXISTS); + +} + +static struct woomera_interface *pull_from_holding_tank(int index, int span , int chan) +{ + struct woomera_interface *woomera = NULL; + + if (index < 1 || index >= CORE_TANK_LEN) { + if (index != 0) { + log_printf(SMG_LOG_ALL, server.log, "%s Error on invalid TANK INDEX = %i\n", + __FUNCTION__,index); + } + return NULL; + } + + pthread_mutex_lock(&server.ht_lock); + if (server.holding_tank[index] && + server.holding_tank[index] != &woomera_dead_dev) { + woomera = server.holding_tank[index]; + + /* Block this index until the call is completed */ + server.holding_tank[index] = &woomera_dead_dev; + + woomera->index = 0; + woomera->span=span; + woomera->chan=chan; + } + pthread_mutex_unlock(&server.ht_lock); + + return woomera; +} + +static void clear_all_holding_tank(void) +{ + int i; + pthread_mutex_lock(&server.ht_lock); + for (i=0;i= CORE_TANK_LEN) { + if (index != 0) { + log_printf(SMG_LOG_ALL, server.log, "%s Error on invalid TANK INDEX = %i\n", + __FUNCTION__,index); + } + return NULL; + } + + pthread_mutex_lock(&server.ht_lock); + woomera = server.holding_tank[index]; + pthread_mutex_unlock(&server.ht_lock); + + return woomera; +} + + +static void clear_from_holding_tank(int index, struct woomera_interface *woomera) +{ + + if (index < 1 || index >= CORE_TANK_LEN) { + if (index != 0) { + log_printf(SMG_LOG_ALL, server.log, "%s Error on invalid TANK INDEX = %i\n", + __FUNCTION__,index); + } + return; + } + + pthread_mutex_lock(&server.ht_lock); + if (server.holding_tank[index] == &woomera_dead_dev) { +#if 0 + log_printf(SMG_LOG_ALL,server.log, "%s Clearing DEAD id=%i OK\n", + __FUNCTION__,index); +#endif + server.holding_tank[index] = NULL; + } else if (woomera && server.holding_tank[index] == woomera) { +#if 0 + log_printf(SMG_LOG_ALL,server.log, "%s Clearing ACTIVE Woomera id=%i OK\n", + __FUNCTION__,index); +#endif + server.holding_tank[index] = NULL; + } else if (server.holding_tank[index]) { + log_printf(SMG_LOG_ALL, server.log, "Critical Error: Holding tank index %i not cleared %p !\n", + index, server.holding_tank[index]); + } + pthread_mutex_unlock(&server.ht_lock); + + return; +} + +static struct woomera_interface *peek_from_holding_tank(int index) +{ + struct woomera_interface *woomera = NULL; + + if (index < 1 || index >= CORE_TANK_LEN) { + if (index != 0) { + log_printf(SMG_LOG_ALL, server.log, "%s Error on invalid TANK INDEX = %i\n", + __FUNCTION__,index); + } + return NULL; + } + + pthread_mutex_lock(&server.ht_lock); + if (server.holding_tank[index] && + server.holding_tank[index] != &woomera_dead_dev) { + woomera = server.holding_tank[index]; + } + pthread_mutex_unlock(&server.ht_lock); + + return woomera; +} + +static int add_to_holding_tank(struct woomera_interface *woomera) +{ + int next, i, found=0; + + pthread_mutex_lock(&server.ht_lock); + + for (i=0;i= CORE_TANK_LEN) { + next = server.holding_tank_index = 1; + } + + if (next == 0) { + log_printf(SMG_LOG_ALL, server.log, "\nCritical Error on TANK INDEX == 0\n"); + continue; + } + + if (server.holding_tank[next]) { + continue; + } + + found=1; + break; + } + + if (!found) { + /* This means all tank vales are busy + * should never happend */ + pthread_mutex_unlock(&server.ht_lock); + log_printf(SMG_LOG_ALL, server.log, "\nCritical Error failed to obtain a TANK INDEX\n"); + return 0; + } + + server.holding_tank[next] = woomera; + woomera->timeout = time(NULL) + server.call_timeout; + + pthread_mutex_unlock(&server.ht_lock); + return next; +} + + + +static void handle_event_dtmf(struct woomera_interface *woomera, unsigned char dtmf_digit) +{ + struct woomera_event wevent; + + memset(&wevent, 0, sizeof(struct woomera_event)); + + new_woomera_event_printf(&wevent, "EVENT DTMF %sUnique-Call-Id:%s%sContent-Length:%d%s%s%c%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + 1, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + dtmf_digit, + WOOMERA_RECORD_SEPERATOR); + + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + return; +} + +static int handle_woomera_progress(struct woomera_interface *woomera, + struct woomera_message *wmsg) +{ + call_signal_event_t event; + int err=-1; + + memset(&event, 0, sizeof(event)); + event.event_id = SIGBOOST_EVENT_CALL_PROGRESS; + sprintf(event.isup_in_rdnis,"SMG003-EVI-2"); + event.isup_in_rdnis_size=strlen(event.isup_in_rdnis); + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s]\n", + wmsg->command, woomera->interface); + + if (!woomera_check_running(woomera)) { + socket_printf(woomera->socket, "405 PROGRESS Channel already hungup%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + if (!woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + + socket_printf(woomera->socket, "405 PROGRESS Channel not aceked%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + err=isup_exec_event(&event); + if (err) { + socket_printf(woomera->socket, + "200 %s PROGRESS OK%s" + "Unique-Call-Id: %s%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } else { + socket_printf(woomera->socket, "405 PROGRESS Boost failure%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } + + return err; +} + +static int handle_woomera_media_accept_answer(struct woomera_interface *woomera, + struct woomera_message *wmsg, + int media, int answer, int accept) +{ + char *raw = woomera_message_header(wmsg, "raw-audio"); + struct woomera_event wevent; + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s]\n", + wmsg->command, woomera->interface); + + if (!woomera_check_running(woomera)) { + + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "ERROR! call was cancelled MEDIA on HANGUP or MEDIA END!\n"); + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(woomera->q931_rel_cause_topbx), + WOOMERA_LINE_SEPERATOR, + woomera->q931_rel_cause_topbx, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call already hungup!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + woomera->timeout=0; + return -1; + } + + log_printf(SMG_LOG_DEBUG_MISC, server.log,"WOOMERA: GOT %s EVENT: [%s] RAW=%s\n", + wmsg->command,wmsg->callid,raw); + + + if (raw && + woomera->raw == NULL && + !woomera_test_flag(woomera, WFLAG_RAW_MEDIA_STARTED)) { + + woomera_set_flag(woomera, WFLAG_RAW_MEDIA_STARTED); + + woomera_set_raw(woomera, raw); + + if (launch_media_thread(woomera)) { + + log_printf(SMG_LOG_DEBUG_8, server.log,"ERROR: Failed to Launch Call [%s]\n", + woomera->interface); + + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(21), + WOOMERA_LINE_SEPERATOR, + 21, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_RUNNING); + + woomera->timeout=0; + return -1; + } + } + + if (!woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + log_printf(SMG_LOG_ALL, server.log,"ERROR! Monitor Thread not running!\n"); + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + return -1; + + } else { + + if (accept) { + if (!autoacm && !woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + woomera_set_flag(woomera,WFLAG_CALL_ACKED); + isup_exec_command(woomera->span, + woomera->chan, + -1, + SIGBOOST_EVENT_CALL_START_ACK, + 0); + } + } + + if (answer) { + struct media_session *ms; + + pthread_mutex_lock(&woomera->ms_lock); + if ((ms=woomera->ms)) { + time(&woomera->ms->answered); + } + pthread_mutex_unlock(&woomera->ms_lock); + + if (ms) { + + if (!autoacm && !woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + woomera_set_flag(woomera,WFLAG_CALL_ACKED); + isup_exec_command(woomera->span, + woomera->chan, + -1, + SIGBOOST_EVENT_CALL_START_ACK, + 0); + } + + isup_exec_command(woomera->span, + woomera->chan, + -1, + SIGBOOST_EVENT_CALL_ANSWERED, + 0); + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "Sent SIGBOOST_EVENT_CALL_ANSWERED [w%dg%d]\n", + woomera->span+1,woomera->chan+1); + } else { + struct woomera_event wevent; + log_printf(SMG_LOG_ALL, server.log, + "WOOMERA ANSWER: FAILED [%s] no Media \n", + wmsg->command,wmsg->callid); + + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(21), + WOOMERA_LINE_SEPERATOR, + 21, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_RUNNING); + woomera->timeout=0; + return -1; + } + } + } + + new_woomera_event_printf(&wevent, "200 %s OK%s" + "Unique-Call-Id: %s%s", + answer ? "ANSWER" : + accept ? "ACCEPT" : "MEDIA", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + return 0; +} + +static int handle_woomera_call_start (struct woomera_interface *woomera, + struct woomera_message *wmsg) +{ + char *raw = woomera_message_header(wmsg, "raw-audio"); + call_signal_event_t event; + char *calling = woomera_message_header(wmsg, "local-number"); +#ifdef SMG_CALLING_NAME + char *calling_name = woomera_message_header(wmsg, "local-name"); +#endif + char *presentation = woomera_message_header(wmsg, "Presentation"); + char *screening = woomera_message_header(wmsg, "Screening"); + char *rdnis = woomera_message_header(wmsg, "RDNIS"); + char *bearer_cap = woomera_message_header(wmsg, "Bearer-Cap"); + char *uil1p = woomera_message_header(wmsg, "uil1p"); + char *called = wmsg->callid; + char *grp = wmsg->callid; + char *p; + int cause = 34; + int tg = 0; + int huntgroup = SIGBOOST_HUNTGRP_SEQ_ASC; + + if (smg_check_all_busy() || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ + + + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, + "405 SMG Server All Ckt Busy!%s", WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "SMG Server Full %d (ckt busy cnt=%i)\n", + server.call_count, server.all_ckt_busy); + return -1; + } + + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "New Call %d/%d\n", server.call_count, server.max_calls); + + switch(called[0]) { + case 'g': + huntgroup = SIGBOOST_HUNTGRP_SEQ_ASC; + break; + case 'G': + huntgroup = SIGBOOST_HUNTGRP_SEQ_DESC; + break; + case 'r': + huntgroup = SIGBOOST_HUNTGRP_RR_ASC; + break; + case 'R': + huntgroup = SIGBOOST_HUNTGRP_RR_DESC; + break; + default: + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, + "Warning: Failed to determine huntgroup (%s)\n", + called); + huntgroup = SIGBOOST_HUNTGRP_SEQ_ASC; + } + + if ((p = strchr(called, '/'))) { + *p = '\0'; + called = p+1; + tg = atoi(grp+1) - 1; + if (tg < 0) { + tg=0; + } + } + + woomera->trunk_group=tg; + if (raw) { + woomera_set_raw(woomera, raw); + } + + woomera->index = add_to_holding_tank(woomera); + if (woomera->index < 1) { + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, + "405 SMG Server All Tanks Busy!%s", + WOOMERA_RECORD_SEPERATOR); + log_printf(SMG_LOG_ALL, woomera->log, "Error: Call Tank Full (Call Cnt=%i)\n", + server.call_count); + return -1; + } + + + woomera->index_hold = woomera->index; + + call_signal_call_init(&event, calling, called, woomera->index); + + if (presentation) { + event.calling_number_presentation = atoi(presentation); + } else { + event.calling_number_presentation = 0; + } + + if (screening) { + event.calling_number_screening_ind = atoi(screening); + } else { + event.calling_number_screening_ind = 0; + } + + event.isup_in_rdnis_size=0; + event.isup_in_rdnis[0]=0; + + if (rdnis && strlen(rdnis) ) { + + if (strlen(rdnis) > sizeof(event.isup_in_rdnis)){ + log_printf(SMG_LOG_ALL,server.log,"Error: RDNIS Overflow (in size=%i max=%i)\n", + strlen(rdnis), sizeof(event.isup_in_rdnis)); + + } else { + + strncpy(event.isup_in_rdnis,rdnis, + sizeof(event.isup_in_rdnis)-1); + event.isup_in_rdnis_size=strlen(rdnis)+1; + log_printf(SMG_LOG_DEBUG_MISC,server.log,"RDNIS %s\n", rdnis); + } + + } + + if (bearer_cap && strlen(bearer_cap)) { + event.bearer.capability = bearer_cap_to_code(bearer_cap); + woomera->bearer_cap = event.bearer.capability; + } + + if (uil1p && strlen(uil1p)) { + event.bearer.uil1p = uil1p_to_code(uil1p); + } + +#ifdef SMG_CALLING_NAME + if (calling_name) { + strncpy((char*)event.calling_name,calling_name, + sizeof(event.calling_name)-1); + } +#endif + + event.trunk_group = tg; + event.hunt_group = huntgroup; + + if (call_signal_connection_write(&server.mcon, &event) < 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", + strerror(errno)); + + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, + "405 SMG Signalling Contestion!%s", + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + socket_printf(woomera->socket, "100 Trying%s", WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Call Called Event [Setup ID: %d] TG=%d\n", + woomera->index,tg); + + return 0; +} + + +static void interpret_command(struct woomera_interface *woomera, struct woomera_message *wmsg) +{ + int answer = 0, media = 0, accept=0; + char *unique_id; + int cause=0; + + + + if (!strcasecmp(wmsg->command, "call")) { + int err; + + if (strlen(woomera->session) != 0) { + /* Call has already been placed */ + socket_printf(woomera->socket, "400 Error Call already in progress %s", + WOOMERA_RECORD_SEPERATOR); + log_printf(SMG_LOG_ALL,server.log,"Woomera RX Call Even while call in progress!\n"); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(34), + WOOMERA_LINE_SEPERATOR, + 34, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, + "405 SMG Server All Ckt Reset!%s", WOOMERA_RECORD_SEPERATOR); + + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + err=handle_woomera_call_start(woomera,wmsg); + if (err) { + woomera_set_flag(woomera, WFLAG_HANGUP); + } + + return; + + } else if (!strcasecmp(wmsg->command, "bye") || !strcasecmp(wmsg->command, "quit")) { + char *cause = woomera_message_header(wmsg, "cause"); + char *q931cause = woomera_message_header(wmsg, "Q931-Cause-Code"); + + if (cause) { + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Bye Cause Received: [%s]\n", cause); + } + if (q931cause && atoi(q931cause)) { + woomera_set_cause_tosig(woomera,atoi(q931cause)); + } + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: Bye Received: [%s]\n", woomera->interface); + + woomera_clear_flag(woomera, WFLAG_RUNNING); + socket_printf(woomera->socket, "200 Connection closed%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return; + + } else if (!strcasecmp(wmsg->command, "listen")) { + + if (woomera_test_flag(woomera, WFLAG_LISTENING)) { + socket_printf(woomera->socket, "405 Listener already started%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } else { + + woomera_set_flag(woomera, WFLAG_LISTENING); + add_listener(woomera); + log_printf(SMG_LOG_ALL,woomera->log, "Starting Listen Device!\n"); + + socket_printf(woomera->socket, "%s", + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, "200 Listener enabled%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } + return; + + } else if ((media = !strcasecmp(wmsg->command, "debug"))) { + + int debug_level=atoi(wmsg->callid); + + if (debug_level < 10) { + server.debug=debug_level; + log_printf(SMG_LOG_ALL,server.log,"SMG Debugging set to %i (window=%i)\n",server.debug,server.mcon.txwindow); + } + + return; + } + + if (!strcasecmp(wmsg->command, "hangup")) { + char *q931cause = woomera_message_header(wmsg, "Q931-Cause-Code"); + + + if (q931cause && atoi(q931cause)) { + log_printf(SMG_LOG_DEBUG_8,server.log,"Woomera Hangup setting cause to %s %i\n", + q931cause,atoi(q931cause)); + woomera_set_cause_tosig(woomera,atoi(q931cause)); + } + + /* Continue Through */ + } + + + + unique_id = woomera_message_header(wmsg, "Unique-Call-Id"); + if (!unique_id) { + + cause=111; + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, "400 Woomera cmd without uniquie id%s" + WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_CALL,server.log,"Woomera RX Event (%s) without unique id!\n",wmsg->command); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + if (strlen(woomera->session) == 0) { + struct woomera_interface *session_woomera=NULL; + char *session=NULL; + int span, chan; + char ifname[100]; + /* If session does not exist this is an incoming call */ + sscanf(unique_id, "w%dg%d", &span, &chan); + span--; + chan--; + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, + "WOOMERA Got CMD %s Span=%d Chan=%d from session %s\n", + wmsg->command,span,chan,unique_id); + + if (smg_validate_span_chan(span,chan) != 0) { + + cause=81; + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, "404 Invalid span/chan in session%s" + WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, + "WOOMERA Warning invalid span chan in session %s %s\n", + wmsg->command,unique_id); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + pthread_mutex_lock(&server.process_lock); + session = server.process_table[span][chan].session; + session_woomera = server.process_table[span][chan].dev; + + /* This scenario is very common when we have multile clients + where multiple clients race get the incoming call */ + if (session_woomera) { + pthread_mutex_unlock(&server.process_lock); + + cause=81; + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, "404 Session not found%s" + WOOMERA_RECORD_SEPERATOR); + + + log_printf(SMG_LOG_DEBUG_8, woomera->log, "WOOMERA Error channel in use %s %s\n", + wmsg->command,unique_id); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + if (!session || strlen(session) == 0 || + strncmp(session,unique_id,sizeof(woomera->session))){ + pthread_mutex_unlock(&server.process_lock); + + /* Invalid call reference */ + cause=81; + socket_printf(woomera->socket, "event hangup %s" + "cause: %s%s" + "q931-cause-code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, "404 Invalid/Expired Session%s" + WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, + "WOOMERA Warning: Cmd=%s with invalid session %s (orig=%s)\n", + wmsg->command,unique_id,session?session:"N/A"); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + +#if 1 + if (!strcasecmp(wmsg->command, "hangup")) { + int clients=server.process_table[span][chan].clients; + if (server.process_table[span][chan].clients < 0) { + + log_printf(SMG_LOG_ALL, woomera->log, "WOOMERA CMD: Warning Clients (%i) Race condition on Hangup [w%dg%d]\n", + clients, span+1,chan+1); + + } else if (server.process_table[span][chan].clients > 1) { + server.process_table[span][chan].clients--; + pthread_mutex_unlock(&server.process_lock); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Got Hungup on Multiple Clients %i, skipping hangup [w%dg%d]\n", + clients, span+1,chan+1); + + cause=16; + socket_printf(woomera->socket, "event hangup %s" + "cause: %s%s" + "q931-cause-code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, "404 Hangup on multiple session%s" + WOOMERA_RECORD_SEPERATOR); + + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Hanging up channel [w%dg%d]\n", + span+1,chan+1); + } +#endif + + server.process_table[span][chan].dev=woomera; + strncpy(woomera->session,unique_id,sizeof(woomera->session)); + sprintf(ifname,"w%dg%d",span+1,chan+1); + woomera_set_interface(woomera, ifname); + + woomera->span=span; + woomera->chan=chan; + + /* Save bearer cap that came in on incoming call event */ + woomera->bearer_cap = server.process_table[span][chan].bearer_cap; + + /* set it to 1 so that queued digits are checked on proceed message */ + woomera->check_digits = 1; + + pthread_mutex_unlock(&server.process_lock); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA Got New If=%s Session %s\n", + woomera->interface, woomera->session); + + + } else if (strncmp(woomera->session,unique_id,sizeof(woomera->session))) { + + cause=81; + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, "404 Session Mis-match%s" + WOOMERA_RECORD_SEPERATOR); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + if (!strcasecmp(wmsg->command, "dtmf")) { + + log_printf(SMG_LOG_WOOMERA, woomera->log, + "WOOMERA CMD: DTMF Received: [%s] Digit %s Body %s\n", + woomera->interface, wmsg->command_args, wmsg->body); + + wanpipe_send_dtmf(woomera,wmsg->body); + + socket_printf(woomera->socket, "200 DTMF OK%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + + } else if (!strcasecmp(wmsg->command, "hangup")) { + + int chan = -1, span = -1; + char *cause = woomera_message_header(wmsg, "cause"); + char *q931cause = woomera_message_header(wmsg, "Q931-Cause-Code"); + + if (q931cause && atoi(q931cause)) { + woomera_set_cause_tosig(woomera,atoi(q931cause)); + } + + span=woomera->span; + chan=woomera->chan; + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Hangup Received: [%s] MEDIA EXIST Cause=%s\n", + woomera->interface,cause); + + if (smg_validate_span_chan(span,chan) != 0) { + + socket_printf(woomera->socket, "405 No Such Channel%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return; + } + + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "Hangup Received: [w%dg%d]\n", + span+1,chan+1); + + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_RUNNING); + + + socket_printf(woomera->socket, "200 HANGUP OK%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + } else if (!strcasecmp(wmsg->command, "proceed")) { + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s]\n", + wmsg->command, woomera->interface); + + socket_printf(woomera->socket, + "200 %s PROCEED OK%s" + "Unique-Call-Id: %s%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + } else if (!strcasecmp(wmsg->command, "progress")) { + + handle_woomera_progress(woomera,wmsg); + + + } else if ((media = !strcasecmp(wmsg->command, "media")) || + (answer = !strcasecmp(wmsg->command, "answer")) || + (accept = !strcasecmp(wmsg->command, "accept"))) { + + handle_woomera_media_accept_answer(woomera, wmsg, media,answer,accept); + + + } else { + log_printf(SMG_LOG_ALL, server.log,"WOOMERA INVALID EVENT: %s [%s] \n", + wmsg->command,wmsg->callid); + socket_printf(woomera->socket, "501 Command '%s' not implemented%s", + wmsg->command, WOOMERA_RECORD_SEPERATOR); + } +} + + +/* + EVENT INCOMING 1 + Remote-Address: 10.3.3.104 + Remote-Number: + Remote-Name: Anthony Minessale!8668630501 + Protocol: H.323 + User-Agent: Post Increment Woomera 1.0alpha1 (OpenH323 v1.17.2) 9/61 + H323-Call-Id: 887b1ff8-bb1f-da11-85c0-0007e98988c4 + Local-Number: 996 + Start-Time: Fri, 09 Sep 2005 12:25:14 -0400 + Local-Name: root +*/ + + +static void handle_call_answer(short_signal_event_t *event) +{ + struct woomera_interface *woomera = NULL; + int kill = 0; + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + pthread_mutex_unlock(&server.process_lock); + + if (woomera) { + char callid[80]; + struct woomera_event wevent; + + woomera->timeout = 0; + + if (!woomera->raw) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to answer call with no media!\n"); + kill++; + goto handle_call_answer_end; + } + + if (woomera_test_flag(woomera, WFLAG_ANSWER)) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to double-answer a call!\n"); + kill++; + goto handle_call_answer_end; + } + + woomera_set_flag(woomera, WFLAG_ANSWER); + + if (woomera->span != event->span || woomera->chan != event->chan) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to start media on a different channel from the one we agreed on.!\n"); + kill++; + goto handle_call_answer_end; + } + + if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to answer a dead call!\n"); + kill++; + } else { + int err; + err=0; + sprintf(callid, "w%dg%d", event->span + 1, event->chan + 1); + woomera_set_interface(woomera, callid); +#ifndef WOOMERA_EARLY_MEDIA + err=launch_media_thread(woomera); + if (err) { + log_printf(SMG_LOG_ALL, server.log,"ERROR: Failed to Launch Call [%s]\n", + woomera->interface); + + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Q931-Cause-Code: %d%s" + "Cause: %s%s", + woomera->interface, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + 21, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(21), + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + kill++; + } +#endif + + if (!kill) { + new_woomera_event_printf(&wevent, "EVENT CONNECT w%dg%d%s" + "Unique-Call-Id: %s%s", + event->span+1, + event->chan+1, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR + ); + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + } + } + } else { + log_printf(SMG_LOG_PROD, server.log, "Answer requested on non-existant session. [w%dg%d]\n", + event->span+1, event->chan+1); + kill++; + } + +handle_call_answer_end: + + if (kill) { + if (woomera) { + woomera_set_flag(woomera,WFLAG_MEDIA_END); + } else { + +/* This can casuse a double STOP + must be debugged further */ +#if 0 + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED, + SIGBOOST_RELEASE_CAUSE_NORMAL); + + log_printf(SMG_LOG_PROD, server.log, "Sent CALL STOP to Answer without session [w%dg%d]\n", + event->span+1, event->chan+1); +#endif + log_printf(SMG_LOG_ALL, server.log, "WARNING: Received Answer with no session [w%dg%d]\n", + event->span+1, event->chan+1); + } + } +} + +static void handle_call_start_ack(short_signal_event_t *event) +{ + struct woomera_interface *woomera = NULL; + struct woomera_event wevent; + int kill = 0; + + if ((woomera = peek_from_holding_tank(event->call_setup_id))) { + char callid[80]; + + if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to ack a dead call!\n"); + kill++; + } else { + int err, span, chan; + + pull_from_holding_tank(event->call_setup_id,event->span,event->chan); + sprintf(callid, "w%dg%d", event->span + 1, event->chan + 1); + + span = event->span; + chan = event->chan; + + woomera_set_flag(woomera,WFLAG_CALL_ACKED); + woomera_set_interface(woomera, callid); + + pthread_mutex_lock(&server.process_lock); + + if (server.process_table[span][chan].dev) { + struct woomera_interface *tmp_woomera = server.process_table[span][chan].dev; + woomera_set_flag(tmp_woomera,WFLAG_HANGUP); + woomera_set_flag(tmp_woomera,WFLAG_MEDIA_END); + log_printf(SMG_LOG_ALL,server.log,"Call Overrun on [w%dg%d] - Call ACK!\n", event->span+1, event->chan+1); + kill++; + } + + server.process_table[span][chan].dev = woomera; + sprintf(woomera->session,"%s-%i-%i",callid,rand(),rand()); + sprintf(server.process_table[span][chan].session,"%s-%s", + callid,woomera->session); + + + memset(server.process_table[span][chan].digits, 0, + sizeof(server.process_table[span][chan].digits)); + + server.process_table[span][chan].digits_len = 0; + + pthread_mutex_unlock(&server.process_lock); + + if (kill) { + goto woomera_call_ack_skip; + } + +#ifdef WOOMERA_EARLY_MEDIA + err=launch_media_thread(woomera); + if (err) { + log_printf(SMG_LOG_ALL, server.log,"ERROR: Failed to Launch Call [%s]\n", + woomera->interface); + + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id:%s%s" + "Q931-Cause-Code: %d%s" + "Cause: %s%s", + woomera->interface, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + 21, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(21), + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + + kill++; + } +#endif + if (!kill) { + + new_woomera_event_printf(&wevent, "EVENT PROCEED w%dg%d%s" + "Channel-Name: g%d/%d%s" + "Unique-Call-Id: %s%s", + event->span+1, + event->chan+1, + WOOMERA_LINE_SEPERATOR, + woomera->trunk_group+1, + (event->span*max_chans)+event->chan+1, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "201 Accepted%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Call Answered Event ID = %d Device = w%dg%d!\n", + event->call_setup_id,woomera->span+1,woomera->chan+1); + } + } + } else { + log_printf(SMG_LOG_PROD, server.log, + "Event (START ACK) %d referrs to a non-existant session (%d) [w%dg%d]!\n", + event->event_id, event->call_setup_id,event->span+1, event->chan+1); + kill++; + } + +woomera_call_ack_skip: + if (kill) { + if (woomera) { + woomera_set_flag(woomera,WFLAG_MEDIA_END); + } else { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED, + SIGBOOST_RELEASE_CAUSE_NORMAL); + + log_printf(SMG_LOG_PROD, server.log, "Sent CALL STOP to CALL START ACK without session [w%dg%d]\n", + event->span+1, event->chan+1); + } + } +} + +static void handle_call_start_nack(short_signal_event_t *event) +{ + struct woomera_interface *woomera = NULL; + int span=-1, chan=-1; + int ack=0; + + /* Always ACK the incoming NACK + * Send out the NACK ACK before pulling the TANK, because + * if we send after the pull, the outgoing call could send + * a message to boost with the pulled TANK value before + * we send a NACK ACK */ + + if (smg_validate_span_chan(event->span,event->chan) == 0) { + span=event->span; + chan=event->chan; + } + + if (event->call_setup_id > 0) { + woomera=peek_from_holding_tank(event->call_setup_id); + } + + if (woomera) { + + struct woomera_event wevent; + + if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_ALL, server.log, "Event CALL START NACK on hungup call [%d]!\n", + event->call_setup_id); + ack++; + } else { + + woomera_set_cause_topbx(woomera,event->release_cause); + woomera_set_flag(woomera, (WFLAG_HANGUP|WFLAG_HANGUP_NACK_ACK)); + + new_woomera_event_printf(&wevent, "EVENT HANGUP w%dg%d%s" + "Unique-Call-Id: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + event->span+1, + event->chan+1, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(woomera->q931_rel_cause_topbx), + WOOMERA_LINE_SEPERATOR, + woomera->q931_rel_cause_topbx, + WOOMERA_RECORD_SEPERATOR + ); + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + woomera_set_flag(woomera, WFLAG_HANGUP); + woomera_clear_flag(woomera, WFLAG_RUNNING); + + /* Do not ack here, let woomera thread ack it */ + ack=0; + } + + + } else if (event->call_setup_id == 0 && + smg_validate_span_chan(event->span,event->chan) == 0) { + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + + if (woomera) { + if (!woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT) && + !woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK_SENT)) { + /* Only if we are not already waiting for hangup */ + server.process_table[event->span][event->chan].dev=NULL; + } else if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + /* At this point call is already hang up */ + woomera=NULL; + } else { + /* At this point call is already hang up */ + woomera=NULL; + } + } + + memset(server.process_table[event->span][event->chan].session, + 0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + + if (woomera) { + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event START NACK on w%dg%d ptr=%p ms=%p\n", + woomera->span+1,woomera->chan+1,woomera,woomera->ms); + + if (!woomera_test_flag(woomera,WFLAG_HANGUP)){ + if (event->release_cause == SIGBOOST_CALL_SETUP_CSUPID_DBL_USE || + event->release_cause == SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY) { + woomera_set_cause_topbx(woomera,17); + } else { + woomera_set_cause_topbx(woomera,event->release_cause); + } + } + + woomera_set_flag(woomera, + (WFLAG_HANGUP|WFLAG_MEDIA_END|WFLAG_HANGUP_NACK_ACK)); + + /* Nack Ack will be sent by the woomera thread */ + ack=0; + } else { + /* Valid state when we are not in autoacm mode */ + ack++; + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event: NACK no woomera on span chan [w%dg%d]!\n", + event->span+1, event->chan+1); + } + + } else { + log_printf(SMG_LOG_ALL, server.log, + "Error: Start Nack Invalid State Should not happen [%d] [w%dg%d]!\n", + event->call_setup_id, event->span+1, event->chan+1); + ack++; + } + + if (event->release_cause == SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY) { + smg_all_ckt_busy(); + log_printf(SMG_LOG_ALL, server.log, "WARNING: All ckt busy Timeout=%i!\n",server.all_ckt_busy); + } + if (event->release_cause == SIGBOOST_CALL_SETUP_CSUPID_DBL_USE) { + log_printf(SMG_LOG_ALL, server.log, "WARNING: Double use on [w%ig%i] setup id %i!\n", + event->span+1,event->chan+1,event->call_setup_id); + } + +#warning "Ignoring CALL GAP" +#if 0 + if (event->release_cause == SIGBOOST_CALL_SETUP_NACK_AUTO_CALL_GAP) { + log_printf(SMG_LOG_ALL, server.log, "WARNING: Call Gapping Detected!\n"); + smg_all_ckt_gap(); + } +#endif + + if (ack) { + span=0; + chan=0; + if (smg_validate_span_chan(event->span,event->chan) == 0) { + span=event->span; + chan=event->chan; + } + + isup_exec_command(span, + chan, + event->call_setup_id, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + 0); + + if (!woomera) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event (NACK ACK) %d referrs to a non-existant session (%d) [w%dg%d]!\n", + event->event_id,event->call_setup_id, event->span+1, event->chan+1); + } + } +} + +static void handle_call_loop_start(short_signal_event_t *event) +{ + struct woomera_interface *woomera; + char callid[20]; + char *session; + + pthread_mutex_lock(&server.process_lock); + if (server.process_table[event->span][event->chan].dev) { + + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + 17); + + + log_printf(SMG_LOG_PROD, server.log, + "Sent (From Handle Loop START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [w%dg] ptr=%d\n", + event->span+1, event->chan+1, server.process_table[event->span][event->chan].dev); + + pthread_mutex_unlock(&server.process_lock); + return; + + } + + sprintf(callid, "w%dg%d", event->span+1,event->chan+1); + sprintf(server.process_table[event->span][event->chan].session, + "%s-%i-%i",callid,rand(),rand()); + session=server.process_table[event->span][event->chan].session; + server.process_table[event->span][event->chan].dev = NULL; + pthread_mutex_unlock(&server.process_lock); + + + woomera=launch_woomera_loop_thread(event); + if (woomera == NULL) { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + 17); + log_printf(SMG_LOG_PROD, server.log, + "Sent (From Handle Loop START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [w%dg] ptr=%d\n", + event->span+1, event->chan+1, server.process_table[event->span][event->chan].dev); + } + + woomera_set_flag(woomera,WFLAG_CALL_ACKED); + + return; +} + + +static void handle_call_start(call_signal_event_t *event) +{ + struct woomera_event wevent; + char callid[20]; + char *session; + int clients; + + remove_end_of_digits_char((unsigned char*)event->called_number_digits); + remove_end_of_digits_char((unsigned char*)event->calling_number_digits); + + if (server.strip_cid_non_digits) { + validate_number((unsigned char*)event->called_number_digits); + validate_number((unsigned char*)event->calling_number_digits); + } + + pthread_mutex_lock(&server.process_lock); + + if (server.process_table[event->span][event->chan].dev) { + + struct woomera_interface *tmp_woomera = server.process_table[event->span][event->chan].dev; + woomera_set_flag(tmp_woomera,WFLAG_HANGUP); + woomera_set_flag(tmp_woomera,WFLAG_MEDIA_END); + log_printf(SMG_LOG_ALL,server.log,"Call Overrun on [w%dg%d] - Call START!\n", event->span+1, event->chan+1); + + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + 17); + + log_printf(SMG_LOG_ALL, server.log, + "Sent (From Handle START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [w%dg%d]\n", + event->span+1, event->chan+1); + + pthread_mutex_unlock(&server.process_lock); + return; + } + + sprintf(callid, "w%dg%d", event->span+1,event->chan+1); + sprintf(server.process_table[event->span][event->chan].session, + "%s-%i-%i",callid,rand(),rand()); + session=server.process_table[event->span][event->chan].session; + server.process_table[event->span][event->chan].dev = NULL; + memset(server.process_table[event->span][event->chan].digits, 0, sizeof(server.process_table[event->span][event->chan].digits)); + server.process_table[event->span][event->chan].digits_len = 0; + server.process_table[event->span][event->chan].bearer_cap = event->bearer.capability; + server.process_table[event->span][event->chan].clients=0; + pthread_mutex_unlock(&server.process_lock); + + if (autoacm) { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_ACK, + 0); + } + + + log_printf(SMG_LOG_DEBUG_8,server.log,"BEARER %i , UIL1P = %i\n", + event->bearer.capability,event->bearer.uil1p); + + if (event->bearer.uil1p == 0) { + if (server.hw_coding) { + event->bearer.uil1p=BC_IE_UIL1P_G711_ALAW; + } else { + event->bearer.uil1p=BC_IE_UIL1P_G711_ULAW; + } + } + + new_woomera_event_printf(&wevent, "EVENT INCOMING w%dg%d%s" + "Unique-Call-Id: %s%s" + "Remote-Number: %s%s" + "Remote-Name: %s%s" +#if defined(BRI_PROT) + "Protocol: BRI%s" +#else +#if defined(PRI_PROT) + "Protocol: PRI%s" +#else + "Protocol: SS7%s" +#endif +#endif + "User-Agent: sangoma_mgd%s" + "Local-Number: %s%s" + "Channel-Name: g%d/%d%s" + "Trunk-Group: %d%s" + "Presentation: %d%s" + "Screening: %d%s" + "RDNIS: %s%s" + "Bearer-Cap: %s%s" + "uil1p: %s%s" + , + event->span+1, + event->chan+1, + WOOMERA_LINE_SEPERATOR, + session, + WOOMERA_LINE_SEPERATOR, + event->calling_number_digits, + WOOMERA_LINE_SEPERATOR, + event->calling_name, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + event->called_number_digits, + WOOMERA_LINE_SEPERATOR, + event->trunk_group+1, + (event->span*max_chans)+event->chan+1, + WOOMERA_LINE_SEPERATOR, + event->trunk_group+1, + WOOMERA_LINE_SEPERATOR, + event->calling_number_presentation, + WOOMERA_LINE_SEPERATOR, + event->calling_number_screening_ind, + WOOMERA_LINE_SEPERATOR, + event->isup_in_rdnis, + WOOMERA_LINE_SEPERATOR, + bearer_cap_to_str(event->bearer.capability), + WOOMERA_LINE_SEPERATOR, + uil1p_to_str(event->bearer.uil1p), + WOOMERA_RECORD_SEPERATOR + ); + + clients=enqueue_event_on_listeners(&wevent); + if (!clients) { + + pthread_mutex_lock(&server.process_lock); + server.process_table[event->span][event->chan].dev = NULL; + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + if (autoacm) { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED, + 17); + log_printf(SMG_LOG_ALL, server.log, + "CALL INCOMING: Enqueue Error Sent SIGBOOST_EVENT_CALL_STOPPED [w%dg%d]\n", + event->span+1, event->chan+1); + + } else { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + 17); + log_printf(SMG_LOG_ALL, server.log, + "CALL INCOMING: Enqueue Error Sent SIGBOOST_EVENT_CALL_START_NACK [w%dg%d]\n", + event->span+1, event->chan+1); + + } + + } else { + server.process_table[event->span][event->chan].clients=clients; + } + + destroy_woomera_event_data(&wevent); + +} + +static void handle_incoming_digit(call_signal_event_t *event) +{ + struct woomera_interface *woomera; + int digits_len; + + if (smg_validate_span_chan(event->span,event->chan)) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event DTMF on invalid span chan [w%dg%d] !\n", + event->span+1, event->chan+1); + return; + } + + + if (!event->called_number_digits_count) { + log_printf(SMG_LOG_ALL, server.log, "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); + } + + log_printf(SMG_LOG_DEBUG_9, server.log, "Queuing incoming digits %s [w%dg%d]\n", + event->called_number_digits, + event->span+1, event->chan+1); + + pthread_mutex_lock(&server.digits_lock); + + digits_len = server.process_table[event->span][event->chan].digits_len; + + strncpy(&server.process_table[event->span][event->chan].digits[digits_len], + event->called_number_digits, + event->called_number_digits_count); + + server.process_table[event->span][event->chan].digits_len += event->called_number_digits_count; + + pthread_mutex_unlock(&server.digits_lock); + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + pthread_mutex_unlock(&server.process_lock); + if (!woomera || !strlen(woomera->session)) { + return; + } + woomera_check_digits(woomera); +} + +static void handle_gap_abate(short_signal_event_t *event) +{ + log_printf(SMG_LOG_ALL, server.log, "NOTICE: GAP Cleared!\n", + event->span+1, event->chan+1); + smg_clear_ckt_gap(); +} + +static void handle_restart(call_signal_connection_t *mcon, short_signal_event_t *event) +{ + if (!woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + log_printf(SMG_LOG_ALL, server.log,"ERROR! Monitor Thread not running!\n"); + } else { + /* Clear Reset */ + /* Tell all threads to go down */ + + log_printf(SMG_LOG_ALL, server.log, + "RESTART Received: resetting all threads\n"); + + smg_all_ckt_busy(); + woomera_set_flag(&server.master_connection, WFLAG_SYSTEM_RESET); + gettimeofday(&server.restart_timeout,NULL); + +#if 0 + sleep(5); + + clear_all_holding_tank(); + + sleep(2); + + event->event_id = SIGBOOST_EVENT_SYSTEM_RESTART_ACK; + err=call_signal_connection_write(&server.mcon, (call_signal_event_t*)event); + if (err < 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", + strerror(errno)); + } + + smg_all_ckt_busy(); + woomera_clear_flag(&server.master_connection, WFLAG_SYSTEM_RESET); +#endif + } + +} + +static void handle_restart_ack(call_signal_connection_t *mcon, short_signal_event_t *event) +{ + + /* Do not reset WFLAG_SYSTEM_RESET flag here!. The flag should + only be reset on restart from boost */ + //woomera_clear_flag(&server.master_connection, WFLAG_SYSTEM_RESET); +} + + + +static void handle_remove_loop(short_signal_event_t *event) +{ + struct woomera_interface *woomera; + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + server.process_table[event->span][event->chan].dev=NULL; + pthread_mutex_unlock(&server.process_lock); + + if (woomera) { + + woomera_set_flag(woomera, + (WFLAG_MEDIA_END|WFLAG_HANGUP)); + + /* We have to close the socket because + At this point we are release span chan */ + + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event REMOVE LOOP on w%dg%d ptr=%p ms=%p\n", + woomera->span+1,woomera->chan+1,woomera,woomera->ms); + + } +} + + +static void handle_call_stop(short_signal_event_t *event) +{ + struct woomera_interface *woomera; + int ack=0; + + woomera = NULL; + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + if (woomera) { + + if (!woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT) && + !woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK_SENT)) { + /* Only if we are not already waiting for hangup */ + //server.process_table[event->span][event->chan].dev=NULL; + + } else if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + /* At this point call is already hangup */ + woomera=NULL; + } else { + /* At this point call is already hangup */ + woomera=NULL; + } + } + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + if (woomera) { + + woomera_set_cause_topbx(woomera,event->release_cause); + + woomera_set_flag(woomera, + (WFLAG_MEDIA_END|WFLAG_HANGUP|WFLAG_HANGUP_ACK)); + + /* We have to close the socket because + At this point we are release span chan */ + + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event CALL STOP on w%dg%d ptr=%p ms=%p\n", + woomera->span+1,woomera->chan+1,woomera,woomera->ms); + + } else { + ack++; + } + + + if (ack) { + /* At this point we have already sent our STOP so its safe to ACK */ + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED_ACK, + 0); + } + + if (!woomera){ + /* This is allowed on incoming call if remote app does not answer it */ + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event CALL STOP referrs to a non-existant session [w%dg%d]!\n", + event->span+1, event->chan+1); + } +} + +static void handle_heartbeat(short_signal_event_t *event) +{ + if (!woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + log_printf(SMG_LOG_ALL, server.log,"ERROR! Monitor Thread not running!\n"); + } else { + int err=call_signal_connection_writep(&server.mconp, (call_signal_event_t*)event); + if (err < 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", + strerror(errno)); + } + } + return; +} + + +static void handle_call_stop_ack(short_signal_event_t *event) +{ + + struct woomera_interface *woomera = NULL; + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + server.process_table[event->span][event->chan].dev=NULL; + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + + if (woomera) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Stop Ack on [w%dg%d] [Setup ID: %d] [%s]!\n", + event->span+1, event->chan+1, event->call_setup_id, + woomera->interface); + + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + + } else { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event CALL_STOP_ACK(%d) referrs to a non-existant session [w%dg%d] [Setup ID: %d]!\n", + event->event_id, event->span+1, event->chan+1, event->call_setup_id); + } + + if (event->call_setup_id > 0) { + clear_from_holding_tank(event->call_setup_id, NULL); + } + + /* No need for us to do any thing here */ + return; +} + + +static void handle_call_start_nack_ack(short_signal_event_t *event) +{ + + struct woomera_interface *woomera = NULL; + + if ((woomera=pull_from_holding_tank(event->call_setup_id,-1,-1))) { + + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + + } else if (event->call_setup_id == 0 && + smg_validate_span_chan(event->span,event->chan) == 0) { + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + server.process_table[event->span][event->chan].dev=NULL; + memset(server.process_table[event->span][event->chan].session, + 0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + if (woomera) { + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + } else { + /* Possible if incoming call is NACKed due to + * woomera client being down, in this case no + * woomera thread is created */ + log_printf(SMG_LOG_DEBUG_8, server.log, + "Event NACK ACK [w%dg%d] with valid span/chan no dev!\n", + event->span+1, event->chan+1); + } + + } else { + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "Event NACK ACK referrs to a non-existant session [w%dg%d] [Setup ID: %d]!\n", + event->span+1, event->chan+1, event->call_setup_id); + } + + if (event->call_setup_id > 0) { + clear_from_holding_tank(event->call_setup_id, NULL); + } + + /* No need for us to do any thing here */ + return; +} + + +static int parse_ss7_event(call_signal_connection_t *mcon, short_signal_event_t *event) +{ + int ret = 0; + + switch(event->event_id) { + + case SIGBOOST_EVENT_CALL_START: + handle_call_start((call_signal_event_t*)event); + break; + case SIGBOOST_EVENT_CALL_STOPPED: + handle_call_stop(event); + break; + case SIGBOOST_EVENT_CALL_START_ACK: + handle_call_start_ack(event); + break; + case SIGBOOST_EVENT_CALL_START_NACK: + handle_call_start_nack(event); + break; + case SIGBOOST_EVENT_CALL_ANSWERED: + handle_call_answer(event); + break; + case SIGBOOST_EVENT_HEARTBEAT: + handle_heartbeat(event); + break; + case SIGBOOST_EVENT_CALL_START_NACK_ACK: + handle_call_start_nack_ack(event); + break; + case SIGBOOST_EVENT_CALL_STOPPED_ACK: + handle_call_stop_ack(event); + break; + case SIGBOOST_EVENT_INSERT_CHECK_LOOP: + handle_call_loop_start(event); + break; + case SIGBOOST_EVENT_SYSTEM_RESTART: + handle_restart(mcon,event); + break; + case SIGBOOST_EVENT_REMOVE_CHECK_LOOP: + handle_remove_loop(event); + break; + case SIGBOOST_EVENT_SYSTEM_RESTART_ACK: + handle_restart_ack(mcon,event); + break; + case SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE: + handle_gap_abate(event); + break; + case SIGBOOST_EVENT_DIGIT_IN: + handle_incoming_digit((call_signal_event_t*)event); + break; + default: + log_printf(SMG_LOG_ALL, server.log, "Warning no handler implemented for [%s val:%d]\n", + call_signal_event_id_name(event->event_id), event->event_id); + break; + } + + return ret; +} + + +static void *monitor_thread_run(void *obj) +{ + int ss = 0; + int policy=0,priority=0; + char a=0,b=0; + call_signal_connection_t *mcon=&server.mcon; + call_signal_connection_t *mconp=&server.mconp; + + pthread_mutex_lock(&server.thread_count_lock); + server.thread_count++; + pthread_mutex_unlock(&server.thread_count_lock); + + woomera_set_flag(&server.master_connection, WFLAG_MONITOR_RUNNING); + + if (call_signal_connection_open(mcon, + mcon->cfg.local_ip, + mcon->cfg.local_port, + mcon->cfg.remote_ip, + mcon->cfg.remote_port) < 0) { + log_printf(SMG_LOG_ALL, server.log, "Error: Opening MCON Socket [%d] %s\n", + mcon->socket,strerror(errno)); + exit(-1); + } + + if (call_signal_connection_open(mconp, + mconp->cfg.local_ip, + mconp->cfg.local_port, + mconp->cfg.remote_ip, + mconp->cfg.remote_port) < 0) { + log_printf(SMG_LOG_ALL, server.log, "Error: Opening MCONP Socket [%d] %s\n", + mconp->socket,strerror(errno)); + exit(-1); + } + + mcon->log = server.log; + mconp->log = server.log; + + isup_exec_commandp(0, + 0, + -1, + SIGBOOST_EVENT_SYSTEM_RESTART, + 0); + + woomera_set_flag(&server.master_connection, WFLAG_SYSTEM_RESET); + + smg_get_current_priority(&policy,&priority); + + log_printf(SMG_LOG_PROD, server.log, "Open udp socket [%d] [%d]\n", + mcon->socket,mconp->socket); + log_printf(SMG_LOG_PROD, server.log, "Monitor Thread Started (%i:%i)\n",policy,priority); + + + while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { +#if 0 + ss = waitfor_socket(server.mcon.socket, 1000, POLLERR | POLLIN); +#else + ss = waitfor_2sockets(mcon->socket, + mconp->socket, + &a, &b, 1000); +#endif + + if (ss > 0) { + + call_signal_event_t *event=NULL; + int i=0; + + if (b) { +mcon_retry_priority: + if ((event = call_signal_connection_readp(mconp,i))) { + log_printf(SMG_LOG_DEBUG_9, server.log, "Socket Event P [%s] \n", + call_signal_event_id_name(event->event_id)); + parse_ss7_event(mconp,(short_signal_event_t*)event); + if (++i < 10) { + goto mcon_retry_priority; + } + + } else if (errno != EAGAIN) { + ss=-1; + log_printf(SMG_LOG_ALL, server.log, + "Error: Reading from Boost P Socket! (%i) %s\n", + errno,strerror(errno)); + break; + } + } + + i=0; + + if (a) { + if ((event = call_signal_connection_read(mcon,i))) { + log_printf(SMG_LOG_DEBUG_9, server.log, "Socket Event [%s]\n", + call_signal_event_id_name(event->event_id)); + parse_ss7_event(mcon,(short_signal_event_t*)event); + + } else if (errno != EAGAIN) { + ss=-1; + log_printf(SMG_LOG_ALL, server.log, + "Error: Reading from Boost Socket! (%i) %s\n", + errno,strerror(errno)); + break; + } + } + + } + + if (ss < 0){ + log_printf(SMG_LOG_ALL, server.log, "Thread Run: Select Socket Error!\n"); + break; + } + + if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)) { + short_signal_event_t event; + struct timeval current; + int elapsed; + gettimeofday(¤t,NULL); + + elapsed=smg_calc_elapsed(&server.restart_timeout, ¤t); + if (elapsed > 5000) { + int err; + log_printf(SMG_LOG_ALL, server.log, "Reset Condition Cleared Elapsed=%i!\n",elapsed); + clear_all_holding_tank(); + memset(&event,0,sizeof(event)); + event.event_id = SIGBOOST_EVENT_SYSTEM_RESTART_ACK; + err=call_signal_connection_write(&server.mcon, (call_signal_event_t*)&event); + if (err < 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", + strerror(errno)); + } + smg_all_ckt_busy(); + woomera_clear_flag(&server.master_connection, WFLAG_SYSTEM_RESET); + } + } + + } + + log_printf(SMG_LOG_PROD, server.log, "Close udp socket [%d] [%d]\n", + mcon->socket,mconp->socket); + call_signal_connection_close(&server.mcon); + call_signal_connection_close(&server.mconp); + + pthread_mutex_lock(&server.thread_count_lock); + server.thread_count--; + pthread_mutex_unlock(&server.thread_count_lock); + + woomera_clear_flag(&server.master_connection, WFLAG_MONITOR_RUNNING); + log_printf(SMG_LOG_ALL, server.log, "Monitor Thread Ended\n"); + + return NULL; +} + +static void woomera_loop_thread_run(struct woomera_interface *woomera) +{ + int err=launch_media_thread(woomera); + if (err) { + log_printf(SMG_LOG_ALL, server.log, "Failed to start loop media thread\n"); + woomera_set_flag(woomera, + (WFLAG_HANGUP|WFLAG_MEDIA_END)); + woomera_clear_flag(woomera, WFLAG_RUNNING); + return; + } + + while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING) && + !woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) && + !woomera_test_flag(woomera, WFLAG_MEDIA_END) && + !woomera_test_flag(woomera, WFLAG_HANGUP)) { + + usleep(300000); + continue; + + } + + woomera_clear_flag(woomera, WFLAG_RUNNING); + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Woomera Session: For Loop Test exiting %s\n",woomera->interface); + + return; +} + +static void woomera_check_digits (struct woomera_interface *woomera) +{ + int span, chan; + + if (!strlen(woomera->session)) { + return; + } + + if (get_span_chan_from_interface(woomera->interface, &span, &chan) == 0) { + pthread_mutex_lock(&server.digits_lock); + if (server.process_table[span-1][chan-1].digits_len > 0) { + int i; + unsigned char digit; + + for (i=0; i < server.process_table[span-1][chan-1].digits_len; i++) { + digit = server.process_table[span-1][chan-1].digits[i]; + + handle_event_dtmf(woomera, digit); + } + + server.process_table[span-1][chan-1].digits_len = 0; + } + pthread_mutex_unlock(&server.digits_lock); + } +} + + +static void *woomera_thread_run(void *obj) +{ + struct woomera_interface *woomera = obj; + struct woomera_message wmsg; + struct woomera_event wevent; + char *event_string; + int mwi; + int err; + int policy=0, priority=0; + int span = -1, chan = -1; + + woomera_message_init(&wmsg); + + smg_get_current_priority(&policy,&priority); + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "WOOMERA session started (ptr=%p : loop=%i)(%i:%i) Index=%i\n", + woomera,woomera->loop_tdm,policy,priority, woomera->index); + + pthread_mutex_lock(&server.thread_count_lock); + server.thread_count++; + pthread_mutex_unlock(&server.thread_count_lock); + + pthread_mutex_init(&woomera->queue_lock, NULL); + pthread_mutex_init(&woomera->ms_lock, NULL); + pthread_mutex_init(&woomera->dtmf_lock, NULL); + pthread_mutex_init(&woomera->vlock, NULL); + pthread_mutex_init(&woomera->flags_lock, NULL); + + if (woomera->loop_tdm) { + /* We must set woomera socket to -1 otherwise + a valid file descriptor will get closed */ + woomera->socket = -1; + woomera_loop_thread_run(woomera); + goto woomera_session_close; + } + + err=socket_printf(woomera->socket, + "EVENT HELLO Sangoma Media Gateway%s" + "Supported-Protocols: TDM%s" + "Version: %s%s" + "Remote-Address: %s%s" + "Remote-Port: %d%s" + "Raw-Format: %s%s", + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + SMG_VERSION, WOOMERA_LINE_SEPERATOR, + inet_ntoa(woomera->addr.sin_addr), WOOMERA_LINE_SEPERATOR, + ntohs(woomera->addr.sin_port), WOOMERA_LINE_SEPERATOR, + server.hw_coding?"ALAW":"ULAW", WOOMERA_RECORD_SEPERATOR + ); + + if (err) { + log_printf(SMG_LOG_ALL, server.log, "Woomera session socket failure! (ptr=%p)\n", + woomera); + woomera_clear_flag(woomera, WFLAG_RUNNING); + goto woomera_session_close; + } + + while ( woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING) && + woomera_test_flag(woomera, WFLAG_RUNNING) && + !woomera_test_flag(woomera, WFLAG_MEDIA_END) && + !woomera_test_flag(woomera, WFLAG_HANGUP)) { + + mwi = woomera_message_parse(woomera, &wmsg, WOOMERA_HARD_TIMEOUT); + if (mwi >= 0) { + + if (mwi) { + interpret_command(woomera, &wmsg); + } else if (woomera_test_flag(woomera, WFLAG_EVENT)){ + while ((event_string = dequeue_event(woomera))) { + if (socket_printf(woomera->socket, "%s", event_string)) { + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_RUNNING); + smg_free(event_string); + log_printf(SMG_LOG_DEBUG_8, server.log, + "WOOMERA session (ptr=%p) print string error\n", + woomera); + break; + } + smg_free(event_string); + } + woomera_clear_flag(woomera, WFLAG_EVENT); + } + + if(woomera->check_digits) { + woomera_check_digits(woomera); + woomera->check_digits = 0; + } + + if (woomera->timeout > 0 && time(NULL) >= woomera->timeout) { + + /* Sent the hangup only after we sent a NACK */ + + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "WOOMERA session Call Timedout ! [%s]\n", + woomera->interface); + + /* Let the Index check below send a NACK */ + if (woomera->index) { + woomera->q931_rel_cause_tosig=19; + woomera_set_flag(woomera, WFLAG_HANGUP); + } + break; + } + + } else { + log_printf(SMG_LOG_DEBUG_MISC, server.log, "WOOMERA session (ptr=%p) [%s] READ MSG Error %i \n", + woomera,woomera->interface,mwi); + break; + } + + } + +woomera_session_close: + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "WOOMERA session (ptr=%p) is dying [%s]: SR=%d WR=%d WF=0x%04X\n", + woomera,woomera->interface, + woomera_test_flag(&server.master_connection, WFLAG_RUNNING), + woomera_test_flag(woomera, WFLAG_RUNNING), + woomera->flags); + + + if (woomera_test_flag(woomera, WFLAG_MEDIA_RUNNING)) { + woomera_set_flag(woomera, WFLAG_MEDIA_END); + while(woomera_test_flag(woomera, WFLAG_MEDIA_RUNNING)) { + usleep(100); + sched_yield(); + } + } + + + /*********************************************** + * Identify the SPAN CHAN to be used below + ***********************************************/ + + chan = woomera->chan; + span = woomera->span; + + + if (!woomera_test_flag(woomera, WFLAG_HANGUP)) { + + /* The call was not HUNGUP. This is the last check, + If the call is valid, hungup the call if the call + was never up the keep going */ + + + if (smg_validate_span_chan(span,chan) == 0) { + + if (!woomera->index) { + + if (autoacm || woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + + woomera_set_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + isup_exec_command(span, + chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED, + woomera->q931_rel_cause_tosig); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK_SENT); + + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Woomera Sent SIGBOOST_EVENT_CALL_STOPPED [w%dg%d] [%s] ptr=%p\n", + span+1, chan+1,woomera->interface,woomera); + } else { + + woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + isup_exec_command(span, + chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + woomera->q931_rel_cause_tosig); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Woomera Sent SIGBOOST_EVENT_CALL_START_NACK [w%dg%d] [%s] ptr=%p\n", + span+1, chan+1,woomera->interface,woomera); + } + } else { + log_printf(SMG_LOG_ALL, woomera->log, "Woomera Not Sent CALL STOPPED - Instead NACK [w%dg%d] [%s] ptr=%p\n", + span+1, chan+1,woomera->interface,woomera); + + } + }else{ + /* This can happend if an outgoing call times out + or gets hungup before it gets acked. Its not a + failure */ + if (!woomera->index) { + /* In this case we really failed to tx STOP */ + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "FAILED: Woomera (R) SIGBOOST_EVENT_CALL_STOPPED [w%dg%d] [%s] Index=%d ptr=%p\n", + span+1, chan+1, woomera->interface, woomera->index, woomera); + } + } + + woomera_set_flag(woomera, WFLAG_HANGUP); + + } + +woo_re_hangup: + + /* We must send a STOP ACK to boost telling it that we are done */ + if (woomera_test_flag(woomera, WFLAG_HANGUP_ACK)) { + + /* SMG received a HANGUP from boost. + We must now send back the ACK to the HANGUP. + Boost will not release channel until we + ACK the hangup */ + + if (smg_validate_span_chan(span,chan) == 0) { + + isup_exec_command(span, + chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED_ACK, + woomera->q931_rel_cause_tosig); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, + "Sent (Ack) to SIGBOOST_EVENT_CALL_STOPPED_ACK [w%dg%d] [%s] ptr=%p\n", + span+1,chan+1,woomera->interface,woomera); + + }else{ + /* This should never happen! If it does + we broke protocol */ + log_printf(SMG_LOG_ALL, woomera->log, + "FAILED: Woomera (R) SIGBOOST_EVENT_CALL_STOPPED_ACK [w%dg%d] [%s] Index=%d ptr=%p\n", + span+1, chan+1, woomera->interface, woomera->index, woomera); + } + + woomera_clear_flag(woomera, WFLAG_HANGUP_ACK); + woomera_set_flag(woomera, WFLAG_HANGUP); + } + + if (woomera_test_flag(woomera, WFLAG_HANGUP_NACK_ACK)) { + + /* SMG received a NACK from boost during call startup. + We must now send back the ACK to the NACK. + Boost will not release channel until we + ACK the NACK */ + + if (smg_validate_span_chan(span,chan) == 0) { + + isup_exec_command(span, + chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + woomera->q931_rel_cause_tosig); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, + "Sent (Nack Ack) to SIGBOOST_EVENT_CALL_START_NACK_ACK [w%dg%d] [%s] ptr=%p\n", + span+1,chan+1,woomera->interface,woomera); + + woomera->index=0; + + } else if (woomera->index) { + isup_exec_command(0, + 0, + woomera->index, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + woomera->q931_rel_cause_tosig); + + woomera->index=0; + + } else { + log_printf(SMG_LOG_ALL, woomera->log, + "FAILED: Sent (Nack Ack) SIGBOOST_EVENT_CALL_START_NACK_ACK [w%dg%d] [%s] Index=%d ptr=%p\n", + span+1, chan+1, woomera->interface, woomera->index, woomera); + } + + woomera_clear_flag(woomera, WFLAG_HANGUP_NACK_ACK); + + } + + if (woomera->index) { + + int index = woomera->index; + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Timeout: %ld%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + woomera->interface, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + woomera->timeout, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(18), + WOOMERA_LINE_SEPERATOR, + 18, + WOOMERA_RECORD_SEPERATOR + ); + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + while ((event_string = dequeue_event(woomera))) { + socket_printf(woomera->socket, "%s", event_string); + smg_free(event_string); + } + + if (peek_from_holding_tank(index)) { + + woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + isup_exec_command(0, + 0, + index, + SIGBOOST_EVENT_CALL_START_NACK, + woomera->q931_rel_cause_tosig); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT); + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, + "Sent SIGBOOST_EVENT_CALL_START_NACK [Setup ID: %d] .. WAITING FOR NACK ACK\n", + index); + } else { + log_printf(SMG_LOG_PROD, woomera->log, + "Error Failed to Sent SIGBOOST_EVENT_CALL_START_NACK [Setup ID: %d] - index stale!\n", + index); + } + } + + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK)) { + int timeout_cnt=0; + int overall_cnt=0; + + /* SMG sent NACK to boost, however we have to wait + for boost to give us the ACK back before we + release resources. */ + + while (woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK)) { + timeout_cnt++; + if (timeout_cnt > 100) { //5sec timeout + timeout_cnt=0; + overall_cnt++; + + log_printf((overall_cnt==1)?0:4, woomera->log, + "Waiting for NACK ACK [Setup ID: %d] ... \n", + woomera->index_hold); + } + + if (overall_cnt > 15) { //50sec timeout + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING)) { + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ + break; + } + + /* If ACK comes in while we wait for NACK ACK, the ACk will + clear the tank causing NACK ACK never to clear the waiting flag + in this case we abort waiting for NACK ACK and we timeout + the wait */ + if (woomera_test_flag(&server.master_connection, WFLAG_CALL_ACKED)){ + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + + usleep(50000); + sched_yield(); + } + + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + + /* If ACK came in while waiting for NACK ACK, we TIMEOUT on purpose. + The ACK has blocked the tank id and now ony NACK ACK can unblock it. + THe only problem is that we blind and have to assume that NACK ACK + will eventually come in :) */ + if (!woomera_test_flag(&server.master_connection, WFLAG_CALL_ACKED)) { + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT)) { + log_printf(SMG_LOG_ALL, woomera->log, + "Waiting for NACK ACK [Setup ID: %d] .. TIMEOUT on NACK ACK\n", + woomera->index_hold); + + } else { + log_printf(SMG_LOG_DEBUG_8, woomera->log, + "Waiting for NACK ACK [Setup ID: %d] .. GOT NACK ACK\n", + woomera->index_hold); + } + } + } + + if (woomera_test_flag(woomera, WFLAG_EVENT)){ + while ((event_string = dequeue_event(woomera))) { + socket_printf(woomera->socket, "%s", event_string); + smg_free(event_string); + } + woomera_clear_flag(woomera, WFLAG_EVENT); + } + + + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK)) { + int timeout_cnt=0; + int overall_cnt=0; + + /* SMG sent HANGUP to boost, however we have to wait + for boost to give us the ACK back before we + release resources. */ + + log_printf(SMG_LOG_DEBUG_8, woomera->log, + "Waiting for STOPPED ACK [%s] [id=%i]... \n", + woomera->interface,woomera->index_hold); + + while (woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK)) { + timeout_cnt++; + if (timeout_cnt > 100) { //5sec timeout + timeout_cnt=0; + overall_cnt++; + log_printf((overall_cnt==1)?0:4, woomera->log, + "Waiting for STOPPED ACK [%s] [id=%i] %i... \n", + woomera->interface,woomera->index_hold,overall_cnt); + } + + if (overall_cnt > 15) { //50sec + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING)) { + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + + if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ + break; + } + + usleep(50000); + sched_yield(); + } + + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT)) { + log_printf(SMG_LOG_ALL, woomera->log, + "Wait TIMEDOUT on STOPPED ACK [%s] [id=%i]... \n", + woomera->interface,woomera->index_hold); + + } else { + log_printf(SMG_LOG_DEBUG_8, woomera->log, + "Wait GOT STOPPED ACK [%s] [id=%i]... \n", + woomera->interface,woomera->index_hold); + } + } + + /***************************************************** + * We must wait for WFLAG_WAIT_FOR_STOPPED_ACK here + * so that STOP_ACK can access the woomera + *****************************************************/ + + if (smg_validate_span_chan(span,chan) == 0) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, + "WOOMERA Clearing Processs Table ... \n", + woomera->interface); + pthread_mutex_lock(&server.process_lock); + if (server.process_table[span][chan].dev == woomera){ + server.process_table[span][chan].dev = NULL; + memset(server.process_table[span][chan].session,0,SMG_SESSION_NAME_SZ); + memset(server.process_table[span][chan].digits,0, + sizeof(server.process_table[span][chan].digits)); + server.process_table[span][chan].digits_len = 0; + } + pthread_mutex_unlock(&server.process_lock); + } + +#if 0 +//Used for testing + if (1) { + int chan = woomera->chan; + int span = woomera->span; + if (smg_validate_span_chan(span,chan) == 0) { + pthread_mutex_lock(&server.process_lock); + /* This is possible in case media thread dies on startup */ + + if (server.process_table[span][chan]){ + log_printf(SMG_LOG_ALL, server.log, + "Sanity Span Chan Still in use: [w%dg%d] [%s] Index=%d ptr=%p\n", + span+1, chan+1, woomera->interface, woomera->index, woomera); + //server.process_table[span][chan] = NULL; + } + pthread_mutex_unlock(&server.process_lock); + } + } +#endif + + usleep(3000000); + + /* Sanity Check */ + if (woomera_test_flag(woomera, WFLAG_HANGUP_ACK)) { + log_printf(SMG_LOG_ALL, woomera->log, + "Woomera MISSED HANGUP ACK: Retry HANGUP ACK\n"); + goto woo_re_hangup; + } + if (woomera_test_flag(woomera, WFLAG_HANGUP_NACK_ACK)) { + log_printf(SMG_LOG_ALL, woomera->log, + "Woomera MISSED HANGUP ACK: Retry HANGUP NACK ACK\n"); + goto woo_re_hangup; + } + + + /* This is where we actually pull the index + * out of the tank. We had to keep the tank + * value until the end of the call. Tank is only + * used on outgoing calls. */ + if (woomera->index_hold >= 1) { + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT)) { + /* Replace real woomera interface with a dummy so + the tank is blocked. The real woomera interface will be + deallocated */ + pull_from_holding_tank(woomera->index_hold,-1,-1); + + if (check_tank_index(woomera->index_hold) != NULL){ + log_printf(SMG_LOG_PROD, woomera->log, "Woomera Thread: [%s] setup id %i blocked waiting for NACK or ACK\n", + woomera->interface ,woomera->index_hold); + } + } else { + clear_from_holding_tank(woomera->index_hold, woomera); + + } + woomera->index_hold=0; + } + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "Woomera Thread Finished %u\n", (unsigned long) woomera->thread); + close_socket(&woomera->socket); + woomera->socket=-1; + + /* delete queue */ + while ((event_string = dequeue_event(woomera))) { + smg_free(event_string); + } + + if (woomera_test_flag(woomera, WFLAG_LISTENING)) { + del_listener(woomera); + } + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "WOOMERA session for [%s] stopped (ptr=%p)\n", + woomera->interface,woomera); + + pthread_mutex_destroy(&woomera->queue_lock); + pthread_mutex_destroy(&woomera->ms_lock); + pthread_mutex_destroy(&woomera->dtmf_lock); + pthread_mutex_destroy(&woomera->vlock); + pthread_mutex_destroy(&woomera->flags_lock); + woomera_set_raw(woomera, NULL); + woomera_set_interface(woomera, NULL); + + woomera_message_clear(&wmsg); + + smg_free(woomera); + pthread_mutex_lock(&server.thread_count_lock); + server.call_count--; + server.thread_count--; + pthread_mutex_unlock(&server.thread_count_lock); + + pthread_exit(NULL); + return NULL; +} + + +static int launch_woomera_thread(struct woomera_interface *woomera) +{ + int result = 0; + pthread_attr_t attr; + + result = pthread_attr_init(&attr); + //pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + //pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + woomera_set_flag(woomera, WFLAG_RUNNING); + result = pthread_create(&woomera->thread, &attr, woomera_thread_run, woomera); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Woomera Thread! (%i) %s\n", + __FUNCTION__,result,strerror(errno)); + woomera_clear_flag(woomera, WFLAG_RUNNING); + } + pthread_attr_destroy(&attr); + + return result; +} + +static int launch_monitor_thread(void) +{ + pthread_attr_t attr; + int result = 0; + struct sched_param param; + + param.sched_priority = 10; + result = pthread_attr_init(&attr); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + result = pthread_attr_setschedparam (&attr, ¶m); + + log_printf(SMG_LOG_ALL,server.log,"%s: Old Priority =%i res=%i \n",__FUNCTION__, + param.sched_priority,result); + + + woomera_set_flag(&server.master_connection, WFLAG_MONITOR_RUNNING); + result = pthread_create(&server.monitor_thread, &attr, monitor_thread_run, NULL); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Thread! %s\n", + __FUNCTION__,strerror(errno)); + woomera_clear_flag(&server.master_connection, WFLAG_MONITOR_RUNNING); + } + pthread_attr_destroy(&attr); + + return result; +} + + +#ifdef WP_HPTDM_API +static void *hp_tdmapi_span_run(void *obj) +{ + hp_tdm_api_span_t *span = obj; + int err; + + log_printf(SMG_LOG_ALL,server.log,"Starting %s span!\n",span->ifname); + + while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + + if (!span->run_span) { + break; + } + + err = span->run_span(span); + if (err) { + break; + } + + } + + if (span->close_span) { + span->close_span(span); + } + + sleep(3); + log_printf(SMG_LOG_ALL,server.log,"Stopping %s span!\n",span->ifname); + + pthread_exit(NULL); +} + + +static int launch_hptdm_api_span_thread(int span) +{ + pthread_attr_t attr; + int result = 0; + struct sched_param param; + + param.sched_priority = 5; + result = pthread_attr_init(&attr); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + result = pthread_attr_setschedparam (&attr, ¶m); + + result = pthread_create(&server.monitor_thread, &attr, hp_tdmapi_span_run, &hptdmspan[span]); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Thread! %s\n", + __FUNCTION__,strerror(errno)); + } + pthread_attr_destroy(&attr); + + return result; +} +#endif + +static int configure_server(void) +{ + struct woomera_config cfg; + char *var, *val; + int cnt = 0; + + server.dtmf_intr_ch = -1; + + if (!woomera_open_file(&cfg, server.config_file)) { + log_printf(SMG_LOG_ALL, server.log, "open of %s failed\n", server.config_file); + return 0; + } + + log_printf(SMG_LOG_ALL,server.log, "\n======================= \n"); + log_printf(SMG_LOG_ALL,server.log, "Server - Configuration \n"); + log_printf(SMG_LOG_ALL,server.log, "======================= \n"); + + while (woomera_next_pair(&cfg, &var, &val)) { + if (!strcasecmp(var, "boost_local_ip")) { + strncpy(server.mcon.cfg.local_ip, val, + sizeof(server.mcon.cfg.local_ip) -1); + strncpy(server.mconp.cfg.local_ip, val, + sizeof(server.mconp.cfg.local_ip) -1); + log_printf(SMG_LOG_ALL,server.log, "Server - Boost Local IP: %s\n",val); + + cnt++; + } else if (!strcasecmp(var, "boost_local_port")) { + server.mcon.cfg.local_port = atoi(val); + server.mconp.cfg.local_port = + server.mcon.cfg.local_port+1; + log_printf(SMG_LOG_ALL,server.log, "Server - Boost Local Port: %i\n",server.mcon.cfg.local_port); + cnt++; + } else if (!strcasecmp(var, "boost_remote_ip")) { + strncpy(server.mcon.cfg.remote_ip, val, + sizeof(server.mcon.cfg.remote_ip) -1); + strncpy(server.mconp.cfg.remote_ip, val, + sizeof(server.mconp.cfg.remote_ip) -1); + log_printf(SMG_LOG_ALL,server.log, "Server - Boost Remote IP: %s\n",server.mcon.cfg.remote_ip); + cnt++; + } else if (!strcasecmp(var, "boost_remote_port")) { + server.mcon.cfg.remote_port = atoi(val); + server.mconp.cfg.remote_port = + server.mcon.cfg.remote_port+1; + log_printf(SMG_LOG_ALL,server.log, "Server - Boost Remote Port: %i\n",server.mcon.cfg.local_port); + cnt++; + } else if (!strcasecmp(var, "logfile_path")) { + if (!server.logfile_path) { + server.logfile_path = smg_strdup(val); + } + } else if (!strcasecmp(var, "woomera_port")) { + server.port = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Woomera Port: %i\n",server.port); + } else if (!strcasecmp(var, "debug_level")) { + server.debug = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Debug Level: %i\n",server.debug); + } else if (!strcasecmp(var, "out_tx_test")) { + server.out_tx_test = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Tx Media Dbg: %s\n",server.out_tx_test?"On":"Off (Default)"); + } else if (!strcasecmp(var, "loop_trace")) { + server.loop_trace = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Media Loop Trace: %s\n",server.loop_trace?"On":"Off (Default)"); + } else if (!strcasecmp(var, "rxgain")) { + server.rxgain = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Rx Gain: %d\n",server.rxgain); + } else if (!strcasecmp(var, "txgain")) { + server.txgain = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Tx Gain: %d\n",server.txgain); + } else if (!strcasecmp(var, "dtmf_on_duration")){ + server.dtmf_on = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - DTMF ON Duration: %d ms\n",server.dtmf_on); + } else if (!strcasecmp(var, "dtmf_off_duration")){ + server.dtmf_off = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - DTMF Off Duration: %d ms\n",server.dtmf_off); + } else if (!strcasecmp(var, "dtmf_inter_ch_duration")){ + server.dtmf_intr_ch = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - DTMF Spacing: %d\n",server.dtmf_intr_ch); + } else if (!strcasecmp(var, "strip_cid_non_digits")){ + server.strip_cid_non_digits = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Strip non digits: %d\n",server.strip_cid_non_digits); + } else if (!strcasecmp(var, "max_calls")) { + int max = atoi(val); + if (max > 0) { + server.max_calls = max; + log_printf(SMG_LOG_ALL,server.log, "Server - Max Active Calls: %d\n",server.max_calls); + } + } else if (!strcasecmp(var, "autoacm")) { + int max = atoi(val); + if (max >= 0) { + autoacm=max; + log_printf(SMG_LOG_ALL,server.log, "Server - Auto ACM: %s\n",autoacm?"On":"Off (Default)"); + } + } else if (!strcasecmp(var, "max_spans")) { + int max = atoi(val); + if (max > 0) { + max_spans = max; + log_printf(SMG_LOG_ALL,server.log, "Server - Max Spans: %d\n",max_spans); + } + } else if (!strcasecmp(var, "call_timeout")) { + int max = atoi(val); + if (max >= 0) { + server.call_timeout=max; + } else { + server.call_timeout=SMG_DEFAULT_CALL_TIMEOUT; + } + log_printf(SMG_LOG_ALL,server.log, "Server - Call Comp Timeout: %d s\n",server.call_timeout); + + } else if (!strcasecmp(var, "base_media_port")) { + int base = atoi(val); + if (base >= 0) { + server.base_media_port = base; + server.next_media_port = base; + server.max_media_port = server.base_media_port + WOOMERA_MAX_MEDIA_PORTS; + log_printf(SMG_LOG_ALL,server.log, "Server - Base Media Port: %d\n",server.base_media_port); + } + + } else if (!strcasecmp(var, "max_media_ports")) { + int max = atoi(val); + if (max > WOOMERA_MAX_MEDIA_PORTS) { + server.max_media_port = server.base_media_port+max; + log_printf(SMG_LOG_ALL,server.log, "Server - Max Media Port: %d\n",server.max_media_port); + } + + } else if (!strcasecmp(var, "media_ip")) { + strncpy(server.media_ip, val, sizeof(server.media_ip) -1); + log_printf(SMG_LOG_ALL,server.log, "Server - Media IP: %s\n",server.media_ip); + } else { + log_printf(SMG_LOG_ALL, server.log, "Invalid Option %s at line %d!\n", var, cfg.lineno); + } + } + + log_printf(SMG_LOG_ALL,server.log, "======================= \n\n"); + + /* Post initialize */ + if (server.dtmf_on == 0){ + server.dtmf_on=SMG_DTMF_ON; + } + if (server.dtmf_off == 0) { + server.dtmf_off=SMG_DTMF_OFF; + } + if (server.dtmf_intr_ch == -1) { + server.dtmf_intr_ch = 0; + } + server.dtmf_size=(server.dtmf_on+server.dtmf_off)*10*2; + + log_printf(SMG_LOG_ALL,server.log, "DTMF On=%i Off=%i IntrCh=%i Size=%i\n", + server.dtmf_on,server.dtmf_off,server.dtmf_intr_ch,server.dtmf_size); + + woomera_close_file(&cfg); + return cnt == 4 ? 1 : 0; +} + + + +static int main_thread(void) +{ + + struct sockaddr_in sock_addr, client_addr; + struct woomera_interface *new_woomera; + int client_sock = -1, pid = 0; + unsigned int len = 0; + FILE *tmp; + + if ((server.master_connection.socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + fprintf(stderr,"%s:%d socket() failed %s\n",__FUNCTION__,__LINE__,strerror(errno)); + return 1; + } + + memset(&sock_addr, 0, sizeof(sock_addr)); /* Zero out structure */ + sock_addr.sin_family = AF_INET; /* Internet address family */ + sock_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ + sock_addr.sin_port = htons(server.port); /* Local port */ + + /* Bind to the local address */ + if (bind(server.master_connection.socket, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) < 0) { + fprintf(stderr,"%s:%d socket() bind %i failed %s\n",__FUNCTION__,__LINE__,server.port,strerror(errno)); + log_printf(SMG_LOG_ALL, server.log, "bind(%d) failed\n", server.port); + return 1; + } + + /* Mark the socket so it will listen for incoming connections */ + if (listen(server.master_connection.socket, MAXPENDING) < 0) { + fprintf(stderr,"%s:%d socket() listen failed %s\n",__FUNCTION__,__LINE__,strerror(errno)); + log_printf(SMG_LOG_ALL, server.log, "listen() failed\n"); + return 1; + } + + if ((pid = get_pid_from_file(PIDFILE))) { + fprintf(stderr,"%s:%d get pid file failed %s\n",__FUNCTION__,__LINE__,strerror(errno)); + log_printf(SMG_LOG_ALL, stderr, "pid %d already exists.\n", pid); + exit(0); + } + + if (!(tmp = safe_fopen(PIDFILE, "w"))) { + fprintf(stderr,"%s:%d open pid file failed %s\n",__FUNCTION__,__LINE__,strerror(errno)); + log_printf(SMG_LOG_ALL, stderr, "Error creating pidfile %s\n", PIDFILE); + return 1; + } else { + fprintf(tmp, "%d", getpid()); + fclose(tmp); + tmp = NULL; + } + + no_nagle(server.master_connection.socket); + +#if 0 + if (1) { + int span,chan; + call_signal_event_t event; +#if 0 + span=1; + chan=30; + event.span=span; + event.chan=chan; + launch_woomera_loop_thread(&event); +#else + for (span=0;span<8;span++) { + for (chan=0;chan<31;chan++) { + event.span=span; + event.chan=chan; + launch_woomera_loop_thread(&event); + } + } +#endif + } +#endif + +#ifdef WP_HPTDM_API + if (1) { + int span; + for (span=0;span<16;span++) { + hptdmspan[span] = sangoma_hptdm_api_span_init(span); + if (!hptdmspan[span]) { + break; + } else { + log_printf(SMG_LOG_ALL, server.log, "HP TDM API Span: %d configured...\n", + span); + launch_hptdm_api_span_thread(span); + } + } + } +#endif + + log_printf(SMG_LOG_PROD, server.log, "Main Process Started: Woomera Ready port: %d\n", server.port); + + while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + + /* Set the size of the in-out parameter */ + len = sizeof(client_addr); + + /* Wait for a client to connect */ + if ((client_sock = accept(server.master_connection.socket, (struct sockaddr *) &client_addr, &len)) < 0) { + /* Check if we are supposed to stop */ + if(!woomera_test_flag(&server.master_connection, WFLAG_RUNNING)) { + log_printf(SMG_LOG_ALL, server.log, "accept() stopped\n"); + return 0; + } + log_printf(SMG_LOG_ALL, server.log, "accept() failed\n"); + return 1; + } + + if ((new_woomera = new_woomera_interface(client_sock, &client_addr, len))) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Starting Thread for New Connection %s:%d Sock=%d\n", + inet_ntoa(new_woomera->addr.sin_addr), + ntohs(new_woomera->addr.sin_port), + client_sock); + + pthread_mutex_lock(&server.thread_count_lock); + server.call_count++; + pthread_mutex_unlock(&server.thread_count_lock); + + + if (launch_woomera_thread(new_woomera)) { + socket_printf(new_woomera->socket, + "501 call was cancelled!%s", + WOOMERA_RECORD_SEPERATOR); + + close_socket(&new_woomera->socket); + new_woomera->socket=-1; + smg_free(new_woomera); + log_printf(SMG_LOG_ALL, server.log, "ERROR: failed to launch woomera thread\n"); + } + } else { + log_printf(SMG_LOG_ALL, server.log, "Critical ERROR: memory/socket error\n"); + } + } + + log_printf(SMG_LOG_PROD, server.log, "Main Process End\n"); + + return 0; +} + +static int do_ignore(int sig) + +{ +#ifdef SMG_DROP_SEQ + drop_seq=4; +#endif + sdla_memdbg_free(0); + return 0; +} + +static int do_shut(int sig) +{ + woomera_clear_flag(&server.master_connection, WFLAG_RUNNING); + close_socket(&server.master_connection.socket); + log_printf(SMG_LOG_PROD, server.log, "Caught SIG %d, Closing Master Socket!\n", sig); + return 0; +} + +static int sangoma_tdm_init (int span) +{ +#ifdef LIBSANGOMA_GET_HWCODING + wanpipe_tdm_api_t tdm_api; + int fd=sangoma_open_tdmapi_span(span); + if (fd < 0 ){ + return -1; + } else { + server.hw_coding=sangoma_tdm_get_hw_coding(fd,&tdm_api); + close_socket(&fd); + } +#else +#error "libsangoma missing hwcoding feature: not up to date!" +#endif + return 0; +} + +static int woomera_startup(int argc, char **argv) +{ + int x = 0, pid = 0, bg = 0; + int span_cnt, chan_cnt; + char *cfg=NULL, *debug=NULL, *arg=NULL; + + while((arg = argv[x++])) { + + if (!strcasecmp(arg, "-hup")) { + if (! (pid = get_pid_from_file(PIDFILE))) { + log_printf(SMG_LOG_ALL, stderr, "Error reading pidfile %s\n", PIDFILE); + exit(1); + } else { + log_printf(SMG_LOG_ALL, stderr, "Killing PID %d\n", pid); + kill(pid, SIGHUP); + sleep(1); + exit(0); + } + + } else if (!strcasecmp(arg, "-term") || !strcasecmp(arg, "--term")) { + if (! (pid = get_pid_from_file(PIDFILE))) { + log_printf(SMG_LOG_ALL, stderr, "Error reading pidfile %s\n", PIDFILE); + exit(1); + } else { + log_printf(SMG_LOG_ALL, stderr, "Killing PID %d\n", pid); + kill(pid, SIGTERM); + unlink(PIDFILE); + sleep(1); + exit(0); + } + + } else if (!strcasecmp(arg, "-version")) { + fprintf(stdout, "\nSangoma Media Gateway: Version %s\n\n", SMG_VERSION); + exit(0); + + } else if (!strcasecmp(arg, "-help")) { + fprintf(stdout, "%s\n%s [-help] | [ -version] | [-hup] | [-wipe] | [[-bg] | [-debug ] | [-cfg ] | [-log ]]\n\n", WELCOME_TEXT, argv[0]); + exit(0); + } else if (!strcasecmp(arg, "-wipe")) { + unlink(PIDFILE); + } else if (!strcasecmp(arg, "-bg")) { + bg = 1; + + } else if (!strcasecmp(arg, "-g")) { + coredump = 1; + + } else if (!strcasecmp(arg, "-debug")) { + if (argv[x] && *(argv[x]) != '-') { + debug = argv[x++]; + } + } else if (!strcasecmp(arg, "-cfg")) { + if (argv[x] && *(argv[x]) != '-') { + cfg = argv[x++]; + } + } else if (!strcasecmp(arg, "-log")) { + if (argv[x] && *(argv[x]) != '-') { + server.logfile_path = smg_strdup(argv[x++]); + } + } else if (*arg == '-') { + log_printf(SMG_LOG_ALL, stderr, "Unknown Option %s\n", arg); + fprintf(stdout, "%s\n%s [-help] | [-hup] | [-wipe] | [[-bg] | [-debug ] | [-cfg ] | [-log ]]\n\n", WELCOME_TEXT, argv[0]); + exit(1); + } + } + + if (1){ + int spn; + for (spn=1;spn<=WOOMERA_MAX_SPAN;spn++) { + if (sangoma_tdm_init(spn) == 0) { + break; + } + } + if (spn>WOOMERA_MAX_SPAN) { + printf("\nError: Failed to access a channel on spans 1-16\n"); + printf(" Please start Wanpipe TDM API drivers\n"); + return 0; + } + } + + if (bg && (pid = fork())) { + log_printf(SMG_LOG_ALL, stderr, "Backgrounding!\n"); + return 0; + } + + for (span_cnt = 0; span_cnt < CORE_MAX_SPANS; span_cnt++) { + for (chan_cnt = 0; chan_cnt < CORE_MAX_CHAN_PER_SPAN; chan_cnt++) { + pthread_mutex_init(&server.process_table[span_cnt][chan_cnt].media_lock, NULL); + } + } + + q931_cause_setup(); + bearer_cap_setup(); + uil1p_to_str_setup(); + + server.port = 42420; + server.debug = 0; + strcpy(server.media_ip, "127.0.0.1"); + server.base_media_port = WOOMERA_MIN_MEDIA_PORT; + server.max_media_port = WOOMERA_MAX_MEDIA_PORT; + server.next_media_port = server.base_media_port; + server.log = stdout; + server.master_connection.socket = -1; + server.master_connection.event_queue = NULL; + server.config_file = cfg ? cfg : "/etc/sangoma_mgd.conf"; + pthread_mutex_init(&server.listen_lock, NULL); + pthread_mutex_init(&server.ht_lock, NULL); + pthread_mutex_init(&server.process_lock, NULL); + pthread_mutex_init(&server.digits_lock, NULL); + pthread_mutex_init(&server.media_udp_port_lock, NULL); + pthread_mutex_init(&server.thread_count_lock, NULL); + pthread_mutex_init(&server.master_connection.queue_lock, NULL); + pthread_mutex_init(&server.master_connection.flags_lock, NULL); + pthread_mutex_init(&server.mcon.lock, NULL); + server.master_connection.chan = -1; + server.master_connection.span = -1; + server.call_timeout=SMG_DEFAULT_CALL_TIMEOUT; + + if (smg_log_init()) { + printf("Error: Logger Launch Failed\n"); + fprintf(stderr, "Error: Logger Launch Failed\n"); + return 0; + } + + if (!configure_server()) { + log_printf(SMG_LOG_ALL, server.log, "configuration failed!\n"); + return 0; + } + +#ifndef USE_SYSLOG + if (server.logfile_path) { + if (!(server.log = safe_fopen(server.logfile_path, "a"))) { + log_printf(SMG_LOG_ALL, stderr, "Error setting logfile %s!\n", server.logfile_path); + server.log = stderr; + return 0; + } + } +#endif + + + if (debug) { + server.debug = atoi(debug); + } + + if (coredump) { + struct rlimit l; + memset(&l, 0, sizeof(l)); + l.rlim_cur = RLIM_INFINITY; + l.rlim_max = RLIM_INFINITY; + if (setrlimit(RLIMIT_CORE, &l)) { + log_printf(SMG_LOG_ALL, stderr, "Warning: Failed to disable core size limit: %s\n", + strerror(errno)); + } + } + +#ifdef __LINUX__ + if (geteuid() && coredump) { + if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) { + log_printf(SMG_LOG_ALL, stderr, "Warning: Failed to disable core size limit for non-root: %s\n", + strerror(errno)); + } + } +#endif + + + + (void) signal(SIGINT,(void *) do_shut); + (void) signal(SIGTERM,(void *) do_shut); + (void) signal(SIGPIPE,(void *) do_ignore); + (void) signal(SIGUSR1,(void *) do_ignore); + (void) signal(SIGHUP,(void *) do_shut); + + /* Wait for logger thread to start */ + usleep(5000); + + woomera_set_flag(&server.master_connection, WFLAG_RUNNING); + if (launch_monitor_thread()) { + woomera_clear_flag(&server.master_connection, WFLAG_RUNNING); + smg_log_cleanup(); + return 0; + } + + fprintf(stderr, "%s", WELCOME_TEXT); + + log_printf(SMG_LOG_ALL, stderr, "Woomera STARTUP Complete. [AutoACM=%i SDigit=%i]\n", + autoacm,server.strip_cid_non_digits); + return 1; +} + +static int woomera_shutdown(void) +{ + char *event_string; + int told = 0, loops = 0; + int span_cnt, chan_cnt; + + for (span_cnt = 0; span_cnt < CORE_MAX_SPANS; span_cnt++) { + for (chan_cnt = 0; chan_cnt < CORE_MAX_CHAN_PER_SPAN; chan_cnt++) { + pthread_mutex_destroy(&server.process_table[span_cnt][chan_cnt].media_lock); + } + } + + close_socket(&server.master_connection.socket); + pthread_mutex_destroy(&server.listen_lock); + pthread_mutex_destroy(&server.ht_lock); + pthread_mutex_destroy(&server.process_lock); + pthread_mutex_destroy(&server.digits_lock); + pthread_mutex_destroy(&server.media_udp_port_lock); + pthread_mutex_destroy(&server.thread_count_lock); + pthread_mutex_destroy(&server.master_connection.queue_lock); + pthread_mutex_destroy(&server.master_connection.flags_lock); + pthread_mutex_destroy(&server.mcon.lock); + woomera_clear_flag(&server.master_connection, WFLAG_RUNNING); + + + if (server.logfile_path) { + smg_free(server.logfile_path); + server.logfile_path = NULL; + } + + /* delete queue */ + while ((event_string = dequeue_event(&server.master_connection))) { + smg_free(event_string); + } + + while(server.thread_count > 0) { + loops++; + + if (loops % 1000 == 0) { + told = 0; + } + + if (loops > 10000) { + log_printf(SMG_LOG_ALL, server.log, "Red Alert! threads did not stop\n"); + assert(server.thread_count == 0); + } + + if (told != server.thread_count) { + log_printf(SMG_LOG_PROD, server.log, "Waiting For %d thread%s.\n", + server.thread_count, server.thread_count == 1 ? "" : "s"); + told = server.thread_count; + } + ysleep(10000); + } + unlink(PIDFILE); + log_printf(SMG_LOG_ALL, stderr, "Woomera SHUTDOWN Complete.\n"); + + smg_log_cleanup(); + + return 0; +} + +int main(int argc, char *argv[]) +{ + int ret = 0; + + memset(&server,0,sizeof(server)); + memset(&woomera_dead_dev,0,sizeof(woomera_dead_dev)); + + mlockall(MCL_FUTURE); + memset(&server, 0, sizeof(server)); + memset(&woomera_dead_dev, 0, sizeof(woomera_dead_dev)); + + ret=nice(-5); + + sdla_memdbg_init(); + + server.hw_coding=0; + + openlog (ps_progname ,LOG_PID, LOG_LOCAL2); + + if (! (ret = woomera_startup(argc, argv))) { + exit(0); + } + ret = main_thread(); + + woomera_shutdown(); + + sdla_memdbg_free(1); + + return ret; +} + +/** EMACS ** + * Local variables: + * mode: C + * c-basic-offset: 4 + * End: + */ + diff --git a/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.7.tmp b/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.7.tmp new file mode 100644 index 0000000..2e24471 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.7.tmp @@ -0,0 +1,740 @@ +/********************************************************************************* + * sangoma_mgd.h -- Sangoma Media Gateway Daemon for Sangoma/Wanpipe Cards + * + * Copyright 05-07, Nenad Corbic + * Anthony Minessale II + * + * This program is free software, distributed under the terms of + * the GNU General Public License + * + * =============================================*/ + +#ifndef __SANGOMA_MGD_H_ +#define __SANGOMA_MGD_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sangoma_mgd_common.h" +#include "call_signal.h" +#include "sangoma_mgd_memdbg.h" + +#ifdef __LINUX__ +#include +#endif + + +#include "call_signal.h" +#include "g711.h" +#include "sangoma_mgd_memdbg.h" +#include "libteletone.h" +#include "switch_buffer.h" +#include "list.h" + +#define USE_LOG_THREAD 1 +#define USE_SYSLOG 1 +#define CODEC_LAW_DEFAULT 1 + +#define WOOMERA_MAX_CHAN 31 + +#define SMG_SESSION_NAME_SZ 100 +#define SMG_CHAN_NAME_SZ 20 + +#define PIDFILE "/var/run/sangoma_mgd.pid" +#define PIDFILE_UNIT "/var/run/sangoma_mgd_unit.pid" + +#define WOOMERA_MAX_MEDIA_PORTS 899 + +#define CORE_EVENT_LEN 512 +#define WOOMERA_STRLEN 256 +#define WOOMERA_ARRAY_LEN 50 +#define WOOMERA_BODYLEN 2048 +#define WOOMERA_MIN_MEDIA_PORT 9000 +#define WOOMERA_MAX_MEDIA_PORT (WOOMERA_MIN_MEDIA_PORT + WOOMERA_MAX_MEDIA_PORTS) +#define WOOMERA_HARD_TIMEOUT 0 +#define WOOMERA_LINE_SEPERATOR "\r\n" +#define WOOMERA_RECORD_SEPERATOR "\r\n\r\n" +#define WOOMERA_DEBUG_PREFIX "[DEBUG] " +#define WOOMERA_DEBUG_LINE "------------------------------------------------------------------------------------------------" + + + + +#define STDERR fileno(stderr) +#define MAXPENDING 500 +#define MGD_STACK_SIZE 1024 * 240 + +#define SMG_SOCKET_EVENT_TIMEOUT 0 + +typedef enum { + WFLAG_RUNNING = (1 << 0), + WFLAG_LISTENING = (1 << 1), + WFLAG_MASTER_DEV = (1 << 2), + WFLAG_EVENT = (1 << 3), + WFLAG_MALLOC = (1 << 4), + WFLAG_MEDIA_RUNNING = (1 << 5), + WFLAG_MEDIA_END = (1 << 6), + WFLAG_MONITOR_RUNNING = (1 << 7), + WFLAG_HANGUP = (1 << 8), + WFLAG_ANSWER = (1 << 9), + WFLAG_MEDIA_TDM_RUNNING = (1 << 10), + WFLAG_HANGUP_ACK = (1 << 11), + WFLAG_HANGUP_NACK_ACK = (1 << 12), + WFLAG_WAIT_FOR_NACK_ACK = (1 << 13), /* Wait flag used to test reception of an NACK */ + WFLAG_WAIT_FOR_STOPPED_ACK = (1 << 14), /* Wait flag used to test reception of an ACK */ + WFLAG_RAW_MEDIA_STARTED = (1 << 15), /* Media has started on this channel */ + WFLAG_CALL_ACKED = (1 << 16), /* Woomera side rx accept so CALL ACK was Sent */ + WFLAG_WAIT_FOR_NACK_ACK_SENT = (1 << 17), /* Call START NACK was sent out on this channel */ + WFLAG_WAIT_FOR_STOPPED_ACK_SENT = (1 << 18), /* Call STOP was sent out on this channel */ + WFLAG_SYSTEM_RESET = (1 << 19), /* Initial System Reset Condition no calls allowed */ + WFLAG_WAIT_FOR_ACK_TIMEOUT = (1 << 20), /* Timeout flag indicating that incoming ACK or NACK timedout */ +} WFLAGS; + +enum { + + SMG_LOG_ALL = 0, + SMG_LOG_PROD = 1, + SMG_LOG_BOOST = 2, + SMG_LOG_WOOMERA = 3, + SMG_LOG_DEBUG_CALL = 4, + SMG_LOG_DEBUG_MISC = 5, + SMG_LOG_DEBUG_6 = 6, + SMG_LOG_DEBUG_7 = 7, + SMG_LOG_DEBUG_8 = 8, + SMG_LOG_DEBUG_9 = 9, + SMG_LOG_DEBUG_10 = 10 +}; + + +#define woomera_print_flags(woomera,level) \ + log_printf(level,woomera->log,"%s: WFLAG_RUNNING = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_RUNNING));\ + log_printf(level,woomera->log,"%s: WFLAG_LISTENING = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_LISTENING));\ + log_printf(level,woomera->log,"%s: WFLAG_MASTER_DEV = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_MASTER_DEV));\ + log_printf(level,woomera->log,"%s: WFLAG_EVENT = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_EVENT));\ + log_printf(level,woomera->log,"%s: WFLAG_MALLOC = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_MALLOC));\ + log_printf(level,woomera->log,"%s: WFLAG_MEDIA_RUNNING = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_MEDIA_RUNNING));\ + log_printf(level,woomera->log,"%s: WFLAG_MEDIA_END = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_MEDIA_END));\ + log_printf(level,woomera->log,"%s: WFLAG_MONITOR_RUNNING = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_MONITOR_RUNNING));\ + log_printf(level,woomera->log,"%s: WFLAG_HANGUP = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_HANGUP));\ + log_printf(level,woomera->log,"%s: WFLAG_ANSWER = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_ANSWER));\ + log_printf(level,woomera->log,"%s: WFLAG_MEDIA_TDM_RUNNING = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_MEDIA_TDM_RUNNING));\ + log_printf(level,woomera->log,"%s: WFLAG_HANGUP_ACK = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_HANGUP_ACK));\ + log_printf(level,woomera->log,"%s: WFLAG_HANGUP_NACK_ACK = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_HANGUP_NACK_ACK));\ + log_printf(level,woomera->log,"%s: WFLAG_WAIT_FOR_NACK_ACK = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_WAIT_FOR_NACK_ACK));\ + log_printf(level,woomera->log,"%s: WFLAG_WAIT_FOR_STOPPED_ACK = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_WAIT_FOR_STOPPED_ACK));\ + log_printf(level,woomera->log,"%s: WFLAG_RAW_MEDIA_STARTED = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_RAW_MEDIA_STARTED));\ + log_printf(level,woomera->log,"%s: WFLAG_CALL_ACKED = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_CALL_ACKED));\ + log_printf(level,woomera->log,"%s: WFLAG_WAIT_FOR_NACK_ACK_SENT = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_WAIT_FOR_NACK_ACK_SENT));\ + log_printf(level,woomera->log,"%s: WFLAG_WAIT_FOR_STOPPED_ACK_SENT = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_WAIT_FOR_STOPPED_ACK_SENT));\ + log_printf(level,woomera->log,"%s: WFLAG_SYSTEM_RESET = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_SYSTEM_RESET));\ + log_printf(level,woomera->log,"%s: WFLAG_WAIT_FOR_ACK_TIMEOUT = %i\n",woomera->interface, woomera_test_flag(woomera,WFLAG_WAIT_FOR_ACK_TIMEOUT));\ + + +typedef enum { + MFLAG_EXISTS = (1 << 0), + MFLAG_CONTENT = (1 << 1), +} MFLAGS; + +typedef enum { + EVENT_FREE_DATA = 1, + EVENT_KEEP_DATA = 0 +} event_args; + + +void __log_printf(int level, FILE *fp, char *file, const char *func, int line, char *fmt, ...); + +#define ysleep(usec) sched_yield() ; usleep(usec); +#define log_printf(level, fp, fmt, ...) __log_printf(level, fp, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__) + + +#define woomera_test_flag(p,flag) ({ \ + ((p)->flags & (flag)); \ + }) + +#define _woomera_set_flag(p,flag) do { \ + ((p)->flags |= (flag)); \ + } while (0) + +#define _woomera_clear_flag(p,flag) do { \ + ((p)->flags &= ~(flag)); \ + } while (0) + +#define woomera_set_flag(p,flag) do { \ + pthread_mutex_lock(&(p)->flags_lock); \ + ((p)->flags |= (flag)); \ + pthread_mutex_unlock(&(p)->flags_lock); \ + } while (0) + +#define woomera_clear_flag(p,flag) do { \ + pthread_mutex_lock(&(p)->flags_lock); \ + ((p)->flags &= ~(flag)); \ + pthread_mutex_unlock(&(p)->flags_lock); \ + } while (0) + +#define woomera_copy_flags(dest,src,flagz) do { \ + pthread_mutex_lock(&(p)->flags_lock); \ + (dest)->flags &= ~(flagz); \ + (dest)->flags |= ((src)->flags & (flagz)); \ + pthread_mutex_unlock(&(p)->flags_lock); \ + } while (0) + + +struct media_session { + int udp_sock; + int sangoma_sock; + char *ip; + int port; + time_t started; + time_t answered; + pthread_t thread; + int socket; + struct woomera_interface *woomera; + struct sockaddr_in local_addr; + struct sockaddr_in remote_addr; + struct hostent remote_hp; + struct hostent local_hp; + int skip_read_frames; + int skip_write_frames; + int hw_coding; + int hw_dtmf; + int udp_sync_cnt; + +#ifdef WP_HPTDM_API + hp_tdm_api_chan_t *tdmchan; +#endif + + teletone_dtmf_detect_state_t dtmf_detect; + teletone_generation_session_t tone_session; + switch_buffer_t *dtmf_buffer; + unsigned char oob_disable; +}; + +struct woomera_message { + char callid[WOOMERA_STRLEN]; + char command[WOOMERA_STRLEN]; + char command_args[WOOMERA_STRLEN]; + char names[WOOMERA_STRLEN][WOOMERA_ARRAY_LEN]; + char values[WOOMERA_STRLEN][WOOMERA_ARRAY_LEN]; + char body[WOOMERA_BODYLEN]; + uint32_t flags; + pthread_mutex_t flags_lock; + int last; + struct woomera_message *next; +}; + +struct woomera_event { + char *data; + uint32_t flags; + struct woomera_event *next; +}; + +struct woomera_listener { + struct woomera_interface *woomera; + struct woomera_listener *next; +}; + +struct woomera_interface { + int socket; + char *raw; + char *interface; + time_t timeout; + struct sockaddr_in addr; + struct media_session *ms; + pthread_mutex_t queue_lock; + pthread_mutex_t ms_lock; + pthread_mutex_t dtmf_lock; + struct woomera_event *event_queue; + struct woomera_event *incoming_event_queue; + pthread_t thread; + uint32_t flags; + pthread_mutex_t flags_lock; + int debug; + int call_id; + FILE *log; + pthread_mutex_t vlock; + int index; + int index_hold; + int span; + int chan; + int trunk_group; + int call_count; + int q931_rel_cause_tosig; + int q931_rel_cause_topbx; + int loop_tdm; + char session[SMG_SESSION_NAME_SZ]; + int check_digits; /* set to 1 when session comes up */ + int bearer_cap; + struct woomera_interface *next; +}; + +struct woomera_session { + struct woomera_interface *dev; + char session[SMG_SESSION_NAME_SZ]; + char digits[MAX_DIALED_DIGITS+1]; + int digits_len; + int bearer_cap; + int clients; + unsigned char media_used; + pthread_mutex_t media_lock; +}; + +#define CORE_TANK_LEN CORE_MAX_CHAN_PER_SPAN*CORE_MAX_SPANS + +struct woomera_server { + struct woomera_session process_table[CORE_MAX_SPANS][CORE_MAX_CHAN_PER_SPAN]; + struct woomera_interface *holding_tank[CORE_TANK_LEN]; + int holding_tank_index; + struct woomera_interface master_connection; + pthread_mutex_t listen_lock; + pthread_mutex_t ht_lock; + pthread_mutex_t process_lock; + pthread_mutex_t digits_lock; + pthread_mutex_t media_udp_port_lock; + pthread_mutex_t thread_count_lock; + call_signal_connection_t mcon; + call_signal_connection_t mconp; + struct woomera_listener *listeners; + char media_ip[WOOMERA_STRLEN]; + int port; + int next_media_port; + int base_media_port; + int max_media_port; + int debug; + int panic; + int thread_count; + char *logfile_path; + FILE *log; + char boost_local_ip[25]; + int boost_local_port; + char boost_remote_ip[25]; + int boost_remote_port; + pthread_t monitor_thread; + char *config_file; + int max_calls; + int call_count; + uint32_t out_tx_test; + uint32_t rxgain; + uint32_t txgain; + uint32_t hw_coding; + uint32_t loop_trace; + uint32_t hungup_waiting; + int all_ckt_gap; + int all_ckt_busy; + struct timeval all_ckt_busy_time; + struct timeval restart_timeout; + int dtmf_on; + int dtmf_off; + int dtmf_intr_ch; + int dtmf_size; + int strip_cid_non_digits; + int call_timeout; +}; + +extern struct woomera_server server; + +struct woomera_config { + FILE *file; + char *path; + char category[256]; + char buf[1024]; + int lineno; +}; + + +static inline void smg_get_current_priority(int *policy, int *priority) +{ + struct sched_param param; + pthread_getschedparam(pthread_self(), policy, ¶m); + *priority = param.sched_priority; + return; +} + +static inline int smg_calc_elapsed(struct timeval *started, struct timeval *ended) +{ + return (((ended->tv_sec * 1000) + ended->tv_usec / 1000) - + ((started->tv_sec * 1000) + started->tv_usec / 1000)); +} + +static inline int smg_check_all_busy(void) +{ + struct timeval ended; + int elapsed; + + if (server.all_ckt_gap) { + return server.all_ckt_gap; + } + + if (server.all_ckt_busy==0) { + return 0; + } + + gettimeofday(&ended,NULL); + elapsed = smg_calc_elapsed(&server.all_ckt_busy_time,&ended); + + /* seconds elapsed */ + if (elapsed > server.all_ckt_busy) { + server.all_ckt_busy=0; + return 0; + } else { + return 1; + } + +#if 0 + + if (server.all_ckt_busy > 50) { + /* When in GAP mode wait 10s */ + return server.all_ckt_busy; + } + + --server.all_ckt_busy; + if (server.all_ckt_busy < 0) { + server.all_ckt_busy=0; + } + + return server.all_ckt_busy; +#endif +} + + +static inline void smg_all_ckt_busy(void) +{ + + if (server.call_count*10 < 1500) { + server.all_ckt_busy+=1500; + } else { + server.all_ckt_busy+=server.call_count*15; + } + + if (server.all_ckt_busy > 10000) { + server.all_ckt_busy = 10000; + } + +#if 0 + if (server.all_ckt_busy >= 5) { + server.all_ckt_busy=10; + } else if (server.all_ckt_busy >= 10) { + server.all_ckt_busy=15; + } else if (server.all_ckt_busy == 0) { + server.all_ckt_busy=5; + } +#endif + gettimeofday(&server.all_ckt_busy_time,NULL); +} + +static inline void smg_all_ckt_gap(void) +{ + server.all_ckt_gap=1; +} + +static inline void smg_clear_ckt_gap(void) +{ + server.all_ckt_gap=0; + gettimeofday(&server.all_ckt_busy_time,NULL); +} + + +static inline int smg_validate_span_chan(int span, int chan) +{ + if (span < 0 || span > max_spans) { + return -1; + } + + if (chan < 0 || chan > WOOMERA_MAX_CHAN) { + return -1; + } + + return 0; +} + + +static inline void close_socket(int *sp) +{ + if (*sp > -1) { + close(*sp); + *sp = -1; + } +} + +static inline FILE *safe_fopen(char *path, char *flags) +{ + char buf[512] = ""; + + if (readlink(path, buf, sizeof(buf)) > 0) { + fprintf(stderr, "Symlinks not allowed! [%s] != [%s]\n", buf, path); + return NULL; + } + + return fopen(path, flags); + +} + +static inline int get_pid_from_file(char *path) +{ + FILE *tmp; + int pid; + int err; + + if (!(tmp = safe_fopen(path, "r"))) { + return 0; + } else { + err=fscanf(tmp, "%d", &pid); + fclose(tmp); + tmp = NULL; + } + + return pid; +} + + +static inline int woomera_open_file(struct woomera_config *cfg, char *path) +{ + FILE *f; + + if (!(f = fopen(path, "r"))) { + log_printf(SMG_LOG_ALL, stderr, "Cannot open file %s\n", path); + return 0; + } + + memset(cfg, 0, sizeof(*cfg)); + cfg->file = f; + cfg->path = path; + return 1; + +} + + +static inline void woomera_close_file(struct woomera_config *cfg) +{ + + if (cfg->file) { + fclose(cfg->file); + } + + memset(cfg, 0, sizeof(*cfg)); +} + + +static inline void woomera_message_init (struct woomera_message *wmsg) +{ + memset (wmsg,0,sizeof(struct woomera_message)); + pthread_mutex_init(&wmsg->flags_lock, NULL); +} + +static inline void woomera_message_clear (struct woomera_message *wmsg) +{ + pthread_mutex_destroy(&wmsg->flags_lock); +} + + +static inline void woomera_set_raw(struct woomera_interface *woomera, char *raw) +{ + char *oldraw=woomera->raw; + + if (raw) { + woomera->raw = smg_strdup(raw); + } else { + woomera->raw = NULL; + } + + if (oldraw) { + smg_free(oldraw); + } +} + +static inline struct media_session * woomera_get_ms(struct woomera_interface *woomera) +{ + struct media_session *ms; + pthread_mutex_lock(&woomera->ms_lock); + ms=woomera->ms; + pthread_mutex_unlock(&woomera->ms_lock); + return ms; +} + +static inline void woomera_set_ms(struct woomera_interface *woomera,struct media_session *ms) +{ + pthread_mutex_lock(&woomera->ms_lock); + woomera->ms=ms; + pthread_mutex_unlock(&woomera->ms_lock); + return; +} + +static inline void woomera_set_interface(struct woomera_interface *woomera, char *interface) +{ + char *iface = woomera->interface; + + if (interface) { + woomera->interface = smg_strdup(interface); + } else { + woomera->interface = NULL; + } + + if (iface) { + smg_free(iface); + } +} + +static inline void woomera_set_cause_tosig(struct woomera_interface *woomera, int cause) +{ + if (!cause) { + cause=SIGBOOST_RELEASE_CAUSE_NORMAL; + } + + woomera->q931_rel_cause_tosig=cause; +} + +static inline void woomera_set_cause_topbx(struct woomera_interface *woomera, int cause) +{ + + if (!cause) { + cause=SIGBOOST_RELEASE_CAUSE_NORMAL; + } + + if (cause == SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY) { + cause=34; + } + + woomera->q931_rel_cause_topbx=cause; + +} + + +static inline struct woomera_event *new_woomera_event(void) +{ + struct woomera_event *event = NULL; + + if ((event = smg_malloc(sizeof(*event)))) { + memset(event, 0, sizeof(*event)); + _woomera_set_flag(event, WFLAG_MALLOC); + } + + return event; +} + + +static inline void destroy_woomera_event_data(struct woomera_event *event) +{ + if (event->data) { + smg_free (event->data); + event->data=NULL; + } +} + +static inline void destroy_woomera_event(struct woomera_event **event, event_args free_data) +{ + if (free_data) { + smg_free ((*event)->data); + } + + (*event)->data=NULL; + if (woomera_test_flag((*event), WFLAG_MALLOC)) { + smg_free (*event); + *event = NULL; + } +} + + +/* disable nagle's algorythm */ +static inline void no_nagle(int socket) +{ + int flag = 1; + setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); +} + + +static inline void remove_end_of_digits_char(unsigned char *s) +{ + unsigned char *p; + for (p = s; *p; p++) { + if (*p == 'F' || *p > 'f') { + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Removing a non-numeric character [%c]!\n", *p); + *p = '\0'; + break; + } + } +} + +static inline void validate_number(unsigned char *s) +{ + unsigned char *p; + for (p = s; *p; p++) { + if (*p < 48 || *p > 57) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Encountered a non-numeric character [%c]!\n", *p); + *p = '\0'; + break; + } + } +} + +static inline int woomera_check_running(struct woomera_interface *woomera) +{ + if (woomera_test_flag(woomera, WFLAG_HANGUP) || + !woomera_test_flag(woomera, WFLAG_RUNNING) || + woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + + return 0; + } + + return 1; +} + +static inline int open_span_chan (unsigned char span, unsigned char chan) +{ + int fd = -1; +#ifndef LIBSANGOMA_VERSION + fd = sangoma_open_tdmapi_span_chan(span, chan); +#else + if (chan == 24) { + pthread_mutex_lock(&server.process_table[span][chan].media_lock); + if(server.process_table[span][chan].media_used > 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical Error: channel already opened [w%ig%i]\n", span, chan); + } else { + server.process_table[span][chan].media_used++; + + fd = __sangoma_open_api_span_chan(span, chan); + } + pthread_mutex_unlock(&server.process_table[span][chan].media_lock); + } else { + fd = sangoma_open_api_span_chan(span, chan); + } +#endif + return fd; +} + +static inline void close_span_chan (int *socket, unsigned char span, unsigned char chan) +{ + if (chan == 24) { + pthread_mutex_lock(&server.process_table[span][chan].media_lock); + if(server.process_table[span][chan].media_used > 0) { + server.process_table[span][chan].media_used--; + } + close_socket(socket); + pthread_mutex_unlock(&server.process_table[span][chan].media_lock); + } else { + close_socket(socket); + } +} + +extern int smg_log_init(void); +extern void smg_log_cleanup(void); +#endif + diff --git a/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.8.tmp b/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.8.tmp new file mode 100644 index 0000000..dbef3c9 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.8.tmp @@ -0,0 +1,159 @@ +/**************************************************************************** + * 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 100 + +#include +#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*/ + 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, +}; + + +#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 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 900 + + +#pragma pack(1) + +typedef struct +{ + uint8_t capability; + uint8_t uil1p; +}t_sigboost_bearer; + +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; + /* struct timeval tv; */ + 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 */ + /* ref. Q.931 Table 4-11 and Q.951 Section 3 */ + uint8_t calling_number_screening_ind; + uint8_t calling_number_presentation; + char calling_name[MAX_DIALED_DIGITS + 1]; + t_sigboost_bearer bearer; + uint8_t hunt_group; + uint16_t isup_in_rdnis_size; + char isup_in_rdnis [SIZE_RDNIS]; /* it's a null terminated string */ +} t_sigboost_callstart; + +#define MIN_SIZE_CALLSTART_MSG sizeof(t_sigboost_callstart) - SIZE_RDNIS + +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; + /* 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: + return 0; + } + + return 0; +} + +#endif diff --git a/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.9.tmp b/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.9.tmp new file mode 100644 index 0000000..ddc4a4d --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/.svn/tmp/tempfile.9.tmp @@ -0,0 +1,5946 @@ +/********************************************************************************* + * sangoma_mgd.c -- Sangoma Media Gateway Daemon for Sangoma/Wanpipe Cards + * + * Copyright 05-09, Nenad Corbic + * Anthony Minessale II + * + * This program is free software, distributed under the terms of + * the GNU General Public License + * + * ============================================= + * + * v1.53 Nenad Corbic + * Added progress message + * + * v1.52 David Yat Sin + * Changed sangoma_open_span_chan to __sangoma_span_chan + * to enabled shared used of file descriptors when using + * with PRI in NFAS mode + * + * v1.51 David Yat Sin + * MAX_SPANS increased to 32. + * Fix for server.process_table declared incorrectly + * + * v1.50 Nenad Corbic + * Logic to support multiple woomera clients hanging up the + * channel. This feature now supprorts woomera loadbalancing + * with extension check on the client end. Example, two Asterisk + * systems setup where Asterisk with correct extension gets the + * call. + * Changed log levels: Loglevel 1 = production + * Loglevel 2 = Boost TX/RX EVENTS + * Loglevel 3 = Woomera RX Messages + * Loglevel 4 = call setup debugging + * Loglevel 5 = extra debugging + * Loglevel 10 = full debugging + * + * v1.49 Nenad Corbic + * Removed tv from sigboost to make it binary compatible + * Updated release cause on double use return code. + * + * v1.48 Nenad Corbic + * Konrad Hammel + * Jun 30 2009 + * Added feature to disable/enable HWEC on channels that are + * passing a call with a "data" only transfer capability + * + * v1.47 Nenad Corbic + * Apr 30 2009 + * Updated hangup woomera events. Each event must + * be followed by a woomera error code 200=ok >299=no ok. + * This update fixes the chan_woomera socket write + * warnings. + * + * v1.46 Nenad Corbic + * Mar 27 2009 + * Major updates on socket handling. A bug was introducted + * when cmm and trunk was merged. + * Added configuration print in the logs. + * + * v1.45 Nenad Corbic + * Mar 19 2009 + * Outbound call timeout defaulted to 300 seconds. + * Timeout on answer instead of accept. + * + * v1.44 Nenad Corbic + * Mar 19 2009 + * Adjustable boost size. Added boost version check. + * + * v1.43 David Yat Sin + * Feb 25 2009 + * Merged CMM and Trunk + * + * v1.42 Nenad Corbic + * Jan 26 2008 + * Call Bearer Capability + * BugFix: Hangup NACK was sent out with invalid cause 0 + * + * v1.41 Nenad Corbic + * Jan 12 2008 + * Fixed the NACK with cause 0 bug. + * Added cause 19 on call timeout. + * Added configuration option call_timeout to timeout + * outbound calls. + * + * v1.40 Nenad Corbic + * Dec 10 2008 + * Check for Rx call overrun. + * In unlikely case sangoma_mgd goes out of + * sync with boost. + * + * v1.39 Nenad Corbic + * Sep 17 2008 + * Updated for Unit Test + * Bug fix in Loop Logic, possible race + * between media and woomera thread on loop end. + * + * v1.38 Nenad Corbic + * Sep 5 2008 + * Added a Double use of setup id logic. + * Currently double use call gets dropped. + * + * v1.37 Nenad Corbic + * Sep 2 2008 + * Bug fix in REMOVE LOOP logic continued + * The woomera->sock in loop logic was set to 0. + * Closing it failed the next call. + * + * v1.36 Nenad Corbic + * Aug 28 2008 + * Bug fix in REMOVE LOOP logic + * Remove F from incoming calls by default + * Check for errno on poll() + * Do not delay in closing socket on media down. + * + * v1.35cmm Nenad Corbic + * Jul 28 2008 + * Bug fixes on ACK Timeout and trunk release + * Added a thread logger + * Increased max spans to 32 + * + * v1.34cmm Nenad Corbic + * Jul 24 2008 + * Clean up the setup id on CALL STOP ACK + * + * v1.33cmm Nenad Corbic + * Jul 24 2008 + * The CALL STOP timeout function had a bug + * resulting in false blocking of tank ids + * due to STOP ACK Timeouts. + * + * v1.33 Nenad Corbic + * Jul 18 2008 + * Added UDP Sequencing to check for dropped frames + * Should only be used for debugging. + * + * v1.32 David Yat Sin + * Jul 17 2008 + * Support for d-channel incoming digit + * passthrough to Asterisk + * + * v1.32cmm Nenad Corbic + * Jun 03 2008 + * Implemented new Restart ACK Policy + * Split the Event packet into 2 types + * + * v1.31cmm Nenad Corbic + * Apr 04 2008 + * New CMM Restart procedure for boost + * Block TRUNK ID on ACK Timeout + * + * v1.31 David Yat Sin + * Apr 29 2008 + * Support for HW DTMF events. + * + * v1.30 Nenad Corbic + * Feb 08 2008 + * The fix in v1.26 causes double stop. + * I took out the v1.26 fix and added + * a big warning if that condition is + * ever reached. + * + * v1.29 Nenad Corbic + * Feb 07 2008 + * Added strip_cid_non_digits option + * + * v1.28 Nenad Corbic + * Feb 06 2008 + * Fixed a memory leak in clone event + * function. Added memory check subsystem + * + * v1.27 Nenad Corbic + * Jan 24 2008 + * Fixed a memory leak on incoming calls + * Removed the use of server listener which + * was not used + * + * v1.26 Nenad Corbic + * Jan 18 2008 + * Fixed hangup after invalid Answer or Ack Session + * Can cause double use of setup id - now fixed + * Update on autoacm on accept check for acked. + * + * v1.25 Nenad Corbic + * Dec 31 2007 + * Removed UDP Resync it can cause skb_over errors. + * Moved RDNIS message to higher debug level + * + * v1.24 Nenad Corbic + * Nov 30 2007 + * Bug fix on return code on ALL ckt busy + * + * v1.23 Nenad Corbic + * Bug fix on socket open. Check for retun code >= 0 + * + * v1.22 Nenad Corbic + * Nov 27 2007 + * Updated DTMF Tx function + * Fixed - dtmf tx without voice + * Fxied - dtmf clipping. + * + * v1.21 Nenad Corbic + * Nov 25 2007 + * Major unit testing of each state + * Numerous bug fixes for non autoacm mode. + * Changed "Channel-Name" to tg/cic + * Added compile option WANPIPE_CHAN_NAME to change Asterisk channel + * name of chan_woomera.so. So one can use Dial(SS7/g1/${EXTE}) + * instead of WOOMERA (for example) + * + * v1.20 Nenad Corbic + * Added option for Auto ACM response mode. + * + * v1.19 Nenad Corbic + * Configurable DTMF + * Bug fix in release codes (all ckt busy) + * + * v1.18 Nenad Corbic + * Added new rel cause support based on + * digits instead of strings. + * + * v1.17 Nenad Corbic + * Added session support + * + * v1.16 Nenad Corbic + * Added hwec disable on loop ccr + * + * v1.15 Nenad Corbic + * Updated DTMF Locking + * Added delay between digits + * + * v1.14 Nenad Corbic + * Updated DTMF Library + * Fixed DTMF synchronization + * + * v1.13 Nenad Corbic + * Woomera OPAL Dialect + * Added Congestion control + * Added SCTP + * Added priority ISUP queue + * Fixed presentation + * + * v1.12 Nenad Corbic + * Fixed CCR + * Removed socket shutdown on end call. + * Let Media thread shutodwn sockets. + * + * v1.11 Nenad Corbic + * Fixed Remote asterisk/woomera connection + * Increased socket timeouts + * + * v1.10 Nenad Corbic + * Added Woomera OPAL dialect. + * Start montor thread in priority + * + * v1.9 Nenad Corbic + * Added Loop mode for ccr + * Added remote debug enable + * Fixed syslog logging. + * + * v1.8 Nenad Corbic + * Added a ccr loop mode for each channel. + * Boost can set any channel in loop mode + * + * v1.7 Nenad Corbic + * Pass trunk group number to incoming call + * chan woomera will use it to append to context + * name. Added presentation feature. + * + * v1.6 Nenad Corbic + * Use only ALAW and MLAW not SLIN. + * This reduces the load quite a bit. + * Send out ALAW/ULAW format on HELLO message. + * RxTx Gain is done now in chan_woomera. + * + * v1.5 Nenad Corbic + * Bug fix in START_NACK_ACK handling. + * When we receive START_NACK we must alwasy pull tank before + * we send out NACK_ACK this way we will not try to send NACK + * ourself. + *********************************************************************************/ + +#include "sangoma_mgd.h" +#include "sangoma_mgd_memdbg.h" +#include "q931_cause.h" +#include "smg_capabilities.h" + +int pipe_fd[2]; + +#ifdef CODEC_LAW_DEFAULT +static uint32_t codec_sample=8; +#else +static uint32_t codec_sample=16; +#endif + +static char ps_progname[]="sangoma_mgd"; + +static struct woomera_interface woomera_dead_dev; + +struct woomera_server server; + +#if 0 +#define DOTRACE +#endif + + +#define SMG_VERSION "v1.53" + +/* enable early media */ +#if 1 +#define WOOMERA_EARLY_MEDIA 1 +#endif + + +#define SMG_DTMF_ON 60 +#define SMG_DTMF_OFF 10 +#define SMG_DTMF_RATE 8000 +#define SMG_DEFAULT_CALL_TIMEOUT 300 + +#if 0 +#define MEDIA_SOCK_SHUTDOWN 1 +#endif + +#ifdef DOTRACE +static int tc = 0; +#endif + +#if 0 +#warning "NENAD: HPTDM API" +#define WP_HPTDM_API 1 +#else +#undef WP_HPTDM_API +#endif + +#ifdef WP_HPTDM_API +hp_tdm_api_span_t *hptdmspan[WOOMERA_MAX_SPAN]; +#endif + +#if 0 +#define SMG_DROP_SEQ 1 +#warning "SMG Debug feature Drop Seq Enabled" +static int drop_seq=0; +#else +#undef SMG_DROP_SEQ +#endif + +const char WELCOME_TEXT[] = +"================================================================================\n" +"Sangoma Media Gateway Daemon v1.53 \n" +"\n" +"TDM Signal Media Gateway for Sangoma/Wanpipe Cards\n" +"Copyright 2005, 2006, 2007 \n" +"Nenad Corbic , Anthony Minessale II \n" +"This program is free software, distributed under the terms of\n" +"the GNU General Public License\n" +"================================================================================\n" +""; + + + +static int coredump=1; +static int autoacm=0; + +/* FIXME: Should be done in cfg file */ +#if defined(BRI_PROT) +int max_spans=WOOMERA_BRI_MAX_SPAN; +int max_chans=WOOMERA_BRI_MAX_CHAN; +#else +/* PRI_PROT uses these defines as well */ +int max_spans=WOOMERA_MAX_SPAN; +int max_chans=WOOMERA_MAX_CHAN; +#endif + +static int launch_media_tdm_thread(struct woomera_interface *woomera); +static int launch_woomera_thread(struct woomera_interface *woomera); +static void woomera_check_digits (struct woomera_interface *woomera); +static struct woomera_interface *alloc_woomera(void); +static void handle_event_dtmf(struct woomera_interface *woomera, unsigned char dtmf_digit); + +q931_cause_to_str_array_t q931_cause_to_str_array[255]; +bearer_cap_to_str_array_t bearer_cap_to_str_array[255]; +uil1p_to_str_array_t uil1p_to_str_array[255]; + +static int isup_exec_command(int span, int chan, int id, int cmd, int cause) +{ + short_signal_event_t oevent; + int retry=5; + + call_signal_event_init((short_signal_event_t*)&oevent, cmd, chan, span); + oevent.release_cause = cause; + + if (id >= 0) { + oevent.call_setup_id = id; + } +isup_exec_cmd_retry: + if (call_signal_connection_write(&server.mcon, (call_signal_event_t*)&oevent) < 0){ + + --retry; + if (retry <= 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket: %s\n", + strerror(errno)); + return -1; + } else { + log_printf(SMG_LOG_ALL, server.log, + "System Warning: Failed to tx on ISUP socket: %s :retry %i\n", + strerror(errno),retry); + } + + goto isup_exec_cmd_retry; + } + + return 0; +} + +static int isup_exec_event(call_signal_event_t *event) +{ + int retry=5; + +isup_exec_cmd_retry: + if (call_signal_connection_write(&server.mcon, event) < 0){ + + --retry; + if (retry <= 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket: %s\n", + strerror(errno)); + return -1; + } else { + log_printf(SMG_LOG_ALL, server.log, + "System Warning: Failed to tx on ISUP socket: %s :retry %i\n", + strerror(errno),retry); + } + + goto isup_exec_cmd_retry; + } + + return 0; +} + + +static int isup_exec_commandp(int span, int chan, int id, int cmd, int cause) +{ + short_signal_event_t oevent; + int retry=5; + + call_signal_event_init(&oevent, cmd, chan, span); + oevent.release_cause = cause; + + if (id >= 0) { + oevent.call_setup_id = id; + } +isup_exec_cmd_retry: + if (call_signal_connection_writep(&server.mconp, (call_signal_event_t*)&oevent) < 0){ + + --retry; + if (retry <= 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket: %s\n", + strerror(errno)); + return -1; + } else { + log_printf(SMG_LOG_ALL, server.log, + "System Warning: Failed to tx on ISUP socket: %s :retry %i\n", + strerror(errno),retry); + } + + goto isup_exec_cmd_retry; + } + + return 0; +} + + +static int get_span_chan_from_interface(char* interface, int *span_ptr, int *chan_ptr) +{ + int span, chan; + + if (sscanf(interface, "w%dg%d", &span, &chan) == 2) { + if (smg_validate_span_chan(span,chan) != 0) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "WOOMERA Warning invalid span chan in interface %s\n", + interface); + return -1; + } + + *span_ptr = span; + *chan_ptr = chan; + return 0; + } + log_printf(SMG_LOG_ALL, server.log, "ERROR: Failed to get span chan from interface:[%s]\n", interface, span, chan); + return -1; +} + +static int socket_printf(int socket, char *fmt, ...) +{ + char *data; + int ret = 0; + va_list ap; + + if (socket < 0) { + return -1; + } + + va_start(ap, fmt); +#ifdef SOLARIS + data = (char *) smg_malloc(2048); + vsnprintf(data, 2048, fmt, ap); +#else + ret = vasprintf(&data, fmt, ap); +#endif + va_end(ap); + if (ret == -1) { + fprintf(stderr, "Memory Error\n"); + log_printf(SMG_LOG_ALL, server.log, "Crtical ERROR: Memory Error!\n"); + } else { + int err; + int len = strlen(data); + err=send(socket, data, strlen(data), 0); + if (err != strlen(data)) { + log_printf(SMG_LOG_DEBUG_8, server.log, "ERROR: Failed to send data to woomera socket(%i): err=%i len=%d %s\n", + socket,err,len,strerror(errno)); + ret = err; + } else { + ret = 0; + } + + free(data); + } + + return ret; +} + + + +static int woomera_next_pair(struct woomera_config *cfg, char **var, char **val) +{ + int ret = 0; + char *p, *end; + + *var = *val = NULL; + + for(;;) { + cfg->lineno++; + + if (!fgets(cfg->buf, sizeof(cfg->buf), cfg->file)) { + ret = 0; + break; + } + + *var = cfg->buf; + + if (**var == '[' && (end = strchr(*var, ']'))) { + *end = '\0'; + (*var)++; + strncpy(cfg->category, *var, sizeof(cfg->category) - 1); + continue; + } + + if (**var == '#' || **var == '\n' || **var == '\r') { + continue; + } + + if ((end = strchr(*var, '#'))) { + *end = '\0'; + end--; + } else if ((end = strchr(*var, '\n'))) { + if (*end - 1 == '\r') { + end--; + } + *end = '\0'; + } + + p = *var; + while ((*p == ' ' || *p == '\t') && p != end) { + *p = '\0'; + p++; + } + *var = p; + + if (!(*val = strchr(*var, '='))) { + ret = -1; + log_printf(SMG_LOG_ALL, 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; + +} + + +#if 0 +static void woomera_set_span_chan(struct woomera_interface *woomera, int span, int chan) +{ + pthread_mutex_lock(&woomera->vlock); + woomera->span = span; + woomera->chan = chan; + pthread_mutex_unlock(&woomera->vlock); + +} +#endif + + +static struct woomera_event *new_woomera_event_printf(struct woomera_event *ebuf, char *fmt, ...) +{ + struct woomera_event *event = NULL; + int ret = 0; + va_list ap; + + if (ebuf) { + event = ebuf; + } else if (!(event = new_woomera_event())) { + log_printf(SMG_LOG_ALL, server.log, "Memory Error queuing event!\n"); + return NULL; + } else { + return NULL; + } + + va_start(ap, fmt); +#ifdef SOLARIS + event->data = (char *) smg_malloc(2048); + vsnprintf(event->data, 2048, fmt, ap); +#else + ret = smg_vasprintf(&event->data, fmt, ap); +#endif + va_end(ap); + if (ret == -1) { + log_printf(SMG_LOG_ALL, server.log, "Memory Error queuing event!\n"); + destroy_woomera_event(&event, EVENT_FREE_DATA); + return NULL; + } + + return event; + +} + +static struct woomera_event *woomera_clone_event(struct woomera_event *event) +{ + struct woomera_event *clone; + + if (!(clone = new_woomera_event())) { + return NULL; + } + + /* This overwrites the MALLOC in clone causing a memory leak */ + memcpy(clone, event, sizeof(*event)); + + /* We must set the malloc flag back so that this event + * will be deleted */ + _woomera_set_flag(clone, WFLAG_MALLOC); + clone->next = NULL; + clone->data = smg_strdup(event->data); + + return clone; +} + +static void enqueue_event(struct woomera_interface *woomera, + struct woomera_event *event, + event_args free_data) +{ + struct woomera_event *ptr, *clone = NULL; + + assert(woomera != NULL); + assert(event != NULL); + + if (!(clone = woomera_clone_event(event))) { + log_printf(SMG_LOG_ALL, server.log, "Error Cloning Event\n"); + return; + } + + pthread_mutex_lock(&woomera->queue_lock); + + for (ptr = woomera->event_queue; ptr && ptr->next ; ptr = ptr->next); + + if (ptr) { + ptr->next = clone; + } else { + woomera->event_queue = clone; + } + + pthread_mutex_unlock(&woomera->queue_lock); + + woomera_set_flag(woomera, WFLAG_EVENT); + + if (free_data && event->data) { + /* The event has been duplicated, the original data + * should be freed */ + smg_free(event->data); + event->data=NULL; + } +} + +static char *dequeue_event(struct woomera_interface *woomera) +{ + struct woomera_event *event; + char *data = NULL; + + if (!woomera) { + return NULL; + } + + pthread_mutex_lock(&woomera->queue_lock); + if (woomera->event_queue) { + event = woomera->event_queue; + woomera->event_queue = event->next; + data = event->data; + pthread_mutex_unlock(&woomera->queue_lock); + + destroy_woomera_event(&event, EVENT_KEEP_DATA); + return data; + } + pthread_mutex_unlock(&woomera->queue_lock); + + return data; +} + + +static int enqueue_event_on_listeners(struct woomera_event *event) +{ + struct woomera_listener *ptr; + int x = 0; + + assert(event != NULL); + + pthread_mutex_lock(&server.listen_lock); + for (ptr = server.listeners ; ptr ; ptr = ptr->next) { + enqueue_event(ptr->woomera, event, EVENT_KEEP_DATA); + x++; + } + pthread_mutex_unlock(&server.listen_lock); + + return x; +} + + +static void del_listener(struct woomera_interface *woomera) +{ + struct woomera_listener *ptr, *last = NULL; + + pthread_mutex_lock(&server.listen_lock); + for (ptr = server.listeners ; ptr ; ptr = ptr->next) { + if (ptr->woomera == woomera) { + if (last) { + last->next = ptr->next; + } else { + server.listeners = ptr->next; + } + smg_free(ptr); + break; + } + last = ptr; + } + pthread_mutex_unlock(&server.listen_lock); +} + +static void add_listener(struct woomera_interface *woomera) +{ + struct woomera_listener *new; + + pthread_mutex_lock(&server.listen_lock); + + if ((new = smg_malloc(sizeof(*new)))) { + memset(new, 0, sizeof(*new)); + new->woomera = woomera; + new->next = server.listeners; + server.listeners = new; + } else { + log_printf(SMG_LOG_ALL, server.log, "Memory Error adding listener!\n"); + } + + pthread_mutex_unlock(&server.listen_lock); +} + + + +static int wanpipe_send_dtmf(struct woomera_interface *woomera, char *digits) +{ + struct media_session *ms = woomera_get_ms(woomera); + char *cur = NULL; + int wrote = 0; + int err; + + if (!ms) { + return -EINVAL; + } + + if (!ms->dtmf_buffer) { + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Allocate DTMF Buffer...."); + + err=switch_buffer_create_dynamic(&ms->dtmf_buffer, 1024, server.dtmf_size, 0); + + if (err != 0) { + log_printf(SMG_LOG_ALL, woomera->log, "Failed to allocate DTMF Buffer!\n"); + return -ENOMEM; + } else { + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "SUCCESS!\n"); + } + + } + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Sending DTMF %s\n",digits); + for (cur = digits; *cur; cur++) { + if ((wrote = teletone_mux_tones(&ms->tone_session, + &ms->tone_session.TONES[(int)*cur]))) { + + pthread_mutex_lock(&woomera->dtmf_lock); + + err=switch_buffer_write(ms->dtmf_buffer, ms->tone_session.buffer, wrote * 2); + + pthread_mutex_unlock(&woomera->dtmf_lock); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Sending DTMF %s Wrote=%i (err=%i)\n", + digits,wrote*2,err); + } else { + log_printf(SMG_LOG_ALL, woomera->log, "Error: Sending DTMF %s (err=%i)\n", + digits,wrote); + } + } + + ms->skip_read_frames = 200; + return 0; +} + +static struct woomera_interface *alloc_woomera(void) +{ + struct woomera_interface *woomera = NULL; + + if ((woomera = smg_malloc(sizeof(struct woomera_interface)))) { + + memset(woomera, 0, sizeof(struct woomera_interface)); + + woomera->chan = -1; + woomera->span = -1; + woomera->log = server.log; + woomera->debug = server.debug; + woomera->call_id = 1; + woomera->event_queue = NULL; + woomera->q931_rel_cause_topbx=SIGBOOST_RELEASE_CAUSE_NORMAL; + woomera->q931_rel_cause_tosig=SIGBOOST_RELEASE_CAUSE_NORMAL; + + woomera_set_interface(woomera, "w-1g-1"); + + + + } + + return woomera; + +} + + +static struct woomera_interface *new_woomera_interface(int socket, struct sockaddr_in *sock_addr, int len) +{ + struct woomera_interface *woomera = NULL; + + if (socket < 0) { + log_printf(SMG_LOG_ALL, server.log, "Critical: Invalid Socket on new interface!\n"); + return NULL; + } + + if ((woomera = alloc_woomera())) { + if (socket >= 0) { + no_nagle(socket); + woomera->socket = socket; + } + + if (sock_addr && len) { + memcpy(&woomera->addr, sock_addr, len); + } + } + + return woomera; + +} + +static char *woomera_message_header(struct woomera_message *wmsg, char *key) +{ + int x = 0; + char *value = NULL; + + for (x = 0 ; x < wmsg->last ; x++) { + if (!strcasecmp(wmsg->names[x], key)) { + value = wmsg->values[x]; + break; + } + } + + return value; +} + + +#define SMG_DECODE_POLL_ERRORS(err) \ + (err & POLLERR) ? "POLLERR" : \ + (err & POLLHUP) ? "POLLHUP" : \ + (err & POLLNVAL) ? "POLLNVAL" : "UNKNOWN" + +static int waitfor_socket(int fd, int timeout, int flags, int *flags_out) +{ + struct pollfd pfds[1]; + int res; + int errflags = (POLLERR | POLLHUP | POLLNVAL); + +waitfor_socket_tryagain: + + memset(&pfds[0], 0, sizeof(pfds[0])); + + pfds[0].fd = fd; + pfds[0].events = flags | errflags; + + res = poll(pfds, 1, timeout); + + if (res == 0) { + return res; + } + + if (res > 0) { + res=-1; + *flags_out = pfds[0].revents; + if (pfds[0].revents & errflags) { + res=-1; +#if 0 + log_printf(SMG_LOG_DEBUG_10,server.log, "Wait for socket Error in revents 0x%X %s Error=%s!\n", + pfds[0].revents,strerror(errno),SMG_DECODE_POLL_ERRORS(pfds[0].revents)); +#endif + return res; + } else { + if (pfds[0].revents & flags) { + res=1; + } else { + log_printf(SMG_LOG_ALL,server.log, "Wait for socket invalid poll event in revents 0x%X Error=%s Errno=%s !\n", + pfds[0].revents,SMG_DECODE_POLL_ERRORS(pfds[0].revents),strerror(errno)); + } + } + } else { + if ((errno == EINTR || errno == EAGAIN)) { + goto waitfor_socket_tryagain; + } + log_printf(SMG_LOG_ALL,server.log, "Wait for socket error!\n"); + } + + return res; +} + + +static int waitfor_2sockets(int fda, int fdb, char *a, char *b, int timeout) +{ + struct pollfd pfds[2]; + int res = 0; + int errflags = (POLLERR | POLLHUP | POLLNVAL); + + if (fda < 0 || fdb < 0) { + return -1; + } + + +waitfor_2sockets_tryagain: + + *a=0; + *b=0; + + + memset(pfds, 0, sizeof(pfds)); + + pfds[0].fd = fda; + pfds[1].fd = fdb; + pfds[0].events = POLLIN | errflags; + pfds[1].events = POLLIN | errflags; + + + + res = poll(pfds, 2, timeout); + + if (res > 0) { + res = 1; + if ((pfds[0].revents & errflags) || (pfds[1].revents & errflags)) { + res = -1; + } else { + if ((pfds[0].revents & POLLIN)) { + *a=1; + res++; + } + if ((pfds[1].revents & POLLIN)) { + *b=1; + res++; + } + } + + if (res == 1) { + /* No event found what to do */ + res=-1; + } + } else if (res < 0) { + + if (errno == EINTR || errno == EAGAIN) { + goto waitfor_2sockets_tryagain; + } + + } + + return res; +} + + +static struct media_session *media_session_new(struct woomera_interface *woomera) +{ + struct media_session *ms = NULL; + int x; + char *p; + int span,chan; + + span=woomera->span; + chan=woomera->chan; + + log_printf(SMG_LOG_DEBUG_CALL, server.log,"Starting new MEDIA session [%s] [%s]\n", + woomera->interface,woomera->raw?woomera->raw:"N/A"); + + if ((ms = smg_malloc(sizeof(struct media_session)))) { + memset(ms, 0, sizeof(struct media_session)); + + if (woomera->loop_tdm != 1) { + for(x = 0; x < strlen(woomera->raw) ; x++) { + if (woomera->raw[x] == ':') { + break; + } + if (woomera->raw[x] == '/') { + break; + } + } + + ms->ip = smg_strndup(woomera->raw, x); + time(&ms->started); + p = woomera->raw + (x+1); + ms->port = atoi(p); + } + + time(&ms->started); + woomera_set_ms(woomera,ms); + ms->woomera = woomera; + + /* Setup artificial DTMF stuff */ + memset(&ms->tone_session, 0, sizeof(ms->tone_session)); + if (teletone_init_session(&ms->tone_session, 0, NULL, NULL)) { + log_printf(SMG_LOG_ALL, server.log, "ERROR: Failed to initialize TONE [w%ig%i]!\n", + span+1,chan+1); + } + + ms->tone_session.rate = SMG_DTMF_RATE; + ms->tone_session.duration = server.dtmf_on * (ms->tone_session.rate / 1000); + ms->tone_session.wait = server.dtmf_off * (ms->tone_session.rate / 1000); + + teletone_dtmf_detect_init (&ms->dtmf_detect, SMG_DTMF_RATE); + + } else { + log_printf(SMG_LOG_ALL, server.log, "ERROR: Memory Alloc Failed [w%ig%i]!\n", + span+1,chan+1); + } + + return ms; +} + +static void media_session_free(struct media_session *ms) +{ + if (ms->ip) { + smg_free(ms->ip); + } + + teletone_destroy_session(&ms->tone_session); + switch_buffer_destroy(&ms->dtmf_buffer); + + ms->woomera = NULL; + + smg_free(ms); +} + + +static int create_udp_socket(struct media_session *ms, 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; + + log_printf(SMG_LOG_DEBUG_9,server.log,"LocalIP %s:%d IP %s:%d \n",local_ip, local_port, ip, port); + + memset(&ms->remote_hp, 0, sizeof(ms->remote_hp)); + memset(&ms->local_hp, 0, sizeof(ms->local_hp)); + if ((ms->socket = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) { + gethostbyname_r(ip, &ms->remote_hp, buf, sizeof(buf), &result, &err); + gethostbyname_r(local_ip, &ms->local_hp, local_buf, sizeof(local_buf), &local_result, &err); + if (result && local_result) { + ms->remote_addr.sin_family = ms->remote_hp.h_addrtype; + memcpy((char *) &ms->remote_addr.sin_addr.s_addr, ms->remote_hp.h_addr_list[0], ms->remote_hp.h_length); + ms->remote_addr.sin_port = htons(port); + + ms->local_addr.sin_family = ms->local_hp.h_addrtype; + memcpy((char *) &ms->local_addr.sin_addr.s_addr, ms->local_hp.h_addr_list[0], ms->local_hp.h_length); + ms->local_addr.sin_port = htons(local_port); + + rc = bind(ms->socket, (struct sockaddr *) &ms->local_addr, sizeof(ms->local_addr)); + if (rc < 0) { + close(ms->socket); + ms->socket = -1; + + log_printf(SMG_LOG_DEBUG_9,server.log, + "Failed to bind LocalIP %s:%d IP %s:%d (%s)\n", + local_ip, local_port, ip, port,strerror(errno)); + } + + /* OK */ + + } else { + log_printf(SMG_LOG_ALL,server.log, + "Failed to get hostbyname LocalIP %s:%d IP %s:%d (%s)\n", + local_ip, local_port, ip, port,strerror(errno)); + } + } else { + log_printf(SMG_LOG_ALL,server.log, + "Failed to create/allocate UDP socket\n"); + } + + return ms->socket; +} + +static int next_media_port(void) +{ + int port; + + pthread_mutex_lock(&server.media_udp_port_lock); + port = ++server.next_media_port; + if (port > server.max_media_port) { + server.next_media_port = server.base_media_port; + port = server.base_media_port; + } + pthread_mutex_unlock(&server.media_udp_port_lock); + + return port; +} + + + +static int woomera_dtmf_transmit(struct media_session *ms, int mtu) +{ + struct woomera_interface *woomera = ms->woomera; + int bread; + unsigned char dtmf[1024]; + unsigned char dtmf_law[1024]; + sangoma_api_hdr_t hdrframe; + int i; + int slin_len = mtu*2; + short *data; + int used; + int res; + int err; + int txdtmf=0; + int flags_out; + memset(&hdrframe,0,sizeof(hdrframe)); + + if (!ms->dtmf_buffer) { + return -1; + } + + for (;;) { + + if ((used=switch_buffer_inuse(ms->dtmf_buffer)) <= 0) { + break; + } + + res = waitfor_socket(ms->sangoma_sock, -1, POLLOUT, &flags_out); + if (res <= 0) { + break; + } + +#ifdef CODEC_LAW_DEFAULT + + pthread_mutex_lock(&woomera->dtmf_lock); + if ((used=switch_buffer_inuse(ms->dtmf_buffer)) <= 0) { + pthread_mutex_unlock(&woomera->dtmf_lock); + break; + } + + bread = switch_buffer_read(ms->dtmf_buffer, dtmf, slin_len); + pthread_mutex_unlock(&woomera->dtmf_lock); + + if (bread <= 0) { + break; + } + + log_printf(SMG_LOG_DEBUG_MISC,woomera->log,"%s: Write DTMF Got %d bytes MTU=%i Coding=%i Used=%i\n", + woomera->interface,bread,mtu,ms->hw_coding,used); + + data=(short*)dtmf; + for (i=0;ihw_coding) { + /* ALAW */ + dtmf_law[i] = linear_to_alaw((int)data[i]); + } else { + /* ULAW */ + dtmf_law[i] = linear_to_ulaw((int)data[i]); + } + } + + err=sangoma_sendmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + dtmf_law, mtu, 0); + + if (err != mtu) { + log_printf(SMG_LOG_ALL, woomera->log, "Error: Failed to TX to TDM API on DTMF (err=%i mtu=%i)!\n",err,mtu); + } + + txdtmf++; + ms->skip_write_frames++; +#else +... + pthread_mutex_lock(&woomera->dtmf_lock); + bread = switch_buffer_read(ms->dtmf_buffer, dtmf, mtu); + pthread_mutex_unlock(&woomera->dtmf_lock); + + log_printf(SMG_LOG_DEBUG_MISC,woomera->log,"%s: Write DTMF Got %d bytes\n", + woomera->interface,bread); + + sangoma_sendmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + dtmf, mtu, 0); + txdtmf++; + ms->skip_write_frames++; +#endif + + } + + if (txdtmf) { + return 0; + } else { + return -1; + } +} + +static void media_loop_run(struct media_session *ms) +{ + struct woomera_interface *woomera = ms->woomera; + int sangoma_frame_len = 160; + int errs=0; + int res=0; + wanpipe_tdm_api_t tdm_api; + unsigned char circuit_frame[1024]; + char filename[100]; + FILE *filed=NULL; + int loops=0,flags_out=0; + int open_cnt = 0; + + open_cnt=0; + + sangoma_api_hdr_t hdrframe; + memset(&hdrframe,0,sizeof(hdrframe)); + memset(circuit_frame,0,sizeof(circuit_frame)); + +retry_loop: + ms->sangoma_sock = open_span_chan(woomera->span+1, woomera->chan+1); + + log_printf(SMG_LOG_PROD, server.log, "Media Loop Started %s fd=%i\n", + woomera->interface,ms->sangoma_sock); + + if (ms->sangoma_sock < 0) { + errs++; + if (errs < 5) { + usleep(500000); + goto retry_loop; + } + log_printf(SMG_LOG_ALL, server.log, "WANPIPE MEDIA Socket Error (%s) if=[%s] [w%ig%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); + + } else { + errs=0; + + if (sangoma_tdm_set_codec(ms->sangoma_sock, &tdm_api, WP_NONE) < 0 ) { + errs++; + } + + if (sangoma_tdm_flush_bufs(ms->sangoma_sock, &tdm_api)) { + errs++; + } + + if (sangoma_tdm_set_usr_period(ms->sangoma_sock, &tdm_api, 20) < 0 ) { + errs++; + } + + sangoma_frame_len = sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); + + sangoma_tdm_disable_hwec(ms->sangoma_sock,&tdm_api); + ms->oob_disable = 0; +#ifdef LIBSANGOMA_VERSION + open_cnt = sangoma_get_open_cnt(ms->sangoma_sock, &tdm_api); + if (open_cnt > 1) { + ms->oob_disable = 1; + } +#endif + } + + if (errs) { + log_printf(SMG_LOG_ALL, server.log, "Media Loop: failed to open tdm device %s\n", + woomera->interface); + return; + } + + if (server.loop_trace) { + sprintf(filename,"/smg/w%ig%i-loop.trace",woomera->span+1,woomera->chan+1); + unlink(filename); + filed = safe_fopen(filename, "w"); + } + + while ( woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + !woomera_test_flag(woomera, WFLAG_MEDIA_END) && + ((res = waitfor_socket(ms->sangoma_sock, 1000, POLLIN, &flags_out)) >= 0)) { + + if (res == SMG_SOCKET_EVENT_TIMEOUT) { + //log_printf(SMG_LOG_DEBUG_8, server.log, "%s: TDM UDP Timeout !!!\n", + // woomera->interface); + /* NENAD Timeout thus just continue */ + continue; + } + + res = sangoma_readmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + circuit_frame, + sizeof(circuit_frame), 0); + if (res < 0) { + log_printf(SMG_LOG_ALL, server.log, "TDM Loop ReadMsg Error: %s\n", + strerror(errno), woomera->interface); + break; + } + + if (server.loop_trace && filed != NULL) { + int i; + for (i=0;isangoma_sock, + &hdrframe, + sizeof(hdrframe), + circuit_frame, + res, 0); + + res=0; + + loops++; + } + + + if (res < 0) { + if (!woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + log_printf(SMG_LOG_ALL, server.log, "Media Loop: socket error %s (fd=%i) %s\n", + woomera->interface, ms->sangoma_sock, strerror(errno)); + } + } + + + if (server.loop_trace && filed != NULL) { + fclose(filed); + } + + sangoma_tdm_enable_hwec(ms->sangoma_sock,&tdm_api); + + close_span_chan(&ms->sangoma_sock, woomera->span+1, woomera->chan+1); + + if (loops < 1) { + log_printf(SMG_LOG_ALL, server.log, "Media Loop FAILED %s Master=%i MediaEnd=%i Loops=%i\n", + woomera->interface, + woomera_test_flag(&server.master_connection, WFLAG_RUNNING), + woomera_test_flag(woomera, WFLAG_MEDIA_END),loops); + } else { + log_printf(SMG_LOG_PROD, server.log, "Media Loop PASSED %s Master=%i MediaEnd=%i Loops=%i\n", + woomera->interface, + woomera_test_flag(&server.master_connection, WFLAG_RUNNING), + woomera_test_flag(woomera, WFLAG_MEDIA_END),loops); + } + + return; + +} + + + + +#ifdef WP_HPTDM_API +static int media_rx_ready(void *p, unsigned char *data, int len) +{ + struct media_session *ms = (struct media_session *)p; + + if (ms->udp_sock < 0) { + return -1; + } + + return sendto(ms->udp_sock, + data,len, 0, + (struct sockaddr *) &ms->remote_addr, + sizeof(ms->remote_addr)); + + +} +#endif + +static void *media_thread_run(void *obj) +{ + struct media_session *ms = obj; + struct woomera_interface *woomera = ms->woomera; + int sangoma_frame_len = 160; + int local_port, x = 0, errs = 0, res = 0, packet_len = 0; + //int udp_cnt=0; + struct woomera_event wevent; + wanpipe_tdm_api_t tdm_api; + FILE *tx_fd=NULL; + int sock_timeout=200; + int hwec_reenable=0; + int open_cnt = 0; + + open_cnt=0; + + if (woomera_test_flag(woomera, WFLAG_MEDIA_END) || + !woomera->interface || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "MEDIA session for [%s] Cancelled! (ptr=%p)\n", + woomera->interface,woomera); + /* In this case the call will be closed via woomera_thread_run + * function. And the process table will be cleard there */ + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_MEDIA_RUNNING); + media_session_free(ms); + pthread_exit(NULL); + return NULL; + } + + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA session for [%s] started (ptr=%p loop=%i)\n", + woomera->interface,woomera,woomera->loop_tdm); + + if (woomera->loop_tdm) { + media_loop_run(ms); + ms->udp_sock=-1; + woomera_set_flag(woomera, WFLAG_HANGUP); + woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + goto media_thread_exit; + } + + for(x = 0; x < 1000 ; x++) { + local_port = next_media_port(); + if ((ms->udp_sock = create_udp_socket(ms, server.media_ip, local_port, ms->ip, ms->port)) > -1) { + break; + } + } + + /* Normal Temporary Failure */ + woomera->q931_rel_cause_topbx = 41; + + if (ms->udp_sock < 0) { + log_printf(SMG_LOG_ALL, server.log, "UDP Socket Error (%s) [%s] LocalPort=%d\n", + strerror(errno), woomera->interface, local_port); + + errs++; + } else { + int media_retry_cnt=0; +#ifdef WP_HPTDM_API + hp_tdm_api_span_t *span=hptdmspan[woomera->span+1]; + if (!span || !span->init) { + errs++; + } else { + hp_tdm_api_usr_callback_t usr_callback; + memset(&usr_callback,0,sizeof(usr_callback)); + usr_callback.p = ms; + usr_callback.rx_avail = media_rx_ready; + if (span->open_chan(span, &usr_callback, &ms->tdmchan,woomera->chan+1)) { + errs++; + /* Switch Congestion */ + woomera->q931_rel_cause_topbx = 42; + } + } +#else +media_retry: + ms->sangoma_sock = open_span_chan(woomera->span+1, woomera->chan+1); + if (ms->sangoma_sock < 0) { + + if (!woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + media_retry_cnt++; + if (media_retry_cnt < 5) { + log_printf(SMG_LOG_ALL, server.log, "WANPIPE Socket Retry [w%ig%i]\n", + woomera->span+1, woomera->chan+1); + usleep(100000); + goto media_retry; + } + + log_printf(SMG_LOG_ALL, server.log, "WANPIPE Socket Error (%s) if=[%s] [w%ig%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); + + /* Switch Congestion */ + woomera->q931_rel_cause_topbx = 42; + } + + errs++; + } else { + +# ifdef CODEC_LAW_DEFAULT + if (sangoma_tdm_set_codec(ms->sangoma_sock, &tdm_api, WP_NONE) < 0 ) { + errs++; + } +# else + if (sangoma_tdm_set_codec(ms->sangoma_sock, &tdm_api, WP_SLINEAR) < 0 ) { + errs++; + } +# endif + if (sangoma_tdm_flush_bufs(ms->sangoma_sock, &tdm_api)) { + errs++; + } + + if (sangoma_tdm_set_usr_period(ms->sangoma_sock, &tdm_api, 20) < 0 ) { + errs++; + } + +# ifdef CODEC_LAW_DEFAULT +# ifdef LIBSANGOMA_GET_HWCODING + ms->hw_coding=sangoma_tdm_get_hw_coding(ms->sangoma_sock, &tdm_api); + if (ms->hw_coding < 0) { + errs++; + } +# else +# error "libsangoma missing hwcoding feature: not up to date!" +# endif +# endif + + +# ifdef LIBSANGOMA_GET_HWDTMF + ms->hw_dtmf=sangoma_tdm_get_hw_dtmf(ms->sangoma_sock, &tdm_api); + if (ms->hw_dtmf) { + log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Supported [w%ig%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); + } else { + log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Not Supported [w%ig%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); + } +#else + ms->hw_dtmf=0; + log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Not Supported [w%ig%i]\n", + strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); +# warning "libsangoma missing hwdtmf feature: not up to date!" + +#endif + + ms->oob_disable = 0; +#ifdef LIBSANGOMA_VERSION + open_cnt = sangoma_get_open_cnt(ms->sangoma_sock, &tdm_api); + if (open_cnt > 1) { + ms->oob_disable = 1; + } +#endif + if (!bearer_cap_is_audio(woomera->bearer_cap)) { + int err; + err=sangoma_tdm_disable_hwec(ms->sangoma_sock, &tdm_api); + if (err == 0) { + hwec_reenable=1; + log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] Disabling hwec Ok\n",woomera->interface); + } else { + log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] Disabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + } + + } + + sangoma_frame_len = sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); + + } +#endif + } + + + +#ifdef WP_HPTDM_API + /* No tdm thread */ +#else + if (!errs && + launch_media_tdm_thread(woomera)) { + errs++; + } +#endif + + if (errs) { + + woomera->q931_rel_cause_topbx = 42; + + } else { + + unsigned char udp_frame[4096]; + unsigned int fromlen = sizeof(struct sockaddr_in); + int flags_out=0; + + sangoma_api_hdr_t hdrframe; + memset(&hdrframe,0,sizeof(hdrframe)); + memset(udp_frame,0,sizeof(udp_frame)); +#ifdef DOTRACE + int fdin, fdout; + char path_in[512], path_out[512]; +#endif + + new_woomera_event_printf(&wevent, + "EVENT MEDIA %s AUDIO%s" + "Unique-Call-Id: %s%s" + "Raw-Audio: %s:%d%s" + "Call-ID: %s%s" + "Raw-Format: PCM-16%s" + "DTMF: %s%s" + , + woomera->interface, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + server.media_ip, + local_port, + WOOMERA_LINE_SEPERATOR, + woomera->interface, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + (ms->hw_dtmf)? "OutofBand": "InBand", + + WOOMERA_RECORD_SEPERATOR + ); + + + enqueue_event(woomera, &wevent, EVENT_FREE_DATA); + +#ifdef DOTRACE + sprintf(path_in, "/tmp/debug-in.%d.raw", tc); + sprintf(path_out, "/tmp/debug-out.%d.raw", tc++); + fdin = open(path_in, O_WRONLY | O_CREAT, O_TRUNC, 0600); + fdout = open(path_out, O_WRONLY | O_CREAT, O_TRUNC, 0600); +#endif + + if (server.out_tx_test) { + tx_fd=fopen("/smg/sound.raw","rb"); + if (!tx_fd) { + log_printf(SMG_LOG_ALL,server.log, "FAILED TO OPEN Sound file!\n"); + } + } + + + for (;;) { + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) || + woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + res=0; + break; + } + + res = waitfor_socket(ms->udp_sock, sock_timeout, POLLIN, &flags_out); + + if (res < 0) { + break; + } + + if (res == 0) { + + if (woomera_dtmf_transmit(ms,sangoma_frame_len) == 0) { + sock_timeout=(sangoma_frame_len/codec_sample); + } else { + sock_timeout=200; + } + + if (ms->skip_write_frames > 0) { + ms->skip_write_frames--; + } + + log_printf(SMG_LOG_DEBUG_8, server.log, "%s: UDP Sock Timeout !!!\n", + woomera->interface); + /* NENAD Timeout thus just continue */ + continue; + } + + if ((packet_len = recvfrom(ms->udp_sock, udp_frame, sizeof(udp_frame), + MSG_DONTWAIT, (struct sockaddr *) &ms->local_addr, &fromlen)) < 1) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "UDP Recv Error: %s\n",strerror(errno)); + break; + } + + +#ifdef SMG_DROP_SEQ + if (drop_seq) { + log_printf(SMG_LOG_ALL,server.log,"Dropping TX Sequence! %i\n",drop_seq); + drop_seq--; + continue; + } +#endif + +#if 0 + log_printf(SMG_LOG_DEBUG_10, server.log, "%s: UDP Receive %i !!!\n", + woomera->interface,packet_len); +#endif + + if (packet_len > 0) { + +#if 0 +/* NC: This can cause skb_over panic must be retested */ + if (packet_len != sangoma_frame_len && ms->udp_sync_cnt <= 5) { + /* Assume that we will always receive SLINEAR here */ + sangoma_tdm_set_usr_period(ms->sangoma_sock, + &tdm_api, packet_len/codec_sample); + sangoma_frame_len = + sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); + + log_printf(SMG_LOG_DEBUG_MISC, server.log, + "%s: UDP TDM Period ReSync to Len=%i %ims (udp=%i) \n", + woomera->interface,sangoma_frame_len, + sangoma_frame_len/codec_sample,packet_len); + + + if (++ms->udp_sync_cnt >= 6) { + sangoma_tdm_set_usr_period(ms->sangoma_sock, + &tdm_api, 20); + sangoma_frame_len = + sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); + log_printf(SMG_LOG_ALL, server.log, + "%s: UDP TDM Period Force ReSync to 20ms \n", + woomera->interface); + } + + } +#endif + if (!server.out_tx_test) { + + if (woomera_dtmf_transmit(ms,sangoma_frame_len) == 0) { + sock_timeout=(sangoma_frame_len/codec_sample); + /* For sanity sake if we are doing the out test + * dont take any chances force tx udp data */ + if (ms->skip_write_frames > 0) { + ms->skip_write_frames--; + } + continue; + } else { + sock_timeout=200; + } + + if (ms->skip_write_frames > 0) { + ms->skip_write_frames--; + continue; + } + + } + + if (server.out_tx_test && tx_fd && + fread((void*)udp_frame, + sizeof(char), + packet_len,tx_fd) <= 0) { + + sangoma_get_full_cfg(ms->sangoma_sock,&tdm_api); + fclose(tx_fd); + tx_fd=NULL; + } + +#ifdef WP_HPTDM_API + if (ms->tdmchan->push) { + ms->tdmchan->push(ms->tdmchan,udp_frame,packet_len); + } +#else + + sangoma_sendmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + udp_frame, + packet_len, 0); +#endif + + } + +#if 0 + if (woomera->span == 1 && woomera->chan == 1) { + udp_cnt++; + if (udp_cnt && udp_cnt % 1000 == 0) { + log_printf(SMG_LOG_ALL, server.log, "%s: MEDIA UDP TX RX CNT %i %i\n", + woomera->interface,udp_cnt,packet_len); + } + } +#endif + } + + if (res < 0) { + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) || + woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + res=0; + } else { + log_printf(SMG_LOG_ALL, server.log, "Media Thread: socket error %s SockID=%i %s Poll=%s!\n", + woomera->interface,ms->sangoma_sock,strerror(errno),SMG_DECODE_POLL_ERRORS(flags_out)); + } + } + } + + new_woomera_event_printf(&wevent, + "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Start-Time: %ld%s" + "End-Time: %ld%s" + "Answer-Time: %ld%s" + "Call-ID: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + woomera->interface, + WOOMERA_LINE_SEPERATOR, + + woomera->session, + WOOMERA_LINE_SEPERATOR, + + time(&ms->started), + WOOMERA_LINE_SEPERATOR, + + time(NULL), + WOOMERA_LINE_SEPERATOR, + + time(&ms->answered), + WOOMERA_LINE_SEPERATOR, + + woomera->interface, + WOOMERA_LINE_SEPERATOR, + + q931_rel_to_str(woomera->q931_rel_cause_topbx), + WOOMERA_LINE_SEPERATOR, + + woomera->q931_rel_cause_topbx, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + +media_thread_exit: + + if (hwec_reenable) { + int err; + err=sangoma_tdm_enable_hwec(ms->sangoma_sock, &tdm_api); + if (err==0) { + log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] Re-enabling hwec ok\n",woomera->interface); + } else { + log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] Re-enabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + } + } + + if (woomera_test_flag(woomera, WFLAG_MEDIA_TDM_RUNNING)) { + woomera_set_flag(woomera, WFLAG_MEDIA_END); + + /* Dont wait for the other thread */ + close_socket(&ms->udp_sock); + close_span_chan(&ms->sangoma_sock, woomera->span+1, woomera->chan+1); + while(woomera_test_flag(woomera, WFLAG_MEDIA_TDM_RUNNING)) { + usleep(1000); + sched_yield(); + } + } + + + close_socket(&ms->udp_sock); + close_span_chan(&ms->sangoma_sock, woomera->span+1, woomera->chan+1); + + if (tx_fd){ + fclose(tx_fd); + tx_fd=NULL; + } + + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + + woomera_set_ms(woomera,NULL); + woomera_clear_flag(woomera, WFLAG_MEDIA_RUNNING); + + media_session_free(ms); + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA session for [%s] ended (ptr=%p)\n", + woomera->interface,woomera); + + + pthread_exit(NULL); + return NULL; +} + + + + +static void *media_tdm_thread_run(void *obj) +{ + wanpipe_tdm_api_t tdm_api; + wp_tdm_api_event_t *rx_event; + + struct media_session *ms = obj; + struct woomera_interface *woomera = ms->woomera; + int res = 0; + unsigned char circuit_frame[1024]; + sangoma_api_hdr_t hdrframe; + int flags_out; + int poll_opt = POLLIN | POLLPRI; + + memset(&hdrframe,0,sizeof(hdrframe)); + memset(circuit_frame,0,sizeof(circuit_frame)); + + memset(&tdm_api, 0, sizeof(wanpipe_tdm_api_t)); + + if (woomera_test_flag(woomera, WFLAG_MEDIA_END) || !woomera->interface) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA TDM session for [%s] Cancelled! (ptr=%p)\n", + woomera->interface,woomera); + /* In this case the call will be closed via woomera_thread_run + * function. And the process table will be cleard there */ + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + pthread_exit(NULL); + return NULL; + } + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA TDM session for [%s] started (ptr=%p)\n", + woomera->interface,woomera); + + + for (;;) { + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) || + woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + res=0; + break; + } + + if (ms->oob_disable) { + poll_opt = POLLIN; + } else { + poll_opt = POLLIN | POLLPRI; + } + res = waitfor_socket(ms->sangoma_sock, 1000, poll_opt, &flags_out); + + + if (res < 0) { + break; + } + + if (res == 0) { +#if 0 + log_printf(SMG_LOG_DEBUG_8, server.log, "%s: TDM UDP Timeout !!!\n", + woomera->interface); + /* NENAD Timeout thus just continue */ +#endif + continue; + } + + + if (flags_out & POLLIN) { + + res = sangoma_readmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + circuit_frame, + sizeof(circuit_frame), 0); + + if (res < 0) { + if (!woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + log_printf(SMG_LOG_ALL, server.log, "TDM Read Data Error: %s %s Sockid=%i\n", + woomera->interface, + strerror(errno),ms->sangoma_sock); + } + break; + } + + res = sendto(ms->udp_sock, + circuit_frame, + res, 0, + (struct sockaddr *) &ms->remote_addr, + sizeof(ms->remote_addr)); + + if (res < 0) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "UDP Sento Error: %s\n", strerror(errno)); + + } + } + + if (flags_out & POLLPRI) { + + res = sangoma_tdm_read_event(ms->sangoma_sock, &tdm_api); + if (res < 0) { + log_printf(SMG_LOG_ALL, server.log, "TDM Read Event Error: %s %s Sockid=%i\n", + woomera->interface, + strerror(errno),ms->sangoma_sock); + break; + } + + rx_event = &tdm_api.wp_tdm_cmd.event; + + switch (rx_event->wp_tdm_api_event_type){ +# ifdef LIBSANGOMA_GET_HWDTMF + case WP_TDMAPI_EVENT_DTMF: + + /* Only handle hw dtmf if hw_dtmf option is enabled */ + if (!ms->hw_dtmf) { + break; + } + + /* PORT_SOUT = 1 */ + if (rx_event->wp_tdm_api_event_dtmf_port == WAN_EC_CHANNEL_PORT_SOUT && + rx_event->wp_tdm_api_event_dtmf_type == WAN_EC_TONE_PRESENT) { + + handle_event_dtmf(woomera, rx_event->wp_tdm_api_event_dtmf_digit); + } + break; +#endif + default: + log_printf(SMG_LOG_ALL, server.log, "TDM API Unknown OOB Event %i\n", + rx_event->wp_tdm_api_event_type); + break; + } + } + +#if 0 + if (woomera->span == 1 && woomera->chan == 1) { + tdm_cnt++; + if (tdm_cnt && tdm_cnt % 1000 == 0) { + log_printf(SMG_LOG_ALL, server.log, "%s: MEDIA TDM TX RX CNT %i %i\n", + woomera->interface,tdm_cnt,res); + } + } +#endif + + } + + if (res < 0) { + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) || + woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + + /* Good reason to exit */ + + } else { + + log_printf(SMG_LOG_ALL, server.log, "Media TDM Thread: socket error %s Sockid=%i %s Woomera Flags=0x%08X Poll=%s!\n", + woomera->interface,ms->sangoma_sock,strerror(errno),woomera->flags,SMG_DECODE_POLL_ERRORS(flags_out)); + woomera_print_flags(woomera,0); + } + } + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA TDM session for [%s] ended (ptr=%p)\n", + woomera->interface,woomera); + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + + pthread_exit(NULL); + return NULL; + +} + + +/* This function must be called with process_lock + * because it modifies shared process_table */ + +static int launch_media_thread(struct woomera_interface *woomera) +{ + pthread_attr_t attr; + int result = -1; + struct media_session *ms; + + if ((ms = media_session_new(woomera))) { + result = pthread_attr_init(&attr); + //pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + //pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + woomera_set_flag(woomera, WFLAG_MEDIA_RUNNING); + result = pthread_create(&ms->thread, &attr, media_thread_run, ms); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Thread! %s\n", + __FUNCTION__,strerror(errno)); + woomera_clear_flag(woomera, WFLAG_MEDIA_RUNNING); + media_session_free(woomera->ms); + + } + pthread_attr_destroy(&attr); + + } else { + log_printf(SMG_LOG_ALL, server.log, "Failed to start new media session\n"); + } + + return result; + +} + +static int launch_media_tdm_thread(struct woomera_interface *woomera) +{ + pthread_attr_t attr; + int result = -1; + struct media_session *ms = woomera_get_ms(woomera); + + if (!ms) { + return result; + } + + result = pthread_attr_init(&attr); + //pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + //pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + woomera_set_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + result = pthread_create(&ms->thread, &attr, media_tdm_thread_run, ms); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Thread! %s\n", + __FUNCTION__,strerror(errno)); + woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING); + } + pthread_attr_destroy(&attr); + + return result; +} + + +static struct woomera_interface * launch_woomera_loop_thread(short_signal_event_t *event) +{ + + struct woomera_interface *woomera = NULL; + char callid[20]; + + sprintf(callid, "w%dg%d", event->span+1,event->chan+1); + + if ((woomera = alloc_woomera())) { + + woomera->chan = event->chan; + woomera->span = event->span; + woomera->log = server.log; + woomera->debug = server.debug; + woomera->call_id = 1; + woomera->event_queue = NULL; + woomera->loop_tdm=1; + woomera->socket=-1; + + } else { + log_printf(SMG_LOG_ALL, server.log, "Critical ERROR: memory/socket error\n"); + return NULL; + } + + woomera_set_interface(woomera,callid); + + pthread_mutex_lock(&server.process_lock); + server.process_table[event->span][event->chan].dev = woomera; + pthread_mutex_unlock(&server.process_lock); + + if (launch_woomera_thread(woomera)) { + pthread_mutex_lock(&server.process_lock); + server.process_table[event->span][event->chan].dev = NULL; + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + smg_free(woomera); + log_printf(SMG_LOG_ALL, server.log, "Critical ERROR: memory/socket error\n"); + return NULL; + } + + return woomera; +} + +static int woomera_message_parse(struct woomera_interface *woomera, struct woomera_message *wmsg, int timeout) +{ + char *cur, *cr, *next = NULL, *eor = NULL; + char buf[2048]; + int res = 0, bytes = 0, sanity = 0; + struct timeval started, ended; + int elapsed, loops = 0; + int failto = 0; + int packet = 0; + int flags_out = 0; + + memset(wmsg, 0, sizeof(*wmsg)); + + if (woomera->socket < 0 ) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, WOOMERA_DEBUG_PREFIX "%s Invalid Socket! %d\n", + woomera->interface,woomera->socket); + return -1; + } + + if (woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_DEBUG_9, woomera->log, WOOMERA_DEBUG_PREFIX + "%s Woomera Message parse: Call Hangup - skipping message parse !\n", + woomera->interface); + return -1; + } + + gettimeofday(&started, NULL); + memset(buf, 0, sizeof(buf)); + + if (timeout < 0) { + timeout = abs(timeout); + failto = 1; + } else if (timeout == 0) { + timeout = -1; + } + + + while (!(eor = strstr(buf, WOOMERA_RECORD_SEPERATOR))) { + if (sanity > 1000) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, WOOMERA_DEBUG_PREFIX "%s Failed Sanity Check!\n[%s]\n\n", woomera->interface, buf); + return -1; + } + + if ((res = waitfor_socket(woomera->socket, 1000, POLLIN, &flags_out) > 0)) { + + res = recv(woomera->socket, buf, sizeof(buf), MSG_PEEK); + + if (res > 1) { + packet++; + } + if (!strncmp(buf, WOOMERA_LINE_SEPERATOR, 2)) { + res = read(woomera->socket, buf, 2); + return 0; + } + if (res == 0) { + sanity++; + /* Looks Like it's time to go! */ + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_DEBUG_9, woomera->log, WOOMERA_DEBUG_PREFIX + "%s MEDIA END or HANGUP \n", woomera->interface); + return -1; + } + ysleep(1000); + continue; + + } else if (res < 0) { + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, WOOMERA_DEBUG_PREFIX + "%s error during packet retry (err=%i) Loops#%d (%s)\n", + woomera->interface, res, loops, + strerror(errno)); + return res; + } else if (loops) { + ysleep(100000); + } + } + + gettimeofday(&ended, NULL); + elapsed = (((ended.tv_sec * 1000) + ended.tv_usec / 1000) - ((started.tv_sec * 1000) + started.tv_usec / 1000)); + + if (res < 0) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, WOOMERA_DEBUG_PREFIX "%s Bad RECV\n", + woomera->interface); + return res; + } else if (res == 0) { + sanity++; + /* Looks Like it's time to go! */ + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + woomera_test_flag(woomera, WFLAG_MEDIA_END) || + woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_DEBUG_9, woomera->log, WOOMERA_DEBUG_PREFIX + "%s MEDIA END or HANGUP \n", woomera->interface); + return -1; + } + ysleep(1000); + continue; + } + + if (packet && loops > 150) { + log_printf(SMG_LOG_PROD, woomera->log, WOOMERA_DEBUG_PREFIX + "%s Timeout waiting for packet.\n", + woomera->interface); + return -1; + } + + if (timeout > 0 && (elapsed > timeout)) { + log_printf(SMG_LOG_PROD, woomera->log, WOOMERA_DEBUG_PREFIX + "%s Timeout [%d] reached\n", + woomera->interface, timeout); + return failto ? -1 : 0; + } + + if (woomera_test_flag(woomera, WFLAG_EVENT)) { + /* BRB! we have an Event to deliver....*/ + return 0; + } + + /* what're we still doing here? */ + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) || + !woomera_test_flag(woomera, WFLAG_RUNNING)) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, WOOMERA_DEBUG_PREFIX + "%s Woomera Message Parse: Server or Woomera not Running\n", woomera->interface); + return -1; + } + loops++; + } + + *eor = '\0'; + bytes = strlen(buf) + 4; + + memset(buf, 0, sizeof(buf)); + res = read(woomera->socket, buf, bytes); + next = buf; + + log_printf(SMG_LOG_WOOMERA, woomera->log, "%s:WOOMERA RX MSG: %s\n",woomera->interface,buf); + + while ((cur = next)) { + + if ((cr = strstr(cur, WOOMERA_LINE_SEPERATOR))) { + *cr = '\0'; + next = cr + (sizeof(WOOMERA_LINE_SEPERATOR) - 1); + if (!strcmp(next, WOOMERA_RECORD_SEPERATOR)) { + break; + } + } + if (!cur || !*cur) { + break; + } + + if (!wmsg->last) { + char *cmd, *id, *args; + woomera_set_flag(wmsg, MFLAG_EXISTS); + cmd = cur; + + if ((id = strchr(cmd, ' '))) { + *id = '\0'; + id++; + if ((args = strchr(id, ' '))) { + *args = '\0'; + args++; + strncpy(wmsg->command_args, args, sizeof(wmsg->command_args)-1); + } + strncpy(wmsg->callid, id, sizeof(wmsg->callid)-1); + } + + strncpy(wmsg->command, cmd, sizeof(wmsg->command)-1); + } else { + char *name, *val; + name = cur; + + if ((val = strchr(name, ':'))) { + *val = '\0'; + val++; + while (*val == ' ') { + *val = '\0'; + val++; + } + strncpy(wmsg->values[wmsg->last-1], val, WOOMERA_STRLEN); + } + strncpy(wmsg->names[wmsg->last-1], name, WOOMERA_STRLEN); + if (name && val && !strcasecmp(name, "content-type")) { + woomera_set_flag(wmsg, MFLAG_CONTENT); + bytes = atoi(val); + } + if (name && val && !strcasecmp(name, "content-length")) { + woomera_set_flag(wmsg, MFLAG_CONTENT); + bytes = atoi(val); + } + + + } + wmsg->last++; + } + + wmsg->last--; + + if (bytes && woomera_test_flag(wmsg, MFLAG_CONTENT)) { + int terr; + terr=read(woomera->socket, wmsg->body, + (bytes > sizeof(wmsg->body)) ? sizeof(wmsg->body) : bytes); + } + + return woomera_test_flag(wmsg, MFLAG_EXISTS); + +} + +static struct woomera_interface *pull_from_holding_tank(int index, int span , int chan) +{ + struct woomera_interface *woomera = NULL; + + if (index < 1 || index >= CORE_TANK_LEN) { + if (index != 0) { + log_printf(SMG_LOG_ALL, server.log, "%s Error on invalid TANK INDEX = %i\n", + __FUNCTION__,index); + } + return NULL; + } + + pthread_mutex_lock(&server.ht_lock); + if (server.holding_tank[index] && + server.holding_tank[index] != &woomera_dead_dev) { + woomera = server.holding_tank[index]; + + /* Block this index until the call is completed */ + server.holding_tank[index] = &woomera_dead_dev; + + woomera->index = 0; + woomera->span=span; + woomera->chan=chan; + } + pthread_mutex_unlock(&server.ht_lock); + + return woomera; +} + +static void clear_all_holding_tank(void) +{ + int i; + pthread_mutex_lock(&server.ht_lock); + for (i=0;i= CORE_TANK_LEN) { + if (index != 0) { + log_printf(SMG_LOG_ALL, server.log, "%s Error on invalid TANK INDEX = %i\n", + __FUNCTION__,index); + } + return NULL; + } + + pthread_mutex_lock(&server.ht_lock); + woomera = server.holding_tank[index]; + pthread_mutex_unlock(&server.ht_lock); + + return woomera; +} + + +static void clear_from_holding_tank(int index, struct woomera_interface *woomera) +{ + + if (index < 1 || index >= CORE_TANK_LEN) { + if (index != 0) { + log_printf(SMG_LOG_ALL, server.log, "%s Error on invalid TANK INDEX = %i\n", + __FUNCTION__,index); + } + return; + } + + pthread_mutex_lock(&server.ht_lock); + if (server.holding_tank[index] == &woomera_dead_dev) { +#if 0 + log_printf(SMG_LOG_ALL,server.log, "%s Clearing DEAD id=%i OK\n", + __FUNCTION__,index); +#endif + server.holding_tank[index] = NULL; + } else if (woomera && server.holding_tank[index] == woomera) { +#if 0 + log_printf(SMG_LOG_ALL,server.log, "%s Clearing ACTIVE Woomera id=%i OK\n", + __FUNCTION__,index); +#endif + server.holding_tank[index] = NULL; + } else if (server.holding_tank[index]) { + log_printf(SMG_LOG_ALL, server.log, "Critical Error: Holding tank index %i not cleared %p !\n", + index, server.holding_tank[index]); + } + pthread_mutex_unlock(&server.ht_lock); + + return; +} + +static struct woomera_interface *peek_from_holding_tank(int index) +{ + struct woomera_interface *woomera = NULL; + + if (index < 1 || index >= CORE_TANK_LEN) { + if (index != 0) { + log_printf(SMG_LOG_ALL, server.log, "%s Error on invalid TANK INDEX = %i\n", + __FUNCTION__,index); + } + return NULL; + } + + pthread_mutex_lock(&server.ht_lock); + if (server.holding_tank[index] && + server.holding_tank[index] != &woomera_dead_dev) { + woomera = server.holding_tank[index]; + } + pthread_mutex_unlock(&server.ht_lock); + + return woomera; +} + +static int add_to_holding_tank(struct woomera_interface *woomera) +{ + int next, i, found=0; + + pthread_mutex_lock(&server.ht_lock); + + for (i=0;i= CORE_TANK_LEN) { + next = server.holding_tank_index = 1; + } + + if (next == 0) { + log_printf(SMG_LOG_ALL, server.log, "\nCritical Error on TANK INDEX == 0\n"); + continue; + } + + if (server.holding_tank[next]) { + continue; + } + + found=1; + break; + } + + if (!found) { + /* This means all tank vales are busy + * should never happend */ + pthread_mutex_unlock(&server.ht_lock); + log_printf(SMG_LOG_ALL, server.log, "\nCritical Error failed to obtain a TANK INDEX\n"); + return 0; + } + + server.holding_tank[next] = woomera; + woomera->timeout = time(NULL) + server.call_timeout; + + pthread_mutex_unlock(&server.ht_lock); + return next; +} + + + +static void handle_event_dtmf(struct woomera_interface *woomera, unsigned char dtmf_digit) +{ + struct woomera_event wevent; + + memset(&wevent, 0, sizeof(struct woomera_event)); + + new_woomera_event_printf(&wevent, "EVENT DTMF %sUnique-Call-Id:%s%sContent-Length:%d%s%s%c%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + 1, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + dtmf_digit, + WOOMERA_RECORD_SEPERATOR); + + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + return; +} + +<<<<<<< .mine +static int handle_woomera_progress(struct woomera_interface *woomera, + struct woomera_message *wmsg) +{ + call_signal_event_t event; + int err=-1; + + memset(&event, 0, sizeof(event)); + event.event_id = SIGBOOST_EVENT_CALL_PROGRESS; + sprintf(event.isup_in_rdnis,"SMG003-EVI-2"); + event.isup_in_rdnis_size=strlen(event.isup_in_rdnis); + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s]\n", + wmsg->command, woomera->interface); + + if (!woomera_check_running(woomera)) { + socket_printf(woomera->socket, "405 PROGRESS Channel already hungup%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + if (!woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + + socket_printf(woomera->socket, "405 PROGRESS Channel not aceked%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + err=isup_exec_event(&event); + if (err) { + socket_printf(woomera->socket, + "200 %s PROGRESS OK%s" + "Unique-Call-Id: %s%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } else { + socket_printf(woomera->socket, "405 PROGRESS Boost failure%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } + + return err; +} +======= +static int handle_woomera_progress(struct woomera_interface *woomera, + struct woomera_message *wmsg) +{ + call_signal_event_t event; + int err=-1; + + memset(&event, 0, sizeof(event)); +>>>>>>> .r203 + +<<<<<<< .mine +======= + call_signal_event_init((short_signal_event_t*)&event, SIGBOOST_EVENT_CALL_PROGRESS, woomera->chan, woomera->span); + sprintf(event.isup_in_rdnis,"SMG003-EVI-2"); + event.isup_in_rdnis_size=strlen(event.isup_in_rdnis); + if (woomera->index >= 0) { + event.call_setup_id = woomera->index; + } + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s]\n", + wmsg->command, woomera->interface); + + if (!woomera_check_running(woomera)) { + socket_printf(woomera->socket, "405 PROGRESS Channel already hungup%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + if (!woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + + socket_printf(woomera->socket, "405 PROGRESS Channel not aceked%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + err=isup_exec_event(&event); + if (err == 0) { + socket_printf(woomera->socket, + "200 %s PROGRESS OK%s" + "Unique-Call-Id: %s%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } else { + socket_printf(woomera->socket, "405 PROGRESS Boost failure%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } + + return err; +} + +>>>>>>> .r203 +static int handle_woomera_media_accept_answer(struct woomera_interface *woomera, + struct woomera_message *wmsg, + int media, int answer, int accept) +{ + char *raw = woomera_message_header(wmsg, "raw-audio"); + struct woomera_event wevent; + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s]\n", + wmsg->command, woomera->interface); + + if (!woomera_check_running(woomera)) { + + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "ERROR! call was cancelled MEDIA on HANGUP or MEDIA END!\n"); + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(woomera->q931_rel_cause_topbx), + WOOMERA_LINE_SEPERATOR, + woomera->q931_rel_cause_topbx, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call already hungup!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + woomera->timeout=0; + return -1; + } + + log_printf(SMG_LOG_DEBUG_MISC, server.log,"WOOMERA: GOT %s EVENT: [%s] RAW=%s\n", + wmsg->command,wmsg->callid,raw); + + + if (raw && + woomera->raw == NULL && + !woomera_test_flag(woomera, WFLAG_RAW_MEDIA_STARTED)) { + + woomera_set_flag(woomera, WFLAG_RAW_MEDIA_STARTED); + + woomera_set_raw(woomera, raw); + + if (launch_media_thread(woomera)) { + + log_printf(SMG_LOG_DEBUG_8, server.log,"ERROR: Failed to Launch Call [%s]\n", + woomera->interface); + + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(21), + WOOMERA_LINE_SEPERATOR, + 21, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_RUNNING); + + woomera->timeout=0; + return -1; + } + } + + if (!woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + log_printf(SMG_LOG_ALL, server.log,"ERROR! Monitor Thread not running!\n"); + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + return -1; + + } else { + + if (accept) { + if (!autoacm && !woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + woomera_set_flag(woomera,WFLAG_CALL_ACKED); + isup_exec_command(woomera->span, + woomera->chan, + -1, + SIGBOOST_EVENT_CALL_START_ACK, + 0); + } + } + + if (answer) { + struct media_session *ms; + + pthread_mutex_lock(&woomera->ms_lock); + if ((ms=woomera->ms)) { + time(&woomera->ms->answered); + } + pthread_mutex_unlock(&woomera->ms_lock); + + if (ms) { + + if (!autoacm && !woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + woomera_set_flag(woomera,WFLAG_CALL_ACKED); + isup_exec_command(woomera->span, + woomera->chan, + -1, + SIGBOOST_EVENT_CALL_START_ACK, + 0); + } + + isup_exec_command(woomera->span, + woomera->chan, + -1, + SIGBOOST_EVENT_CALL_ANSWERED, + 0); + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "Sent SIGBOOST_EVENT_CALL_ANSWERED [w%dg%d]\n", + woomera->span+1,woomera->chan+1); + } else { + struct woomera_event wevent; + log_printf(SMG_LOG_ALL, server.log, + "WOOMERA ANSWER: FAILED [%s] no Media \n", + wmsg->command,wmsg->callid); + + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(21), + WOOMERA_LINE_SEPERATOR, + 21, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_RUNNING); + woomera->timeout=0; + return -1; + } + } + } + + new_woomera_event_printf(&wevent, "200 %s OK%s" + "Unique-Call-Id: %s%s", + answer ? "ANSWER" : + accept ? "ACCEPT" : "MEDIA", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + return 0; +} + +static int handle_woomera_call_start (struct woomera_interface *woomera, + struct woomera_message *wmsg) +{ + char *raw = woomera_message_header(wmsg, "raw-audio"); + call_signal_event_t event; + char *calling = woomera_message_header(wmsg, "local-number"); +#ifdef SMG_CALLING_NAME + char *calling_name = woomera_message_header(wmsg, "local-name"); +#endif + char *presentation = woomera_message_header(wmsg, "Presentation"); + char *screening = woomera_message_header(wmsg, "Screening"); + char *rdnis = woomera_message_header(wmsg, "RDNIS"); + char *bearer_cap = woomera_message_header(wmsg, "Bearer-Cap"); + char *uil1p = woomera_message_header(wmsg, "uil1p"); + char *called = wmsg->callid; + char *grp = wmsg->callid; + char *p; + int cause = 34; + int tg = 0; + int huntgroup = SIGBOOST_HUNTGRP_SEQ_ASC; + + if (smg_check_all_busy() || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ + + + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, + "405 SMG Server All Ckt Busy!%s", WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "SMG Server Full %d (ckt busy cnt=%i)\n", + server.call_count, server.all_ckt_busy); + return -1; + } + + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "New Call %d/%d\n", server.call_count, server.max_calls); + + switch(called[0]) { + case 'g': + huntgroup = SIGBOOST_HUNTGRP_SEQ_ASC; + break; + case 'G': + huntgroup = SIGBOOST_HUNTGRP_SEQ_DESC; + break; + case 'r': + huntgroup = SIGBOOST_HUNTGRP_RR_ASC; + break; + case 'R': + huntgroup = SIGBOOST_HUNTGRP_RR_DESC; + break; + default: + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, + "Warning: Failed to determine huntgroup (%s)\n", + called); + huntgroup = SIGBOOST_HUNTGRP_SEQ_ASC; + } + + if ((p = strchr(called, '/'))) { + *p = '\0'; + called = p+1; + tg = atoi(grp+1) - 1; + if (tg < 0) { + tg=0; + } + } + + woomera->trunk_group=tg; + if (raw) { + woomera_set_raw(woomera, raw); + } + + woomera->index = add_to_holding_tank(woomera); + if (woomera->index < 1) { + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, + "405 SMG Server All Tanks Busy!%s", + WOOMERA_RECORD_SEPERATOR); + log_printf(SMG_LOG_ALL, woomera->log, "Error: Call Tank Full (Call Cnt=%i)\n", + server.call_count); + return -1; + } + + + woomera->index_hold = woomera->index; + + call_signal_call_init(&event, calling, called, woomera->index); + + if (presentation) { + event.calling_number_presentation = atoi(presentation); + } else { + event.calling_number_presentation = 0; + } + + if (screening) { + event.calling_number_screening_ind = atoi(screening); + } else { + event.calling_number_screening_ind = 0; + } + + event.isup_in_rdnis_size=0; + event.isup_in_rdnis[0]=0; + + if (rdnis && strlen(rdnis) ) { + + if (strlen(rdnis) > sizeof(event.isup_in_rdnis)){ + log_printf(SMG_LOG_ALL,server.log,"Error: RDNIS Overflow (in size=%i max=%i)\n", + strlen(rdnis), sizeof(event.isup_in_rdnis)); + + } else { + + strncpy(event.isup_in_rdnis,rdnis, + sizeof(event.isup_in_rdnis)-1); + event.isup_in_rdnis_size=strlen(rdnis)+1; + log_printf(SMG_LOG_DEBUG_MISC,server.log,"RDNIS %s\n", rdnis); + } + + } + + if (bearer_cap && strlen(bearer_cap)) { + event.bearer.capability = bearer_cap_to_code(bearer_cap); + woomera->bearer_cap = event.bearer.capability; + } + + if (uil1p && strlen(uil1p)) { + event.bearer.uil1p = uil1p_to_code(uil1p); + } + +#ifdef SMG_CALLING_NAME + if (calling_name) { + strncpy((char*)event.calling_name,calling_name, + sizeof(event.calling_name)-1); + } +#endif + + event.trunk_group = tg; + event.hunt_group = huntgroup; + + if (call_signal_connection_write(&server.mcon, &event) < 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", + strerror(errno)); + + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, + "405 SMG Signalling Contestion!%s", + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + socket_printf(woomera->socket, "100 Trying%s", WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Call Called Event [Setup ID: %d] TG=%d\n", + woomera->index,tg); + + return 0; +} + + +static void interpret_command(struct woomera_interface *woomera, struct woomera_message *wmsg) +{ + int answer = 0, media = 0, accept=0; + char *unique_id; + int cause=0; + + + + if (!strcasecmp(wmsg->command, "call")) { + int err; + + if (strlen(woomera->session) != 0) { + /* Call has already been placed */ + socket_printf(woomera->socket, "400 Error Call already in progress %s", + WOOMERA_RECORD_SEPERATOR); + log_printf(SMG_LOG_ALL,server.log,"Woomera RX Call Even while call in progress!\n"); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(34), + WOOMERA_LINE_SEPERATOR, + 34, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, + "405 SMG Server All Ckt Reset!%s", WOOMERA_RECORD_SEPERATOR); + + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + err=handle_woomera_call_start(woomera,wmsg); + if (err) { + woomera_set_flag(woomera, WFLAG_HANGUP); + } + + return; + + } else if (!strcasecmp(wmsg->command, "bye") || !strcasecmp(wmsg->command, "quit")) { + char *cause = woomera_message_header(wmsg, "cause"); + char *q931cause = woomera_message_header(wmsg, "Q931-Cause-Code"); + + if (cause) { + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Bye Cause Received: [%s]\n", cause); + } + if (q931cause && atoi(q931cause)) { + woomera_set_cause_tosig(woomera,atoi(q931cause)); + } + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: Bye Received: [%s]\n", woomera->interface); + + woomera_clear_flag(woomera, WFLAG_RUNNING); + socket_printf(woomera->socket, "200 Connection closed%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return; + + } else if (!strcasecmp(wmsg->command, "listen")) { + + if (woomera_test_flag(woomera, WFLAG_LISTENING)) { + socket_printf(woomera->socket, "405 Listener already started%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } else { + + woomera_set_flag(woomera, WFLAG_LISTENING); + add_listener(woomera); + log_printf(SMG_LOG_ALL,woomera->log, "Starting Listen Device!\n"); + + socket_printf(woomera->socket, "%s", + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, "200 Listener enabled%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } + return; + + } else if ((media = !strcasecmp(wmsg->command, "debug"))) { + + int debug_level=atoi(wmsg->callid); + + if (debug_level < 10) { + server.debug=debug_level; + log_printf(SMG_LOG_ALL,server.log,"SMG Debugging set to %i (window=%i)\n",server.debug,server.mcon.txwindow); + } + + return; + } + + if (!strcasecmp(wmsg->command, "hangup")) { + char *q931cause = woomera_message_header(wmsg, "Q931-Cause-Code"); + + + if (q931cause && atoi(q931cause)) { + log_printf(SMG_LOG_DEBUG_8,server.log,"Woomera Hangup setting cause to %s %i\n", + q931cause,atoi(q931cause)); + woomera_set_cause_tosig(woomera,atoi(q931cause)); + } + + /* Continue Through */ + } + + + + unique_id = woomera_message_header(wmsg, "Unique-Call-Id"); + if (!unique_id) { + + cause=111; + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, "400 Woomera cmd without uniquie id%s" + WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_CALL,server.log,"Woomera RX Event (%s) without unique id!\n",wmsg->command); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + if (strlen(woomera->session) == 0) { + struct woomera_interface *session_woomera=NULL; + char *session=NULL; + int span, chan; + char ifname[100]; + /* If session does not exist this is an incoming call */ + sscanf(unique_id, "w%dg%d", &span, &chan); + span--; + chan--; + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, + "WOOMERA Got CMD %s Span=%d Chan=%d from session %s\n", + wmsg->command,span,chan,unique_id); + + if (smg_validate_span_chan(span,chan) != 0) { + + cause=81; + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, "404 Invalid span/chan in session%s" + WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, + "WOOMERA Warning invalid span chan in session %s %s\n", + wmsg->command,unique_id); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + pthread_mutex_lock(&server.process_lock); + session = server.process_table[span][chan].session; + session_woomera = server.process_table[span][chan].dev; + + /* This scenario is very common when we have multile clients + where multiple clients race get the incoming call */ + if (session_woomera) { + pthread_mutex_unlock(&server.process_lock); + + cause=81; + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + socket_printf(woomera->socket, "404 Session not found%s" + WOOMERA_RECORD_SEPERATOR); + + + log_printf(SMG_LOG_DEBUG_8, woomera->log, "WOOMERA Error channel in use %s %s\n", + wmsg->command,unique_id); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + if (!session || strlen(session) == 0 || + strncmp(session,unique_id,sizeof(woomera->session))){ + pthread_mutex_unlock(&server.process_lock); + + /* Invalid call reference */ + cause=81; + socket_printf(woomera->socket, "event hangup %s" + "cause: %s%s" + "q931-cause-code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, "404 Invalid/Expired Session%s" + WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, + "WOOMERA Warning: Cmd=%s with invalid session %s (orig=%s)\n", + wmsg->command,unique_id,session?session:"N/A"); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + +#if 1 + if (!strcasecmp(wmsg->command, "hangup")) { + int clients=server.process_table[span][chan].clients; + if (server.process_table[span][chan].clients < 0) { + + log_printf(SMG_LOG_ALL, woomera->log, "WOOMERA CMD: Warning Clients (%i) Race condition on Hangup [w%dg%d]\n", + clients, span+1,chan+1); + + } else if (server.process_table[span][chan].clients > 1) { + server.process_table[span][chan].clients--; + pthread_mutex_unlock(&server.process_lock); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Got Hungup on Multiple Clients %i, skipping hangup [w%dg%d]\n", + clients, span+1,chan+1); + + cause=16; + socket_printf(woomera->socket, "event hangup %s" + "cause: %s%s" + "q931-cause-code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, "404 Hangup on multiple session%s" + WOOMERA_RECORD_SEPERATOR); + + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Hanging up channel [w%dg%d]\n", + span+1,chan+1); + } +#endif + + server.process_table[span][chan].dev=woomera; + strncpy(woomera->session,unique_id,sizeof(woomera->session)); + sprintf(ifname,"w%dg%d",span+1,chan+1); + woomera_set_interface(woomera, ifname); + + woomera->span=span; + woomera->chan=chan; + + /* Save bearer cap that came in on incoming call event */ + woomera->bearer_cap = server.process_table[span][chan].bearer_cap; + + /* set it to 1 so that queued digits are checked on proceed message */ + woomera->check_digits = 1; + + pthread_mutex_unlock(&server.process_lock); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA Got New If=%s Session %s\n", + woomera->interface, woomera->session); + + + } else if (strncmp(woomera->session,unique_id,sizeof(woomera->session))) { + + cause=81; + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, "404 Session Mis-match%s" + WOOMERA_RECORD_SEPERATOR); + woomera_set_flag(woomera, WFLAG_HANGUP); + return; + } + + if (!strcasecmp(wmsg->command, "dtmf")) { + + log_printf(SMG_LOG_WOOMERA, woomera->log, + "WOOMERA CMD: DTMF Received: [%s] Digit %s Body %s\n", + woomera->interface, wmsg->command_args, wmsg->body); + + wanpipe_send_dtmf(woomera,wmsg->body); + + socket_printf(woomera->socket, "200 DTMF OK%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + + } else if (!strcasecmp(wmsg->command, "hangup")) { + + int chan = -1, span = -1; + char *cause = woomera_message_header(wmsg, "cause"); + char *q931cause = woomera_message_header(wmsg, "Q931-Cause-Code"); + + if (q931cause && atoi(q931cause)) { + woomera_set_cause_tosig(woomera,atoi(q931cause)); + } + + span=woomera->span; + chan=woomera->chan; + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Hangup Received: [%s] MEDIA EXIST Cause=%s\n", + woomera->interface,cause); + + if (smg_validate_span_chan(span,chan) != 0) { + + socket_printf(woomera->socket, "405 No Such Channel%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return; + } + + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "Hangup Received: [w%dg%d]\n", + span+1,chan+1); + + + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_RUNNING); + + + socket_printf(woomera->socket, "200 HANGUP OK%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + } else if (!strcasecmp(wmsg->command, "proceed")) { + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s]\n", + wmsg->command, woomera->interface); + + socket_printf(woomera->socket, + "200 %s PROCEED OK%s" + "Unique-Call-Id: %s%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + } else if (!strcasecmp(wmsg->command, "progress")) { + + handle_woomera_progress(woomera,wmsg); + + + } else if ((media = !strcasecmp(wmsg->command, "media")) || + (answer = !strcasecmp(wmsg->command, "answer")) || + (accept = !strcasecmp(wmsg->command, "accept"))) { + + handle_woomera_media_accept_answer(woomera, wmsg, media,answer,accept); + + + } else { + log_printf(SMG_LOG_ALL, server.log,"WOOMERA INVALID EVENT: %s [%s] \n", + wmsg->command,wmsg->callid); + socket_printf(woomera->socket, "501 Command '%s' not implemented%s", + wmsg->command, WOOMERA_RECORD_SEPERATOR); + } +} + + +/* + EVENT INCOMING 1 + Remote-Address: 10.3.3.104 + Remote-Number: + Remote-Name: Anthony Minessale!8668630501 + Protocol: H.323 + User-Agent: Post Increment Woomera 1.0alpha1 (OpenH323 v1.17.2) 9/61 + H323-Call-Id: 887b1ff8-bb1f-da11-85c0-0007e98988c4 + Local-Number: 996 + Start-Time: Fri, 09 Sep 2005 12:25:14 -0400 + Local-Name: root +*/ + + +static void handle_call_answer(short_signal_event_t *event) +{ + struct woomera_interface *woomera = NULL; + int kill = 0; + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + pthread_mutex_unlock(&server.process_lock); + + if (woomera) { + char callid[80]; + struct woomera_event wevent; + + woomera->timeout = 0; + + if (!woomera->raw) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to answer call with no media!\n"); + kill++; + goto handle_call_answer_end; + } + + if (woomera_test_flag(woomera, WFLAG_ANSWER)) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to double-answer a call!\n"); + kill++; + goto handle_call_answer_end; + } + + woomera_set_flag(woomera, WFLAG_ANSWER); + + if (woomera->span != event->span || woomera->chan != event->chan) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to start media on a different channel from the one we agreed on.!\n"); + kill++; + goto handle_call_answer_end; + } + + if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to answer a dead call!\n"); + kill++; + } else { + int err; + err=0; + sprintf(callid, "w%dg%d", event->span + 1, event->chan + 1); + woomera_set_interface(woomera, callid); +#ifndef WOOMERA_EARLY_MEDIA + err=launch_media_thread(woomera); + if (err) { + log_printf(SMG_LOG_ALL, server.log,"ERROR: Failed to Launch Call [%s]\n", + woomera->interface); + + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Q931-Cause-Code: %d%s" + "Cause: %s%s", + woomera->interface, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + 21, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(21), + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + kill++; + } +#endif + + if (!kill) { + new_woomera_event_printf(&wevent, "EVENT CONNECT w%dg%d%s" + "Unique-Call-Id: %s%s", + event->span+1, + event->chan+1, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR + ); + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + } + } + } else { + log_printf(SMG_LOG_PROD, server.log, "Answer requested on non-existant session. [w%dg%d]\n", + event->span+1, event->chan+1); + kill++; + } + +handle_call_answer_end: + + if (kill) { + if (woomera) { + woomera_set_flag(woomera,WFLAG_MEDIA_END); + } else { + +/* This can casuse a double STOP + must be debugged further */ +#if 0 + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED, + SIGBOOST_RELEASE_CAUSE_NORMAL); + + log_printf(SMG_LOG_PROD, server.log, "Sent CALL STOP to Answer without session [w%dg%d]\n", + event->span+1, event->chan+1); +#endif + log_printf(SMG_LOG_ALL, server.log, "WARNING: Received Answer with no session [w%dg%d]\n", + event->span+1, event->chan+1); + } + } +} + +static void handle_call_start_ack(short_signal_event_t *event) +{ + struct woomera_interface *woomera = NULL; + struct woomera_event wevent; + int kill = 0; + + if ((woomera = peek_from_holding_tank(event->call_setup_id))) { + char callid[80]; + + if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_PROD, server.log, "Refusing to ack a dead call!\n"); + kill++; + } else { + int err, span, chan; + + pull_from_holding_tank(event->call_setup_id,event->span,event->chan); + sprintf(callid, "w%dg%d", event->span + 1, event->chan + 1); + + span = event->span; + chan = event->chan; + + woomera_set_flag(woomera,WFLAG_CALL_ACKED); + woomera_set_interface(woomera, callid); + + pthread_mutex_lock(&server.process_lock); + + if (server.process_table[span][chan].dev) { + struct woomera_interface *tmp_woomera = server.process_table[span][chan].dev; + woomera_set_flag(tmp_woomera,WFLAG_HANGUP); + woomera_set_flag(tmp_woomera,WFLAG_MEDIA_END); + log_printf(SMG_LOG_ALL,server.log,"Call Overrun on [w%dg%d] - Call ACK!\n", event->span+1, event->chan+1); + kill++; + } + + server.process_table[span][chan].dev = woomera; + sprintf(woomera->session,"%s-%i-%i",callid,rand(),rand()); + sprintf(server.process_table[span][chan].session,"%s-%s", + callid,woomera->session); + + + memset(server.process_table[span][chan].digits, 0, + sizeof(server.process_table[span][chan].digits)); + + server.process_table[span][chan].digits_len = 0; + + pthread_mutex_unlock(&server.process_lock); + + if (kill) { + goto woomera_call_ack_skip; + } + +#ifdef WOOMERA_EARLY_MEDIA + err=launch_media_thread(woomera); + if (err) { + log_printf(SMG_LOG_ALL, server.log,"ERROR: Failed to Launch Call [%s]\n", + woomera->interface); + + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id:%s%s" + "Q931-Cause-Code: %d%s" + "Cause: %s%s", + woomera->interface, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + 21, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(21), + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + + kill++; + } +#endif + if (!kill) { + + new_woomera_event_printf(&wevent, "EVENT PROCEED w%dg%d%s" + "Channel-Name: g%d/%d%s" + "Unique-Call-Id: %s%s", + event->span+1, + event->chan+1, + WOOMERA_LINE_SEPERATOR, + woomera->trunk_group+1, + (event->span*max_chans)+event->chan+1, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR + ); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "201 Accepted%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Call Answered Event ID = %d Device = w%dg%d!\n", + event->call_setup_id,woomera->span+1,woomera->chan+1); + } + } + } else { + log_printf(SMG_LOG_PROD, server.log, + "Event (START ACK) %d referrs to a non-existant session (%d) [w%dg%d]!\n", + event->event_id, event->call_setup_id,event->span+1, event->chan+1); + kill++; + } + +woomera_call_ack_skip: + if (kill) { + if (woomera) { + woomera_set_flag(woomera,WFLAG_MEDIA_END); + } else { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED, + SIGBOOST_RELEASE_CAUSE_NORMAL); + + log_printf(SMG_LOG_PROD, server.log, "Sent CALL STOP to CALL START ACK without session [w%dg%d]\n", + event->span+1, event->chan+1); + } + } +} + +static void handle_call_start_nack(short_signal_event_t *event) +{ + struct woomera_interface *woomera = NULL; + int span=-1, chan=-1; + int ack=0; + + /* Always ACK the incoming NACK + * Send out the NACK ACK before pulling the TANK, because + * if we send after the pull, the outgoing call could send + * a message to boost with the pulled TANK value before + * we send a NACK ACK */ + + if (smg_validate_span_chan(event->span,event->chan) == 0) { + span=event->span; + chan=event->chan; + } + + if (event->call_setup_id > 0) { + woomera=peek_from_holding_tank(event->call_setup_id); + } + + if (woomera) { + + struct woomera_event wevent; + + if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + log_printf(SMG_LOG_ALL, server.log, "Event CALL START NACK on hungup call [%d]!\n", + event->call_setup_id); + ack++; + } else { + + woomera_set_cause_topbx(woomera,event->release_cause); + woomera_set_flag(woomera, (WFLAG_HANGUP|WFLAG_HANGUP_NACK_ACK)); + + new_woomera_event_printf(&wevent, "EVENT HANGUP w%dg%d%s" + "Unique-Call-Id: %s%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + event->span+1, + event->chan+1, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(woomera->q931_rel_cause_topbx), + WOOMERA_LINE_SEPERATOR, + woomera->q931_rel_cause_topbx, + WOOMERA_RECORD_SEPERATOR + ); + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + woomera_set_flag(woomera, WFLAG_HANGUP); + woomera_clear_flag(woomera, WFLAG_RUNNING); + + /* Do not ack here, let woomera thread ack it */ + ack=0; + } + + + } else if (event->call_setup_id == 0 && + smg_validate_span_chan(event->span,event->chan) == 0) { + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + + if (woomera) { + if (!woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT) && + !woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK_SENT)) { + /* Only if we are not already waiting for hangup */ + server.process_table[event->span][event->chan].dev=NULL; + } else if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + /* At this point call is already hang up */ + woomera=NULL; + } else { + /* At this point call is already hang up */ + woomera=NULL; + } + } + + memset(server.process_table[event->span][event->chan].session, + 0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + + if (woomera) { + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event START NACK on w%dg%d ptr=%p ms=%p\n", + woomera->span+1,woomera->chan+1,woomera,woomera->ms); + + if (!woomera_test_flag(woomera,WFLAG_HANGUP)){ + if (event->release_cause == SIGBOOST_CALL_SETUP_CSUPID_DBL_USE || + event->release_cause == SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY) { + woomera_set_cause_topbx(woomera,17); + } else { + woomera_set_cause_topbx(woomera,event->release_cause); + } + } + + woomera_set_flag(woomera, + (WFLAG_HANGUP|WFLAG_MEDIA_END|WFLAG_HANGUP_NACK_ACK)); + + /* Nack Ack will be sent by the woomera thread */ + ack=0; + } else { + /* Valid state when we are not in autoacm mode */ + ack++; + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event: NACK no woomera on span chan [w%dg%d]!\n", + event->span+1, event->chan+1); + } + + } else { + log_printf(SMG_LOG_ALL, server.log, + "Error: Start Nack Invalid State Should not happen [%d] [w%dg%d]!\n", + event->call_setup_id, event->span+1, event->chan+1); + ack++; + } + + if (event->release_cause == SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY) { + smg_all_ckt_busy(); + log_printf(SMG_LOG_ALL, server.log, "WARNING: All ckt busy Timeout=%i!\n",server.all_ckt_busy); + } + if (event->release_cause == SIGBOOST_CALL_SETUP_CSUPID_DBL_USE) { + log_printf(SMG_LOG_ALL, server.log, "WARNING: Double use on [w%ig%i] setup id %i!\n", + event->span+1,event->chan+1,event->call_setup_id); + } + +#warning "Ignoring CALL GAP" +#if 0 + if (event->release_cause == SIGBOOST_CALL_SETUP_NACK_AUTO_CALL_GAP) { + log_printf(SMG_LOG_ALL, server.log, "WARNING: Call Gapping Detected!\n"); + smg_all_ckt_gap(); + } +#endif + + if (ack) { + span=0; + chan=0; + if (smg_validate_span_chan(event->span,event->chan) == 0) { + span=event->span; + chan=event->chan; + } + + isup_exec_command(span, + chan, + event->call_setup_id, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + 0); + + if (!woomera) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event (NACK ACK) %d referrs to a non-existant session (%d) [w%dg%d]!\n", + event->event_id,event->call_setup_id, event->span+1, event->chan+1); + } + } +} + +static void handle_call_loop_start(short_signal_event_t *event) +{ + struct woomera_interface *woomera; + char callid[20]; + char *session; + + pthread_mutex_lock(&server.process_lock); + if (server.process_table[event->span][event->chan].dev) { + + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + 17); + + + log_printf(SMG_LOG_PROD, server.log, + "Sent (From Handle Loop START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [w%dg] ptr=%d\n", + event->span+1, event->chan+1, server.process_table[event->span][event->chan].dev); + + pthread_mutex_unlock(&server.process_lock); + return; + + } + + sprintf(callid, "w%dg%d", event->span+1,event->chan+1); + sprintf(server.process_table[event->span][event->chan].session, + "%s-%i-%i",callid,rand(),rand()); + session=server.process_table[event->span][event->chan].session; + server.process_table[event->span][event->chan].dev = NULL; + pthread_mutex_unlock(&server.process_lock); + + + woomera=launch_woomera_loop_thread(event); + if (woomera == NULL) { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + 17); + log_printf(SMG_LOG_PROD, server.log, + "Sent (From Handle Loop START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [w%dg] ptr=%d\n", + event->span+1, event->chan+1, server.process_table[event->span][event->chan].dev); + } + + woomera_set_flag(woomera,WFLAG_CALL_ACKED); + + return; +} + + +static void handle_call_start(call_signal_event_t *event) +{ + struct woomera_event wevent; + char callid[20]; + char *session; + int clients; + + remove_end_of_digits_char((unsigned char*)event->called_number_digits); + remove_end_of_digits_char((unsigned char*)event->calling_number_digits); + + if (server.strip_cid_non_digits) { + validate_number((unsigned char*)event->called_number_digits); + validate_number((unsigned char*)event->calling_number_digits); + } + + pthread_mutex_lock(&server.process_lock); + + if (server.process_table[event->span][event->chan].dev) { + + struct woomera_interface *tmp_woomera = server.process_table[event->span][event->chan].dev; + woomera_set_flag(tmp_woomera,WFLAG_HANGUP); + woomera_set_flag(tmp_woomera,WFLAG_MEDIA_END); + log_printf(SMG_LOG_ALL,server.log,"Call Overrun on [w%dg%d] - Call START!\n", event->span+1, event->chan+1); + + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + 17); + + log_printf(SMG_LOG_ALL, server.log, + "Sent (From Handle START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [w%dg%d]\n", + event->span+1, event->chan+1); + + pthread_mutex_unlock(&server.process_lock); + return; + } + + sprintf(callid, "w%dg%d", event->span+1,event->chan+1); + sprintf(server.process_table[event->span][event->chan].session, + "%s-%i-%i",callid,rand(),rand()); + session=server.process_table[event->span][event->chan].session; + server.process_table[event->span][event->chan].dev = NULL; + memset(server.process_table[event->span][event->chan].digits, 0, sizeof(server.process_table[event->span][event->chan].digits)); + server.process_table[event->span][event->chan].digits_len = 0; + server.process_table[event->span][event->chan].bearer_cap = event->bearer.capability; + server.process_table[event->span][event->chan].clients=0; + pthread_mutex_unlock(&server.process_lock); + + if (autoacm) { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_ACK, + 0); + } + + + log_printf(SMG_LOG_DEBUG_8,server.log,"BEARER %i , UIL1P = %i\n", + event->bearer.capability,event->bearer.uil1p); + + if (event->bearer.uil1p == 0) { + if (server.hw_coding) { + event->bearer.uil1p=BC_IE_UIL1P_G711_ALAW; + } else { + event->bearer.uil1p=BC_IE_UIL1P_G711_ULAW; + } + } + + new_woomera_event_printf(&wevent, "EVENT INCOMING w%dg%d%s" + "Unique-Call-Id: %s%s" + "Remote-Number: %s%s" + "Remote-Name: %s%s" +#if defined(BRI_PROT) + "Protocol: BRI%s" +#else +#if defined(PRI_PROT) + "Protocol: PRI%s" +#else + "Protocol: SS7%s" +#endif +#endif + "User-Agent: sangoma_mgd%s" + "Local-Number: %s%s" + "Channel-Name: g%d/%d%s" + "Trunk-Group: %d%s" + "Presentation: %d%s" + "Screening: %d%s" + "RDNIS: %s%s" + "Bearer-Cap: %s%s" + "uil1p: %s%s" + , + event->span+1, + event->chan+1, + WOOMERA_LINE_SEPERATOR, + session, + WOOMERA_LINE_SEPERATOR, + event->calling_number_digits, + WOOMERA_LINE_SEPERATOR, + event->calling_name, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + event->called_number_digits, + WOOMERA_LINE_SEPERATOR, + event->trunk_group+1, + (event->span*max_chans)+event->chan+1, + WOOMERA_LINE_SEPERATOR, + event->trunk_group+1, + WOOMERA_LINE_SEPERATOR, + event->calling_number_presentation, + WOOMERA_LINE_SEPERATOR, + event->calling_number_screening_ind, + WOOMERA_LINE_SEPERATOR, + event->isup_in_rdnis, + WOOMERA_LINE_SEPERATOR, + bearer_cap_to_str(event->bearer.capability), + WOOMERA_LINE_SEPERATOR, + uil1p_to_str(event->bearer.uil1p), + WOOMERA_RECORD_SEPERATOR + ); + + clients=enqueue_event_on_listeners(&wevent); + if (!clients) { + + pthread_mutex_lock(&server.process_lock); + server.process_table[event->span][event->chan].dev = NULL; + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + if (autoacm) { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED, + 17); + log_printf(SMG_LOG_ALL, server.log, + "CALL INCOMING: Enqueue Error Sent SIGBOOST_EVENT_CALL_STOPPED [w%dg%d]\n", + event->span+1, event->chan+1); + + } else { + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + 17); + log_printf(SMG_LOG_ALL, server.log, + "CALL INCOMING: Enqueue Error Sent SIGBOOST_EVENT_CALL_START_NACK [w%dg%d]\n", + event->span+1, event->chan+1); + + } + + } else { + server.process_table[event->span][event->chan].clients=clients; + } + + destroy_woomera_event_data(&wevent); + +} + +static void handle_incoming_digit(call_signal_event_t *event) +{ + struct woomera_interface *woomera; + int digits_len; + + if (smg_validate_span_chan(event->span,event->chan)) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event DTMF on invalid span chan [w%dg%d] !\n", + event->span+1, event->chan+1); + return; + } + + + if (!event->called_number_digits_count) { + log_printf(SMG_LOG_ALL, server.log, "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); + } + + log_printf(SMG_LOG_DEBUG_9, server.log, "Queuing incoming digits %s [w%dg%d]\n", + event->called_number_digits, + event->span+1, event->chan+1); + + pthread_mutex_lock(&server.digits_lock); + + digits_len = server.process_table[event->span][event->chan].digits_len; + + strncpy(&server.process_table[event->span][event->chan].digits[digits_len], + event->called_number_digits, + event->called_number_digits_count); + + server.process_table[event->span][event->chan].digits_len += event->called_number_digits_count; + + pthread_mutex_unlock(&server.digits_lock); + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + pthread_mutex_unlock(&server.process_lock); + if (!woomera || !strlen(woomera->session)) { + return; + } + woomera_check_digits(woomera); +} + +static void handle_gap_abate(short_signal_event_t *event) +{ + log_printf(SMG_LOG_ALL, server.log, "NOTICE: GAP Cleared!\n", + event->span+1, event->chan+1); + smg_clear_ckt_gap(); +} + +static void handle_restart(call_signal_connection_t *mcon, short_signal_event_t *event) +{ + if (!woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + log_printf(SMG_LOG_ALL, server.log,"ERROR! Monitor Thread not running!\n"); + } else { + /* Clear Reset */ + /* Tell all threads to go down */ + + log_printf(SMG_LOG_ALL, server.log, + "RESTART Received: resetting all threads\n"); + + smg_all_ckt_busy(); + woomera_set_flag(&server.master_connection, WFLAG_SYSTEM_RESET); + gettimeofday(&server.restart_timeout,NULL); + +#if 0 + sleep(5); + + clear_all_holding_tank(); + + sleep(2); + + event->event_id = SIGBOOST_EVENT_SYSTEM_RESTART_ACK; + err=call_signal_connection_write(&server.mcon, (call_signal_event_t*)event); + if (err < 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", + strerror(errno)); + } + + smg_all_ckt_busy(); + woomera_clear_flag(&server.master_connection, WFLAG_SYSTEM_RESET); +#endif + } + +} + +static void handle_restart_ack(call_signal_connection_t *mcon, short_signal_event_t *event) +{ + + /* Do not reset WFLAG_SYSTEM_RESET flag here!. The flag should + only be reset on restart from boost */ + //woomera_clear_flag(&server.master_connection, WFLAG_SYSTEM_RESET); +} + + + +static void handle_remove_loop(short_signal_event_t *event) +{ + struct woomera_interface *woomera; + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + server.process_table[event->span][event->chan].dev=NULL; + pthread_mutex_unlock(&server.process_lock); + + if (woomera) { + + woomera_set_flag(woomera, + (WFLAG_MEDIA_END|WFLAG_HANGUP)); + + /* We have to close the socket because + At this point we are release span chan */ + + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event REMOVE LOOP on w%dg%d ptr=%p ms=%p\n", + woomera->span+1,woomera->chan+1,woomera,woomera->ms); + + } +} + + +static void handle_call_stop(short_signal_event_t *event) +{ + struct woomera_interface *woomera; + int ack=0; + + woomera = NULL; + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + if (woomera) { + + if (!woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT) && + !woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK_SENT)) { + /* Only if we are not already waiting for hangup */ + //server.process_table[event->span][event->chan].dev=NULL; + + } else if (woomera_test_flag(woomera, WFLAG_HANGUP)) { + /* At this point call is already hangup */ + woomera=NULL; + } else { + /* At this point call is already hangup */ + woomera=NULL; + } + } + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + if (woomera) { + + woomera_set_cause_topbx(woomera,event->release_cause); + + woomera_set_flag(woomera, + (WFLAG_MEDIA_END|WFLAG_HANGUP|WFLAG_HANGUP_ACK)); + + /* We have to close the socket because + At this point we are release span chan */ + + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event CALL STOP on w%dg%d ptr=%p ms=%p\n", + woomera->span+1,woomera->chan+1,woomera,woomera->ms); + + } else { + ack++; + } + + + if (ack) { + /* At this point we have already sent our STOP so its safe to ACK */ + isup_exec_command(event->span, + event->chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED_ACK, + 0); + } + + if (!woomera){ + /* This is allowed on incoming call if remote app does not answer it */ + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event CALL STOP referrs to a non-existant session [w%dg%d]!\n", + event->span+1, event->chan+1); + } +} + +static void handle_heartbeat(short_signal_event_t *event) +{ + if (!woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + log_printf(SMG_LOG_ALL, server.log,"ERROR! Monitor Thread not running!\n"); + } else { + int err=call_signal_connection_writep(&server.mconp, (call_signal_event_t*)event); + if (err < 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", + strerror(errno)); + } + } + return; +} + + +static void handle_call_stop_ack(short_signal_event_t *event) +{ + + struct woomera_interface *woomera = NULL; + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + server.process_table[event->span][event->chan].dev=NULL; + memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + + if (woomera) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Stop Ack on [w%dg%d] [Setup ID: %d] [%s]!\n", + event->span+1, event->chan+1, event->call_setup_id, + woomera->interface); + + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + + } else { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event CALL_STOP_ACK(%d) referrs to a non-existant session [w%dg%d] [Setup ID: %d]!\n", + event->event_id, event->span+1, event->chan+1, event->call_setup_id); + } + + if (event->call_setup_id > 0) { + clear_from_holding_tank(event->call_setup_id, NULL); + } + + /* No need for us to do any thing here */ + return; +} + + +static void handle_call_start_nack_ack(short_signal_event_t *event) +{ + + struct woomera_interface *woomera = NULL; + + if ((woomera=pull_from_holding_tank(event->call_setup_id,-1,-1))) { + + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + + } else if (event->call_setup_id == 0 && + smg_validate_span_chan(event->span,event->chan) == 0) { + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + server.process_table[event->span][event->chan].dev=NULL; + memset(server.process_table[event->span][event->chan].session, + 0,SMG_SESSION_NAME_SZ); + pthread_mutex_unlock(&server.process_lock); + + if (woomera) { + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + } else { + /* Possible if incoming call is NACKed due to + * woomera client being down, in this case no + * woomera thread is created */ + log_printf(SMG_LOG_DEBUG_8, server.log, + "Event NACK ACK [w%dg%d] with valid span/chan no dev!\n", + event->span+1, event->chan+1); + } + + } else { + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "Event NACK ACK referrs to a non-existant session [w%dg%d] [Setup ID: %d]!\n", + event->span+1, event->chan+1, event->call_setup_id); + } + + if (event->call_setup_id > 0) { + clear_from_holding_tank(event->call_setup_id, NULL); + } + + /* No need for us to do any thing here */ + return; +} + + +static int parse_ss7_event(call_signal_connection_t *mcon, short_signal_event_t *event) +{ + int ret = 0; + + switch(event->event_id) { + + case SIGBOOST_EVENT_CALL_START: + handle_call_start((call_signal_event_t*)event); + break; + case SIGBOOST_EVENT_CALL_STOPPED: + handle_call_stop(event); + break; + case SIGBOOST_EVENT_CALL_START_ACK: + handle_call_start_ack(event); + break; + case SIGBOOST_EVENT_CALL_START_NACK: + handle_call_start_nack(event); + break; + case SIGBOOST_EVENT_CALL_ANSWERED: + handle_call_answer(event); + break; + case SIGBOOST_EVENT_HEARTBEAT: + handle_heartbeat(event); + break; + case SIGBOOST_EVENT_CALL_START_NACK_ACK: + handle_call_start_nack_ack(event); + break; + case SIGBOOST_EVENT_CALL_STOPPED_ACK: + handle_call_stop_ack(event); + break; + case SIGBOOST_EVENT_INSERT_CHECK_LOOP: + handle_call_loop_start(event); + break; + case SIGBOOST_EVENT_SYSTEM_RESTART: + handle_restart(mcon,event); + break; + case SIGBOOST_EVENT_REMOVE_CHECK_LOOP: + handle_remove_loop(event); + break; + case SIGBOOST_EVENT_SYSTEM_RESTART_ACK: + handle_restart_ack(mcon,event); + break; + case SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE: + handle_gap_abate(event); + break; + case SIGBOOST_EVENT_DIGIT_IN: + handle_incoming_digit((call_signal_event_t*)event); + break; + default: + log_printf(SMG_LOG_ALL, server.log, "Warning no handler implemented for [%s val:%d]\n", + call_signal_event_id_name(event->event_id), event->event_id); + break; + } + + return ret; +} + + +static void *monitor_thread_run(void *obj) +{ + int ss = 0; + int policy=0,priority=0; + char a=0,b=0; + call_signal_connection_t *mcon=&server.mcon; + call_signal_connection_t *mconp=&server.mconp; + + pthread_mutex_lock(&server.thread_count_lock); + server.thread_count++; + pthread_mutex_unlock(&server.thread_count_lock); + + woomera_set_flag(&server.master_connection, WFLAG_MONITOR_RUNNING); + + if (call_signal_connection_open(mcon, + mcon->cfg.local_ip, + mcon->cfg.local_port, + mcon->cfg.remote_ip, + mcon->cfg.remote_port) < 0) { + log_printf(SMG_LOG_ALL, server.log, "Error: Opening MCON Socket [%d] %s\n", + mcon->socket,strerror(errno)); + exit(-1); + } + + if (call_signal_connection_open(mconp, + mconp->cfg.local_ip, + mconp->cfg.local_port, + mconp->cfg.remote_ip, + mconp->cfg.remote_port) < 0) { + log_printf(SMG_LOG_ALL, server.log, "Error: Opening MCONP Socket [%d] %s\n", + mconp->socket,strerror(errno)); + exit(-1); + } + + mcon->log = server.log; + mconp->log = server.log; + + isup_exec_commandp(0, + 0, + -1, + SIGBOOST_EVENT_SYSTEM_RESTART, + 0); + + woomera_set_flag(&server.master_connection, WFLAG_SYSTEM_RESET); + + smg_get_current_priority(&policy,&priority); + + log_printf(SMG_LOG_PROD, server.log, "Open udp socket [%d] [%d]\n", + mcon->socket,mconp->socket); + log_printf(SMG_LOG_PROD, server.log, "Monitor Thread Started (%i:%i)\n",policy,priority); + + + while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { +#if 0 + ss = waitfor_socket(server.mcon.socket, 1000, POLLERR | POLLIN); +#else + ss = waitfor_2sockets(mcon->socket, + mconp->socket, + &a, &b, 1000); +#endif + + if (ss > 0) { + + call_signal_event_t *event=NULL; + int i=0; + + if (b) { +mcon_retry_priority: + if ((event = call_signal_connection_readp(mconp,i))) { + log_printf(SMG_LOG_DEBUG_9, server.log, "Socket Event P [%s] \n", + call_signal_event_id_name(event->event_id)); + parse_ss7_event(mconp,(short_signal_event_t*)event); + if (++i < 10) { + goto mcon_retry_priority; + } + + } else if (errno != EAGAIN) { + ss=-1; + log_printf(SMG_LOG_ALL, server.log, + "Error: Reading from Boost P Socket! (%i) %s\n", + errno,strerror(errno)); + break; + } + } + + i=0; + + if (a) { + if ((event = call_signal_connection_read(mcon,i))) { + log_printf(SMG_LOG_DEBUG_9, server.log, "Socket Event [%s]\n", + call_signal_event_id_name(event->event_id)); + parse_ss7_event(mcon,(short_signal_event_t*)event); + + } else if (errno != EAGAIN) { + ss=-1; + log_printf(SMG_LOG_ALL, server.log, + "Error: Reading from Boost Socket! (%i) %s\n", + errno,strerror(errno)); + break; + } + } + + } + + if (ss < 0){ + log_printf(SMG_LOG_ALL, server.log, "Thread Run: Select Socket Error!\n"); + break; + } + + if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)) { + short_signal_event_t event; + struct timeval current; + int elapsed; + gettimeofday(¤t,NULL); + + elapsed=smg_calc_elapsed(&server.restart_timeout, ¤t); + if (elapsed > 5000) { + int err; + log_printf(SMG_LOG_ALL, server.log, "Reset Condition Cleared Elapsed=%i!\n",elapsed); + clear_all_holding_tank(); + memset(&event,0,sizeof(event)); + event.event_id = SIGBOOST_EVENT_SYSTEM_RESTART_ACK; + err=call_signal_connection_write(&server.mcon, (call_signal_event_t*)&event); + if (err < 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", + strerror(errno)); + } + smg_all_ckt_busy(); + woomera_clear_flag(&server.master_connection, WFLAG_SYSTEM_RESET); + } + } + + } + + log_printf(SMG_LOG_PROD, server.log, "Close udp socket [%d] [%d]\n", + mcon->socket,mconp->socket); + call_signal_connection_close(&server.mcon); + call_signal_connection_close(&server.mconp); + + pthread_mutex_lock(&server.thread_count_lock); + server.thread_count--; + pthread_mutex_unlock(&server.thread_count_lock); + + woomera_clear_flag(&server.master_connection, WFLAG_MONITOR_RUNNING); + log_printf(SMG_LOG_ALL, server.log, "Monitor Thread Ended\n"); + + return NULL; +} + +static void woomera_loop_thread_run(struct woomera_interface *woomera) +{ + int err=launch_media_thread(woomera); + if (err) { + log_printf(SMG_LOG_ALL, server.log, "Failed to start loop media thread\n"); + woomera_set_flag(woomera, + (WFLAG_HANGUP|WFLAG_MEDIA_END)); + woomera_clear_flag(woomera, WFLAG_RUNNING); + return; + } + + while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING) && + !woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) && + !woomera_test_flag(woomera, WFLAG_MEDIA_END) && + !woomera_test_flag(woomera, WFLAG_HANGUP)) { + + usleep(300000); + continue; + + } + + woomera_clear_flag(woomera, WFLAG_RUNNING); + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Woomera Session: For Loop Test exiting %s\n",woomera->interface); + + return; +} + +static void woomera_check_digits (struct woomera_interface *woomera) +{ + int span, chan; + + if (!strlen(woomera->session)) { + return; + } + + if (get_span_chan_from_interface(woomera->interface, &span, &chan) == 0) { + pthread_mutex_lock(&server.digits_lock); + if (server.process_table[span-1][chan-1].digits_len > 0) { + int i; + unsigned char digit; + + for (i=0; i < server.process_table[span-1][chan-1].digits_len; i++) { + digit = server.process_table[span-1][chan-1].digits[i]; + + handle_event_dtmf(woomera, digit); + } + + server.process_table[span-1][chan-1].digits_len = 0; + } + pthread_mutex_unlock(&server.digits_lock); + } +} + + +static void *woomera_thread_run(void *obj) +{ + struct woomera_interface *woomera = obj; + struct woomera_message wmsg; + struct woomera_event wevent; + char *event_string; + int mwi; + int err; + int policy=0, priority=0; + int span = -1, chan = -1; + + woomera_message_init(&wmsg); + + smg_get_current_priority(&policy,&priority); + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "WOOMERA session started (ptr=%p : loop=%i)(%i:%i) Index=%i\n", + woomera,woomera->loop_tdm,policy,priority, woomera->index); + + pthread_mutex_lock(&server.thread_count_lock); + server.thread_count++; + pthread_mutex_unlock(&server.thread_count_lock); + + pthread_mutex_init(&woomera->queue_lock, NULL); + pthread_mutex_init(&woomera->ms_lock, NULL); + pthread_mutex_init(&woomera->dtmf_lock, NULL); + pthread_mutex_init(&woomera->vlock, NULL); + pthread_mutex_init(&woomera->flags_lock, NULL); + + if (woomera->loop_tdm) { + /* We must set woomera socket to -1 otherwise + a valid file descriptor will get closed */ + woomera->socket = -1; + woomera_loop_thread_run(woomera); + goto woomera_session_close; + } + + err=socket_printf(woomera->socket, + "EVENT HELLO Sangoma Media Gateway%s" + "Supported-Protocols: TDM%s" + "Version: %s%s" + "Remote-Address: %s%s" + "Remote-Port: %d%s" + "Raw-Format: %s%s", + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + SMG_VERSION, WOOMERA_LINE_SEPERATOR, + inet_ntoa(woomera->addr.sin_addr), WOOMERA_LINE_SEPERATOR, + ntohs(woomera->addr.sin_port), WOOMERA_LINE_SEPERATOR, + server.hw_coding?"ALAW":"ULAW", WOOMERA_RECORD_SEPERATOR + ); + + if (err) { + log_printf(SMG_LOG_ALL, server.log, "Woomera session socket failure! (ptr=%p)\n", + woomera); + woomera_clear_flag(woomera, WFLAG_RUNNING); + goto woomera_session_close; + } + + while ( woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING) && + woomera_test_flag(woomera, WFLAG_RUNNING) && + !woomera_test_flag(woomera, WFLAG_MEDIA_END) && + !woomera_test_flag(woomera, WFLAG_HANGUP)) { + + mwi = woomera_message_parse(woomera, &wmsg, WOOMERA_HARD_TIMEOUT); + if (mwi >= 0) { + + if (mwi) { + interpret_command(woomera, &wmsg); + } else if (woomera_test_flag(woomera, WFLAG_EVENT)){ + while ((event_string = dequeue_event(woomera))) { + if (socket_printf(woomera->socket, "%s", event_string)) { + woomera_set_flag(woomera, WFLAG_MEDIA_END); + woomera_clear_flag(woomera, WFLAG_RUNNING); + smg_free(event_string); + log_printf(SMG_LOG_DEBUG_8, server.log, + "WOOMERA session (ptr=%p) print string error\n", + woomera); + break; + } + smg_free(event_string); + } + woomera_clear_flag(woomera, WFLAG_EVENT); + } + + if(woomera->check_digits) { + woomera_check_digits(woomera); + woomera->check_digits = 0; + } + + if (woomera->timeout > 0 && time(NULL) >= woomera->timeout) { + + /* Sent the hangup only after we sent a NACK */ + + log_printf(SMG_LOG_DEBUG_CALL, server.log, + "WOOMERA session Call Timedout ! [%s]\n", + woomera->interface); + + /* Let the Index check below send a NACK */ + if (woomera->index) { + woomera->q931_rel_cause_tosig=19; + woomera_set_flag(woomera, WFLAG_HANGUP); + } + break; + } + + } else { + log_printf(SMG_LOG_DEBUG_MISC, server.log, "WOOMERA session (ptr=%p) [%s] READ MSG Error %i \n", + woomera,woomera->interface,mwi); + break; + } + + } + +woomera_session_close: + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "WOOMERA session (ptr=%p) is dying [%s]: SR=%d WR=%d WF=0x%04X\n", + woomera,woomera->interface, + woomera_test_flag(&server.master_connection, WFLAG_RUNNING), + woomera_test_flag(woomera, WFLAG_RUNNING), + woomera->flags); + + + if (woomera_test_flag(woomera, WFLAG_MEDIA_RUNNING)) { + woomera_set_flag(woomera, WFLAG_MEDIA_END); + while(woomera_test_flag(woomera, WFLAG_MEDIA_RUNNING)) { + usleep(100); + sched_yield(); + } + } + + + /*********************************************** + * Identify the SPAN CHAN to be used below + ***********************************************/ + + chan = woomera->chan; + span = woomera->span; + + + if (!woomera_test_flag(woomera, WFLAG_HANGUP)) { + + /* The call was not HUNGUP. This is the last check, + If the call is valid, hungup the call if the call + was never up the keep going */ + + + if (smg_validate_span_chan(span,chan) == 0) { + + if (!woomera->index) { + + if (autoacm || woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + + woomera_set_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + isup_exec_command(span, + chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED, + woomera->q931_rel_cause_tosig); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK_SENT); + + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Woomera Sent SIGBOOST_EVENT_CALL_STOPPED [w%dg%d] [%s] ptr=%p\n", + span+1, chan+1,woomera->interface,woomera); + } else { + + woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + isup_exec_command(span, + chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK, + woomera->q931_rel_cause_tosig); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Woomera Sent SIGBOOST_EVENT_CALL_START_NACK [w%dg%d] [%s] ptr=%p\n", + span+1, chan+1,woomera->interface,woomera); + } + } else { + log_printf(SMG_LOG_ALL, woomera->log, "Woomera Not Sent CALL STOPPED - Instead NACK [w%dg%d] [%s] ptr=%p\n", + span+1, chan+1,woomera->interface,woomera); + + } + }else{ + /* This can happend if an outgoing call times out + or gets hungup before it gets acked. Its not a + failure */ + if (!woomera->index) { + /* In this case we really failed to tx STOP */ + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "FAILED: Woomera (R) SIGBOOST_EVENT_CALL_STOPPED [w%dg%d] [%s] Index=%d ptr=%p\n", + span+1, chan+1, woomera->interface, woomera->index, woomera); + } + } + + woomera_set_flag(woomera, WFLAG_HANGUP); + + } + +woo_re_hangup: + + /* We must send a STOP ACK to boost telling it that we are done */ + if (woomera_test_flag(woomera, WFLAG_HANGUP_ACK)) { + + /* SMG received a HANGUP from boost. + We must now send back the ACK to the HANGUP. + Boost will not release channel until we + ACK the hangup */ + + if (smg_validate_span_chan(span,chan) == 0) { + + isup_exec_command(span, + chan, + -1, + SIGBOOST_EVENT_CALL_STOPPED_ACK, + woomera->q931_rel_cause_tosig); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, + "Sent (Ack) to SIGBOOST_EVENT_CALL_STOPPED_ACK [w%dg%d] [%s] ptr=%p\n", + span+1,chan+1,woomera->interface,woomera); + + }else{ + /* This should never happen! If it does + we broke protocol */ + log_printf(SMG_LOG_ALL, woomera->log, + "FAILED: Woomera (R) SIGBOOST_EVENT_CALL_STOPPED_ACK [w%dg%d] [%s] Index=%d ptr=%p\n", + span+1, chan+1, woomera->interface, woomera->index, woomera); + } + + woomera_clear_flag(woomera, WFLAG_HANGUP_ACK); + woomera_set_flag(woomera, WFLAG_HANGUP); + } + + if (woomera_test_flag(woomera, WFLAG_HANGUP_NACK_ACK)) { + + /* SMG received a NACK from boost during call startup. + We must now send back the ACK to the NACK. + Boost will not release channel until we + ACK the NACK */ + + if (smg_validate_span_chan(span,chan) == 0) { + + isup_exec_command(span, + chan, + -1, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + woomera->q931_rel_cause_tosig); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, + "Sent (Nack Ack) to SIGBOOST_EVENT_CALL_START_NACK_ACK [w%dg%d] [%s] ptr=%p\n", + span+1,chan+1,woomera->interface,woomera); + + woomera->index=0; + + } else if (woomera->index) { + isup_exec_command(0, + 0, + woomera->index, + SIGBOOST_EVENT_CALL_START_NACK_ACK, + woomera->q931_rel_cause_tosig); + + woomera->index=0; + + } else { + log_printf(SMG_LOG_ALL, woomera->log, + "FAILED: Sent (Nack Ack) SIGBOOST_EVENT_CALL_START_NACK_ACK [w%dg%d] [%s] Index=%d ptr=%p\n", + span+1, chan+1, woomera->interface, woomera->index, woomera); + } + + woomera_clear_flag(woomera, WFLAG_HANGUP_NACK_ACK); + + } + + if (woomera->index) { + + int index = woomera->index; + + new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s" + "Unique-Call-Id: %s%s" + "Timeout: %ld%s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + woomera->interface, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + woomera->timeout, + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(18), + WOOMERA_LINE_SEPERATOR, + 18, + WOOMERA_RECORD_SEPERATOR + ); + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + new_woomera_event_printf(&wevent, "501 call was cancelled!%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + while ((event_string = dequeue_event(woomera))) { + socket_printf(woomera->socket, "%s", event_string); + smg_free(event_string); + } + + if (peek_from_holding_tank(index)) { + + woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + isup_exec_command(0, + 0, + index, + SIGBOOST_EVENT_CALL_START_NACK, + woomera->q931_rel_cause_tosig); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT); + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, + "Sent SIGBOOST_EVENT_CALL_START_NACK [Setup ID: %d] .. WAITING FOR NACK ACK\n", + index); + } else { + log_printf(SMG_LOG_PROD, woomera->log, + "Error Failed to Sent SIGBOOST_EVENT_CALL_START_NACK [Setup ID: %d] - index stale!\n", + index); + } + } + + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK)) { + int timeout_cnt=0; + int overall_cnt=0; + + /* SMG sent NACK to boost, however we have to wait + for boost to give us the ACK back before we + release resources. */ + + while (woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK)) { + timeout_cnt++; + if (timeout_cnt > 100) { //5sec timeout + timeout_cnt=0; + overall_cnt++; + + log_printf((overall_cnt==1)?0:4, woomera->log, + "Waiting for NACK ACK [Setup ID: %d] ... \n", + woomera->index_hold); + } + + if (overall_cnt > 15) { //50sec timeout + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING)) { + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ + break; + } + + /* If ACK comes in while we wait for NACK ACK, the ACk will + clear the tank causing NACK ACK never to clear the waiting flag + in this case we abort waiting for NACK ACK and we timeout + the wait */ + if (woomera_test_flag(&server.master_connection, WFLAG_CALL_ACKED)){ + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + + usleep(50000); + sched_yield(); + } + + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK); + + /* If ACK came in while waiting for NACK ACK, we TIMEOUT on purpose. + The ACK has blocked the tank id and now ony NACK ACK can unblock it. + THe only problem is that we blind and have to assume that NACK ACK + will eventually come in :) */ + if (!woomera_test_flag(&server.master_connection, WFLAG_CALL_ACKED)) { + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT)) { + log_printf(SMG_LOG_ALL, woomera->log, + "Waiting for NACK ACK [Setup ID: %d] .. TIMEOUT on NACK ACK\n", + woomera->index_hold); + + } else { + log_printf(SMG_LOG_DEBUG_8, woomera->log, + "Waiting for NACK ACK [Setup ID: %d] .. GOT NACK ACK\n", + woomera->index_hold); + } + } + } + + if (woomera_test_flag(woomera, WFLAG_EVENT)){ + while ((event_string = dequeue_event(woomera))) { + socket_printf(woomera->socket, "%s", event_string); + smg_free(event_string); + } + woomera_clear_flag(woomera, WFLAG_EVENT); + } + + + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK)) { + int timeout_cnt=0; + int overall_cnt=0; + + /* SMG sent HANGUP to boost, however we have to wait + for boost to give us the ACK back before we + release resources. */ + + log_printf(SMG_LOG_DEBUG_8, woomera->log, + "Waiting for STOPPED ACK [%s] [id=%i]... \n", + woomera->interface,woomera->index_hold); + + while (woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK)) { + timeout_cnt++; + if (timeout_cnt > 100) { //5sec timeout + timeout_cnt=0; + overall_cnt++; + log_printf((overall_cnt==1)?0:4, woomera->log, + "Waiting for STOPPED ACK [%s] [id=%i] %i... \n", + woomera->interface,woomera->index_hold,overall_cnt); + } + + if (overall_cnt > 15) { //50sec + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + + + if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING)) { + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + woomera_set_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT); + break; + } + + if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ + break; + } + + usleep(50000); + sched_yield(); + } + + woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); + + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT)) { + log_printf(SMG_LOG_ALL, woomera->log, + "Wait TIMEDOUT on STOPPED ACK [%s] [id=%i]... \n", + woomera->interface,woomera->index_hold); + + } else { + log_printf(SMG_LOG_DEBUG_8, woomera->log, + "Wait GOT STOPPED ACK [%s] [id=%i]... \n", + woomera->interface,woomera->index_hold); + } + } + + /***************************************************** + * We must wait for WFLAG_WAIT_FOR_STOPPED_ACK here + * so that STOP_ACK can access the woomera + *****************************************************/ + + if (smg_validate_span_chan(span,chan) == 0) { + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, + "WOOMERA Clearing Processs Table ... \n", + woomera->interface); + pthread_mutex_lock(&server.process_lock); + if (server.process_table[span][chan].dev == woomera){ + server.process_table[span][chan].dev = NULL; + memset(server.process_table[span][chan].session,0,SMG_SESSION_NAME_SZ); + memset(server.process_table[span][chan].digits,0, + sizeof(server.process_table[span][chan].digits)); + server.process_table[span][chan].digits_len = 0; + } + pthread_mutex_unlock(&server.process_lock); + } + +#if 0 +//Used for testing + if (1) { + int chan = woomera->chan; + int span = woomera->span; + if (smg_validate_span_chan(span,chan) == 0) { + pthread_mutex_lock(&server.process_lock); + /* This is possible in case media thread dies on startup */ + + if (server.process_table[span][chan]){ + log_printf(SMG_LOG_ALL, server.log, + "Sanity Span Chan Still in use: [w%dg%d] [%s] Index=%d ptr=%p\n", + span+1, chan+1, woomera->interface, woomera->index, woomera); + //server.process_table[span][chan] = NULL; + } + pthread_mutex_unlock(&server.process_lock); + } + } +#endif + + usleep(3000000); + + /* Sanity Check */ + if (woomera_test_flag(woomera, WFLAG_HANGUP_ACK)) { + log_printf(SMG_LOG_ALL, woomera->log, + "Woomera MISSED HANGUP ACK: Retry HANGUP ACK\n"); + goto woo_re_hangup; + } + if (woomera_test_flag(woomera, WFLAG_HANGUP_NACK_ACK)) { + log_printf(SMG_LOG_ALL, woomera->log, + "Woomera MISSED HANGUP ACK: Retry HANGUP NACK ACK\n"); + goto woo_re_hangup; + } + + + /* This is where we actually pull the index + * out of the tank. We had to keep the tank + * value until the end of the call. Tank is only + * used on outgoing calls. */ + if (woomera->index_hold >= 1) { + if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_ACK_TIMEOUT)) { + /* Replace real woomera interface with a dummy so + the tank is blocked. The real woomera interface will be + deallocated */ + pull_from_holding_tank(woomera->index_hold,-1,-1); + + if (check_tank_index(woomera->index_hold) != NULL){ + log_printf(SMG_LOG_PROD, woomera->log, "Woomera Thread: [%s] setup id %i blocked waiting for NACK or ACK\n", + woomera->interface ,woomera->index_hold); + } + } else { + clear_from_holding_tank(woomera->index_hold, woomera); + + } + woomera->index_hold=0; + } + + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "Woomera Thread Finished %u\n", (unsigned long) woomera->thread); + close_socket(&woomera->socket); + woomera->socket=-1; + + /* delete queue */ + while ((event_string = dequeue_event(woomera))) { + smg_free(event_string); + } + + if (woomera_test_flag(woomera, WFLAG_LISTENING)) { + del_listener(woomera); + } + + log_printf(SMG_LOG_DEBUG_CALL, server.log, "WOOMERA session for [%s] stopped (ptr=%p)\n", + woomera->interface,woomera); + + pthread_mutex_destroy(&woomera->queue_lock); + pthread_mutex_destroy(&woomera->ms_lock); + pthread_mutex_destroy(&woomera->dtmf_lock); + pthread_mutex_destroy(&woomera->vlock); + pthread_mutex_destroy(&woomera->flags_lock); + woomera_set_raw(woomera, NULL); + woomera_set_interface(woomera, NULL); + + woomera_message_clear(&wmsg); + + smg_free(woomera); + pthread_mutex_lock(&server.thread_count_lock); + server.call_count--; + server.thread_count--; + pthread_mutex_unlock(&server.thread_count_lock); + + pthread_exit(NULL); + return NULL; +} + + +static int launch_woomera_thread(struct woomera_interface *woomera) +{ + int result = 0; + pthread_attr_t attr; + + result = pthread_attr_init(&attr); + //pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + //pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + woomera_set_flag(woomera, WFLAG_RUNNING); + result = pthread_create(&woomera->thread, &attr, woomera_thread_run, woomera); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Woomera Thread! (%i) %s\n", + __FUNCTION__,result,strerror(errno)); + woomera_clear_flag(woomera, WFLAG_RUNNING); + } + pthread_attr_destroy(&attr); + + return result; +} + +static int launch_monitor_thread(void) +{ + pthread_attr_t attr; + int result = 0; + struct sched_param param; + + param.sched_priority = 10; + result = pthread_attr_init(&attr); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + result = pthread_attr_setschedparam (&attr, ¶m); + + log_printf(SMG_LOG_ALL,server.log,"%s: Old Priority =%i res=%i \n",__FUNCTION__, + param.sched_priority,result); + + + woomera_set_flag(&server.master_connection, WFLAG_MONITOR_RUNNING); + result = pthread_create(&server.monitor_thread, &attr, monitor_thread_run, NULL); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Thread! %s\n", + __FUNCTION__,strerror(errno)); + woomera_clear_flag(&server.master_connection, WFLAG_MONITOR_RUNNING); + } + pthread_attr_destroy(&attr); + + return result; +} + + +#ifdef WP_HPTDM_API +static void *hp_tdmapi_span_run(void *obj) +{ + hp_tdm_api_span_t *span = obj; + int err; + + log_printf(SMG_LOG_ALL,server.log,"Starting %s span!\n",span->ifname); + + while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + + if (!span->run_span) { + break; + } + + err = span->run_span(span); + if (err) { + break; + } + + } + + if (span->close_span) { + span->close_span(span); + } + + sleep(3); + log_printf(SMG_LOG_ALL,server.log,"Stopping %s span!\n",span->ifname); + + pthread_exit(NULL); +} + + +static int launch_hptdm_api_span_thread(int span) +{ + pthread_attr_t attr; + int result = 0; + struct sched_param param; + + param.sched_priority = 5; + result = pthread_attr_init(&attr); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + result = pthread_attr_setschedparam (&attr, ¶m); + + result = pthread_create(&server.monitor_thread, &attr, hp_tdmapi_span_run, &hptdmspan[span]); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Thread! %s\n", + __FUNCTION__,strerror(errno)); + } + pthread_attr_destroy(&attr); + + return result; +} +#endif + +static int configure_server(void) +{ + struct woomera_config cfg; + char *var, *val; + int cnt = 0; + + server.dtmf_intr_ch = -1; + + if (!woomera_open_file(&cfg, server.config_file)) { + log_printf(SMG_LOG_ALL, server.log, "open of %s failed\n", server.config_file); + return 0; + } + + log_printf(SMG_LOG_ALL,server.log, "\n======================= \n"); + log_printf(SMG_LOG_ALL,server.log, "Server - Configuration \n"); + log_printf(SMG_LOG_ALL,server.log, "======================= \n"); + + while (woomera_next_pair(&cfg, &var, &val)) { + if (!strcasecmp(var, "boost_local_ip")) { + strncpy(server.mcon.cfg.local_ip, val, + sizeof(server.mcon.cfg.local_ip) -1); + strncpy(server.mconp.cfg.local_ip, val, + sizeof(server.mconp.cfg.local_ip) -1); + log_printf(SMG_LOG_ALL,server.log, "Server - Boost Local IP: %s\n",val); + + cnt++; + } else if (!strcasecmp(var, "boost_local_port")) { + server.mcon.cfg.local_port = atoi(val); + server.mconp.cfg.local_port = + server.mcon.cfg.local_port+1; + log_printf(SMG_LOG_ALL,server.log, "Server - Boost Local Port: %i\n",server.mcon.cfg.local_port); + cnt++; + } else if (!strcasecmp(var, "boost_remote_ip")) { + strncpy(server.mcon.cfg.remote_ip, val, + sizeof(server.mcon.cfg.remote_ip) -1); + strncpy(server.mconp.cfg.remote_ip, val, + sizeof(server.mconp.cfg.remote_ip) -1); + log_printf(SMG_LOG_ALL,server.log, "Server - Boost Remote IP: %s\n",server.mcon.cfg.remote_ip); + cnt++; + } else if (!strcasecmp(var, "boost_remote_port")) { + server.mcon.cfg.remote_port = atoi(val); + server.mconp.cfg.remote_port = + server.mcon.cfg.remote_port+1; + log_printf(SMG_LOG_ALL,server.log, "Server - Boost Remote Port: %i\n",server.mcon.cfg.local_port); + cnt++; + } else if (!strcasecmp(var, "logfile_path")) { + if (!server.logfile_path) { + server.logfile_path = smg_strdup(val); + } + } else if (!strcasecmp(var, "woomera_port")) { + server.port = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Woomera Port: %i\n",server.port); + } else if (!strcasecmp(var, "debug_level")) { + server.debug = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Debug Level: %i\n",server.debug); + } else if (!strcasecmp(var, "out_tx_test")) { + server.out_tx_test = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Tx Media Dbg: %s\n",server.out_tx_test?"On":"Off (Default)"); + } else if (!strcasecmp(var, "loop_trace")) { + server.loop_trace = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Media Loop Trace: %s\n",server.loop_trace?"On":"Off (Default)"); + } else if (!strcasecmp(var, "rxgain")) { + server.rxgain = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Rx Gain: %d\n",server.rxgain); + } else if (!strcasecmp(var, "txgain")) { + server.txgain = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Tx Gain: %d\n",server.txgain); + } else if (!strcasecmp(var, "dtmf_on_duration")){ + server.dtmf_on = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - DTMF ON Duration: %d ms\n",server.dtmf_on); + } else if (!strcasecmp(var, "dtmf_off_duration")){ + server.dtmf_off = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - DTMF Off Duration: %d ms\n",server.dtmf_off); + } else if (!strcasecmp(var, "dtmf_inter_ch_duration")){ + server.dtmf_intr_ch = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - DTMF Spacing: %d\n",server.dtmf_intr_ch); + } else if (!strcasecmp(var, "strip_cid_non_digits")){ + server.strip_cid_non_digits = atoi(val); + log_printf(SMG_LOG_ALL,server.log, "Server - Strip non digits: %d\n",server.strip_cid_non_digits); + } else if (!strcasecmp(var, "max_calls")) { + int max = atoi(val); + if (max > 0) { + server.max_calls = max; + log_printf(SMG_LOG_ALL,server.log, "Server - Max Active Calls: %d\n",server.max_calls); + } + } else if (!strcasecmp(var, "autoacm")) { + int max = atoi(val); + if (max >= 0) { + autoacm=max; + log_printf(SMG_LOG_ALL,server.log, "Server - Auto ACM: %s\n",autoacm?"On":"Off (Default)"); + } + } else if (!strcasecmp(var, "max_spans")) { + int max = atoi(val); + if (max > 0) { + max_spans = max; + log_printf(SMG_LOG_ALL,server.log, "Server - Max Spans: %d\n",max_spans); + } + } else if (!strcasecmp(var, "call_timeout")) { + int max = atoi(val); + if (max >= 0) { + server.call_timeout=max; + } else { + server.call_timeout=SMG_DEFAULT_CALL_TIMEOUT; + } + log_printf(SMG_LOG_ALL,server.log, "Server - Call Comp Timeout: %d s\n",server.call_timeout); + + } else if (!strcasecmp(var, "base_media_port")) { + int base = atoi(val); + if (base >= 0) { + server.base_media_port = base; + server.next_media_port = base; + server.max_media_port = server.base_media_port + WOOMERA_MAX_MEDIA_PORTS; + log_printf(SMG_LOG_ALL,server.log, "Server - Base Media Port: %d\n",server.base_media_port); + } + + } else if (!strcasecmp(var, "max_media_ports")) { + int max = atoi(val); + if (max > WOOMERA_MAX_MEDIA_PORTS) { + server.max_media_port = server.base_media_port+max; + log_printf(SMG_LOG_ALL,server.log, "Server - Max Media Port: %d\n",server.max_media_port); + } + + } else if (!strcasecmp(var, "media_ip")) { + strncpy(server.media_ip, val, sizeof(server.media_ip) -1); + log_printf(SMG_LOG_ALL,server.log, "Server - Media IP: %s\n",server.media_ip); + } else { + log_printf(SMG_LOG_ALL, server.log, "Invalid Option %s at line %d!\n", var, cfg.lineno); + } + } + + log_printf(SMG_LOG_ALL,server.log, "======================= \n\n"); + + /* Post initialize */ + if (server.dtmf_on == 0){ + server.dtmf_on=SMG_DTMF_ON; + } + if (server.dtmf_off == 0) { + server.dtmf_off=SMG_DTMF_OFF; + } + if (server.dtmf_intr_ch == -1) { + server.dtmf_intr_ch = 0; + } + server.dtmf_size=(server.dtmf_on+server.dtmf_off)*10*2; + + log_printf(SMG_LOG_ALL,server.log, "DTMF On=%i Off=%i IntrCh=%i Size=%i\n", + server.dtmf_on,server.dtmf_off,server.dtmf_intr_ch,server.dtmf_size); + + woomera_close_file(&cfg); + return cnt == 4 ? 1 : 0; +} + + + +static int main_thread(void) +{ + + struct sockaddr_in sock_addr, client_addr; + struct woomera_interface *new_woomera; + int client_sock = -1, pid = 0; + unsigned int len = 0; + FILE *tmp; + + if ((server.master_connection.socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + fprintf(stderr,"%s:%d socket() failed %s\n",__FUNCTION__,__LINE__,strerror(errno)); + return 1; + } + + memset(&sock_addr, 0, sizeof(sock_addr)); /* Zero out structure */ + sock_addr.sin_family = AF_INET; /* Internet address family */ + sock_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ + sock_addr.sin_port = htons(server.port); /* Local port */ + + /* Bind to the local address */ + if (bind(server.master_connection.socket, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) < 0) { + fprintf(stderr,"%s:%d socket() bind %i failed %s\n",__FUNCTION__,__LINE__,server.port,strerror(errno)); + log_printf(SMG_LOG_ALL, server.log, "bind(%d) failed\n", server.port); + return 1; + } + + /* Mark the socket so it will listen for incoming connections */ + if (listen(server.master_connection.socket, MAXPENDING) < 0) { + fprintf(stderr,"%s:%d socket() listen failed %s\n",__FUNCTION__,__LINE__,strerror(errno)); + log_printf(SMG_LOG_ALL, server.log, "listen() failed\n"); + return 1; + } + + if ((pid = get_pid_from_file(PIDFILE))) { + fprintf(stderr,"%s:%d get pid file failed %s\n",__FUNCTION__,__LINE__,strerror(errno)); + log_printf(SMG_LOG_ALL, stderr, "pid %d already exists.\n", pid); + exit(0); + } + + if (!(tmp = safe_fopen(PIDFILE, "w"))) { + fprintf(stderr,"%s:%d open pid file failed %s\n",__FUNCTION__,__LINE__,strerror(errno)); + log_printf(SMG_LOG_ALL, stderr, "Error creating pidfile %s\n", PIDFILE); + return 1; + } else { + fprintf(tmp, "%d", getpid()); + fclose(tmp); + tmp = NULL; + } + + no_nagle(server.master_connection.socket); + +#if 0 + if (1) { + int span,chan; + call_signal_event_t event; +#if 0 + span=1; + chan=30; + event.span=span; + event.chan=chan; + launch_woomera_loop_thread(&event); +#else + for (span=0;span<8;span++) { + for (chan=0;chan<31;chan++) { + event.span=span; + event.chan=chan; + launch_woomera_loop_thread(&event); + } + } +#endif + } +#endif + +#ifdef WP_HPTDM_API + if (1) { + int span; + for (span=0;span<16;span++) { + hptdmspan[span] = sangoma_hptdm_api_span_init(span); + if (!hptdmspan[span]) { + break; + } else { + log_printf(SMG_LOG_ALL, server.log, "HP TDM API Span: %d configured...\n", + span); + launch_hptdm_api_span_thread(span); + } + } + } +#endif + + log_printf(SMG_LOG_PROD, server.log, "Main Process Started: Woomera Ready port: %d\n", server.port); + + while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) && + woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { + + /* Set the size of the in-out parameter */ + len = sizeof(client_addr); + + /* Wait for a client to connect */ + if ((client_sock = accept(server.master_connection.socket, (struct sockaddr *) &client_addr, &len)) < 0) { + /* Check if we are supposed to stop */ + if(!woomera_test_flag(&server.master_connection, WFLAG_RUNNING)) { + log_printf(SMG_LOG_ALL, server.log, "accept() stopped\n"); + return 0; + } + log_printf(SMG_LOG_ALL, server.log, "accept() failed\n"); + return 1; + } + + if ((new_woomera = new_woomera_interface(client_sock, &client_addr, len))) { + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Starting Thread for New Connection %s:%d Sock=%d\n", + inet_ntoa(new_woomera->addr.sin_addr), + ntohs(new_woomera->addr.sin_port), + client_sock); + + pthread_mutex_lock(&server.thread_count_lock); + server.call_count++; + pthread_mutex_unlock(&server.thread_count_lock); + + + if (launch_woomera_thread(new_woomera)) { + socket_printf(new_woomera->socket, + "501 call was cancelled!%s", + WOOMERA_RECORD_SEPERATOR); + + close_socket(&new_woomera->socket); + new_woomera->socket=-1; + smg_free(new_woomera); + log_printf(SMG_LOG_ALL, server.log, "ERROR: failed to launch woomera thread\n"); + } + } else { + log_printf(SMG_LOG_ALL, server.log, "Critical ERROR: memory/socket error\n"); + } + } + + log_printf(SMG_LOG_PROD, server.log, "Main Process End\n"); + + return 0; +} + +static int do_ignore(int sig) + +{ +#ifdef SMG_DROP_SEQ + drop_seq=4; +#endif + sdla_memdbg_free(0); + return 0; +} + +static int do_shut(int sig) +{ + woomera_clear_flag(&server.master_connection, WFLAG_RUNNING); + close_socket(&server.master_connection.socket); + log_printf(SMG_LOG_PROD, server.log, "Caught SIG %d, Closing Master Socket!\n", sig); + return 0; +} + +static int sangoma_tdm_init (int span) +{ +#ifdef LIBSANGOMA_GET_HWCODING + wanpipe_tdm_api_t tdm_api; + int fd=sangoma_open_tdmapi_span(span); + if (fd < 0 ){ + return -1; + } else { + server.hw_coding=sangoma_tdm_get_hw_coding(fd,&tdm_api); + close_socket(&fd); + } +#else +#error "libsangoma missing hwcoding feature: not up to date!" +#endif + return 0; +} + +static int woomera_startup(int argc, char **argv) +{ + int x = 0, pid = 0, bg = 0; + int span_cnt, chan_cnt; + char *cfg=NULL, *debug=NULL, *arg=NULL; + + while((arg = argv[x++])) { + + if (!strcasecmp(arg, "-hup")) { + if (! (pid = get_pid_from_file(PIDFILE))) { + log_printf(SMG_LOG_ALL, stderr, "Error reading pidfile %s\n", PIDFILE); + exit(1); + } else { + log_printf(SMG_LOG_ALL, stderr, "Killing PID %d\n", pid); + kill(pid, SIGHUP); + sleep(1); + exit(0); + } + + } else if (!strcasecmp(arg, "-term") || !strcasecmp(arg, "--term")) { + if (! (pid = get_pid_from_file(PIDFILE))) { + log_printf(SMG_LOG_ALL, stderr, "Error reading pidfile %s\n", PIDFILE); + exit(1); + } else { + log_printf(SMG_LOG_ALL, stderr, "Killing PID %d\n", pid); + kill(pid, SIGTERM); + unlink(PIDFILE); + sleep(1); + exit(0); + } + + } else if (!strcasecmp(arg, "-version")) { + fprintf(stdout, "\nSangoma Media Gateway: Version %s\n\n", SMG_VERSION); + exit(0); + + } else if (!strcasecmp(arg, "-help")) { + fprintf(stdout, "%s\n%s [-help] | [ -version] | [-hup] | [-wipe] | [[-bg] | [-debug ] | [-cfg ] | [-log ]]\n\n", WELCOME_TEXT, argv[0]); + exit(0); + } else if (!strcasecmp(arg, "-wipe")) { + unlink(PIDFILE); + } else if (!strcasecmp(arg, "-bg")) { + bg = 1; + + } else if (!strcasecmp(arg, "-g")) { + coredump = 1; + + } else if (!strcasecmp(arg, "-debug")) { + if (argv[x] && *(argv[x]) != '-') { + debug = argv[x++]; + } + } else if (!strcasecmp(arg, "-cfg")) { + if (argv[x] && *(argv[x]) != '-') { + cfg = argv[x++]; + } + } else if (!strcasecmp(arg, "-log")) { + if (argv[x] && *(argv[x]) != '-') { + server.logfile_path = smg_strdup(argv[x++]); + } + } else if (*arg == '-') { + log_printf(SMG_LOG_ALL, stderr, "Unknown Option %s\n", arg); + fprintf(stdout, "%s\n%s [-help] | [-hup] | [-wipe] | [[-bg] | [-debug ] | [-cfg ] | [-log ]]\n\n", WELCOME_TEXT, argv[0]); + exit(1); + } + } + + if (1){ + int spn; + for (spn=1;spn<=WOOMERA_MAX_SPAN;spn++) { + if (sangoma_tdm_init(spn) == 0) { + break; + } + } + if (spn>WOOMERA_MAX_SPAN) { + printf("\nError: Failed to access a channel on spans 1-16\n"); + printf(" Please start Wanpipe TDM API drivers\n"); + return 0; + } + } + + if (bg && (pid = fork())) { + log_printf(SMG_LOG_ALL, stderr, "Backgrounding!\n"); + return 0; + } + + for (span_cnt = 0; span_cnt < CORE_MAX_SPANS; span_cnt++) { + for (chan_cnt = 0; chan_cnt < CORE_MAX_CHAN_PER_SPAN; chan_cnt++) { + pthread_mutex_init(&server.process_table[span_cnt][chan_cnt].media_lock, NULL); + } + } + + q931_cause_setup(); + bearer_cap_setup(); + uil1p_to_str_setup(); + + server.port = 42420; + server.debug = 0; + strcpy(server.media_ip, "127.0.0.1"); + server.base_media_port = WOOMERA_MIN_MEDIA_PORT; + server.max_media_port = WOOMERA_MAX_MEDIA_PORT; + server.next_media_port = server.base_media_port; + server.log = stdout; + server.master_connection.socket = -1; + server.master_connection.event_queue = NULL; + server.config_file = cfg ? cfg : "/etc/sangoma_mgd.conf"; + pthread_mutex_init(&server.listen_lock, NULL); + pthread_mutex_init(&server.ht_lock, NULL); + pthread_mutex_init(&server.process_lock, NULL); + pthread_mutex_init(&server.digits_lock, NULL); + pthread_mutex_init(&server.media_udp_port_lock, NULL); + pthread_mutex_init(&server.thread_count_lock, NULL); + pthread_mutex_init(&server.master_connection.queue_lock, NULL); + pthread_mutex_init(&server.master_connection.flags_lock, NULL); + pthread_mutex_init(&server.mcon.lock, NULL); + server.master_connection.chan = -1; + server.master_connection.span = -1; + server.call_timeout=SMG_DEFAULT_CALL_TIMEOUT; + + if (smg_log_init()) { + printf("Error: Logger Launch Failed\n"); + fprintf(stderr, "Error: Logger Launch Failed\n"); + return 0; + } + + if (!configure_server()) { + log_printf(SMG_LOG_ALL, server.log, "configuration failed!\n"); + return 0; + } + +#ifndef USE_SYSLOG + if (server.logfile_path) { + if (!(server.log = safe_fopen(server.logfile_path, "a"))) { + log_printf(SMG_LOG_ALL, stderr, "Error setting logfile %s!\n", server.logfile_path); + server.log = stderr; + return 0; + } + } +#endif + + + if (debug) { + server.debug = atoi(debug); + } + + if (coredump) { + struct rlimit l; + memset(&l, 0, sizeof(l)); + l.rlim_cur = RLIM_INFINITY; + l.rlim_max = RLIM_INFINITY; + if (setrlimit(RLIMIT_CORE, &l)) { + log_printf(SMG_LOG_ALL, stderr, "Warning: Failed to disable core size limit: %s\n", + strerror(errno)); + } + } + +#ifdef __LINUX__ + if (geteuid() && coredump) { + if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) { + log_printf(SMG_LOG_ALL, stderr, "Warning: Failed to disable core size limit for non-root: %s\n", + strerror(errno)); + } + } +#endif + + + + (void) signal(SIGINT,(void *) do_shut); + (void) signal(SIGTERM,(void *) do_shut); + (void) signal(SIGPIPE,(void *) do_ignore); + (void) signal(SIGUSR1,(void *) do_ignore); + (void) signal(SIGHUP,(void *) do_shut); + + /* Wait for logger thread to start */ + usleep(5000); + + woomera_set_flag(&server.master_connection, WFLAG_RUNNING); + if (launch_monitor_thread()) { + woomera_clear_flag(&server.master_connection, WFLAG_RUNNING); + smg_log_cleanup(); + return 0; + } + + fprintf(stderr, "%s", WELCOME_TEXT); + + log_printf(SMG_LOG_ALL, stderr, "Woomera STARTUP Complete. [AutoACM=%i SDigit=%i]\n", + autoacm,server.strip_cid_non_digits); + return 1; +} + +static int woomera_shutdown(void) +{ + char *event_string; + int told = 0, loops = 0; + int span_cnt, chan_cnt; + + for (span_cnt = 0; span_cnt < CORE_MAX_SPANS; span_cnt++) { + for (chan_cnt = 0; chan_cnt < CORE_MAX_CHAN_PER_SPAN; chan_cnt++) { + pthread_mutex_destroy(&server.process_table[span_cnt][chan_cnt].media_lock); + } + } + + close_socket(&server.master_connection.socket); + pthread_mutex_destroy(&server.listen_lock); + pthread_mutex_destroy(&server.ht_lock); + pthread_mutex_destroy(&server.process_lock); + pthread_mutex_destroy(&server.digits_lock); + pthread_mutex_destroy(&server.media_udp_port_lock); + pthread_mutex_destroy(&server.thread_count_lock); + pthread_mutex_destroy(&server.master_connection.queue_lock); + pthread_mutex_destroy(&server.master_connection.flags_lock); + pthread_mutex_destroy(&server.mcon.lock); + woomera_clear_flag(&server.master_connection, WFLAG_RUNNING); + + + if (server.logfile_path) { + smg_free(server.logfile_path); + server.logfile_path = NULL; + } + + /* delete queue */ + while ((event_string = dequeue_event(&server.master_connection))) { + smg_free(event_string); + } + + while(server.thread_count > 0) { + loops++; + + if (loops % 1000 == 0) { + told = 0; + } + + if (loops > 10000) { + log_printf(SMG_LOG_ALL, server.log, "Red Alert! threads did not stop\n"); + assert(server.thread_count == 0); + } + + if (told != server.thread_count) { + log_printf(SMG_LOG_PROD, server.log, "Waiting For %d thread%s.\n", + server.thread_count, server.thread_count == 1 ? "" : "s"); + told = server.thread_count; + } + ysleep(10000); + } + unlink(PIDFILE); + log_printf(SMG_LOG_ALL, stderr, "Woomera SHUTDOWN Complete.\n"); + + smg_log_cleanup(); + + return 0; +} + +int main(int argc, char *argv[]) +{ + int ret = 0; + + memset(&server,0,sizeof(server)); + memset(&woomera_dead_dev,0,sizeof(woomera_dead_dev)); + + mlockall(MCL_FUTURE); + memset(&server, 0, sizeof(server)); + memset(&woomera_dead_dev, 0, sizeof(woomera_dead_dev)); + + ret=nice(-5); + + sdla_memdbg_init(); + + server.hw_coding=0; + + openlog (ps_progname ,LOG_PID, LOG_LOCAL2); + + if (! (ret = woomera_startup(argc, argv))) { + exit(0); + } + ret = main_thread(); + + woomera_shutdown(); + + sdla_memdbg_free(1); + + return ret; +} + +/** EMACS ** + * Local variables: + * mode: C + * c-basic-offset: 4 + * End: + */ + diff --git a/ssmg/sangoma_mgd.trunk/Makefile b/ssmg/sangoma_mgd.trunk/Makefile index 67d8e83..b41306c 100644 --- a/ssmg/sangoma_mgd.trunk/Makefile +++ b/ssmg/sangoma_mgd.trunk/Makefile @@ -33,12 +33,34 @@ CC = gcc ifndef DESTDIR ifdef INSTALLPREFIX - DESTDIR=$(INSTALL_PREFIX) + DESTDIR=$(INSTALLPREFIX) else DESTDIR= endif endif +ifndef BRI +BRI:=NO +ifneq (,$(wildcard ./.bri)) + BRI=$(shell cat .bri) +endif +endif + +ifndef PRI +PRI:=NO +ifneq (,$(wildcard ./.pri)) + PRI=$(shell cat .pri) +endif +endif + + +$(shell echo $(BRI) > .bri) +$(shell echo $(PRI) > .pri) + + +SAFE_D=/etc/wanpipe/safe_sangoma.d +SMGCTRL_D=/etc/wanpipe/smg_ctrl.d + INCLUDES = -I ../../ssmg/libsangoma.trunk -I. -I ../../patches/kdrivers/include -I ../../patches/kdrivers/wanec/oct6100_api/include -I ../../patches/kdrivers/wanec -I/usr/local/include -I../../patches/kdrivers/include -I/usr/include/wanpipe -Ilib/libteletone/src CFLAGS = -D__LINUX__ -D_REENTRANT -D_GNU_SOURCE -O6 @@ -91,12 +113,15 @@ sangoma_mgd_memdbg.o: sangoma_mgd_memdbg.c sangoma_mgd_memdbg.h sangoma_mgd_logger.o: sangoma_mgd_logger.c $(CC) $(CCFLAGS) $(INCLUDES) $(CFLAGS) -c -o sangoma_mgd_logger.o sangoma_mgd_logger.c +sangoma_mgd_ip_bridge.o: sangoma_mgd_ip_bridge.c + $(CC) $(CCFLAGS) $(INCLUDES) $(CFLAGS) -c -o sangoma_mgd_ip_bridge.o sangoma_mgd_ip_bridge.c + sangoma_mgd.o: sangoma_mgd.c sangoma_mgd.h sigboost.h $(CC) $(CCFLAGS) $(INCLUDES) $(CFLAGS) -c -o sangoma_mgd.o sangoma_mgd.c -sangoma_mgd: sangoma_mgd.o sangoma_mgd_memdbg.o sangoma_mgd_logger.o call_signal.o switch_buffer.o sigboost.h sangoma_mgd_memdbg.h +sangoma_mgd: sangoma_mgd.o sangoma_mgd_memdbg.o sangoma_mgd_logger.o sangoma_mgd_ip_bridge.o call_signal.o switch_buffer.o sigboost.h sangoma_mgd_memdbg.h rm -fr core* - $(CC) $(CCFLAGS) $(INCLUDES) $(CFLAGS) -o sangoma_mgd sangoma_mgd.o sangoma_mgd_memdbg.o sangoma_mgd_logger.o switch_buffer.o call_signal.o $(LDFLAGS) + $(CC) $(CCFLAGS) $(INCLUDES) $(CFLAGS) -o sangoma_mgd sangoma_mgd.o sangoma_mgd_memdbg.o sangoma_mgd_logger.o sangoma_mgd_ip_bridge.o switch_buffer.o call_signal.o $(LDFLAGS) @@ -117,29 +142,49 @@ install_smg: old_cleanup fi install -D -m 755 ./safe_sangoma $(DESTDIR)/usr/sbin/safe_sangoma - install -D -m 755 ./__smg_ctrl_common $(DESTDIR)/usr/sbin/__smg_ctrl_common + install -D -m 755 ./smg_ctrl $(DESTDIR)/usr/sbin/smg_ctrl + + @if [ ! -f $(DESTDIR)/etc/wanpipe/safe_sangoma.rc ]; then \ + install -D -m 755 ./rc/safe_sangoma.rc $(DESTDIR)/etc/wanpipe/safe_sangoma.rc; \ + fi + + @if [ ! -d ${SAFE_D} ]; then \ + mkdir -p ${SAFE_D}; \ + fi + @if [ ! -d ${SMGCTRL_D} ]; then \ + mkdir -p ${SMGCTRL_D}; \ + fi + ifeq "${PRI}" "YES" @echo "PRI control scripts installed" - @if [ ! -e $(INSTALLPREFIX)/etc/sangoma_mgd.conf ]; then \ - install -D -m 755 sangoma_mgd.conf.sample.pri $(INSTALLPREFIX)/etc/sangoma_mgd.conf; \ + @if [ ! -e $(DESTDIR)/etc/sangoma_mgd.conf ]; then \ + install -D -m 755 sangoma_mgd.conf.sample.pri $(DESTDIR)/etc/sangoma_mgd.conf; \ fi - install -D -m 755 smg_ctrl_pri $(INSTALLPREFIX)/usr/sbin/smg_ctrl + install -D -m 755 rc/smg.rc.pri $(DESTDIR)/etc/wanpipe/smg.rc else ifeq "${BRI}" "YES" @echo "BRI control scripts installed" @if [ ! -e $(DESTDIR)/etc/sangoma_mgd.conf ]; then \ install -D -m 755 sangoma_mgd.conf.sample.bri $(DESTDIR)/etc/sangoma_mgd.conf; \ fi - install -D -m 755 smg_ctrl_bri $(DESTDIR)/usr/sbin/smg_ctrl + install -D -m 755 rc/smg.rc.bri $(DESTDIR)/etc/wanpipe/smg.rc else @echo "SS7 control scripts installed" @if [ ! -e $(DESTDIR)/etc/sangoma_mgd.conf ]; then \ install -D -m 755 sangoma_mgd.conf.sample.ss7 $(DESTDIR)/etc/sangoma_mgd.conf; \ fi - install -D -m 755 smg_ctrl_ss7 $(DESTDIR)/usr/sbin/smg_ctrl + @if [ -e $(DESTDIR)/usr/local/ss7box/ss7boost ]; then \ + echo "install -D -m 755 rc/smg.rc.ss7boost $(DESTDIR)/etc/wanpipe/smg.rc"; \ + install -D -m 755 rc/smg.rc.ss7boost $(DESTDIR)/etc/wanpipe/smg.rc; \ + else \ + echo "install -D -m 755 rc/smg.rc.isupd $(DESTDIR)/etc/wanpipe/smg.rc"; \ + install -D -m 755 rc/smg.rc.isupd $(DESTDIR)/etc/wanpipe/smg.rc; \ + fi + install -D -m 755 scripts/init.d/smgss7_init_ctrl $(DESTDIR)/etc/init.d/smgss7_init_ctrl install -D -m 755 scripts/init.d/smgss7_init_ctrl $(DESTDIR)/usr/sbin/smgss7_init_ctrl + install -D -m 755 scripts/init.d/ss7boxd_monitor.sh $(DESTDIR)/usr/sbin/ss7boxd_monitor.sh endif endif diff --git a/ssmg/sangoma_mgd.trunk/app/.svn/entries b/ssmg/sangoma_mgd.trunk/app/.svn/entries index fd63e95..d754f0d 100644 --- a/ssmg/sangoma_mgd.trunk/app/.svn/entries +++ b/ssmg/sangoma_mgd.trunk/app/.svn/entries @@ -1,7 +1,7 @@ 8 dir -193 +276 https://www.sangomapbx.com/svn/sangoma_mgd/trunk/app https://www.sangomapbx.com/svn/sangoma_mgd @@ -32,7 +32,7 @@ file -2009-03-30T18:03:11.000000Z +2009-08-25T20:44:41.000000Z 0b2da6b313f90b443457c4815ab95dab 2007-09-21T20:53:51.260136Z 1 @@ -45,7 +45,7 @@ file -2009-03-30T18:03:11.000000Z +2009-08-25T20:44:41.000000Z 604a892b5a763fef18fe92aa29f82bc9 2007-09-21T20:53:51.260136Z 1 diff --git a/ssmg/sangoma_mgd.trunk/call_signal.c b/ssmg/sangoma_mgd.trunk/call_signal.c index 4ead23b..db0decd 100644 --- a/ssmg/sangoma_mgd.trunk/call_signal.c +++ b/ssmg/sangoma_mgd.trunk/call_signal.c @@ -38,7 +38,8 @@ static struct call_signal_map call_signal_table[] = { {SIGBOOST_EVENT_HEARTBEAT, "HEARTBEAT"}, {SIGBOOST_EVENT_INSERT_CHECK_LOOP, "LOOP START"}, {SIGBOOST_EVENT_REMOVE_CHECK_LOOP, "LOOP STOP"}, - {SIGBOOST_EVENT_DIGIT_IN, "DIGIT_IN"} + {SIGBOOST_EVENT_DIGIT_IN, "DIGIT_IN"}, + {SIGBOOST_EVENT_CALL_PROGRESS, "CALL_PROGRESS"} }; #define USE_SCTP 1 @@ -79,6 +80,22 @@ static int create_udp_socket(call_signal_connection_t *mcon, char *local_ip, int (char *)&flag, sizeof(int)); #endif + + rc = fcntl(mcon->socket, F_GETFL); + if ( rc < 0 ) { + close(mcon->socket); + mcon->socket = -1; + return mcon->socket; + } + + rc |= O_NONBLOCK; + rc = fcntl(mcon->socket, F_SETFL, rc); + if ( rc < 0 ) { + close(mcon->socket); + mcon->socket = -1; + return mcon->socket; + } + if ((rc = bind(mcon->socket, (struct sockaddr *) &mcon->local_addr, sizeof(mcon->local_addr))) < 0) { @@ -89,7 +106,7 @@ static int create_udp_socket(call_signal_connection_t *mcon, char *local_ip, int rc=listen(mcon->socket,100); if (rc) { close(mcon->socket); - mcon->socket = -1; + mcon->socket = -1; } #endif } @@ -119,23 +136,24 @@ static int smg_event_dbg=SMG_LOG_BOOST; static void clog_print_event_call(call_signal_connection_t *mcon,call_signal_event_t *event, int priority, int dir) { clog_printf((event->event_id==SIGBOOST_EVENT_HEARTBEAT)?SMG_LOG_DEBUG_CALL:smg_event_dbg, mcon->log, - "%s EVENT (%s): %s:(%X) [w%dg%d] CSid=%i Seq=%i Cn=[%s] Cd=[%s] Ci=[%s]\n", + "%s EVENT (%s): %s:(%X) [s%dc%d] Tg=%i CSid=%i Seq=%i Cn=[%s] Cd=[%s] Ci=[%s]\n", dir ? "TX":"RX", priority ? "P":"N", call_signal_event_id_name(event->event_id), event->event_id, event->span+1, event->chan+1, + event->trunk_group, event->call_setup_id, event->fseqno, - strlen(event->calling_name)?event->calling_name:"N/A", + 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")); } static void clog_print_event_short(call_signal_connection_t *mcon,short_signal_event_t *event, int priority, int dir) { clog_printf((event->event_id==SIGBOOST_EVENT_HEARTBEAT)?SMG_LOG_DEBUG_CALL:smg_event_dbg, mcon->log, - "%s EVENT (%s): %s:(%X) [w%dg%d] Rc=%i CSid=%i Seq=%i \n", + "%s EVENT (%s): %s:(%X) [s%dc%d] Rc=%i CSid=%i Seq=%i \n", dir ? "TX":"RX", priority ? "P":"N", call_signal_event_id_name(event->event_id), @@ -213,6 +231,9 @@ call_signal_event_t *call_signal_connection_read(call_signal_connection_t *mcon, mcon->event.event_id,mcon->rxseq,mcon->event.fseqno); clog_printf(SMG_LOG_ALL, mcon->log, "------------------------------------------\n"); + + /* NC: Recover from a sequence error */ + mcon->rxseq = mcon->event.fseqno; } #ifdef SANGOMA_UNIT_TESTER @@ -350,11 +371,10 @@ int call_signal_connection_writep(call_signal_connection_t *mcon, call_signal_ev } #endif - pthread_mutex_lock(&mcon->lock); event->version=SIGBOOST_VERSION; + /* Sendto is thread safe so no lock needed */ err=sendto(mcon->socket, event, event_size, 0, (struct sockaddr *) &mcon->remote_addr, sizeof(mcon->remote_addr)); - pthread_mutex_unlock(&mcon->lock); if (err != event_size) { err = -1; @@ -422,8 +442,13 @@ int call_signal_connection_write(call_signal_connection_t *mcon, call_signal_eve 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)); + + if (err != event_size) { + mcon->txseq--; + } pthread_mutex_unlock(&mcon->lock); if (err != event_size) { diff --git a/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.ast14_check b/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.ast14_check index d00491f..0cfbf08 100644 --- a/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.ast14_check +++ b/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.ast14_check @@ -1 +1 @@ -1 +2 diff --git a/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.ast_src_check b/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.ast_src_check index 573541a..d00491f 100644 --- a/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.ast_src_check +++ b/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.ast_src_check @@ -1 +1 @@ -0 +1 diff --git a/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.cw_module_info_check b/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.cw_module_info_check deleted file mode 100644 index 573541a..0000000 --- a/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.cw_module_info_check +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.log b/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.log index 8b5589f..5e1335c 100644 --- a/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.log +++ b/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.log @@ -1,144 +1,490 @@ -AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -grep -c AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -opbx_cli_entry /usr/src/asterisk/include/callweaver/cli.h .opbx_cli_entry_check -echo 0 .opbx_cli_entry_check -cw_cli_entry /usr/src/asterisk/include/callweaver/cli.h .cw_cli_entry_check -echo 0 .cw_cli_entry_check -MODULE_INFO /usr/src/asterisk/include/callweaver/module.h .cw_module_info_check -echo 0 .cw_module_info_check -1.6 /usr/src/asterisk/.version .ast16_check -grep -c 1.6 /usr/src/asterisk/.version .ast16_check -1.4 /usr/src/asterisk/.version .ast14_check -grep -c 1.4 /usr/src/asterisk/.version .ast14_check -AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -grep -c AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -opbx_cli_entry /usr/src/asterisk/include/callweaver/cli.h .opbx_cli_entry_check -echo 0 .opbx_cli_entry_check -cw_cli_entry /usr/src/asterisk/include/callweaver/cli.h .cw_cli_entry_check -echo 0 .cw_cli_entry_check -MODULE_INFO /usr/src/asterisk/include/callweaver/module.h .cw_module_info_check -echo 0 .cw_module_info_check -1.6 /usr/src/asterisk/.version .ast16_check -grep -c 1.6 /usr/src/asterisk/.version .ast16_check -1.4 /usr/src/asterisk/.version .ast14_check -grep -c 1.4 /usr/src/asterisk/.version .ast14_check -AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -grep -c AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -opbx_cli_entry /usr/src/asterisk/include/callweaver/cli.h .opbx_cli_entry_check -echo 0 .opbx_cli_entry_check -cw_cli_entry /usr/src/asterisk/include/callweaver/cli.h .cw_cli_entry_check -echo 0 .cw_cli_entry_check -MODULE_INFO /usr/src/asterisk/include/callweaver/module.h .cw_module_info_check -echo 0 .cw_module_info_check -1.6 /usr/src/asterisk/.version .ast16_check -grep -c 1.6 /usr/src/asterisk/.version .ast16_check -1.4 /usr/src/asterisk/.version .ast14_check -grep -c 1.4 /usr/src/asterisk/.version .ast14_check -AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -grep -c AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -opbx_cli_entry /usr/src/asterisk/include/callweaver/cli.h .opbx_cli_entry_check -echo 0 .opbx_cli_entry_check -cw_cli_entry /usr/src/asterisk/include/callweaver/cli.h .cw_cli_entry_check -echo 0 .cw_cli_entry_check -MODULE_INFO /usr/src/asterisk/include/callweaver/module.h .cw_module_info_check -echo 0 .cw_module_info_check -1.6 /usr/src/asterisk/.version .ast16_check -grep -c 1.6 /usr/src/asterisk/.version .ast16_check -1.4 /usr/src/asterisk/.version .ast14_check -grep -c 1.4 /usr/src/asterisk/.version .ast14_check -AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -grep -c AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -opbx_cli_entry /usr/src/asterisk/include/callweaver/cli.h .opbx_cli_entry_check -echo 0 .opbx_cli_entry_check -cw_cli_entry /usr/src/asterisk/include/callweaver/cli.h .cw_cli_entry_check -echo 0 .cw_cli_entry_check -MODULE_INFO /usr/src/asterisk/include/callweaver/module.h .cw_module_info_check -echo 0 .cw_module_info_check -1.6 /usr/src/asterisk/.version .ast16_check -grep -c 1.6 /usr/src/asterisk/.version .ast16_check -1.4 /usr/src/asterisk/.version .ast14_check -grep -c 1.4 /usr/src/asterisk/.version .ast14_check -AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -grep -c AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -opbx_cli_entry /usr/src/asterisk/include/callweaver/cli.h .opbx_cli_entry_check -echo 0 .opbx_cli_entry_check -cw_cli_entry /usr/src/asterisk/include/callweaver/cli.h .cw_cli_entry_check -echo 0 .cw_cli_entry_check -MODULE_INFO /usr/src/asterisk/include/callweaver/module.h .cw_module_info_check -echo 0 .cw_module_info_check -1.6 /usr/src/asterisk/.version .ast16_check -grep -c 1.6 /usr/src/asterisk/.version .ast16_check -1.4 /usr/src/asterisk/.version .ast14_check -grep -c 1.4 /usr/src/asterisk/.version .ast14_check -AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -grep -c AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -opbx_cli_entry /usr/src/asterisk/include/callweaver/cli.h .opbx_cli_entry_check -echo 0 .opbx_cli_entry_check -cw_cli_entry /usr/src/asterisk/include/callweaver/cli.h .cw_cli_entry_check -echo 0 .cw_cli_entry_check -MODULE_INFO /usr/src/asterisk/include/callweaver/module.h .cw_module_info_check -echo 0 .cw_module_info_check -1.6 /usr/src/asterisk/.version .ast16_check -grep -c 1.6 /usr/src/asterisk/.version .ast16_check -1.4 /usr/src/asterisk/.version .ast14_check -grep -c 1.4 /usr/src/asterisk/.version .ast14_check -AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -grep -c AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -opbx_cli_entry /usr/src/asterisk/include/callweaver/cli.h .opbx_cli_entry_check -echo 0 .opbx_cli_entry_check -cw_cli_entry /usr/src/asterisk/include/callweaver/cli.h .cw_cli_entry_check -echo 0 .cw_cli_entry_check -MODULE_INFO /usr/src/asterisk/include/callweaver/module.h .cw_module_info_check -echo 0 .cw_module_info_check -1.6 /usr/src/asterisk/.version .ast16_check -grep -c 1.6 /usr/src/asterisk/.version .ast16_check -1.4 /usr/src/asterisk/.version .ast14_check -grep -c 1.4 /usr/src/asterisk/.version .ast14_check -AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -grep -c AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -opbx_cli_entry /usr/src/asterisk/include/callweaver/cli.h .opbx_cli_entry_check -echo 0 .opbx_cli_entry_check -cw_cli_entry /usr/src/asterisk/include/callweaver/cli.h .cw_cli_entry_check -echo 0 .cw_cli_entry_check -MODULE_INFO /usr/src/asterisk/include/callweaver/module.h .cw_module_info_check -echo 0 .cw_module_info_check -1.6 /usr/src/asterisk/.version .ast16_check -grep -c 1.6 /usr/src/asterisk/.version .ast16_check -1.4 /usr/src/asterisk/.version .ast14_check -grep -c 1.4 /usr/src/asterisk/.version .ast14_check -AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -grep -c AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -opbx_cli_entry /usr/src/asterisk/include/callweaver/cli.h .opbx_cli_entry_check -echo 0 .opbx_cli_entry_check -cw_cli_entry /usr/src/asterisk/include/callweaver/cli.h .cw_cli_entry_check -echo 0 .cw_cli_entry_check -MODULE_INFO /usr/src/asterisk/include/callweaver/module.h .cw_module_info_check -echo 0 .cw_module_info_check -1.6 /usr/src/asterisk/.version .ast16_check -grep -c 1.6 /usr/src/asterisk/.version .ast16_check -1.4 /usr/src/asterisk/.version .ast14_check -grep -c 1.4 /usr/src/asterisk/.version .ast14_check -AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -grep -c AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -opbx_cli_entry /usr/src/asterisk/include/callweaver/cli.h .opbx_cli_entry_check -echo 0 .opbx_cli_entry_check -cw_cli_entry /usr/src/asterisk/include/callweaver/cli.h .cw_cli_entry_check -echo 0 .cw_cli_entry_check -MODULE_INFO /usr/src/asterisk/include/callweaver/module.h .cw_module_info_check -echo 0 .cw_module_info_check -1.6 /usr/src/asterisk/.version .ast16_check -grep -c 1.6 /usr/src/asterisk/.version .ast16_check -1.4 /usr/src/asterisk/.version .ast14_check -grep -c 1.4 /usr/src/asterisk/.version .ast14_check -AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -grep -c AST_CONTROL_SRC /usr/src/asterisk/include/asterisk/frame.h .ast_src_check -opbx_cli_entry /usr/src/asterisk/include/callweaver/cli.h .opbx_cli_entry_check -echo 0 .opbx_cli_entry_check -cw_cli_entry /usr/src/asterisk/include/callweaver/cli.h .cw_cli_entry_check -echo 0 .cw_cli_entry_check -MODULE_INFO /usr/src/asterisk/include/callweaver/module.h .cw_module_info_check -echo 0 .cw_module_info_check -1.6 /usr/src/asterisk/.version .ast16_check -grep -c 1.6 /usr/src/asterisk/.version .ast16_check -1.4 /usr/src/asterisk/.version .ast14_check -grep -c 1.4 /usr/src/asterisk/.version .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check +AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +grep -c AST_CONTROL_SRC /usr/include/asterisk/frame.h .ast_src_check +opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +grep -c opbx_cli_entry /usr/include/asterisk/cli.h .opbx_cli_entry_check +cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +grep -c cw_cli_entry /usr/include/asterisk/cli.h .cw_cli_entry_check +1.6 /usr/include/asterisk/.version .ast16_check +echo 0 .ast16_check +1.4 /usr/include/asterisk/.version .ast14_check +echo 0 .ast14_check +1.6 /usr/include/asterisk/version.h .ast16_check +grep -c 1.6 /usr/include/asterisk/version.h .ast16_check +1.4 /usr/include/asterisk/version.h .ast14_check +grep -c 1.4 /usr/include/asterisk/version.h .ast14_check diff --git a/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.pbxdir b/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.pbxdir index 4d2816d..b2df86a 100644 --- a/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.pbxdir +++ b/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.pbxdir @@ -1 +1 @@ -/usr/src/asterisk +/usr/include/asterisk diff --git a/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.svn/all-wcprops b/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.svn/all-wcprops index d03de49..f538316 100644 --- a/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.svn/all-wcprops +++ b/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.svn/all-wcprops @@ -1,13 +1,13 @@ K 25 svn:wc:ra_dav:version-url V 36 -/svn/chan_woomera/!svn/ver/117/trunk +/svn/chan_woomera/!svn/ver/145/trunk END chan_woomera.c K 25 svn:wc:ra_dav:version-url V 51 -/svn/chan_woomera/!svn/ver/117/trunk/chan_woomera.c +/svn/chan_woomera/!svn/ver/145/trunk/chan_woomera.c END g711.h K 25 @@ -31,7 +31,7 @@ Makefile K 25 svn:wc:ra_dav:version-url V 45 -/svn/chan_woomera/!svn/ver/111/trunk/Makefile +/svn/chan_woomera/!svn/ver/131/trunk/Makefile END README K 25 @@ -42,6 +42,6 @@ END woomera.conf K 25 svn:wc:ra_dav:version-url -V 48 -/svn/chan_woomera/!svn/ver/97/trunk/woomera.conf +V 49 +/svn/chan_woomera/!svn/ver/138/trunk/woomera.conf END diff --git a/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.svn/entries b/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.svn/entries index 4c61d7e..8dbe69c 100644 --- a/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.svn/entries +++ b/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.svn/entries @@ -1,15 +1,15 @@ 8 dir -117 +145 https://www.sangomapbx.com/svn/chan_woomera/trunk https://www.sangomapbx.com/svn/chan_woomera -2009-07-16T21:31:38.540981Z -117 -ncorbic +2010-03-19T17:36:36.398260Z +145 +davidy svn:special svn:externals svn:needs-lock @@ -32,11 +32,11 @@ file -2009-07-17T19:30:44.000000Z -49f4e6b0a5c6406879aad08e7630066c -2009-07-16T21:31:38.540981Z -117 -ncorbic +2010-03-23T16:12:54.000000Z +f8cd84d8d36b819badd469b59d1678c4 +2010-03-19T17:36:36.398260Z +145 +davidy g711.h file @@ -44,7 +44,7 @@ file -2009-03-30T18:03:40.000000Z +2009-08-25T20:44:41.000000Z 0f725f95ced42af15dcaef21f3a1722b 2007-09-21T20:29:00.887216Z 1 @@ -56,7 +56,7 @@ file -2009-06-23T19:02:26.000000Z +2009-08-25T20:44:41.000000Z 87692613e1994883c4f56dfd86056347 2009-06-23T19:36:48.898376Z 113 @@ -68,7 +68,7 @@ file -2009-04-16T18:25:03.000000Z +2009-08-25T20:44:41.000000Z 0eb3ff60c8a8a748512ad6fe7c3fec5f 2009-04-14T01:12:18.691621Z 109 @@ -81,10 +81,10 @@ file -2009-05-01T15:11:07.000000Z -699049f068b9ca61569cc469cb8c7ad4 -2009-04-22T20:06:05.265796Z -111 +2009-12-22T16:06:01.000000Z +e7faa935e11b7d4a3f56e343db47befd +2009-11-18T20:20:28.099174Z +131 ncorbic README @@ -93,7 +93,7 @@ file -2009-03-30T18:03:40.000000Z +2009-08-25T20:44:41.000000Z 6b44396eddae33cda9cdb078e5030a1f 2007-10-30T20:04:39.055688Z 14 @@ -105,9 +105,9 @@ file -2009-03-30T18:03:40.000000Z -be959aafe84874470b0c5ed23094a7a2 -2009-03-28T00:51:34.257571Z -97 +2010-02-10T23:06:48.000000Z +90505fb252939f5bb7fcb982e100d3b4 +2010-01-26T07:04:32.101982Z +138 ncorbic diff --git a/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.svn/text-base/Makefile.svn-base b/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.svn/text-base/Makefile.svn-base index e57a769..3257be8 100644 --- a/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.svn/text-base/Makefile.svn-base +++ b/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.svn/text-base/Makefile.svn-base @@ -13,7 +13,7 @@ ################################################################################ ifndef PBXDIR - PBXDIR=/usr/src/asterisk + PBXDIR=/usr/include/asterisk ifneq (,$(wildcard ./.pbxdir)) PBXDIR=$(shell cat .pbxdir) endif @@ -28,8 +28,25 @@ PBXCFGDIR=/etc/asterisk PBXDEFINES= PBXFLAGS_EXTRA= + +#Check if PBXDIR is asterisk source or include + +INCDIR=$(PBXDIR) +INCLUDES= + +ifneq (,$(wildcard $(PBXDIR)/include/asterisk/frame.h)) + INCLUDES= -I$(PBXDIR) -I$(PBXDIR)/include + INCDIR=$(PBXDIR)/include/asterisk +endif +ifneq (,$(wildcard $(PBXDIR)/include/callweaver/cli.h)) + INCLUDES= -I/$(PBXDIR) -I$(PBXDIR)/include + INCDIR=$(PBXDIR)/include/callweaver +endif + + + #Check for Asterisk AST_CONTROL_SRC feature -DUMMY:=$(shell ./check_for.sh AST_CONTROL_SRC $(PBXDIR)/include/asterisk/frame.h .ast_src_check;) +DUMMY:=$(shell ./check_for.sh AST_CONTROL_SRC $(INCDIR)/frame.h .ast_src_check;) ifeq (1,$(shell cat .ast_src_check)) PBXDEFINES+=-DWOO_CONTROL_SRC_FEATURE @@ -37,7 +54,7 @@ endif #Check for Callweaver opbx_cli_entry -DUMMY:=$(shell ./check_for.sh opbx_cli_entry $(PBXDIR)/include/callweaver/cli.h .opbx_cli_entry_check;) +DUMMY:=$(shell ./check_for.sh opbx_cli_entry $(INCDIR)/cli.h .opbx_cli_entry_check;) ifneq (0,$(shell cat .opbx_cli_entry_check)) PBXFLAGS_EXTRA+=-DCALLWEAVER_OPBX_CLI_ENTRY @@ -45,31 +62,31 @@ endif #Check for callweaver cw_cli_entry -DUMMY:=$(shell ./check_for.sh cw_cli_entry $(PBXDIR)/include/callweaver/cli.h .cw_cli_entry_check;) +DUMMY:=$(shell ./check_for.sh cw_cli_entry $(INCDIR)/cli.h .cw_cli_entry_check;) ifneq (0,$(shell cat .cw_cli_entry_check)) PBXFLAGS_EXTRA+=-DCALLWEAVER_CW_CLI_ENTRY endif -#Check for callweaver MODULE_INFO -DUMMY:=$(shell ./check_for.sh MODULE_INFO $(PBXDIR)/include/callweaver/module.h .cw_module_info_check;) - -ifneq (0,$(shell cat .cw_module_info_check)) - PBXFLAGS_EXTRA+=-DCALLWEAVER_MODULE_INFO -endif - - -ifneq (,$(wildcard $(PBXDIR)/include/callweaver/cwobj.h)) +ifneq (,$(wildcard $(INCDIR)/cwobj.h)) PBXFLAGS_EXTRA+=-DCALLWEAVER_CWOBJ endif -ifneq (,$(wildcard $(PBXDIR)/include/callweaver.h)) +ifneq (,$(wildcard $(INCDIR)/../callweaver.h)) PBXFLAGS_EXTRA+=-DCALLWEAVER -DHAVE_CONFIG_H PBXMODDIR=/usr/local/lib/callweaver/modules PBXCFGDIR=/usr/local/etc/callweaver + #Check for callweaver MODULE_INFO + DUMMY:=$(shell ./check_for.sh MODULE_INFO $(INCDIR)/module.h .cw_module_info_check;) + + ifneq (0,$(shell cat .cw_module_info_check)) + PBXFLAGS_EXTRA+=-DCALLWEAVER_MODULE_INFO + endif + + else #Check for Asterisk 1.6 @@ -82,6 +99,18 @@ DUMMY:=$(shell ./check_for.sh 1.4 $(PBXDIR)/.version .ast14_check;) ifneq (0,$(shell cat .ast14_check)) PBXFLAGS_EXTRA+=-DAST14 endif + +DUMMY:=$(shell ./check_for.sh 1.6 $(INCDIR)/version.h .ast16_check;) +ifneq (0,$(shell cat .ast16_check)) + PBXFLAGS_EXTRA+=-DAST16 +endif + +DUMMY:=$(shell ./check_for.sh 1.4 $(INCDIR)/version.h .ast14_check;) +ifneq (0,$(shell cat .ast14_check)) + PBXFLAGS_EXTRA+=-DAST14 +endif + + endif @@ -90,14 +119,13 @@ PBXFLAGS_EXTRA+= -DAST_MODULE=\"chan_woomera\" CC = gcc -INCLUDES= -I/usr/include -I./ CFLAGS = -D__LINUX__ -D_REENTRANT -D_GNU_SOURCE -O6 CCFLAGS = -Wall -Wstrict-prototypes -Wmissing-prototypes -g LDFLAGS=-L lib/libteletone/.libs -L. -L/usr/local/lib -L ../../ssmg/libsangoma.trunk/.libs -lpthread -lsangoma -lm -PBXFLAGS= $(INCLUDES) -I$(PBXDIR) -I$(PBXDIR)/include -pipe -Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -g3 -Iinclude -I../include -D_REENTRANT -DWOOMERA_CHAN_NAME=\"$(CHAN_NAME)\" -D_GNU_SOURCE $(PBXDEFINES) -O6 -fomit-frame-pointer -fPIC +PBXFLAGS= $(INCLUDES) -pipe -Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -g3 -D_REENTRANT -DWOOMERA_CHAN_NAME=\"$(CHAN_NAME)\" -D_GNU_SOURCE $(PBXDEFINES) -O6 -fomit-frame-pointer -fPIC all: chan_woomera.so diff --git a/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.svn/text-base/chan_woomera.c.svn-base b/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.svn/text-base/chan_woomera.c.svn-base index d75fbec..6152c25 100644 --- a/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.svn/text-base/chan_woomera.c.svn-base +++ b/ssmg/sangoma_mgd.trunk/chan_woomera.trunk/.svn/text-base/chan_woomera.c.svn-base @@ -14,6 +14,75 @@ * This program is free software, distributed under the terms of * the GNU General Public License * ============================================= + * + * v1.68 David Yat Sin + * Mar 19 2010 + * MEDIA and RING flags set on answer, based on tech_indicate + * + * v1.67 David Yat Sin + * Mar 15 2010 + * Fix cause code for rejected outbound calls + * when sangoma_mgd not running. + * + * v1.66 David Yat Sin + * Mar 10 2010 + * Support for TON and NPI passthrough + * + * v1.65 David Yat Sin + * Feb 22 2010 + * RDnis parameter only contains RDNIS + * Additional parameters now transferred as xCustom parameter + * WOOMERA_CUSTOM variable exposed to user + * + * v1.64 Nenad Corbic + * Jan 27 2010 + * Enabled media pass through so that + * two woomera servers pass media directly. + * + * v1.63 Nenad Corbic + * Jan 26 2010 + * Added bridge code and rbs relay + * + * v1.62 Nenad Corbic + * Jan 24 2010 + * Added woomer called blacklist + * + * v1.61 Nenad Corbic + * Jan 14 2010 + * Added media sequencing. + * Enabled only if server HELLO message contains + * sequence enable status. + * + * v1.60 Nenad Corbic + * Nov 22 2009 + * Added Woomera No Answer feature for calling cards. + * + * v1.59 Nenad Corbic + * Nov 16 2009 + * Bug fix in woomera profile verbose introduced in 1.58 + * + * v1.58 Nenad Corbic + * Nov 10 2009 + * Added verbosity per profile. + * Updated cli commands + * + * v1.57 Nenad Corbic + * Nov 10 2009 + * Fixed global set flag. When one profile goes down it shuts down + * all calls. + * + * v1.56 Nenad Corbic + * Oct 29 2009 + * Fixed the call incoming call problem on second profile + * + * v1.55 Nenad Corbic + * Sep 22 2009 + * Updated udp base port to 20000 and numbe of ports to 5000 + * + * v1.54 Nenad Corbic + * Sep 16 2009 + * Added Progress Messages + * * v1.53 Nenad Corbic * Jul 16 2009 * Updated for Asterisk load balancing and well @@ -243,6 +312,9 @@ * from CLI. */ +#if !defined(CALLWEAVER) +#include "asterisk.h" +#endif #if defined(CALLWEAVER) && defined(HAVE_CONFIG_H) #include "confdefs.h" @@ -292,7 +364,7 @@ #include "asterisk/musiconhold.h" #include "asterisk/transcap.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 1.53 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 1.68 $") #else @@ -343,7 +415,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision: 1.53 $") #define CALLWEAVER_19 1 #endif -CALLWEAVER_FILE_VERSION(__FILE__, "$Revision: 1.53 $") +CALLWEAVER_FILE_VERSION(__FILE__, "$Revision: 1.68 $") #if defined(DSP_FEATURE_FAX_CNG_DETECT) #undef DSP_FEATURE_FAX_DETECT @@ -650,7 +722,7 @@ CALLWEAVER_FILE_VERSION(__FILE__, "$Revision: 1.53 $") extern int option_verbose; -#define WOOMERA_VERSION "v1.53" +#define WOOMERA_VERSION "v1.68" #ifndef WOOMERA_CHAN_NAME #define WOOMERA_CHAN_NAME "SS7" #endif @@ -667,14 +739,14 @@ extern int option_verbose; #define woomera_printf(a,b,c,msg...) __woomera_printf(a,b,c,##msg) #endif +#define WOOMERA_MAX_CALLED_IGNORE 32 + static int tech_count = 0; static const char desc[] = "Woomera Channel Driver"; //static const char type[] = "WOOMERA"; static const char tdesc[] = "Woomera Channel Driver"; static char configfile[] = "woomera.conf"; -static char smgversion_init=0; -static char smgversion[100] = "N/A"; static char mohinterpret[MAX_MUSICCLASS] = "default"; static char mohsuggest[MAX_MUSICCLASS] = ""; @@ -721,11 +793,11 @@ static struct ast_jb_conf global_jbconf; #define WOOMERA_ULAW 1 #define WOOMERA_ALAW 2 -#define WOOMERA_MAX_MEDIA_PORTS 899 +#define WOOMERA_MAX_MEDIA_PORTS 5000 #define WOOMERA_STRLEN 256 #define WOOMERA_ARRAY_LEN 50 -#define WOOMERA_MIN_PORT 10000 +#define WOOMERA_MIN_PORT 20000 #define WOOMERA_MAX_PORT WOOMERA_MIN_PORT+WOOMERA_MAX_MEDIA_PORTS #define WOOMERA_BODYLEN 2048 #define WOOMERA_LINE_SEPARATOR "\r\n" @@ -762,7 +834,6 @@ static int woomera_max_media_port = WOOMERA_MAX_PORT; }) #endif - #define FRAME_LEN 480 #if 0 @@ -811,7 +882,12 @@ typedef enum { TFLAG_ANSWER_RECEIVED = (1 << 17), TFLAG_CONFIRM_ANSWER = (1 << 18), TFLAG_CONFIRM_ANSWER_ENABLED = (1 << 19), - TFLAG_AST_HANGUP = (1 << 20) + TFLAG_AST_HANGUP = (1 << 20), + TFLAG_PROGRESS = (1 << 21), + TFLAG_RBS = (1 << 22), + TFLAG_MEDIA_RELAY = (1 << 23), + TFLAG_MEDIA_AVAIL = (1 << 24), + TFLAG_RING_AVAIL = (1 << 25), } TFLAGS; static int usecnt = 0; @@ -864,17 +940,20 @@ struct woomera_profile { struct woomera_event_queue event_queue; int jb_enable; int progress_enable; + int progress_on_accept; int coding; float rxgain_val; float txgain_val; unsigned char rxgain[256]; unsigned char txgain[256]; - int call_out; - int call_in; - int call_ok; - int call_end; - int call_abort; - int call_ast_hungup; + unsigned int call_out; + unsigned int call_in; + unsigned int call_ok; + unsigned int call_end; + unsigned int call_abort; + unsigned int call_ast_hungup; + unsigned int media_rx_seq_err; + unsigned int media_tx_seq_err; char default_context[WOOMERA_STRLEN]; char* tg_context [WOOMERA_MAX_TRUNKGROUPS+1]; char language[WOOMERA_STRLEN]; @@ -883,6 +962,13 @@ struct woomera_profile { int rx_sync_check_opt; int tx_sync_check_opt; int tx_sync_gen_opt; + unsigned int verbose; + char called_ignore[WOOMERA_MAX_CALLED_IGNORE][WOOMERA_STRLEN]; + int called_ignore_idx; + unsigned char rbs_relay; + unsigned char bridge_disable; + unsigned char media_pass_through; + char smgversion[100]; }; @@ -898,6 +984,8 @@ struct private_object { struct ast_frame frame; short fdata[FRAME_LEN + AST_FRIENDLY_OFFSET]; struct woomera_message call_info; + struct woomera_message media_info; + char *woomera_relay; struct woomera_profile *profile; char dest[WOOMERA_STRLEN]; char proto[WOOMERA_STRLEN]; @@ -905,6 +993,7 @@ struct private_object { struct timeval started; int timeout; char dtmfbuf[WOOMERA_STRLEN]; + unsigned char rbsbuf; char cid_name[WOOMERA_STRLEN]; char cid_num[WOOMERA_STRLEN]; char mohinterpret[MAX_MUSICCLASS]; @@ -925,8 +1014,8 @@ struct private_object { struct woomera_event_queue event_queue; int coding; int pri_cause; - int rx_udp_seq; - int tx_udp_seq; + unsigned int rx_udp_seq; + unsigned int tx_udp_seq; #ifdef AST_JB struct ast_jb_conf jbconf; #endif /* AST_JB */ @@ -936,6 +1025,9 @@ struct private_object { unsigned char sync_data_w; unsigned char sync_data_r; int capability; + int bridge; + int ignore_dtmf; + struct ast_frame rbs_frame; }; @@ -955,6 +1047,17 @@ typedef struct woomera_event_queue woomera_event_queue; #endif +static inline int woomera_profile_verbose(woomera_profile *profile) +{ + if (!profile) { + /* If profile does not exists then log by default */ + return 100; + } + + return profile->verbose; +} + + static int my_ast_channel_trylock(struct ast_channel *chan) { #if defined (AST14) || defined (AST16) @@ -1052,7 +1155,7 @@ AST_MUTEX_DEFINE_STATIC(lock); /* local prototypes */ static void woomera_close_socket(int *socket); -static void global_set_flag(int flags); +static void global_set_flag(woomera_profile *profile,int flags); #ifdef WOOMERA_PRINTF_DEBUG static int __woomera_printf(const char* file, int line, woomera_profile *profile, int fd, char *fmt, ...); #else @@ -1126,7 +1229,7 @@ static int tech_send_text(struct ast_channel *self, const char *text); static int tech_send_image(struct ast_channel *self, struct ast_frame *frame); static int tech_setoption(struct ast_channel *self, int option, void *data, int datalen); static int tech_queryoption(struct ast_channel *self, int option, void *data, int *datalen); -//static enum ast_bridge_result tech_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms); +static enum ast_bridge_result tech_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms); static int tech_transfer(struct ast_channel *self, const char *newdest); static int tech_write_video(struct ast_channel *self, struct ast_frame *frame); //static struct ast_channel *tech_bridged_channel(struct ast_channel *self, struct ast_channel *bridge); @@ -1153,7 +1256,7 @@ static const struct ast_channel_tech technology = { .send_digit = tech_send_digit, #endif .call = tech_call, - //.bridge = tech_bridge, + .bridge = tech_bridge, .hangup = tech_hangup, .answer = tech_answer, .transfer = tech_transfer, @@ -1196,14 +1299,16 @@ static int woomera_message_reply_ok(woomera_message *wmsg) } -static void global_set_flag(int flags) +static void global_set_flag(woomera_profile * profile, int flags) { private_object *tech_pvt; ASTOBJ_CONTAINER_TRAVERSE(&private_object_list, 1, do { ASTOBJ_RDLOCK(iterator); - tech_pvt = iterator; - ast_set_flag(tech_pvt, flags); + tech_pvt = iterator; + if (profile == NULL || tech_pvt->profile == profile ) { + ast_set_flag(tech_pvt, flags); + } ASTOBJ_UNLOCK(iterator); } while(0)); } @@ -1366,7 +1471,7 @@ static int __woomera_printf(woomera_profile *profile, int fd, char *fmt, ...) } else { res=0; if (profile && globals.debug) { - if (option_verbose > 2) { + if (option_verbose > 2 && woomera_profile_verbose(profile) > 2) { ast_verbose(WOOMERA_DEBUG_PREFIX "Send Message: {%s} [%s/%d]\n%s\n%s", profile->name, profile->woomera_host, profile->woomera_port, WOOMERA_DEBUG_LINE, stuff); } } @@ -1534,7 +1639,7 @@ static int woomera_message_parse(int fd, woomera_message *wmsg, int timeout, if (res == 0) { sanity++; } else if (res < 0) { - if (option_verbose > 2) { + if (option_verbose > 2 && woomera_profile_verbose(profile) > 2) { ast_verbose(WOOMERA_DEBUG_PREFIX "{%s} error during packet retry #%d\n", profile->name, loops); } return res; @@ -1582,7 +1687,7 @@ static int woomera_message_parse(int fd, woomera_message *wmsg, int timeout, next = buf; if (globals.debug) { - if (option_verbose > 2) { + if (option_verbose > 2 && woomera_profile_verbose(profile) > 2) { ast_verbose(WOOMERA_DEBUG_PREFIX "Receive Message: {%s} [%s/%d]\n%s\n%s", profile->name, profile->woomera_host, profile->woomera_port, WOOMERA_DEBUG_LINE, buf); } } @@ -1676,7 +1781,7 @@ static int woomera_message_parse(int fd, woomera_message *wmsg, int timeout, int terr; terr=read(fd, wmsg->body, (bytes > sizeof(wmsg->body)) ? sizeof(wmsg->body) : bytes); if (globals.debug) { - if (option_verbose > 2) { + if (option_verbose > 2 && woomera_profile_verbose(profile) > 2) { ast_verbose("%s\n", wmsg->body); } } @@ -1684,7 +1789,7 @@ static int woomera_message_parse(int fd, woomera_message *wmsg, int timeout, if (event_queue && ast_test_flag(wmsg, WFLAG_EVENT)) { if (globals.debug) { - if (option_verbose > 2) { + if (option_verbose > 2 && woomera_profile_verbose(profile) > 2) { ast_verbose(WOOMERA_DEBUG_PREFIX "Queue Event: {%s} [%s]\n", profile->name, wmsg->command); } } @@ -1785,6 +1890,16 @@ retry_udp: return tech_pvt->udp_socket; } +static unsigned char get_digits_val_from_pbx_var(struct ast_channel *owner, char* var_name, unsigned char default_val) +{ + const char* pbx_var = NULL; + pbx_var = pbx_builtin_getvar_helper(owner, var_name); + if (pbx_var && (atoi(pbx_var) >= 0)) { + return atoi(pbx_var); + } + return default_val; +} + #define WOOMERA_MAX_CALLS 600 static struct private_object *tech_pvt_idx[WOOMERA_MAX_CALLS]; static ast_mutex_t tech_pvt_idx_lock[WOOMERA_MAX_CALLS]; @@ -1795,6 +1910,8 @@ static int tech_activate(private_object *tech_pvt) woomera_message wmsg; char *callid; int err=0; + + tech_pvt->owner->hangupcause = 34; memset(&wmsg,0,sizeof(wmsg)); retry_activate_again: @@ -1824,7 +1941,7 @@ retry_activate_again: } - if (globals.debug > 1 && option_verbose > 1) { + if (globals.debug > 1 && option_verbose > 1 && woomera_profile_verbose(tech_pvt->profile) > 1) { ast_log(LOG_ERROR, "Error: %s call connect to TCP/Woomera Server! tpvt=%p: %s\n", ast_test_flag(tech_pvt, TFLAG_OUTBOUND)?"Out":"In", tech_pvt,strerror(errno)); @@ -1832,6 +1949,7 @@ retry_activate_again: goto tech_activate_failed; } + retry_activate_call=0; if (ast_test_flag(tech_pvt, TFLAG_OUTBOUND)) { @@ -1847,7 +1965,13 @@ retry_activate_again: "Screening:%d%s" "Bearer-Cap:%s%s" "uil1p:%s%s" - "RDNIS:%s%s", + "RDNIS:%s%s" + "xCalledTon:%d:%s" + "xCalledNpi:%d:%s" + "xCallingTon:%d:%s" + "xCallingNpi:%d:%s" + "xRdnisTon:%d:%s" + "xRdnisNpi:%d:%s", tech_pvt->proto, tech_pvt->dest, WOOMERA_LINE_SEPARATOR, @@ -1868,6 +1992,18 @@ retry_activate_again: woomera_ast_coding_to_string(tech_pvt->coding), WOOMERA_LINE_SEPARATOR, tech_pvt->cid_rdnis?tech_pvt->cid_rdnis:"", + WOOMERA_LINE_SEPARATOR, + get_digits_val_from_pbx_var(tech_pvt->owner, "OUTBOUND_CALLED_TON", 255), + WOOMERA_LINE_SEPARATOR, + get_digits_val_from_pbx_var(tech_pvt->owner, "OUTBOUND_CALLED_NPI", 255), + WOOMERA_LINE_SEPARATOR, + get_digits_val_from_pbx_var(tech_pvt->owner, "OUTBOUND_CALLING_TON", 255), + WOOMERA_LINE_SEPARATOR, + get_digits_val_from_pbx_var(tech_pvt->owner, "OUTBOUND_CALLING_NPI", 255), + WOOMERA_LINE_SEPARATOR, + get_digits_val_from_pbx_var(tech_pvt->owner, "OUTBOUND_RDNIS_TON", 255), + WOOMERA_LINE_SEPARATOR, + get_digits_val_from_pbx_var(tech_pvt->owner, "OUTBOUND_RDNIS_NPI", 255), WOOMERA_RECORD_SEPARATOR ); @@ -1882,7 +2018,13 @@ retry_activate_again: "Screening:%d%s" "Bearer-Cap:%s%s" "uil1p:%s%s" - "RDNIS:%s%s", + "RDNIS:%s%s" + "xCalledTon:%d:%s" + "xCalledNpi:%d:%s" + "xCallingTon:%d:%s" + "xCallingNpi:%d:%s" + "xRdnisTon:%d:%s" + "xRdnisNpi:%d:%s", tech_pvt->dest, WOOMERA_LINE_SEPARATOR, tech_pvt->profile->audio_ip, @@ -1902,6 +2044,18 @@ retry_activate_again: woomera_ast_coding_to_string(tech_pvt->coding), WOOMERA_LINE_SEPARATOR, tech_pvt->cid_rdnis?tech_pvt->cid_rdnis:"", + WOOMERA_LINE_SEPARATOR, + get_digits_val_from_pbx_var(tech_pvt->owner, "OUTBOUND_CALLED_TON", 255), + WOOMERA_LINE_SEPARATOR, + get_digits_val_from_pbx_var(tech_pvt->owner, "OUTBOUND_CALLED_NPI", 255), + WOOMERA_LINE_SEPARATOR, + get_digits_val_from_pbx_var(tech_pvt->owner, "OUTBOUND_CALLING_TON", 255), + WOOMERA_LINE_SEPARATOR, + get_digits_val_from_pbx_var(tech_pvt->owner, "OUTBOUND_CALLING_NPI", 255), + WOOMERA_LINE_SEPARATOR, + get_digits_val_from_pbx_var(tech_pvt->owner, "OUTBOUND_RDNIS_TON", 255), + WOOMERA_LINE_SEPARATOR, + get_digits_val_from_pbx_var(tech_pvt->owner, "OUTBOUND_RDNIS_NPI", 255), WOOMERA_RECORD_SEPARATOR ); } @@ -2096,7 +2250,7 @@ static int tech_init(private_object *tech_pvt, woomera_profile *profile, int fla memcpy(&tech_pvt->jbconf, &global_jbconf, sizeof(struct ast_jb_conf)); ast_jb_configure(self, &tech_pvt->jbconf); - if (globals.debug > 1 && option_verbose > 10) { + if (globals.debug > 1 && option_verbose > 10 && woomera_profile_verbose(profile) > 10) { ast_log(LOG_NOTICE, "%s: Cfg JitterBuffer (F=%i MS=%li Rs=%li Impl=%s)\n", self->name, tech_pvt->jbconf.flags, @@ -2176,7 +2330,7 @@ static void tech_destroy(private_object *tech_pvt, struct ast_channel *owner) if (tech_pvt->profile && tech_pvt->command_channel > -1) { - if (globals.debug > 1 && option_verbose > 1) { + if (globals.debug > 1 && option_verbose > 1 && woomera_profile_verbose(tech_pvt->profile) > 1) { ast_log(LOG_NOTICE, "+++DESTROY sent HANGUP %s\n", tech_pvt->callid); } @@ -2655,12 +2809,18 @@ static void *tech_monitor_thread(void *obj) ast_set_flag(tech_pvt,TFLAG_ACCEPTED); ast_clear_flag(tech_pvt,TFLAG_ACCEPT); - + + if (tech_pvt->profile->progress_on_accept) { + ast_set_flag(tech_pvt, TFLAG_PROGRESS); + } + err=woomera_printf(tech_pvt->profile, tech_pvt->command_channel, "ACCEPT %s%s" "Raw-Audio: %s:%d%s" "Request-Audio: Raw%s" - "Unique-Call-Id: %s%s", + "Unique-Call-Id: %s%s" + "xMedia:%s%s" + "xRing:%s%s", tech_pvt->callid, WOOMERA_LINE_SEPARATOR, tech_pvt->profile->audio_ip, @@ -2668,6 +2828,10 @@ static void *tech_monitor_thread(void *obj) WOOMERA_LINE_SEPARATOR, WOOMERA_LINE_SEPARATOR, tech_pvt->callid, + WOOMERA_LINE_SEPARATOR, + ast_test_flag(tech_pvt, TFLAG_MEDIA_AVAIL)? "1":"0", + WOOMERA_LINE_SEPARATOR, + ast_test_flag(tech_pvt, TFLAG_RING_AVAIL)? "1":"0", WOOMERA_RECORD_SEPARATOR); if(err < 0 || woomera_message_parse_wait(tech_pvt,&wmsg) < 0) { @@ -2679,9 +2843,30 @@ static void *tech_monitor_thread(void *obj) goto tech_thread_continue; continue; } - } + if (ast_test_flag(tech_pvt, TFLAG_PROGRESS)) { + int err; + ast_clear_flag(tech_pvt, TFLAG_PROGRESS); + err=woomera_printf(tech_pvt->profile, tech_pvt->command_channel, + "PROGRESS %s%s" + "Unique-Call-Id: %s%s", + tech_pvt->callid, + WOOMERA_LINE_SEPARATOR, + tech_pvt->callid, + WOOMERA_RECORD_SEPARATOR); + + if(err < 0 || woomera_message_parse_wait(tech_pvt,&wmsg) < 0) { + ast_set_flag(tech_pvt, TFLAG_ABORT); + if (globals.debug > 2) { + ast_log(LOG_NOTICE, "PROGRESS ABORT Ch=%d\n", + tech_pvt->command_channel); + } + goto tech_thread_continue; + continue; + } + } + if (ast_test_flag(tech_pvt, TFLAG_ANSWER)) { int err; @@ -2764,6 +2949,61 @@ static void *tech_monitor_thread(void *obj) } } + if (ast_test_flag(tech_pvt, TFLAG_RBS)) { + int err; + if (globals.debug > 2) { + ast_log(LOG_NOTICE, "Woomera Tx RBS %s tpvt=%p %X\n", + tech_pvt->callid,tech_pvt,tech_pvt->rbsbuf); + } + + ast_mutex_lock(&tech_pvt->iolock); + + err=woomera_printf(tech_pvt->profile, tech_pvt->command_channel, + "RBS %sUnique-Call-Id:%s%sContent-Length:%d%s%s%X%s", + WOOMERA_LINE_SEPARATOR, + tech_pvt->callid, + WOOMERA_LINE_SEPARATOR, + 1, + WOOMERA_LINE_SEPARATOR, + WOOMERA_LINE_SEPARATOR, + tech_pvt->rbsbuf, + WOOMERA_RECORD_SEPARATOR); + + ast_clear_flag(tech_pvt, TFLAG_RBS); + tech_pvt->rbsbuf=0; + ast_mutex_unlock(&tech_pvt->iolock); + + if (err<0 || woomera_message_parse_wait(tech_pvt,&wmsg) < 0) { + ast_set_flag(tech_pvt, TFLAG_ABORT); + ast_log(LOG_NOTICE, "RBS ABORT Ch=%d\n", + tech_pvt->command_channel); + ast_copy_string(tech_pvt->ds, "PROTOCOL_ERROR", sizeof(tech_pvt->ds)); + tech_pvt->pri_cause=111; + goto tech_thread_continue; + continue; + } + } + + if (ast_test_flag(tech_pvt, TFLAG_MEDIA_RELAY)) { + ast_clear_flag(tech_pvt, TFLAG_MEDIA_RELAY); + int err; + err=woomera_printf(tech_pvt->profile, tech_pvt->command_channel, tech_pvt->woomera_relay); + + ast_free(tech_pvt->woomera_relay); + tech_pvt->woomera_relay=NULL; + + if (err<0 || woomera_message_parse_wait(tech_pvt,&wmsg) < 0) { + ast_set_flag(tech_pvt, TFLAG_ABORT); + ast_log(LOG_NOTICE, "MEDIA RELAY ABORT Ch=%d\n", + tech_pvt->command_channel); + ast_copy_string(tech_pvt->ds, "PROTOCOL_ERROR", sizeof(tech_pvt->ds)); + tech_pvt->pri_cause=111; + goto tech_thread_continue; + continue; + } + + } + if(tech_pvt->timeout) { struct timeval now; int elapsed; @@ -2820,7 +3060,7 @@ static void *tech_monitor_thread(void *obj) } if (globals.debug > 4) { - if (option_verbose > 2) { + if (option_verbose > 2 && woomera_profile_verbose(tech_pvt->profile) > 2) { ast_verbose(WOOMERA_DEBUG_PREFIX "CHECK {%s} (%d) %s\n", tech_pvt->profile->name, res,tech_pvt->callid); @@ -2880,7 +3120,7 @@ static int woomera_locate_socket(woomera_profile *profile, int *woomera_socket) profile->name); /* When we establish connection update smg version */ - smgversion_init=0; + profile->smgversion[0]='\0'; sleep(5); } @@ -2909,6 +3149,7 @@ static int woomera_locate_socket(woomera_profile *profile, int *woomera_socket) profile->name,__FUNCTION__,__LINE__); if (*woomera_socket > -1) { woomera_close_socket(woomera_socket); + profile->smgversion[0]='\0'; } continue; } @@ -3006,7 +3247,7 @@ static void *woomera_thread_run(void *obj) woomera_close_socket(&woomera_socket); profile->woomera_socket=-1; } - global_set_flag(TFLAG_ABORT); + global_set_flag(profile,TFLAG_ABORT); if (globals.panic > 2) { break; } @@ -3097,7 +3338,7 @@ static void *woomera_thread_run(void *obj) } } if(globals.debug > 4) { - if (option_verbose > 2) { + if (option_verbose > 2 && woomera_profile_verbose(profile) > 2) { ast_verbose(WOOMERA_DEBUG_PREFIX "Main Thread {%s} Select Return %d\n", profile->name, res); } } @@ -3145,7 +3386,7 @@ static int launch_tech_thread(private_object *tech_pvt) int result = 0; if (globals.debug > 2) { - if (option_verbose > 2) { + if (option_verbose > 2 && woomera_profile_verbose(tech_pvt->profile) > 2) { ast_verbose(WOOMERA_DEBUG_PREFIX "+++LAUCN TECH THREAD\n"); } } @@ -3333,8 +3574,11 @@ static int config_woomera(void) } } } + default_context_set = 0; strncpy(profile->name, entry, sizeof(profile->name) - 1); profile->coding=AST_FORMAT_SLINEAR; + profile->verbose=100; + profile->smgversion[0]='\0'; /*default is inbound and outbound enabled */ ast_set_flag(profile, PFLAG_INBOUND | PFLAG_OUTBOUND); @@ -3390,8 +3634,22 @@ static int config_woomera(void) } } else if (!strcmp(v->name, "language")) { strncpy(profile->language, v->value, sizeof(profile->language) - 1); + + } else if (!strcmp(v->name, "exten_blacklist")) { + if (profile->called_ignore_idx < WOOMERA_MAX_CALLED_IGNORE) { + strncpy(profile->called_ignore[profile->called_ignore_idx], v->value, WOOMERA_STRLEN - 1); + profile->called_ignore_idx++; + } + } else if (!strcmp(v->name, "dtmf_enable")) { profile->dtmf_enable = atoi(v->value); + + } else if (!strcmp(v->name, "bridge_disable")) { + profile->bridge_disable = atoi(v->value); + + } else if (!strcmp(v->name, "rbs_relay")) { + profile->rbs_relay = atoi(v->value); + } else if (!strcmp(v->name, "fax_detect")) { profile->faxdetect = atoi(v->value); ast_log(LOG_NOTICE, "Profile {%s} Fax Detect %s %p \n", @@ -3409,6 +3667,9 @@ static int config_woomera(void) } else if (!strcmp(v->name, "progress_enable")) { profile->progress_enable = atoi(v->value); + + } else if (!strcmp(v->name, "progress_on_accept")) { + profile->progress_on_accept = atoi(v->value); } else if (!strcmp(v->name, "coding")) { if (strcmp(v->value, "alaw") == 0) { @@ -3417,12 +3678,6 @@ static int config_woomera(void) if (strcmp(v->value, "ulaw") == 0) { profile->coding=AST_FORMAT_ULAW; } - } else if (!strcmp(v->name, "woomera_udp_seq")) { - int udp_seq = atoi(v->value); - if (udp_seq > 0) { - profile->udp_seq=1; - } - } else if (!strcmp(v->name, "base_media_port")) { int base_port = atoi(v->value); if (base_port > 0) { @@ -3631,16 +3886,54 @@ static int connect_woomera(int *new_socket, woomera_profile *profile, int flags) return *new_socket; }else{ - char *audio_format; + char *audio_format,*udp_seq, *media_pass; if (globals.debug > 2) { - ast_log(LOG_NOTICE, "Woomera Got HELLO on connect! %s SMG Version %s\n", profile->name,woomera_message_header(&wmsg, "Version")); + ast_log(LOG_NOTICE, "{%s} Woomera Got HELLO on connect! SMG Version %s\n", + profile->name,woomera_message_header(&wmsg, "Version")); } - if (!smgversion_init && woomera_message_header(&wmsg, "Version")) { - smgversion_init=1; - strncpy(smgversion, + if (profile->smgversion[0] == '\0' && woomera_message_header(&wmsg, "Version")) { + strncpy(profile->smgversion, woomera_message_header(&wmsg, "Version"), - sizeof(smgversion)-1); + sizeof(profile->smgversion)-1); } + + udp_seq = woomera_message_header(&wmsg, "xUDP-Seq"); + if (udp_seq && strncasecmp(udp_seq,"Enabled",20) == 0) { + if (profile->udp_seq == 0) { + ast_log(LOG_NOTICE, "{%s} Woomera UDP Sequencing Enabled\n", profile->name); + profile->udp_seq=1; + } + udp_seq = woomera_message_header(&wmsg, "xUDP-Seq-Err"); + if (udp_seq) { + int seq_err=atoi(udp_seq); + if (seq_err > 0) { + ast_mutex_lock(&profile->call_count_lock); + if (seq_err > profile->media_tx_seq_err) { + profile->media_tx_seq_err=seq_err; + } + ast_mutex_unlock(&profile->call_count_lock); + } + } + } else { + if (profile->udp_seq == 1) { + ast_log(LOG_NOTICE, "{%s} Woomera UDP Sequencing Disabled\n", profile->name); + profile->udp_seq=0; + } + } + + media_pass = woomera_message_header(&wmsg, "xNative-Bridge"); + if (media_pass && strncasecmp(media_pass,"Enabled",20) == 0) { + if (profile->media_pass_through == 0) { + ast_log(LOG_NOTICE, "{%s} Woomera Media Pass Through Enabled\n", profile->name); + } + profile->media_pass_through=1; + } else { + if (profile->media_pass_through) { + ast_log(LOG_NOTICE, "{%s} Woomera Media Pass Through Disable\n", profile->name); + } + profile->media_pass_through=0; + } + audio_format = woomera_message_header(&wmsg, "Raw-Format"); if (!audio_format) { @@ -3683,28 +3976,6 @@ static int connect_woomera(int *new_socket, woomera_profile *profile, int flags) return *new_socket; } -static int init_woomera(void) -{ - woomera_profile *profile; - ast_mutex_lock(&lock); - - if (!config_woomera()) { - ast_mutex_unlock(&lock); - return 0; - } - - ASTOBJ_CONTAINER_TRAVERSE(&woomera_profile_list, 1, do { - ASTOBJ_RDLOCK(iterator); - profile = iterator; - if (!ast_test_flag(profile, PFLAG_DISABLED)) { - launch_woomera_thread(profile); - } - ASTOBJ_UNLOCK(iterator); - } while(0)); - - ast_mutex_unlock(&lock); - return 1; -} static struct ast_channel *woomera_new(const char *type, int format, void *data, int *cause, @@ -3811,10 +4082,8 @@ static struct ast_channel *tech_requester(const char *type, int format, void *da ast_set_flag(tech_pvt, TFLAG_PBX); /* so we know we dont have to free the channel ourselves */ - if (globals.debug > 1 && option_verbose > 1) { - if (option_verbose > 2) { - ast_verbose(WOOMERA_DEBUG_PREFIX "+++REQ %s\n", chan->name); - } + if (globals.debug > 1 && option_verbose > 8) { + ast_verbose(WOOMERA_DEBUG_PREFIX "+++REQ %s\n", chan->name); } } else { @@ -3838,10 +4107,8 @@ static int tech_send_digit(struct ast_channel *self, char digit) private_object *tech_pvt = self->tech_pvt; int res = 0; - if (globals.debug > 1 && option_verbose > 2) { - if (option_verbose > 2) { - ast_verbose(WOOMERA_DEBUG_PREFIX "+++DIGIT %s '%c'\n",self->name, digit); - } + if (globals.debug > 1 && option_verbose > 2 && woomera_profile_verbose(tech_pvt->profile) > 2) { + ast_verbose(WOOMERA_DEBUG_PREFIX "+++DIGIT %s '%c'\n",self->name, digit); } /* we don't have time to make sure the dtmf command is successful cos asterisk again @@ -3856,6 +4123,29 @@ static int tech_send_digit(struct ast_channel *self, char digit) return res; } +/*--- tech_senddigit: Send a DTMF character */ +static int tech_send_rbs(struct ast_channel *self, unsigned char digit) +{ + private_object *tech_pvt = self->tech_pvt; + int res = 0; + + if (globals.debug > 1 && option_verbose > 2 && woomera_profile_verbose(tech_pvt->profile) > 2) { + ast_verbose(WOOMERA_DEBUG_PREFIX "+++RBS %s '%X'\n",self->name, digit); + } + + /* we don't have time to make sure the dtmf command is successful cos asterisk again + is much too impaitent... so we will cache the digits so the monitor thread can send + it for us when it has time to actually wait. + */ + ast_mutex_lock(&tech_pvt->iolock); + tech_pvt->rbsbuf=digit; + ast_set_flag(tech_pvt, TFLAG_RBS); + ast_mutex_unlock(&tech_pvt->iolock); + + return res; +} + + /*--- tech_call: Initiate a call on my channel * 'dest' has been passed telling you where to call * but you may already have that information from the requester method @@ -3888,8 +4178,6 @@ static int tech_call(struct ast_channel *self, char *dest, int timeout) dest); } - - if (self->cid.cid_name) { strncpy(tech_pvt->cid_name, self->cid.cid_name, sizeof(tech_pvt->cid_name)-1); } @@ -3966,7 +4254,7 @@ static int tech_call(struct ast_channel *self, char *dest, int timeout) if (profile->max_calls) { if (profile->call_count >= profile->max_calls) { - if (globals.debug > 1 && option_verbose > 2) { + if (globals.debug > 1 && option_verbose > 2 && woomera_profile_verbose(profile) > 2) { ast_log(LOG_ERROR, "This profile is at call limit of %d\n", profile->max_calls); } @@ -4009,7 +4297,7 @@ static int tech_call(struct ast_channel *self, char *dest, int timeout) return 0; tech_call_failed: - if (globals.debug > 1 && option_verbose > 2) { + if (globals.debug > 1 && option_verbose > 1) { ast_log(LOG_ERROR, "Error: Outbound Call Failed %p \n",tech_pvt); } self->hangupcause = AST_CAUSE_NORMAL_CIRCUIT_CONGESTION; @@ -4139,6 +4427,7 @@ static int tech_answer(struct ast_channel *self) { private_object *tech_pvt; int res = 0; + const char *noanswer; tech_pvt = self->tech_pvt; if (!tech_pvt) { @@ -4147,19 +4436,22 @@ static int tech_answer(struct ast_channel *self) ast_mutex_lock(&tech_pvt->iolock); - if (globals.debug > 1 && option_verbose > 1) { - if (option_verbose > 2) { - ast_verbose(WOOMERA_DEBUG_PREFIX "+++ANSWER %s\n",self->name); - } + if (globals.debug > 1 && option_verbose > 2 && woomera_profile_verbose(tech_pvt->profile) > 2) { + ast_verbose(WOOMERA_DEBUG_PREFIX "+++ANSWER %s\n",self->name); } if (!ast_test_flag(tech_pvt, TFLAG_OUTBOUND)) { /* Only answer the inbound calls */ - ast_set_flag(tech_pvt, TFLAG_ANSWER); + noanswer=pbx_builtin_getvar_helper(self, "WOOMERA_NO_ANSWER"); + if (noanswer && atoi(noanswer) == 1) { + ast_clear_flag(tech_pvt, TFLAG_ANSWER); + } else { + ast_set_flag(tech_pvt, TFLAG_ANSWER); + } } else { ast_log(LOG_ERROR, "Warning: AST trying to Answer OUTBOUND Call!\n"); } - + ast_set_flag(tech_pvt, TFLAG_UP); ast_setstate(self, AST_STATE_UP); @@ -4187,7 +4479,7 @@ static void handle_fax(private_object *tech_pvt) const char *target_context = ast_strlen_zero(owner->macrocontext) ? owner->context : owner->macrocontext; #endif if (ast_exists_extension(owner, target_context, "fax", 1, owner->cid.cid_num)) { - if (option_verbose > 2) { + if (option_verbose > 2 && woomera_profile_verbose(tech_pvt->profile) > 2) { ast_verbose(VERBOSE_PREFIX_3 "Redirecting %s to fax extension\n", owner->name); } @@ -4250,9 +4542,14 @@ tech_read_again: } else { tech_pvt->rx_udp_seq++; if (tech_pvt->rx_udp_seq != *((unsigned int*)(&rxdata[res]))) { - ast_log(LOG_NOTICE, "%s: Error: Missing Rx Sequence Expect %i Received %i!\n", - self->name,tech_pvt->rx_udp_seq, *((unsigned int*)(&rxdata[res]))); + if (globals.debug > 2) { + ast_log(LOG_NOTICE, "%s: Error: Missing Rx Sequence Expect %i Received %i!\n", + self->name,tech_pvt->rx_udp_seq, *((unsigned int*)(&rxdata[res]))); + } tech_pvt->rx_udp_seq = *((unsigned int*)(&rxdata[res])); + ast_mutex_lock(&tech_pvt->profile->call_count_lock); + tech_pvt->profile->media_rx_seq_err++; + ast_mutex_unlock(&tech_pvt->profile->call_count_lock); } } } @@ -4300,7 +4597,7 @@ tech_read_again: } } - if (tech_pvt->owner && (tech_pvt->faxdetect || tech_pvt->ast_dsp)) { + if (tech_pvt->owner && !tech_pvt->ignore_dtmf && (tech_pvt->faxdetect || tech_pvt->ast_dsp)) { f = ast_dsp_process(tech_pvt->owner, tech_pvt->dsp, &tech_pvt->frame); if (f && f->frametype == AST_FRAME_DTMF){ int answer = 0; @@ -4349,7 +4646,7 @@ tech_read_again: if (globals.debug > 4) { - if (option_verbose > 2) { + if (option_verbose > 2 && woomera_profile_verbose(tech_pvt->profile) > 2) { ast_verbose(WOOMERA_DEBUG_PREFIX "+++READ %s %d coding %d\n",self->name, res, tech_pvt->coding); } @@ -4413,6 +4710,7 @@ static int tech_write(struct ast_channel *self, struct ast_frame *frame) } } + if(ast_test_flag(tech_pvt, TFLAG_MEDIA) && frame->datalen) { if (frame->frametype == AST_FRAME_VOICE) { @@ -4425,8 +4723,8 @@ static int tech_write(struct ast_channel *self, struct ast_frame *frame) if (tech_pvt->profile->udp_seq){ unsigned char *txdata=frame->woo_ast_data_ptr; - tech_pvt->tx_udp_seq++; *((unsigned int*)&txdata[frame->datalen]) = tech_pvt->tx_udp_seq; + tech_pvt->tx_udp_seq++; frame->datalen+=4; } @@ -4436,7 +4734,7 @@ static int tech_write(struct ast_channel *self, struct ast_frame *frame) return -1; } if (globals.debug > 4) { - if (option_verbose > 4) { + if (option_verbose > 9 && woomera_profile_verbose(tech_pvt->profile) > 9) { ast_verbose(WOOMERA_DEBUG_PREFIX "+++WRITE %s %d\n",self->name, i); } } @@ -4466,10 +4764,8 @@ static struct ast_frame *tech_exception(struct ast_channel *self) private_object *tech_pvt; tech_pvt = self->tech_pvt; - if (globals.debug > 1 && option_verbose > 2) { - if (option_verbose > 2) { - ast_verbose(WOOMERA_DEBUG_PREFIX "+++EXCEPT %s\n",self->name); - } + if (globals.debug > 1 && option_verbose > 2 && woomera_profile_verbose(tech_pvt->profile) > 2) { + ast_verbose(WOOMERA_DEBUG_PREFIX "+++EXCEPT %s\n",self->name); } return &ast_null_frame; @@ -4486,6 +4782,7 @@ static int tech_indicate(struct ast_channel *self, int condition) private_object *tech_pvt; int res = -1; + tech_pvt = self->tech_pvt; if (!tech_pvt) { return res; @@ -4494,14 +4791,17 @@ static int tech_indicate(struct ast_channel *self, int condition) ast_mutex_lock(&tech_pvt->iolock); switch(condition) { - - case AST_CONTROL_RINGING: + case AST_CONTROL_RINGING: if (globals.debug > 3) { ast_log(LOG_NOTICE, "TECH INDICATE: Ringing\n"); } if (!ast_test_flag(tech_pvt,TFLAG_ACCEPTED)) { ast_set_flag(tech_pvt, TFLAG_ACCEPT); } + ast_set_flag(tech_pvt, TFLAG_RING_AVAIL); + if (tech_pvt->capability != AST_TRANS_CAP_DIGITAL) { + ast_set_flag(tech_pvt, TFLAG_MEDIA_AVAIL); + } break; case AST_CONTROL_BUSY: if (globals.debug > 3) { @@ -4526,6 +4826,9 @@ static int tech_indicate(struct ast_channel *self, int condition) if (!ast_test_flag(tech_pvt,TFLAG_ACCEPTED)) { ast_set_flag(tech_pvt, TFLAG_ACCEPT); } + if (tech_pvt->capability != AST_TRANS_CAP_DIGITAL) { + ast_set_flag(tech_pvt, TFLAG_MEDIA_AVAIL); + } break; case AST_CONTROL_PROGRESS: if (globals.debug > 3) { @@ -4534,6 +4837,15 @@ static int tech_indicate(struct ast_channel *self, int condition) if (!ast_test_flag(tech_pvt,TFLAG_ACCEPTED)) { ast_set_flag(tech_pvt, TFLAG_ACCEPT); } + if (tech_pvt->capability != AST_TRANS_CAP_DIGITAL) { + ast_set_flag(tech_pvt, TFLAG_MEDIA_AVAIL); + } +#if 0 + if (!tech_pvt->profile->progress_on_accept) { + ast_set_flag(tech_pvt, TFLAG_PROGRESS); + } +#endif + break; case AST_CONTROL_HOLD: if (globals.debug > 3) { @@ -4578,7 +4890,7 @@ static int tech_indicate(struct ast_channel *self, int condition) res = -1; break; } - + ast_mutex_unlock(&tech_pvt->iolock); return res; @@ -4590,15 +4902,15 @@ static int tech_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) { struct private_object *p; - if (!oldchan || !newchan) { - ast_log(LOG_ERROR, "Error: Invalid Pointers oldchan=%p newchan=%p\n",oldchan,newchan); - return -1; - } - if (!newchan->tech_pvt) { - ast_log(LOG_ERROR, "Error: Invalid Pointer newchan->tech_pvt=%p\n", - newchan->tech_pvt); - return -1; - } + if (!oldchan || !newchan) { + ast_log(LOG_ERROR, "Error: Invalid Pointers oldchan=%p newchan=%p\n",oldchan,newchan); + return -1; + } + if (!newchan->tech_pvt) { + ast_log(LOG_ERROR, "Error: Invalid Pointer newchan->tech_pvt=%p\n", + newchan->tech_pvt); + return -1; + } p = newchan->tech_pvt; @@ -4606,9 +4918,9 @@ static int tech_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) if (p->owner == oldchan) { p->owner = newchan; } else { - ast_log(LOG_ERROR, "Error: New p owner=%p instead of %p \n", - p->owner, oldchan); - } + ast_log(LOG_ERROR, "Error: New p owner=%p instead of %p \n", + p->owner, oldchan); + } #if 0 if (newchan->_state == AST_STATE_RINGING) @@ -4616,11 +4928,9 @@ static int tech_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) update_conf(p); #endif - if (globals.debug > 1 && option_verbose > 2) { - if (option_verbose > 2) { + if (globals.debug > 1 && option_verbose > 9) { ast_verbose(WOOMERA_DEBUG_PREFIX "+++FIXUP ChOld=%s ChNew=%s\n", oldchan->name,newchan->name); - } } ast_mutex_unlock(&p->iolock); @@ -4661,10 +4971,8 @@ static int tech_setoption(struct ast_channel *self, int option, void *data, int { int res = 0; - if (globals.debug > 1 && option_verbose > 2) { - if (option_verbose > 2) { - ast_verbose(WOOMERA_DEBUG_PREFIX "+++SETOPT %s\n",self->name); - } + if (globals.debug > 1 && option_verbose > 9) { + ast_verbose(WOOMERA_DEBUG_PREFIX "+++SETOPT %s\n",self->name); } return res; @@ -4675,10 +4983,8 @@ static int tech_queryoption(struct ast_channel *self, int option, void *data, in { int res = 0; - if (globals.debug > 1 && option_verbose > 2) { - if (option_verbose > 2) { + if (globals.debug > 1 && option_verbose > 9) { ast_verbose(WOOMERA_DEBUG_PREFIX "+++GETOPT %s\n",self->name); - } } return res; } @@ -4689,7 +4995,7 @@ static struct ast_channel *tech_bridged_channel(struct ast_channel *self, struct { struct ast_channel *chan = NULL; - if (globals.debug > 1 && option_verbose > 2) { + if (globals.debug > 1 && option_verbose > 2 && woomera_profile_verbose(profile) > 2) { ast_verbose(WOOMERA_DEBUG_PREFIX "+++BRIDGED %s\n",self->name); } return chan; @@ -4702,14 +5008,211 @@ static int tech_transfer(struct ast_channel *self, const char *newdest) { int res = -1; - if (globals.debug > 1 && option_verbose > 2) { - if (option_verbose > 2) { + if (globals.debug > 1 && option_verbose > 9) { ast_verbose(WOOMERA_DEBUG_PREFIX "+++TRANSFER %s\n",self->name); - } } return res; } + +static int woomera_rbs_relay(struct private_object *ch0, struct private_object *ch1, struct ast_channel *c1) +{ + + if (ch0->profile->rbs_relay && + ch1->profile->rbs_relay && + ch0->rbs_frame.frametype == 99) { + tech_send_rbs(c1, ch0->rbs_frame.subclass); + ch0->rbs_frame.frametype=0; + } + + return 0; +} + +#define WOOMERA_RELAY_SIZE 4096 +static int woomera_media_pass_through(struct private_object *ch0, struct private_object *ch1) +{ + + if (ch0->woomera_relay) { + ast_log(LOG_NOTICE,"%s: Error: woomera_media_pass_through relay used!\n", + ch0->callid); + return -1; + } + + if (woomera_message_header(&ch1->media_info, "Raw-Audio") == NULL) { + ast_log(LOG_NOTICE,"%s: Error: woomera_media_pass_through media info not available!\n", + ch1->callid); + return -1; + } + + ch0->woomera_relay = (char *)ast_malloc(WOOMERA_RELAY_SIZE); + if (!ch0->woomera_relay) { + return -1; + } + + memset(ch0->woomera_relay,0,WOOMERA_RELAY_SIZE); + + sprintf(ch0->woomera_relay, + "MEDIA %s%s" + "Raw-Audio: %s%s" + "Request-Audio: Raw%s" + "Unique-Call-Id: %s%s", + ch0->callid, + WOOMERA_LINE_SEPARATOR, + woomera_message_header(&ch1->media_info, "Raw-Audio"), + WOOMERA_LINE_SEPARATOR, + WOOMERA_LINE_SEPARATOR, + ch0->callid, + WOOMERA_RECORD_SEPARATOR); + + ast_set_flag(ch0, TFLAG_MEDIA_RELAY); + + return 0; +} + + +static enum ast_bridge_result tech_bridge (struct ast_channel *c0, + struct ast_channel *c1, int flags, + struct ast_frame **fo, + struct ast_channel **rc, + int timeoutms) + +{ + struct private_object *ch0, *ch1; + struct ast_channel *carr[2], *who; + int to = -1; + struct ast_frame *f; + int err=0; + int media_pass_through=0; + + + ch0 = c0->tech_pvt; + ch1 = c1->tech_pvt; + + carr[0] = c0; + carr[1] = c1; + + if (!ch0 || !ch0->profile || !ch1 || !ch1->profile) { + return AST_BRIDGE_FAILED; + } + + if (ch0->profile->bridge_disable) { + return AST_BRIDGE_FAILED; + } + + if (ch1->profile->bridge_disable) { + return AST_BRIDGE_FAILED; + } + + if (option_verbose > 5) { + ast_verbose(VERBOSE_PREFIX_3 "Native bridging %s and %s\n", c0->name, c1->name); + } + + //ast_log(LOG_NOTICE, "* Making Native Bridge between %s and %s\n", c0->name, c1->name); + + if (! (flags & AST_BRIDGE_DTMF_CHANNEL_0) ) + ch0->ignore_dtmf = 1; + + if (! (flags & AST_BRIDGE_DTMF_CHANNEL_1) ) + ch1->ignore_dtmf = 1; + + ch0->bridge=1; + ch1->bridge=1; + + if (ch0->profile->media_pass_through && + ch1->profile->media_pass_through) { + + /* Attempt */ + err=woomera_media_pass_through(ch0,ch1); + if (err == 0) { + err=woomera_media_pass_through(ch1,ch0); + } + + if (err == 0) { + ast_log(LOG_NOTICE, "woomera: Media pass throught complete %s <--> %s\n", c0->name, c1->name); + media_pass_through=1; + timeoutms=50; + + } else { + ast_log(LOG_NOTICE, "woomera: Media pass throught failed, proceeding to bridge! %s <-!-> %s\n", c0->name, c1->name); + } + } + + + for (;/*ever*/;) { + to = timeoutms; + who = ast_waitfor_n(carr, 2, &to); + + if (!who) { + if (media_pass_through) { + if (ast_test_flag(ch0, TFLAG_ABORT) || + ast_test_flag(ch1, TFLAG_ABORT)) { + break; + } + woomera_rbs_relay(ch0,ch1,c1); + woomera_rbs_relay(ch1,ch0,c0); + continue; + + } + + ast_log(LOG_NOTICE, "woomera: Bridge empty read, breaking out\n"); + break; + } + + f = ast_read(who); + + if (!f || f->frametype == AST_FRAME_CONTROL) { + /* got hangup .. */ + + if (!f) { + if (option_verbose > 10) { + ast_log(LOG_NOTICE, "woomera: Bridge Read Null Frame\n"); + } + } else { + if (option_verbose > 10) { + ast_log(LOG_NOTICE, "woomera: Bridge Read Frame Control class:%d\n", f->subclass); + } + } + + *fo = f; + *rc = who; + break; + } + + if (f->frametype == AST_FRAME_DTMF) { + ast_log(LOG_NOTICE, "woomera: Bridge Read DTMF %d from %s\n", f->subclass, who->exten); + + *fo = f; + *rc = who; + break; + } + + + +#if 0 + if (f->frametype == AST_FRAME_VOICE) { + chan_misdn_log(1, ch1->bc->port, "I SEND: Splitting conference with Number:%d\n", ch1->bc->pid +1); + + continue; + } +#endif + + woomera_rbs_relay(ch0,ch1,c1); + woomera_rbs_relay(ch1,ch0,c0); + + if (who == c0) { + ast_write(c1, f); + } else { + ast_write(c0, f); + } + } + + ch0->bridge=0; + ch1->bridge=0; + + return AST_BRIDGE_COMPLETE; +} + + /*--- tech_bridge: Technology-specific code executed to natively bridge 2 of our channels ---*/ #if 0 static enum ast_bridge_result tech_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms) @@ -4740,33 +5243,84 @@ static char *ast16_woomera_cli(struct ast_cli_entry *e, int cmd, struct ast_cli_ } #endif +static int woomera_full_status(int fd) +{ + woomera_profile *profile; + ASTOBJ_CONTAINER_TRAVERSE(&woomera_profile_list, 1, do { + ASTOBJ_RDLOCK(iterator); + profile = iterator; + + if (!ast_test_flag(profile, PFLAG_DISABLED)) { + ast_cli(fd,"Profile: {%s} Host=%s WooVer=%s SmgVer=%s CurCalls=%i TotCalls=%i Verb=%i RxSeqErr=%i TxSeqErr=%i\n", + profile->name, profile->woomera_host, + WOOMERA_VERSION, profile->smgversion[0] == '\0'?"N/A":profile->smgversion, + profile->call_count, + profile->call_out+profile->call_in, + woomera_profile_verbose(profile), + profile->media_rx_seq_err, + profile->media_tx_seq_err); + } + ASTOBJ_UNLOCK(iterator); + } while(0)); + + return 0; +} + + static int woomera_cli(int fd, int argc, char *argv[]) { struct woomera_profile *profile; char *profile_name="default"; - if (argc > 2) { + if (argc > 1) { profile_name=argv[1]; profile = ASTOBJ_CONTAINER_FIND(&woomera_profile_list, profile_name); if (!profile) { - if (strcmp(profile_name,"version") == 0) { - ast_cli(fd, "Woomera version %s : SMG Version %s \n", - WOOMERA_VERSION,smgversion); + if (strcmp(profile_name,"version") == 0) { + ast_cli(fd, "Woomera version %s\n", + WOOMERA_VERSION); return 0; } + if (strcmp(profile_name, "debug") == 0) { + if (argc > 2) { + globals.debug = atoi(argv[2]); + } + ast_cli(fd, "Woomera debug=%d\n", globals.debug); + return 0; + } + + if (!strcmp(profile_name, "panic")) { + if (argc > 2) { + globals.panic = atoi(argv[2]); + } + ast_cli(fd, "Woomera panic=%d\n", globals.panic); + return 0; + } + + if (!strcmp(profile_name, "status")) { + woomera_full_status(fd); + return 0; + } + ast_cli(fd, "Woomera: Invalid profile name %s\n", profile_name); + ast_cli(fd, "Usage: woomera

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

  • qCK_t(JO0`>H9F!%t^Yv^uS; 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%0D29-c^GLcG{RopK9h$|K1Dk^Y?dhes!6KJP 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 /dev/null > /dev/null" + eval "make PBXDIR=$pbxdir" + if [ $? -ne 0 ]; then + exit 1; + fi + make INSTALLPREFIX=$rootdir install + if [ $? -ne 0 ]; then + echo + echo "Error: Failed to compile chan_woomera into PBX source!" + echo + exit 1; + fi + echo "Ok." + chan_woomera_installed=1; fi + else - eval "make clean 2> /dev/null > /dev/null" - eval "make PBXDIR=$pbxdir" - if [ $? -ne 0 ]; then - exit 1; - fi - make INSTALLPREFIX=$rootdir install - if [ $? -ne 0 ]; then - echo - echo "Error: Failed to compile chan_woomera into PBX source!" - echo - exit 1; - fi - echo "Ok." - chan_woomera_installed=1; + echo "Warning: chan_woomera directory does not exist!" + exit 1 fi - -else - echo "Warning: chan_woomera directory does not exist!" - exit 1 fi if [ $smg_installed -eq 1 ]; then @@ -389,6 +392,10 @@ if [ $chan_woomera_installed -eq 1 ]; then echo "--> Start: start $pbxd (part of $pbxd)" echo echo "---------------------------------" +elif [ $no_woomera = "true" ];then + echo + echo " You chose not to install Chan Woomera" + echo else echo echo " Chan Woomera Installatin Failed" diff --git a/ssmg/sangoma_mgd.trunk/lib/.svn/entries b/ssmg/sangoma_mgd.trunk/lib/.svn/entries index 1cb9ba7..6c0d964 100644 --- a/ssmg/sangoma_mgd.trunk/lib/.svn/entries +++ b/ssmg/sangoma_mgd.trunk/lib/.svn/entries @@ -1,7 +1,7 @@ 8 dir -193 +276 https://www.sangomapbx.com/svn/sangoma_mgd/trunk/lib https://www.sangomapbx.com/svn/sangoma_mgd diff --git a/ssmg/sangoma_mgd.trunk/lib/libteletone/.svn/entries b/ssmg/sangoma_mgd.trunk/lib/libteletone/.svn/entries index cb108f9..cb082d0 100644 --- a/ssmg/sangoma_mgd.trunk/lib/libteletone/.svn/entries +++ b/ssmg/sangoma_mgd.trunk/lib/libteletone/.svn/entries @@ -1,7 +1,7 @@ 8 dir -193 +276 https://www.sangomapbx.com/svn/sangoma_mgd/trunk/lib/libteletone https://www.sangomapbx.com/svn/sangoma_mgd @@ -32,7 +32,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:42.000000Z 792922784eade1d03ecb1b33ba8bb7c3 2007-09-21T20:53:51.260136Z 1 @@ -44,7 +44,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:42.000000Z 4df7fcb99a2e1e89d87133917c6e97dc 2008-07-25T16:12:13.826864Z 102 @@ -57,7 +57,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:42.000000Z e8a38be05770cad7d1a5bca4154e2cd2 2008-07-25T16:12:13.826864Z 102 @@ -69,7 +69,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:42.000000Z 2a307cc617b923385f157cc693f908e9 2007-09-21T20:53:51.260136Z 1 @@ -81,7 +81,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:42.000000Z 4b0bfa262e16e1c9538664e5b42a0134 2007-09-21T20:53:51.260136Z 1 @@ -94,7 +94,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:42.000000Z 7e26ecc61d5c27c50d334ebe19d5ef06 2007-09-21T20:53:51.260136Z 1 @@ -106,7 +106,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:42.000000Z d41d8cd98f00b204e9800998ecf8427e 2007-09-21T20:53:51.260136Z 1 @@ -121,7 +121,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:42.000000Z b2a35e98453194ca837c363ee9c0d379 2007-09-21T20:53:51.260136Z 1 @@ -134,7 +134,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:42.000000Z e8f87acaddff75a691755466f0918100 2007-09-21T20:53:51.260136Z 1 @@ -146,7 +146,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:42.000000Z c2defdef8cd93c3d9e29628267699702 2007-09-21T20:53:51.260136Z 1 @@ -158,7 +158,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:42.000000Z 234c8df99750448a5b1c4709304a38e5 2007-09-21T20:53:51.260136Z 1 @@ -170,7 +170,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:42.000000Z 0f44520873ce706852086fc2ad45106f 2007-09-21T20:53:51.260136Z 1 @@ -182,7 +182,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:42.000000Z 74587542264e2bb761ee931ddb7d69f1 2007-09-21T20:53:51.260136Z 1 @@ -194,7 +194,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:42.000000Z 9f3e20fdff9c78aa8e3f9b42be166769 2007-09-21T20:53:51.260136Z 1 @@ -206,7 +206,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:42.000000Z 7652cc60b4a56426a8c94b20217b7f86 2007-09-21T20:53:51.260136Z 1 @@ -218,7 +218,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:42.000000Z c878b7dac31225fc5b03ff92d985f147 2007-09-21T20:53:51.260136Z 1 @@ -230,7 +230,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:42.000000Z cdd15e1ba31199e3299b958981cde8d9 2007-09-21T20:53:51.260136Z 1 @@ -242,7 +242,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:42.000000Z 0a5e8ed778878a2656e3ea2313dac833 2007-09-21T20:53:51.260136Z 1 @@ -254,7 +254,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:42.000000Z 53eff28ecd16c5529e73546a297e9f53 2007-09-21T20:53:51.260136Z 1 @@ -266,7 +266,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:42.000000Z e4a88cfdc6991c1740b000753b905eec 2007-09-21T20:53:51.260136Z 1 @@ -278,7 +278,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:42.000000Z dd02d76e53ac83f5112d68797e8f52e3 2008-07-25T16:12:13.826864Z 102 @@ -290,7 +290,7 @@ file -2009-03-30T18:03:13.000000Z +2009-08-25T20:44:42.000000Z d4c3da374db4aa2301a21ab3e51ddb21 2007-09-21T20:53:51.260136Z 1 diff --git a/ssmg/sangoma_mgd.trunk/lib/libteletone/src/.svn/entries b/ssmg/sangoma_mgd.trunk/lib/libteletone/src/.svn/entries index 6276666..7dcc7ab 100644 --- a/ssmg/sangoma_mgd.trunk/lib/libteletone/src/.svn/entries +++ b/ssmg/sangoma_mgd.trunk/lib/libteletone/src/.svn/entries @@ -1,7 +1,7 @@ 8 dir -193 +276 https://www.sangomapbx.com/svn/sangoma_mgd/trunk/lib/libteletone/src https://www.sangomapbx.com/svn/sangoma_mgd @@ -32,7 +32,7 @@ file -2009-03-30T18:03:12.000000Z +2009-08-25T20:44:42.000000Z d01d28a1232e2c040d77587c86728a78 2007-09-21T20:53:51.260136Z 1 @@ -44,7 +44,7 @@ file -2009-03-30T18:03:12.000000Z +2009-08-25T20:44:42.000000Z bfc7a66515e43ad78a29545c6031c6ea 2007-09-21T20:53:51.260136Z 1 @@ -56,7 +56,7 @@ file -2009-03-30T18:03:12.000000Z +2009-08-25T20:44:42.000000Z b282cf61fecc1945f17aae2deb3b0aa8 2007-09-21T20:53:51.260136Z 1 @@ -68,7 +68,7 @@ file -2009-03-30T18:03:12.000000Z +2009-08-25T20:44:42.000000Z a2780ff716b7232f5830f99c04b4f949 2007-09-21T20:53:51.260136Z 1 @@ -80,7 +80,7 @@ file -2009-03-30T18:03:12.000000Z +2009-08-25T20:44:42.000000Z 64ea78a421b845fc49e4a1b0dcb9872e 2007-09-21T20:53:51.260136Z 1 diff --git a/ssmg/sangoma_mgd.trunk/rc/.svn/all-wcprops b/ssmg/sangoma_mgd.trunk/rc/.svn/all-wcprops new file mode 100644 index 0000000..1ebb0e2 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/rc/.svn/all-wcprops @@ -0,0 +1,53 @@ +K 25 +svn:wc:ra_dav:version-url +V 38 +/svn/sangoma_mgd/!svn/ver/262/trunk/rc +END +smg.rc.pri +K 25 +svn:wc:ra_dav:version-url +V 49 +/svn/sangoma_mgd/!svn/ver/262/trunk/rc/smg.rc.pri +END +smg.rc.pri_only +K 25 +svn:wc:ra_dav:version-url +V 54 +/svn/sangoma_mgd/!svn/ver/262/trunk/rc/smg.rc.pri_only +END +smg.rc.bri +K 25 +svn:wc:ra_dav:version-url +V 49 +/svn/sangoma_mgd/!svn/ver/262/trunk/rc/smg.rc.bri +END +smg.rc.bri_only +K 25 +svn:wc:ra_dav:version-url +V 54 +/svn/sangoma_mgd/!svn/ver/262/trunk/rc/smg.rc.bri_only +END +safe_sangoma.rc +K 25 +svn:wc:ra_dav:version-url +V 54 +/svn/sangoma_mgd/!svn/ver/224/trunk/rc/safe_sangoma.rc +END +smg.rc.ss7boost +K 25 +svn:wc:ra_dav:version-url +V 54 +/svn/sangoma_mgd/!svn/ver/203/trunk/rc/smg.rc.ss7boost +END +smg.rc.isupd +K 25 +svn:wc:ra_dav:version-url +V 51 +/svn/sangoma_mgd/!svn/ver/224/trunk/rc/smg.rc.isupd +END +smg.rc.isupd_only +K 25 +svn:wc:ra_dav:version-url +V 56 +/svn/sangoma_mgd/!svn/ver/262/trunk/rc/smg.rc.isupd_only +END diff --git a/ssmg/sangoma_mgd.trunk/rc/.svn/entries b/ssmg/sangoma_mgd.trunk/rc/.svn/entries new file mode 100644 index 0000000..d7e906c --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/rc/.svn/entries @@ -0,0 +1,124 @@ +8 + +dir +276 +https://www.sangomapbx.com/svn/sangoma_mgd/trunk/rc +https://www.sangomapbx.com/svn/sangoma_mgd + + + +2010-02-26T02:13:07.697472Z +262 +ncorbic + + +svn:special svn:externals svn:needs-lock + + + + + + + + + + + +27f70977-ab3a-0410-9ff6-8a87bf49d3db + +smg.rc.pri +file + + + + +2010-02-25T23:21:28.000000Z +71c4dfbc41f2bfe5c9bbc58e417c4e0a +2010-02-26T02:13:07.697472Z +262 +ncorbic + +smg.rc.pri_only +file + + + + +2010-02-25T23:21:35.000000Z +89d8d0df2ea4cd4cb6cebebe19223883 +2010-02-26T02:13:07.697472Z +262 +ncorbic + +smg.rc.bri +file + + + + +2010-02-25T23:20:56.000000Z +4f3752109a155f63112d6322b11452e1 +2010-02-26T02:13:07.697472Z +262 +ncorbic + +smg.rc.bri_only +file + + + + +2010-02-25T23:21:05.000000Z +e9c6fa80d662679d316cb49d65cbb0e5 +2010-02-26T02:13:07.697472Z +262 +ncorbic + +safe_sangoma.rc +file + + + + +2009-11-16T23:04:14.000000Z +645af0279043395cde238ac1a2246b52 +2009-11-13T19:11:52.785901Z +224 +ncorbic + +smg.rc.ss7boost +file + + + + +2009-09-17T21:17:04.000000Z +2955ed3b82f0e8892a59cbe74d19ac74 +2009-09-17T21:48:55.845572Z +203 +ncorbic + +smg.rc.isupd +file + + + + +2009-11-16T23:04:14.000000Z +4df11cf9c5f0ff1e62b0b480ea8f0d6c +2009-11-13T19:11:52.785901Z +224 +ncorbic + +smg.rc.isupd_only +file + + + + +2010-02-25T23:21:21.000000Z +517599c4ac16c8926e4b85f3d080f1da +2010-02-26T02:13:07.697472Z +262 +ncorbic + diff --git a/util/wancfg_zaptel/.svn/format b/ssmg/sangoma_mgd.trunk/rc/.svn/format similarity index 100% rename from util/wancfg_zaptel/.svn/format rename to ssmg/sangoma_mgd.trunk/rc/.svn/format diff --git a/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/safe_sangoma.rc.svn-base b/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/safe_sangoma.rc.svn-base new file mode 100644 index 0000000..e2bad07 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/safe_sangoma.rc.svn-base @@ -0,0 +1,18 @@ +#!/bin/bash +# safe_sangoma.rc + +#NOTIFY is list of email address that will be notified if +#safe_sangoma has to restart a daemon +# +#Note: NOTIFY uses mail command to send email. The from +#address will be the user@hostname.com. Some mail servers +#will reject this email as junk/spam. +#Gmail accounts work very well. +#-------------------------------------------------------- +NOTIFY="" + + +#EXEC is the absolute path to an external script/app +#that is to be executed after safe_sangoma restarts a daemon +#-------------------------------------------------------- +EXEC="" diff --git a/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.bri.svn-base b/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.bri.svn-base new file mode 100644 index 0000000..5144ed7 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.bri.svn-base @@ -0,0 +1,10 @@ +#!/bin/sh +# smg.rc SMG_RC FILE +# Note: This file is 'executed' by the shell script, so +# the usual shell syntax must be observed. +SANGOMA_PRID="NO" +SANGOMA_BRID="YES" +SANGOMA_SS7ISUP="NO" +SANGOMA_SS7BOOST="NO" +SANGOMA_MEDIA_GATEWAY="YES" +SANGOMA_SAFE_MODE="YES" diff --git a/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.bri_only.svn-base b/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.bri_only.svn-base new file mode 100644 index 0000000..a9169ea --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.bri_only.svn-base @@ -0,0 +1,11 @@ +#!/bin/sh +# smg.rc SMG_RC FILE +# Note: This file is 'executed' by the shell script, so +# the usual shell syntax must be observed. +SANGOMA_PRID="NO" +SANGOMA_BRID="YES" +SANGOMA_SS7ISUP="NO" +SANGOMA_SS7BOOST="NO" +SANGOMA_MEDIA_GATEWAY="NO" +SANGOMA_SAFE_MODE="YES" + diff --git a/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.isupd.svn-base b/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.isupd.svn-base new file mode 100644 index 0000000..05e596e --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.isupd.svn-base @@ -0,0 +1,11 @@ +#!/bin/sh +# smg.rc SMG_RC FILE +# Note: This file is 'executed' by the shell script, so +# the usual shell syntax must be observed. +SANGOMA_PRID="NO" +SANGOMA_BRID="NO" +SANGOMA_SS7ISUP="YES" +SANGOMA_SS7BOOST="NO" +SANGOMA_MEDIA_GATEWAY="YES" +SANGOMA_SAFE_MODE="YES" + diff --git a/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.isupd_only.svn-base b/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.isupd_only.svn-base new file mode 100644 index 0000000..85de411 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.isupd_only.svn-base @@ -0,0 +1,11 @@ +#!/bin/sh +# smg.rc SMG_RC FILE +# Note: This file is 'executed' by the shell script, so +# the usual shell syntax must be observed. +SANGOMA_PRID="NO" +SANGOMA_BRID="NO" +SANGOMA_SS7ISUP="YES" +SANGOMA_SS7BOOST="NO" +SANGOMA_MEDIA_GATEWAY="NO" +SANGOMA_SAFE_MODE="YES" + diff --git a/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.pri.svn-base b/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.pri.svn-base new file mode 100644 index 0000000..4254cab --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.pri.svn-base @@ -0,0 +1,11 @@ +#!/bin/sh +# smg.rc SMG_RC FILE +# Note: This file is 'executed' by the shell script, so +# the usual shell syntax must be observed. +SANGOMA_PRID="YES" +SANGOMA_BRID="NO" +SANGOMA_SS7ISUP="NO" +SANGOMA_SS7BOOST="NO" +SANGOMA_MEDIA_GATEWAY="YES" +SANGOMA_SAFE_MODE="YES" + diff --git a/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.pri_only.svn-base b/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.pri_only.svn-base new file mode 100644 index 0000000..6ebc48a --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.pri_only.svn-base @@ -0,0 +1,11 @@ +#!/bin/sh +# smg.rc SMG_RC FILE +# Note: This file is 'executed' by the shell script, so +# the usual shell syntax must be observed. +SANGOMA_PRID="YES" +SANGOMA_BRID="NO" +SANGOMA_SS7ISUP="NO" +SANGOMA_SS7BOOST="NO" +SANGOMA_MEDIA_GATEWAY="NO" +SANGOMA_SAFE_MODE="YES" + diff --git a/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.ss7boost.svn-base b/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.ss7boost.svn-base new file mode 100644 index 0000000..c68d292 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/rc/.svn/text-base/smg.rc.ss7boost.svn-base @@ -0,0 +1,10 @@ +#!/bin/sh +# smg.rc SMG_RC FILE +# Note: This file is 'executed' by the shell script, so +# the usual shell syntax must be observed. +SANGOMA_PRID="NO" +SANGOMA_BRID="NO" +SANGOMA_SS7ISUP="NO" +SANGOMA_SS7BOOST="YES" +SANGOMA_MEDIA_GATEWAY="YES" + diff --git a/ssmg/sangoma_mgd.trunk/rc/safe_sangoma.rc b/ssmg/sangoma_mgd.trunk/rc/safe_sangoma.rc new file mode 100644 index 0000000..e2bad07 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/rc/safe_sangoma.rc @@ -0,0 +1,18 @@ +#!/bin/bash +# safe_sangoma.rc + +#NOTIFY is list of email address that will be notified if +#safe_sangoma has to restart a daemon +# +#Note: NOTIFY uses mail command to send email. The from +#address will be the user@hostname.com. Some mail servers +#will reject this email as junk/spam. +#Gmail accounts work very well. +#-------------------------------------------------------- +NOTIFY="" + + +#EXEC is the absolute path to an external script/app +#that is to be executed after safe_sangoma restarts a daemon +#-------------------------------------------------------- +EXEC="" diff --git a/ssmg/sangoma_mgd.trunk/rc/smg.rc.bri b/ssmg/sangoma_mgd.trunk/rc/smg.rc.bri new file mode 100644 index 0000000..5144ed7 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/rc/smg.rc.bri @@ -0,0 +1,10 @@ +#!/bin/sh +# smg.rc SMG_RC FILE +# Note: This file is 'executed' by the shell script, so +# the usual shell syntax must be observed. +SANGOMA_PRID="NO" +SANGOMA_BRID="YES" +SANGOMA_SS7ISUP="NO" +SANGOMA_SS7BOOST="NO" +SANGOMA_MEDIA_GATEWAY="YES" +SANGOMA_SAFE_MODE="YES" diff --git a/ssmg/sangoma_mgd.trunk/rc/smg.rc.bri_only b/ssmg/sangoma_mgd.trunk/rc/smg.rc.bri_only new file mode 100644 index 0000000..a9169ea --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/rc/smg.rc.bri_only @@ -0,0 +1,11 @@ +#!/bin/sh +# smg.rc SMG_RC FILE +# Note: This file is 'executed' by the shell script, so +# the usual shell syntax must be observed. +SANGOMA_PRID="NO" +SANGOMA_BRID="YES" +SANGOMA_SS7ISUP="NO" +SANGOMA_SS7BOOST="NO" +SANGOMA_MEDIA_GATEWAY="NO" +SANGOMA_SAFE_MODE="YES" + diff --git a/ssmg/sangoma_mgd.trunk/rc/smg.rc.isupd b/ssmg/sangoma_mgd.trunk/rc/smg.rc.isupd new file mode 100644 index 0000000..05e596e --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/rc/smg.rc.isupd @@ -0,0 +1,11 @@ +#!/bin/sh +# smg.rc SMG_RC FILE +# Note: This file is 'executed' by the shell script, so +# the usual shell syntax must be observed. +SANGOMA_PRID="NO" +SANGOMA_BRID="NO" +SANGOMA_SS7ISUP="YES" +SANGOMA_SS7BOOST="NO" +SANGOMA_MEDIA_GATEWAY="YES" +SANGOMA_SAFE_MODE="YES" + diff --git a/ssmg/sangoma_mgd.trunk/rc/smg.rc.isupd_only b/ssmg/sangoma_mgd.trunk/rc/smg.rc.isupd_only new file mode 100644 index 0000000..85de411 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/rc/smg.rc.isupd_only @@ -0,0 +1,11 @@ +#!/bin/sh +# smg.rc SMG_RC FILE +# Note: This file is 'executed' by the shell script, so +# the usual shell syntax must be observed. +SANGOMA_PRID="NO" +SANGOMA_BRID="NO" +SANGOMA_SS7ISUP="YES" +SANGOMA_SS7BOOST="NO" +SANGOMA_MEDIA_GATEWAY="NO" +SANGOMA_SAFE_MODE="YES" + diff --git a/ssmg/sangoma_mgd.trunk/rc/smg.rc.pri b/ssmg/sangoma_mgd.trunk/rc/smg.rc.pri new file mode 100644 index 0000000..4254cab --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/rc/smg.rc.pri @@ -0,0 +1,11 @@ +#!/bin/sh +# smg.rc SMG_RC FILE +# Note: This file is 'executed' by the shell script, so +# the usual shell syntax must be observed. +SANGOMA_PRID="YES" +SANGOMA_BRID="NO" +SANGOMA_SS7ISUP="NO" +SANGOMA_SS7BOOST="NO" +SANGOMA_MEDIA_GATEWAY="YES" +SANGOMA_SAFE_MODE="YES" + diff --git a/ssmg/sangoma_mgd.trunk/rc/smg.rc.pri_only b/ssmg/sangoma_mgd.trunk/rc/smg.rc.pri_only new file mode 100644 index 0000000..6ebc48a --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/rc/smg.rc.pri_only @@ -0,0 +1,11 @@ +#!/bin/sh +# smg.rc SMG_RC FILE +# Note: This file is 'executed' by the shell script, so +# the usual shell syntax must be observed. +SANGOMA_PRID="YES" +SANGOMA_BRID="NO" +SANGOMA_SS7ISUP="NO" +SANGOMA_SS7BOOST="NO" +SANGOMA_MEDIA_GATEWAY="NO" +SANGOMA_SAFE_MODE="YES" + diff --git a/ssmg/sangoma_mgd.trunk/rc/smg.rc.ss7boost b/ssmg/sangoma_mgd.trunk/rc/smg.rc.ss7boost new file mode 100644 index 0000000..c68d292 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/rc/smg.rc.ss7boost @@ -0,0 +1,10 @@ +#!/bin/sh +# smg.rc SMG_RC FILE +# Note: This file is 'executed' by the shell script, so +# the usual shell syntax must be observed. +SANGOMA_PRID="NO" +SANGOMA_BRID="NO" +SANGOMA_SS7ISUP="NO" +SANGOMA_SS7BOOST="YES" +SANGOMA_MEDIA_GATEWAY="YES" + diff --git a/ssmg/sangoma_mgd.trunk/safe_sangoma b/ssmg/sangoma_mgd.trunk/safe_sangoma index 5c7c604..19a6bfe 100755 --- a/ssmg/sangoma_mgd.trunk/safe_sangoma +++ b/ssmg/sangoma_mgd.trunk/safe_sangoma @@ -7,31 +7,46 @@ shift APPARGS="$*" # Grab any args passed to safe_sangoma TTY= # TTY (if you want one) for APP to run on CONSOLE=no # Whether or not you want a console -#NOTIFY=user@email.com # Who to notify about crashes -#EXEC=/path/to/somescript # Run this command if Asterisk crashes MACHINE=`hostname` # To specify which machine has crashed when getting the mail DUMPDROP=/tmp SLEEPSECS=2 BINDIR=/usr/sbin +BINDIR_SS7="/usr/local/ss7box" PIDFILE=/var/run/$APP.pid CORENAME=$(cat /proc/sys/kernel/core_pattern) VARDIR=/var/log +PROD="safe_sangoma" + +WAN_HOME=/etc/wanpipe +META_SMG_CONF=$WAN_HOME/safe_sangoma.rc + +# Read meta-configuration file. +# metconf file should define NOTIFY or EXEC path script +# to run +if [ -f $META_SMG_CONF ] + then . $META_SMG_CONF +fi if [ -z $APP ]; then echo - echo "Error: APP argument not specified" + logit "Error: APP argument not specified" echo - echo "Usage: safe_sangoma " + logit "Usage: safe_sangoma " echo exit 1 fi +if [ -e $BINDIR_SS7/$APP ]; then + BINDIR=$BINDIR_SS7 +fi + if [ ! -e $BINDIR/$APP ]; then echo - echo "Error: APP not found: $BINDIR/$APP" + logit "Error: APP not found: $BINDIR/$APP" exit 1 fi + # run APP with this priority PRIORITY=0 @@ -46,12 +61,12 @@ MAXFILES=32768 # starting safe_sangoma when APP is running is very bad. PID=`pidof $APP` if [ ! -z $PID ]; then - echo "Error: $APP already running. $0 will exit now." + logit "Error: $APP already running. $0 will exit now." exit 1 fi if [ -f $PIDFILE ]; then - echo "Error: $APP pid file $PIDFILE exists!. $0 will exit now." + logit "Error: $APP pid file $PIDFILE exists!. $0 will exit now." exit 1 fi @@ -61,8 +76,8 @@ fi # if we're not root, fall back to standard everything. if [ `id -u` != 0 ] then - echo "Oops. I'm not root. Falling back to standard prio and file max." >&2 - echo "This is NOT suitable for large systems." >&2 + logit "Oops. I'm not root. Falling back to standard prio and file max." >&2 + logit "This is NOT suitable for large systems." >&2 PRIORITY=0 else if `echo $OSTYPE | grep linux 2>&1 > /dev/null ` @@ -124,7 +139,7 @@ ulimit -c unlimited #fi if [ ! -w ${DUMPDROP} ]; then - echo "Cannot write to ${DUMPDROP}" >&2 + logit "Cannot write to ${DUMPDROP}" >&2 exit 1 fi @@ -136,15 +151,24 @@ trap '' PIPE # # Run scripts to set any environment variables or do any other system-specific setup needed # - if [ -d /etc/wanpipe/safe_startup.d ]; then for script in /etc/wanpipe/safe_startup.d/*.sh; do - if [ -x ${script} ]; then - source ${script} + if [ -x $script ]; then + logit "Executing startup script: $script" + source $script fi done fi + +logit() +{ + local data="$1" + + echo "$PROD: $data" + logger "$PROD: $data" +} + run_sangoma() { while :; do @@ -153,26 +177,38 @@ run_sangoma() nice -n $PRIORITY ${BINDIR}/$APP ${APPARGS} EXITSTATUS=$? - echo "$APP ended with exit status $EXITSTATUS" + logit "$APP ended with exit status $EXITSTATUS" if [ "$EXITSTATUS" = "0" ]; then # Properly shutdown.... - echo "$APP shutdown normally." + logit "$APP shutdown normally." exit 0 elif [ $EXITSTATUS -gt 128 ]; then let EXITSIGNAL=EXITSTATUS-128 - echo "$APP exited on signal $EXITSIGNAL." + logit "$APP exited on signal $EXITSIGNAL." else - echo "$APP died with code $EXITSTATUS." + logit "$APP died with code $EXITSTATUS." fi if [ "$NOTIFY" != "" ]; then - echo "$APP on $MACHINE exited on signal $EXITSIGNAL. Might want to take a peek." | \ - mail -s "$APP Died" $NOTIFY + logfile="/etc/wanpipe/safe_sangoma_crash_"$APP"_"$$".log" + echo > $logfile + date >> $logfile + echo >> $logfile + echo "$APP on $MACHINE exited on signal $EXITSIGNAL. " >> $logfile + echo >> $logfile + echo "/var/log/messages" >> $logfile + echo "====================" >> $logfile + tail -n 50 /var/log/messages >> $logfile + echo "====================" >> $logfile + echo >> $logfile + cat $logfile | \ + mail -s "$APP on $MACHINE Died" $NOTIFY + ret=$? + logit "Email sent to $NOTIFY result $ret" fi if [ "$EXEC" != "" ]; then $EXEC fi - PID=`cat ${PIDFILE}` if [ -f /tmp/$CORENAME.${PID} ]; then mv /tmp/$CORENAME.${PID} ${DUMPDROP}/core.$APP.`hostname`-`date -Iseconds` & @@ -180,7 +216,7 @@ run_sangoma() mv /tmp/$CORENAME ${DUMPDROP}/core.$APP.`hostname`-`date -Iseconds` & fi - echo "Automatically restarting $APP." + logit "Automatically restarting $APP." sleep $SLEEPSECS \rm -f ${PIDFILE} done diff --git a/ssmg/sangoma_mgd.trunk/sangoma_mgd.c b/ssmg/sangoma_mgd.trunk/sangoma_mgd.c index d0d7366..852ec4a 100644 --- a/ssmg/sangoma_mgd.trunk/sangoma_mgd.c +++ b/ssmg/sangoma_mgd.trunk/sangoma_mgd.c @@ -9,7 +9,72 @@ * * ============================================= * + * v1.68 David Yat Sin + * Mar 18 2010 + * Media and RING bits implemented + * + * v1.67 David Yat Sin + * Mar 16 2010 + * Added sanity check for span chan on call stopped and call answered + + * v1.66 David Yat Sin + * Mar 15 2010 + * Fix for WFLAG_SYSTEM_RESET being cleared if when + * no boost msg was received from signalling daemon + * + * v1.65 David Yat Sin + * Mar 10 2010 + * Support for TON and NPI passthrough * + * v1.64 David Yat Sin + * Feb 22 2010 + * Updated to sigboost 103 + * Added checks for hwec present + * + * v1.63 Nenad Corbic + * Jan 27 2010 + * Enabled media pass through so that + * two woomera servers pass media directly. + * + * v1.62 Konrad Hammel + * Jan 26 2010 + * Added rbs relay code + * + * v1.61 Konrad Hammel + * Jan 19 2010 + * changed all_ckt_busy to be per trunk group. + * + * v1.60 Nenad Corbic + * Jan 14 2010 + * Added media sequencing option. + * Check if server has it enabled. + * + * v1.59 Nenad Corbic + * Changed w1g1 to s1c1 + * + * v1.58 Nenad Corbic + * Added bridge tdm to ip functionality + * + * v1.57 Nenad Corbic + * Support for woomera multiple profiles + * + * v1.56 David Yat Sin + * Changed BRI to run with HWEC in non-persist mode + * + * v1.55 Nenad Corbic + * Updated the base media port to 10000 and max media ports to 5000 + * + * v1.54 Nenad Corbic + * Bug added in 1.51 release causing call on channel 31 to fail. + * + * v1.53 Nenad Corbic + * Added progress message + * + * v1.52 David Yat Sin + * Changed sangoma_open_span_chan to __sangoma_span_chan + * to enabled shared used of file descriptors when using + * with PRI in NFAS mode + * * v1.51 David Yat Sin * MAX_SPANS increased to 32. * Fix for server.process_table declared incorrectly @@ -289,14 +354,23 @@ static char ps_progname[]="sangoma_mgd"; static struct woomera_interface woomera_dead_dev; + +#ifdef BRI_PROT +static unsigned char tdmv_hwec_persist = 0; +#else +static unsigned char tdmv_hwec_persist = 1; +#endif struct woomera_server server; +struct smg_tdm_ip_bridge g_smg_ip_bridge_idx[MAX_SMG_BRIDGE]; +pthread_mutex_t g_smg_ip_bridge_lock; + #if 0 #define DOTRACE #endif -#define SMG_VERSION "v1.51" +#define SMG_VERSION "v1.68" /* enable early media */ #if 1 @@ -309,6 +383,8 @@ struct woomera_server server; #define SMG_DTMF_RATE 8000 #define SMG_DEFAULT_CALL_TIMEOUT 300 +#define SANGOMA_USR_PERIOD 20 + #if 0 #define MEDIA_SOCK_SHUTDOWN 1 #endif @@ -336,9 +412,17 @@ static int drop_seq=0; #undef SMG_DROP_SEQ #endif + +#if 0 +#define SMG_NO_MEDIA +#warning "SMG No Media Defined" +#else +#undef SMG_NO_MEDIA +#endif + const char WELCOME_TEXT[] = "================================================================================\n" -"Sangoma Media Gateway Daemon v1.51 \n" +"Sangoma Media Gateway Daemon v1.68 \n" "\n" "TDM Signal Media Gateway for Sangoma/Wanpipe Cards\n" "Copyright 2005, 2006, 2007 \n" @@ -368,6 +452,8 @@ static int launch_woomera_thread(struct woomera_interface *woomera); static void woomera_check_digits (struct woomera_interface *woomera); static struct woomera_interface *alloc_woomera(void); static void handle_event_dtmf(struct woomera_interface *woomera, unsigned char dtmf_digit); +static int handle_event_rbs(struct woomera_interface *woomera, unsigned char rbs_digit); +static int handle_dequeue_and_woomera_tx_event_rbs(struct woomera_interface *woomera); q931_cause_to_str_array_t q931_cause_to_str_array[255]; bearer_cap_to_str_array_t bearer_cap_to_str_array[255]; @@ -378,7 +464,7 @@ static int isup_exec_command(int span, int chan, int id, int cmd, int cause) short_signal_event_t oevent; int retry=5; - call_signal_event_init(&oevent, cmd, chan, span); + call_signal_event_init((short_signal_event_t*)&oevent, cmd, chan, span); oevent.release_cause = cause; if (id >= 0) { @@ -405,6 +491,31 @@ isup_exec_cmd_retry: return 0; } +static int isup_exec_event(call_signal_event_t *event) +{ + int retry=5; + +isup_exec_cmd_retry: + if (call_signal_connection_write(&server.mcon, event) < 0){ + + --retry; + if (retry <= 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical System Error: Failed to tx on ISUP socket: %s\n", + strerror(errno)); + return -1; + } else { + log_printf(SMG_LOG_ALL, server.log, + "System Warning: Failed to tx on ISUP socket: %s :retry %i\n", + strerror(errno),retry); + } + + goto isup_exec_cmd_retry; + } + + return 0; +} + static int isup_exec_commandp(int span, int chan, int id, int cmd, int cause) { @@ -442,8 +553,14 @@ isup_exec_cmd_retry: static int get_span_chan_from_interface(char* interface, int *span_ptr, int *chan_ptr) { int span, chan; + int err; - if (sscanf(interface, "w%dg%d", &span, &chan) == 2) { + err=sscanf(interface, "s%dc%d", &span, &chan); + if (err!=2) { + err=sscanf(interface, "w%dg%d", &span, &chan); + } + + if (err==2) { if (smg_validate_span_chan(span,chan) != 0) { log_printf(SMG_LOG_DEBUG_CALL, server.log, "WOOMERA Warning invalid span chan in interface %s\n", @@ -761,6 +878,41 @@ static void add_listener(struct woomera_interface *woomera) pthread_mutex_unlock(&server.listen_lock); } +static int wanpipe_send_rbs(struct woomera_interface *woomera, char *digits) +{ + struct media_session *ms = woomera_get_ms(woomera); + int err; + wanpipe_tdm_api_t tdm_api; + unsigned int digit; + + memset(&tdm_api,0,sizeof(tdm_api)); + + if (!ms) { + log_printf(SMG_LOG_PROD, server.log, "[%s]: wanpipe_send_rbs (%X) Error no ms\n", + woomera->interface,digit); + return -EINVAL; + } + + if ((woomera->span+1) <= 0 || !server.rbs_relay[woomera->span+1]) { + log_printf(SMG_LOG_PROD, server.log, "[%s]: wanpipe_send_rbs (%X) Error rbs relay not enabled on span %i\n", + woomera->interface,digit,woomera->span+1); + return -EINVAL; + } + + err=sscanf(digits,"%x",&digit); + if (err == 1) { + log_printf(SMG_LOG_PROD, server.log, "[%s]: Transmitting RBS 0x%X\n", + woomera->interface,digit); + +#ifdef LIBSANGOMA_VERSION + err=sangoma_tdm_write_rbs(ms->sangoma_sock, &tdm_api, woomera->chan+1, digit); +#else + err=sangoma_tdm_write_rbs(ms->sangoma_sock, &tdm_api, digit); +#endif + } + + return err; +} static int wanpipe_send_dtmf(struct woomera_interface *woomera, char *digits) @@ -932,7 +1084,7 @@ waitfor_socket_tryagain: } -static int waitfor_2sockets(int fda, int fdb, char *a, char *b, int timeout) +int waitfor_2sockets(int fda, int fdb, char *a, char *b, int timeout) { struct pollfd pfds[2]; int res = 0; @@ -956,8 +1108,6 @@ waitfor_2sockets_tryagain: pfds[0].events = POLLIN | errflags; pfds[1].events = POLLIN | errflags; - - res = poll(pfds, 2, timeout); if (res > 0) { @@ -990,58 +1140,73 @@ waitfor_2sockets_tryagain: return res; } +static int woomera_raw_to_ip(struct media_session *ms, char *raw) +{ + int x; + char *p; + + for(x = 0; x < strlen(raw) ; x++) { + if (raw[x] == ':') { + break; + } + if (raw[x] == '/') { + break; + } + } + + media_set_raw(ms,raw); + + if (ms->ip) { + smg_free(ms->ip); + } + ms->ip = smg_strndup(raw, x); + p = raw + (x+1); + ms->port = atoi(p); + + return 0; +} static struct media_session *media_session_new(struct woomera_interface *woomera) { - struct media_session *ms = NULL; - int x; - char *p; - int span,chan; - + struct media_session *ms = NULL; + int span,chan; + span=woomera->span; chan=woomera->chan; - log_printf(SMG_LOG_DEBUG_CALL, server.log,"Starting new MEDIA session [%s] [%s]\n", - woomera->interface,woomera->raw?woomera->raw:"N/A"); + log_printf(SMG_LOG_DEBUG_CALL, server.log,"Starting new MEDIA session [%s] [%s]\n", + woomera->interface,woomera->raw?woomera->raw:"N/A"); - if ((ms = smg_malloc(sizeof(struct media_session)))) { + if ((ms = smg_malloc(sizeof(struct media_session)))) { memset(ms, 0, sizeof(struct media_session)); if (woomera->loop_tdm != 1) { - for(x = 0; x < strlen(woomera->raw) ; x++) { - if (woomera->raw[x] == ':') { - break; - } - if (woomera->raw[x] == '/') { - break; - } - } - ms->ip = smg_strndup(woomera->raw, x); + woomera_raw_to_ip(ms,woomera->raw); time(&ms->started); - p = woomera->raw + (x+1); - ms->port = atoi(p); + } time(&ms->started); - woomera_set_ms(woomera,ms); ms->woomera = woomera; /* Setup artificial DTMF stuff */ memset(&ms->tone_session, 0, sizeof(ms->tone_session)); if (teletone_init_session(&ms->tone_session, 0, NULL, NULL)) { - log_printf(SMG_LOG_ALL, server.log, "ERROR: Failed to initialize TONE [w%ig%i]!\n", + log_printf(SMG_LOG_ALL, server.log, "ERROR: Failed to initialize TONE [s%ic%i]!\n", span+1,chan+1); } - + ms->tone_session.rate = SMG_DTMF_RATE; ms->tone_session.duration = server.dtmf_on * (ms->tone_session.rate / 1000); ms->tone_session.wait = server.dtmf_off * (ms->tone_session.rate / 1000); teletone_dtmf_detect_init (&ms->dtmf_detect, SMG_DTMF_RATE); - - } else { - log_printf(SMG_LOG_ALL, server.log, "ERROR: Memory Alloc Failed [w%ig%i]!\n", + + woomera_set_ms(woomera,ms); + + } else { + log_printf(SMG_LOG_ALL, server.log, "ERROR: Memory Alloc Failed [s%ic%i]!\n", span+1,chan+1); } @@ -1053,7 +1218,10 @@ static void media_session_free(struct media_session *ms) if (ms->ip) { smg_free(ms->ip); } - + if (ms->raw) { + smg_free(ms->raw); + } + teletone_destroy_session(&ms->tone_session); switch_buffer_destroy(&ms->dtmf_buffer); @@ -1062,6 +1230,20 @@ static void media_session_free(struct media_session *ms) smg_free(ms); } +static int update_udp_socket(struct media_session *ms, char *ip, int port) +{ + struct hostent *result; + char buf[512]; + int err = 0; + + memset(&ms->remote_hp, 0, sizeof(ms->remote_hp)); + gethostbyname_r(ip, &ms->remote_hp, buf, sizeof(buf), &result, &err); + ms->remote_addr.sin_family = ms->remote_hp.h_addrtype; + memcpy((char *) &ms->remote_addr.sin_addr.s_addr, ms->remote_hp.h_addr_list[0], ms->remote_hp.h_length); + ms->remote_addr.sin_port = htons(port); + + return 0; +} static int create_udp_socket(struct media_session *ms, char *local_ip, int local_port, char *ip, int port) { @@ -1237,13 +1419,16 @@ static void media_loop_run(struct media_session *ms) char filename[100]; FILE *filed=NULL; int loops=0,flags_out=0; + int open_cnt = 0; + + open_cnt=0; sangoma_api_hdr_t hdrframe; memset(&hdrframe,0,sizeof(hdrframe)); memset(circuit_frame,0,sizeof(circuit_frame)); retry_loop: - ms->sangoma_sock = sangoma_open_tdmapi_span_chan(woomera->span+1, woomera->chan+1); + ms->sangoma_sock = open_span_chan(woomera->span+1, woomera->chan+1); log_printf(SMG_LOG_PROD, server.log, "Media Loop Started %s fd=%i\n", woomera->interface,ms->sangoma_sock); @@ -1254,7 +1439,7 @@ retry_loop: usleep(500000); goto retry_loop; } - log_printf(SMG_LOG_ALL, server.log, "WANPIPE MEDIA Socket Error (%s) if=[%s] [w%ig%i]\n", + log_printf(SMG_LOG_ALL, server.log, "WANPIPE MEDIA Socket Error (%s) if=[%s] [s%ic%i]\n", strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); } else { @@ -1268,25 +1453,38 @@ retry_loop: errs++; } - if (sangoma_tdm_set_usr_period(ms->sangoma_sock, &tdm_api, 20) < 0 ) { + if (sangoma_tdm_set_usr_period(ms->sangoma_sock, &tdm_api, SANGOMA_USR_PERIOD) < 0 ) { errs++; } sangoma_frame_len = sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); +#ifdef LIBSANGOMA_VERSION + ms->has_hwec = sangoma_tdm_get_hw_ec(ms->sangoma_sock, &tdm_api); + if (ms->has_hwec) { + sangoma_tdm_disable_hwec(ms->sangoma_sock,&tdm_api); + } +#else + ms->has_hwec=1; sangoma_tdm_disable_hwec(ms->sangoma_sock,&tdm_api); - +#endif + ms->oob_disable = 0; +#ifdef LIBSANGOMA_VERSION + open_cnt = sangoma_get_open_cnt(ms->sangoma_sock, &tdm_api); + if (open_cnt > 1) { + ms->oob_disable = 1; + } +#endif } - - if (errs) { + if (errs) { log_printf(SMG_LOG_ALL, server.log, "Media Loop: failed to open tdm device %s\n", woomera->interface); return; } if (server.loop_trace) { - sprintf(filename,"/smg/w%ig%i-loop.trace",woomera->span+1,woomera->chan+1); + sprintf(filename,"/smg/s%ic%i-loop.trace",woomera->span+1,woomera->chan+1); unlink(filename); filed = safe_fopen(filename, "w"); } @@ -1302,21 +1500,6 @@ retry_loop: continue; } -#if 0 - if (res == SMG_SOCKET_EVENT_NVAL) { - close_socket(&ms->sangoma_sock); - - if (woomera_test_flag(woomera, WFLAG_MEDIA_END)) { - break; - } - - ms->sangoma_sock = sangoma_open_tdmapi_span_chan(woomera->span+1, woomera->chan+1); - log_printf(SMG_LOG_ALL, server.log, "Media Loop Restart %s\n", - woomera->interface); - continue; - } -#endif - res = sangoma_readmsg_socket(ms->sangoma_sock, &hdrframe, sizeof(hdrframe), @@ -1363,9 +1546,11 @@ retry_loop: fclose(filed); } - sangoma_tdm_enable_hwec(ms->sangoma_sock,&tdm_api); + if (ms->has_hwec) { + sangoma_tdm_enable_hwec(ms->sangoma_sock,&tdm_api); + } - close_socket(&ms->sangoma_sock); + close_span_chan(&ms->sangoma_sock, woomera->span+1, woomera->chan+1); if (loops < 1) { log_printf(SMG_LOG_ALL, server.log, "Media Loop FAILED %s Master=%i MediaEnd=%i Loops=%i\n", @@ -1415,7 +1600,12 @@ static void *media_thread_run(void *obj) wanpipe_tdm_api_t tdm_api; FILE *tx_fd=NULL; int sock_timeout=200; - int hwec_reenable=0; + + int hwec_enabled=0, hwec_reenable=0; + + int open_cnt = 0; + + open_cnt=0; if (woomera_test_flag(woomera, WFLAG_MEDIA_END) || !woomera->interface || @@ -1478,19 +1668,19 @@ static void *media_thread_run(void *obj) } #else media_retry: - ms->sangoma_sock = sangoma_open_tdmapi_span_chan(woomera->span+1, woomera->chan+1); + ms->sangoma_sock = open_span_chan(woomera->span+1, woomera->chan+1); if (ms->sangoma_sock < 0) { if (!woomera_test_flag(woomera, WFLAG_MEDIA_END)) { media_retry_cnt++; if (media_retry_cnt < 5) { - log_printf(SMG_LOG_ALL, server.log, "WANPIPE Socket Retry [w%ig%i]\n", + log_printf(SMG_LOG_ALL, server.log, "WANPIPE Socket Retry [s%ic%i]\n", woomera->span+1, woomera->chan+1); usleep(100000); goto media_retry; } - log_printf(SMG_LOG_ALL, server.log, "WANPIPE Socket Error (%s) if=[%s] [w%ig%i]\n", + log_printf(SMG_LOG_ALL, server.log, "WANPIPE Socket Error (%s) if=[%s] [s%ic%i]\n", strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); /* Switch Congestion */ @@ -1513,10 +1703,16 @@ media_retry: errs++; } - if (sangoma_tdm_set_usr_period(ms->sangoma_sock, &tdm_api, 20) < 0 ) { + if (sangoma_tdm_set_usr_period(ms->sangoma_sock, &tdm_api, SANGOMA_USR_PERIOD) < 0 ) { errs++; } +#ifdef LIBSANGOMA_VERSION + ms->has_hwec = sangoma_tdm_get_hw_ec(ms->sangoma_sock, &tdm_api); +#else + ms->has_hwec = 1; +#endif + # ifdef CODEC_LAW_DEFAULT # ifdef LIBSANGOMA_GET_HWCODING ms->hw_coding=sangoma_tdm_get_hw_coding(ms->sangoma_sock, &tdm_api); @@ -1530,36 +1726,67 @@ media_retry: # ifdef LIBSANGOMA_GET_HWDTMF + + if (ms->has_hwec) { ms->hw_dtmf=sangoma_tdm_get_hw_dtmf(ms->sangoma_sock, &tdm_api); - if (ms->hw_dtmf) { - log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Supported [w%ig%i]\n", + if (ms->hw_dtmf) { + log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Supported [s%ic%i]\n", strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); } else { - log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Not Supported [w%ig%i]\n", + log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Not Supported [s%ic%i]\n", strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); } + } + #else ms->hw_dtmf=0; - log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Not Supported [w%ig%i]\n", + log_printf(SMG_LOG_DEBUG_9, server.log, "HW DTMF Not Supported [s%ic%i]\n", strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1); # warning "libsangoma missing hwdtmf feature: not up to date!" #endif - if (!bearer_cap_is_audio(woomera->bearer_cap)) { - int err; - err=sangoma_tdm_disable_hwec(ms->sangoma_sock, &tdm_api); - if (err == 0) { - hwec_reenable=1; - log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] Disabling hwec Ok\n",woomera->interface); + ms->oob_disable = 0; +#ifdef LIBSANGOMA_VERSION + open_cnt = sangoma_get_open_cnt(ms->sangoma_sock, &tdm_api); + if (open_cnt > 1) { + ms->oob_disable = 1; + } +#endif + + + if (ms->has_hwec) { + if (!tdmv_hwec_persist) { + // BRI cards start with HWEC in bypass disable state + if (bearer_cap_is_audio(woomera->bearer_cap)) { + int err; + err=sangoma_tdm_enable_hwec(ms->sangoma_sock, &tdm_api); + if (err == 0) { + hwec_enabled=1; + log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] Enabling hwec Ok\n",woomera->interface); + } else { + log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] Enabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + } + } } else { - log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] Disabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + if (!bearer_cap_is_audio(woomera->bearer_cap)) { + int err; + err=sangoma_tdm_disable_hwec(ms->sangoma_sock, &tdm_api); + if (err == 0) { + hwec_reenable=1; + log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] Disabling hwec Ok\n",woomera->interface); + } else { + log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] Disabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + } + } } - } sangoma_frame_len = sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api); + if (server.rbs_relay[woomera->span+1]) { + sangoma_tdm_enable_rbs_events(ms->sangoma_sock, &tdm_api, 20); + } } #endif } @@ -1569,10 +1796,12 @@ media_retry: #ifdef WP_HPTDM_API /* No tdm thread */ #else +#ifndef SMG_NO_MEDIA if (!errs && launch_media_tdm_thread(woomera)) { errs++; } +#endif #endif if (errs) { @@ -1584,6 +1813,8 @@ media_retry: unsigned char udp_frame[4096]; unsigned int fromlen = sizeof(struct sockaddr_in); int flags_out=0; + int rc; + unsigned int carrier=0,carrier_diff=0; sangoma_api_hdr_t hdrframe; memset(&hdrframe,0,sizeof(hdrframe)); @@ -1632,7 +1863,14 @@ media_retry: log_printf(SMG_LOG_ALL,server.log, "FAILED TO OPEN Sound file!\n"); } } - + + for (;;) { + if ((packet_len = recvfrom(ms->udp_sock, udp_frame, sizeof(udp_frame), + MSG_DONTWAIT, (struct sockaddr *) &ms->local_addr, &fromlen)) < 1) { + break; + } + } + for (;;) { @@ -1644,12 +1882,17 @@ media_retry: break; } + res = waitfor_socket(ms->udp_sock, sock_timeout, POLLIN, &flags_out); if (res < 0) { break; } +#ifdef SMG_NO_MEDIA + continue; +#endif + if (res == 0) { if (woomera_dtmf_transmit(ms,sangoma_frame_len) == 0) { @@ -1690,6 +1933,21 @@ media_retry: if (packet_len > 0) { + if (server.udp_seq && packet_len > 4) { + packet_len-=4; + if ( woomera->rx_udp_seq != *(unsigned int*)&udp_frame[packet_len]) { + woomera->rx_udp_seq = *(unsigned int*)&udp_frame[packet_len]; + log_printf(SMG_LOG_WOOMERA,server.log,"RX UDP SEQ=%i Expected=%i\n", + *(unsigned int*)&udp_frame[packet_len],woomera->rx_udp_seq); + + pthread_mutex_lock(&server.thread_count_lock); + server.media_rx_seq_err++; + pthread_mutex_unlock(&server.thread_count_lock); + } else { + woomera->rx_udp_seq++; + } + } + #if 0 /* NC: This can cause skb_over panic must be retested */ if (packet_len != sangoma_frame_len && ms->udp_sync_cnt <= 5) { @@ -1748,17 +2006,45 @@ media_retry: tx_fd=NULL; } + #ifdef WP_HPTDM_API if (ms->tdmchan->push) { ms->tdmchan->push(ms->tdmchan,udp_frame,packet_len); } #else - sangoma_sendmsg_socket(ms->sangoma_sock, + rc=sangoma_sendmsg_socket(ms->sangoma_sock, &hdrframe, sizeof(hdrframe), udp_frame, packet_len, 0); + if (rc != packet_len) { + log_printf(SMG_LOG_PROD, server.log, "Error: Sangoma Tx error [%s] len=%i (%s)\n",woomera->interface, packet_len, strerror(errno)); + pthread_mutex_lock(&server.thread_count_lock); + server.media_rx_seq_err++; + pthread_mutex_unlock(&server.thread_count_lock); + } + + sangoma_get_full_cfg(ms->sangoma_sock,&tdm_api); + if (carrier == 0) { + carrier=tdm_api.wp_tdm_cmd.stats.tx_carrier_errors; + } + if (carrier != tdm_api.wp_tdm_cmd.stats.tx_carrier_errors) { + carrier_diff+=tdm_api.wp_tdm_cmd.stats.tx_carrier_errors-carrier; + carrier=tdm_api.wp_tdm_cmd.stats.tx_carrier_errors; + log_printf(SMG_LOG_WOOMERA, server.log, "Error: Sangoma Tx carrier error [%s] cnt=%i\n", + woomera->interface, carrier_diff); + pthread_mutex_lock(&server.thread_count_lock); + server.media_rx_seq_err++; + pthread_mutex_unlock(&server.thread_count_lock); + + sangoma_sendmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + udp_frame, + packet_len, 0); + } + #endif } @@ -1826,22 +2112,38 @@ media_retry: media_thread_exit: - if (hwec_reenable) { - int err; - err=sangoma_tdm_enable_hwec(ms->sangoma_sock, &tdm_api); - if (err==0) { - log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] Re-enabling hwec ok\n",woomera->interface); + if (ms->has_hwec) { + + if (!tdmv_hwec_persist) { + if (hwec_enabled) { + int err; + err=sangoma_tdm_disable_hwec(ms->sangoma_sock, &tdm_api); + if (err==0) { + log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] disabling hwec ok\n",woomera->interface); + } else { + log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] disabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + } + } } else { - log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] Re-enabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + if (hwec_reenable) { + int err; + err=sangoma_tdm_enable_hwec(ms->sangoma_sock, &tdm_api); + if (err==0) { + log_printf(SMG_LOG_DEBUG_8, server.log, "MEDIA [%s] Re-enabling hwec ok\n",woomera->interface); + } else { + log_printf(SMG_LOG_PROD, server.log, "MEDIA [%s] Re-enabling hwec Failed (%s)\n",woomera->interface, strerror(errno)); + } + } } + } - + if (woomera_test_flag(woomera, WFLAG_MEDIA_TDM_RUNNING)) { woomera_set_flag(woomera, WFLAG_MEDIA_END); /* Dont wait for the other thread */ close_socket(&ms->udp_sock); - close_socket(&ms->sangoma_sock); + close_span_chan(&ms->sangoma_sock, woomera->span+1, woomera->chan+1); while(woomera_test_flag(woomera, WFLAG_MEDIA_TDM_RUNNING)) { usleep(1000); sched_yield(); @@ -1850,7 +2152,7 @@ media_thread_exit: close_socket(&ms->udp_sock); - close_socket(&ms->sangoma_sock); + close_span_chan(&ms->sangoma_sock, woomera->span+1, woomera->chan+1); if (tx_fd){ fclose(tx_fd); @@ -1887,10 +2189,7 @@ static void *media_tdm_thread_run(void *obj) unsigned char circuit_frame[1024]; sangoma_api_hdr_t hdrframe; int flags_out; - -#if 0 - int tdm_cnt=0; -#endif + int poll_opt = POLLIN | POLLPRI; memset(&hdrframe,0,sizeof(hdrframe)); memset(circuit_frame,0,sizeof(circuit_frame)); @@ -1911,6 +2210,16 @@ static void *media_tdm_thread_run(void *obj) log_printf(SMG_LOG_DEBUG_CALL, server.log, "MEDIA TDM session for [%s] started (ptr=%p)\n", woomera->interface,woomera); + for (;;) { + res = sangoma_readmsg_socket(ms->sangoma_sock, + &hdrframe, + sizeof(hdrframe), + circuit_frame, + sizeof(circuit_frame), 0); + if (res < 0) { + break; + } + } for (;;) { @@ -1920,9 +2229,14 @@ static void *media_tdm_thread_run(void *obj) res=0; break; } + - - res = waitfor_socket(ms->sangoma_sock, 1000, (POLLIN | POLLPRI), &flags_out); + if (ms->oob_disable) { + poll_opt = POLLIN; + } else { + poll_opt = POLLIN | POLLPRI; + } + res = waitfor_socket(ms->sangoma_sock, 1000, poll_opt, &flags_out); if (res < 0) { @@ -1941,6 +2255,10 @@ static void *media_tdm_thread_run(void *obj) if (flags_out & POLLIN) { + if (server.rbs_relay[woomera->span+1]) { + handle_dequeue_and_woomera_tx_event_rbs(woomera); + } + res = sangoma_readmsg_socket(ms->sangoma_sock, &hdrframe, sizeof(hdrframe), @@ -1956,6 +2274,13 @@ static void *media_tdm_thread_run(void *obj) break; } + + if (server.udp_seq) { + *(unsigned int*)&circuit_frame[res] = woomera->tx_udp_seq; + woomera->tx_udp_seq++; + res+=4; + } + res = sendto(ms->udp_sock, circuit_frame, res, 0, @@ -1964,7 +2289,6 @@ static void *media_tdm_thread_run(void *obj) if (res < 0) { log_printf(SMG_LOG_DEBUG_CALL, server.log, "UDP Sento Error: %s\n", strerror(errno)); - } } @@ -1997,6 +2321,12 @@ static void *media_tdm_thread_run(void *obj) } break; #endif + case WP_TDMAPI_EVENT_RBS: + log_printf(SMG_LOG_WOOMERA, server.log, "[%s] Rx RBS Event Bits=0x%02X\n", + woomera->interface, rx_event->wp_tdm_api_event_rbs_bits); + handle_event_rbs(woomera, rx_event->wp_tdm_api_event_rbs_bits); + break; + default: log_printf(SMG_LOG_ALL, server.log, "TDM API Unknown OOB Event %i\n", rx_event->wp_tdm_api_event_type); @@ -2114,9 +2444,9 @@ static struct woomera_interface * launch_woomera_loop_thread(short_signal_event_ struct woomera_interface *woomera = NULL; char callid[20]; - sprintf(callid, "w%dg%d", event->span+1,event->chan+1); + sprintf(callid, "s%dc%d", event->span+1,event->chan+1); - if ((woomera = alloc_woomera())) { + if ((woomera = alloc_woomera())) { woomera->chan = event->chan; woomera->span = event->span; @@ -2515,7 +2845,101 @@ static int add_to_holding_tank(struct woomera_interface *woomera) } - +static int handle_event_rbs(struct woomera_interface *woomera, unsigned char rbs_digit) +{ + woomera_rbs_relay_t *rbsrelay; + + if (smg_validate_span_chan(woomera->span,woomera->chan) != 0) { + log_printf(SMG_LOG_ALL, server.log, "[%s] handle_event_rbs invalid span chan\n", + woomera->interface); + return -1; + } + + if (!server.rbs_relay[woomera->span+1]) { + log_printf(SMG_LOG_ALL, server.log, "[%s] handle_event_rbs rbs_relan not enabled\n", + woomera->interface); + return -1; + } + + rbsrelay=&server.process_table[woomera->span][woomera->chan].rbs_relay; + + if (rbsrelay->rbs_bits[rbsrelay->rx_idx].init) { + log_printf(SMG_LOG_ALL, server.log, "[%s] Critical Error Rx RBS Overrun\n", + woomera->interface); + return -1; + } + + log_printf(SMG_LOG_ALL, server.log, "[%s] woomera rx queue rbs %X idx %i\n", + woomera->interface,rbs_digit, rbsrelay->rx_idx); + + rbsrelay->rbs_bits[rbsrelay->rx_idx].abcd=rbs_digit; + rbsrelay->rbs_bits[rbsrelay->rx_idx].init=500/SANGOMA_USR_PERIOD; + rbsrelay->rx_idx = ((rbsrelay->rx_idx + 1) % WOOMERA_MAX_RBS_BITS); + + return 0; +} + +static int handle_dequeue_and_woomera_tx_event_rbs(struct woomera_interface *woomera) +{ + struct woomera_event wevent; + woomera_rbs_relay_t *rbsrelay; + unsigned char abcd; + + if (smg_validate_span_chan(woomera->span,woomera->chan) != 0) { + log_printf(SMG_LOG_ALL, server.log, "[%s] handle_dequeue_and_woomera_tx_event_rbs invalid span chan\n", + woomera->interface); + return -1; + } + + if (!server.rbs_relay[woomera->span+1]) { + log_printf(SMG_LOG_ALL, server.log, "[%s] handle_dequeue_and_woomera_tx_event_rbs rbs_relan not enabled\n", + woomera->interface); + return -1; + } + + if (!woomera_test_flag(woomera,WFLAG_ANSWER)) { + return 0; + } + + rbsrelay=&server.process_table[woomera->span][woomera->chan].rbs_relay; + + if (rbsrelay->rbs_bits[rbsrelay->tx_idx].init == 0) { + return -1; + } + + if (rbsrelay->rbs_bits[rbsrelay->tx_idx].init > 1) { + rbsrelay->rbs_bits[rbsrelay->tx_idx].init--; + return -1; + } + + + abcd = rbsrelay->rbs_bits[rbsrelay->tx_idx].abcd; + rbsrelay->rbs_bits[rbsrelay->tx_idx].init=0; + + + log_printf(SMG_LOG_ALL, server.log, "[%s] woomera tx rbs %X idx %i\n", + woomera->interface,abcd, rbsrelay->tx_idx); + + rbsrelay->tx_idx = ((rbsrelay->tx_idx + 1) % WOOMERA_MAX_RBS_BITS); + + memset(&wevent, 0, sizeof(struct woomera_event)); + + new_woomera_event_printf(&wevent, "EVENT RBS %sUnique-Call-Id:%s%sContent-Length:%d%s%s%X%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_LINE_SEPERATOR, + 1, + WOOMERA_LINE_SEPERATOR, + WOOMERA_LINE_SEPERATOR, + abcd, + WOOMERA_RECORD_SEPERATOR); + + + enqueue_event(woomera, &wevent,EVENT_FREE_DATA); + + return 0; +} + static void handle_event_dtmf(struct woomera_interface *woomera, unsigned char dtmf_digit) { struct woomera_event wevent; @@ -2537,22 +2961,76 @@ static void handle_event_dtmf(struct woomera_interface *woomera, unsigned char d return; } +static int handle_woomera_progress(struct woomera_interface *woomera, + struct woomera_message *wmsg) +{ + call_signal_event_t event; + int err=-1; + + memset(&event, 0, sizeof(event)); + call_signal_event_init((short_signal_event_t*)&event, SIGBOOST_EVENT_CALL_PROGRESS, woomera->chan, woomera->span); + sprintf(event.isup_in_rdnis,"SMG003-EVI-2"); + event.isup_in_rdnis_size=strlen(event.isup_in_rdnis); + if (woomera->index >= 0) { + event.call_setup_id = woomera->index; + } + + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s]\n", + wmsg->command, woomera->interface); + + if (!woomera_check_running(woomera)) { + socket_printf(woomera->socket, "405 PROGRESS Channel already hungup%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + if (!woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + + socket_printf(woomera->socket, "405 PROGRESS Channel not aceked%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + return -1; + } + + err=isup_exec_event(&event); + if (err == 0) { + socket_printf(woomera->socket, + "200 %s PROGRESS OK%s" + "Unique-Call-Id: %s%s", + wmsg->callid, + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } else { + socket_printf(woomera->socket, "405 PROGRESS Boost failure%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + } + + return err; +} static int handle_woomera_media_accept_answer(struct woomera_interface *woomera, struct woomera_message *wmsg, int media, int answer, int accept) { char *raw = woomera_message_header(wmsg, "raw-audio"); + char *media_available = woomera_message_header(wmsg, "xMedia"); + char *ring_available = woomera_message_header(wmsg, "xRing"); struct woomera_event wevent; - log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s]\n", - wmsg->command, woomera->interface); + log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s] raw=%s\n", + wmsg->command, woomera->interface, raw?raw:"N/A"); - - if (woomera_test_flag(woomera, WFLAG_HANGUP) || - !woomera_test_flag(woomera, WFLAG_RUNNING) || - woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + if (!woomera_check_running(woomera)) { log_printf(SMG_LOG_DEBUG_CALL, server.log, "ERROR! call was cancelled MEDIA on HANGUP or MEDIA END!\n"); @@ -2633,6 +3111,18 @@ static int handle_woomera_media_accept_answer(struct woomera_interface *woomera, woomera->timeout=0; return -1; } + } else if (media && raw && woomera->ms) { + if (strncmp(raw,woomera->raw,20) != 0) { + log_printf(SMG_LOG_WOOMERA, server.log, "[%s] MEDIA Address Change from %s to %s\n", + woomera->interface,woomera->raw, raw); + + pthread_mutex_lock(&woomera->ms_lock); + woomera_set_raw(woomera,raw); + woomera_raw_to_ip(woomera->ms,woomera->raw); + update_udp_socket(woomera->ms,woomera->ms->ip, woomera->ms->port); + + pthread_mutex_unlock(&woomera->ms_lock); + } } if (!woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) { @@ -2650,12 +3140,19 @@ static int handle_woomera_media_accept_answer(struct woomera_interface *woomera, if (accept) { if (!autoacm && !woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + short_signal_event_t event; + memset(&event, 0, sizeof(event)); + call_signal_event_init(&event, SIGBOOST_EVENT_CALL_START_ACK, woomera->chan, woomera->span); + if (media_available && (atoi(media_available) == 1)) { + event.flags |= SIGBOOST_PROGRESS_MEDIA; + } + + if (ring_available && (atoi(ring_available) == 1)) { + event.flags |= SIGBOOST_PROGRESS_RING; + } woomera_set_flag(woomera,WFLAG_CALL_ACKED); - isup_exec_command(woomera->span, - woomera->chan, - -1, - SIGBOOST_EVENT_CALL_START_ACK, - 0); + + isup_exec_event((call_signal_event_t*)&event); } } @@ -2671,23 +3168,33 @@ static int handle_woomera_media_accept_answer(struct woomera_interface *woomera, if (ms) { if (!autoacm && !woomera_test_flag(woomera,WFLAG_CALL_ACKED)) { + short_signal_event_t event; + memset(&event, 0, sizeof(event)); + call_signal_event_init(&event, SIGBOOST_EVENT_CALL_START_ACK, woomera->chan, woomera->span); + if (media_available && (atoi(media_available) == 1)) { + event.flags |= SIGBOOST_PROGRESS_MEDIA; + } + + if (ring_available && (atoi(ring_available) == 1)) { + event.flags |= SIGBOOST_PROGRESS_RING; + } woomera_set_flag(woomera,WFLAG_CALL_ACKED); - isup_exec_command(woomera->span, - woomera->chan, - -1, - SIGBOOST_EVENT_CALL_START_ACK, - 0); + + isup_exec_event((call_signal_event_t*)&event); } + woomera_set_flag(woomera, WFLAG_ANSWER); + isup_exec_command(woomera->span, woomera->chan, -1, SIGBOOST_EVENT_CALL_ANSWERED, 0); log_printf(SMG_LOG_DEBUG_CALL, server.log, - "Sent SIGBOOST_EVENT_CALL_ANSWERED [w%dg%d]\n", + "Sent SIGBOOST_EVENT_CALL_ANSWERED [s%dc%d]\n", woomera->span+1,woomera->chan+1); } else { + struct woomera_event wevent; log_printf(SMG_LOG_ALL, server.log, "WOOMERA ANSWER: FAILED [%s] no Media \n", @@ -2736,7 +3243,7 @@ static int handle_woomera_media_accept_answer(struct woomera_interface *woomera, enqueue_event(woomera, &wevent,EVENT_FREE_DATA); - return 0; + return 0; } static int handle_woomera_call_start (struct woomera_interface *woomera, @@ -2753,36 +3260,24 @@ static int handle_woomera_call_start (struct woomera_interface *woomera, char *rdnis = woomera_message_header(wmsg, "RDNIS"); char *bearer_cap = woomera_message_header(wmsg, "Bearer-Cap"); char *uil1p = woomera_message_header(wmsg, "uil1p"); + char *called = wmsg->callid; char *grp = wmsg->callid; + char *profile; char *p; int cause = 34; int tg = 0; int huntgroup = SIGBOOST_HUNTGRP_SEQ_ASC; - if (smg_check_all_busy() || - woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ - - - socket_printf(woomera->socket, "EVENT HANGUP %s" - "Cause: %s%s" - "Q931-Cause-Code: %d%s", - WOOMERA_LINE_SEPERATOR, - q931_rel_to_str(cause), - WOOMERA_LINE_SEPERATOR, - cause, - WOOMERA_RECORD_SEPERATOR); - - socket_printf(woomera->socket, - "405 SMG Server All Ckt Busy!%s", WOOMERA_RECORD_SEPERATOR); - - log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "SMG Server Full %d (ckt busy cnt=%i)\n", - server.call_count, server.all_ckt_busy); - return -1; + /* Remove profile name out of destiantion */ + if ((profile = strchr(called, ':'))) { + profile++; + called=profile; + grp=profile; } - - log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "New Call %d/%d\n", server.call_count, server.max_calls); + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "New Call %d/%d origdest=%s newdest=%s\n", + server.call_count, server.max_calls, wmsg->callid, called); switch(called[0]) { case 'g': @@ -2814,6 +3309,50 @@ static int handle_woomera_call_start (struct woomera_interface *woomera, } woomera->trunk_group=tg; + + if ( woomera->trunk_group > SMG_MAX_TG ) { + + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, + "405 SMG Server: trunk group value not valid!%s", WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "SMG Server: trunk group value not valid %d\n", + woomera->trunk_group); + + return -1; + } + + if (smg_check_all_busy(woomera->trunk_group) || + woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)){ + + + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); + + socket_printf(woomera->socket, + "405 SMG Server All Ckt Busy!%s", WOOMERA_RECORD_SEPERATOR); + + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "SMG Server Full %d (ckt busy cnt=%i on tg=%d)\n", + server.call_count, server.all_ckt_busy[woomera->trunk_group],woomera->trunk_group); + return -1; + } + + + if (raw) { woomera_set_raw(woomera, raw); } @@ -2891,6 +3430,13 @@ static int handle_woomera_call_start (struct woomera_interface *woomera, event.trunk_group = tg; event.hunt_group = huntgroup; + set_digits_info(&event.calling.ton, woomera_message_header(wmsg, "xCallingTon")); + set_digits_info(&event.calling.npi, woomera_message_header(wmsg, "xCallingNpi")); + set_digits_info(&event.called.ton, woomera_message_header(wmsg, "xCalledTon")); + set_digits_info(&event.called.npi, woomera_message_header(wmsg, "xCalledNpi")); + set_digits_info(&event.rdnis.ton, woomera_message_header(wmsg, "xRdnisTon")); + set_digits_info(&event.rdnis.npi, woomera_message_header(wmsg, "xRdnisNpi")); + if (call_signal_connection_write(&server.mcon, &event) < 0) { log_printf(SMG_LOG_ALL, server.log, "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", @@ -3061,8 +3607,13 @@ static void interpret_command(struct woomera_interface *woomera, struct woomera_ char *session=NULL; int span, chan; char ifname[100]; + int err; + /* If session does not exist this is an incoming call */ - sscanf(unique_id, "w%dg%d", &span, &chan); + err=sscanf(unique_id, "s%dc%d", &span, &chan); + if (err!=2) { + err=sscanf(unique_id, "w%dg%d", &span, &chan); + } span--; chan--; @@ -3081,7 +3632,7 @@ static void interpret_command(struct woomera_interface *woomera, struct woomera_ WOOMERA_LINE_SEPERATOR, cause, WOOMERA_RECORD_SEPERATOR); - socket_printf(woomera->socket, "404 Invalid span/chan in session%s" + socket_printf(woomera->socket, "404 Invalid span/chan in session%s", WOOMERA_RECORD_SEPERATOR); log_printf(SMG_LOG_DEBUG_CALL, woomera->log, @@ -3134,7 +3685,7 @@ static void interpret_command(struct woomera_interface *woomera, struct woomera_ cause, WOOMERA_RECORD_SEPERATOR); - socket_printf(woomera->socket, "404 Invalid/Expired Session%s" + socket_printf(woomera->socket, "404 Invalid/Expired Session%s", WOOMERA_RECORD_SEPERATOR); log_printf(SMG_LOG_DEBUG_MISC, woomera->log, @@ -3149,14 +3700,14 @@ static void interpret_command(struct woomera_interface *woomera, struct woomera_ int clients=server.process_table[span][chan].clients; if (server.process_table[span][chan].clients < 0) { - log_printf(SMG_LOG_ALL, woomera->log, "WOOMERA CMD: Warning Clients (%i) Race condition on Hangup [w%dg%d]\n", + log_printf(SMG_LOG_ALL, woomera->log, "WOOMERA CMD: Warning Clients (%i) Race condition on Hangup [s%dc%d]\n", clients, span+1,chan+1); } else if (server.process_table[span][chan].clients > 1) { server.process_table[span][chan].clients--; pthread_mutex_unlock(&server.process_lock); - log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Got Hungup on Multiple Clients %i, skipping hangup [w%dg%d]\n", + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Got Hungup on Multiple Clients %i, skipping hangup [s%dc%d]\n", clients, span+1,chan+1); cause=16; @@ -3175,14 +3726,14 @@ static void interpret_command(struct woomera_interface *woomera, struct woomera_ woomera_set_flag(woomera, WFLAG_HANGUP); return; } - log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Hanging up channel [w%dg%d]\n", + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "WOOMERA CMD: Hanging up channel [s%dc%d]\n", span+1,chan+1); } #endif server.process_table[span][chan].dev=woomera; strncpy(woomera->session,unique_id,sizeof(woomera->session)); - sprintf(ifname,"w%dg%d",span+1,chan+1); + sprintf(ifname,"s%dc%d",span+1,chan+1); woomera_set_interface(woomera, ifname); woomera->span=span; @@ -3202,27 +3753,41 @@ static void interpret_command(struct woomera_interface *woomera, struct woomera_ } else if (strncmp(woomera->session,unique_id,sizeof(woomera->session))) { - cause=81; - socket_printf(woomera->socket, "EVENT HANGUP %s" - "Cause: %s%s" - "Q931-Cause-Code: %d%s", - WOOMERA_LINE_SEPERATOR, - q931_rel_to_str(cause), - WOOMERA_LINE_SEPERATOR, - cause, - WOOMERA_RECORD_SEPERATOR); + cause=81; + socket_printf(woomera->socket, "EVENT HANGUP %s" + "Cause: %s%s" + "Q931-Cause-Code: %d%s", + WOOMERA_LINE_SEPERATOR, + q931_rel_to_str(cause), + WOOMERA_LINE_SEPERATOR, + cause, + WOOMERA_RECORD_SEPERATOR); socket_printf(woomera->socket, "404 Session Mis-match%s" WOOMERA_RECORD_SEPERATOR); - woomera_set_flag(woomera, WFLAG_HANGUP); + woomera_set_flag(woomera, WFLAG_HANGUP); return; } - - if (!strcasecmp(wmsg->command, "dtmf")) { + if (!strcasecmp(wmsg->command, "rbs")) { - log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: DTMF Received: [%s] Digit %s Body %s\n", - woomera->interface, wmsg->command_args, wmsg->body); + log_printf(SMG_LOG_PROD, server.log, + "WOOMERA CMD: RBS Received: [%s] Digit %s Body %s\n", + woomera->interface, wmsg->command_args, wmsg->body); + + wanpipe_send_rbs(woomera,wmsg->body); + + socket_printf(woomera->socket, "200 RBS OK%s" + "Unique-Call-Id: %s%s", + WOOMERA_LINE_SEPERATOR, + woomera->session, + WOOMERA_RECORD_SEPERATOR); + + } else if (!strcasecmp(wmsg->command, "dtmf")) { + + log_printf(SMG_LOG_WOOMERA, woomera->log, + "WOOMERA CMD: DTMF Received: [%s] Digit %s Body %s\n", + woomera->interface, wmsg->command_args, wmsg->body); wanpipe_send_dtmf(woomera,wmsg->body); @@ -3260,7 +3825,7 @@ static void interpret_command(struct woomera_interface *woomera, struct woomera_ } - log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "Hangup Received: [w%dg%d]\n", + log_printf(SMG_LOG_DEBUG_CALL, woomera->log, "Hangup Received: [s%dc%d]\n", span+1,chan+1); @@ -3273,8 +3838,7 @@ static void interpret_command(struct woomera_interface *woomera, struct woomera_ WOOMERA_LINE_SEPERATOR, woomera->session, WOOMERA_RECORD_SEPERATOR); - - + } else if (!strcasecmp(wmsg->command, "proceed")) { log_printf(SMG_LOG_WOOMERA, woomera->log, "WOOMERA CMD: %s [%s]\n", @@ -3287,7 +3851,12 @@ static void interpret_command(struct woomera_interface *woomera, struct woomera_ WOOMERA_LINE_SEPERATOR, woomera->session, WOOMERA_RECORD_SEPERATOR); - + + } else if (!strcasecmp(wmsg->command, "progress")) { + + handle_woomera_progress(woomera,wmsg); + + } else if ((media = !strcasecmp(wmsg->command, "media")) || (answer = !strcasecmp(wmsg->command, "answer")) || (accept = !strcasecmp(wmsg->command, "accept"))) { @@ -3320,12 +3889,18 @@ static void interpret_command(struct woomera_interface *woomera, struct woomera_ static void handle_call_answer(short_signal_event_t *event) { - struct woomera_interface *woomera = NULL; - int kill = 0; + struct woomera_interface *woomera = NULL; + int kill = 0; - pthread_mutex_lock(&server.process_lock); - woomera = server.process_table[event->span][event->chan].dev; - pthread_mutex_unlock(&server.process_lock); + if (smg_validate_span_chan(event->span,event->chan)) { + log_printf(0,server.log, "Error: invalid span=% chan=%i on call answer [s%dc%d]!\n", + event->span+1, event->chan+1, event->span+1,event->chan+1); + return; + } + + pthread_mutex_lock(&server.process_lock); + woomera = server.process_table[event->span][event->chan].dev; + pthread_mutex_unlock(&server.process_lock); if (woomera) { char callid[80]; @@ -3359,7 +3934,7 @@ static void handle_call_answer(short_signal_event_t *event) } else { int err; err=0; - sprintf(callid, "w%dg%d", event->span + 1, event->chan + 1); + sprintf(callid, "s%dc%d", event->span + 1, event->chan + 1); woomera_set_interface(woomera, callid); #ifndef WOOMERA_EARLY_MEDIA err=launch_media_thread(woomera); @@ -3397,7 +3972,7 @@ static void handle_call_answer(short_signal_event_t *event) #endif if (!kill) { - new_woomera_event_printf(&wevent, "EVENT CONNECT w%dg%d%s" + new_woomera_event_printf(&wevent, "EVENT CONNECT s%dc%d%s" "Unique-Call-Id: %s%s", event->span+1, event->chan+1, @@ -3409,7 +3984,7 @@ static void handle_call_answer(short_signal_event_t *event) } } } else { - log_printf(SMG_LOG_PROD, server.log, "Answer requested on non-existant session. [w%dg%d]\n", + log_printf(SMG_LOG_PROD, server.log, "Answer requested on non-existant session. [s%dc%d]\n", event->span+1, event->chan+1); kill++; } @@ -3430,10 +4005,10 @@ handle_call_answer_end: SIGBOOST_EVENT_CALL_STOPPED, SIGBOOST_RELEASE_CAUSE_NORMAL); - log_printf(SMG_LOG_PROD, server.log, "Sent CALL STOP to Answer without session [w%dg%d]\n", + log_printf(SMG_LOG_PROD, server.log, "Sent CALL STOP to Answer without session [s%dc%d]\n", event->span+1, event->chan+1); #endif - log_printf(SMG_LOG_ALL, server.log, "WARNING: Received Answer with no session [w%dg%d]\n", + log_printf(SMG_LOG_ALL, server.log, "WARNING: Received Answer with no session [s%dc%d]\n", event->span+1, event->chan+1); } } @@ -3455,7 +4030,7 @@ static void handle_call_start_ack(short_signal_event_t *event) int err, span, chan; pull_from_holding_tank(event->call_setup_id,event->span,event->chan); - sprintf(callid, "w%dg%d", event->span + 1, event->chan + 1); + sprintf(callid, "s%dc%d", event->span + 1, event->chan + 1); span = event->span; chan = event->chan; @@ -3469,7 +4044,7 @@ static void handle_call_start_ack(short_signal_event_t *event) struct woomera_interface *tmp_woomera = server.process_table[span][chan].dev; woomera_set_flag(tmp_woomera,WFLAG_HANGUP); woomera_set_flag(tmp_woomera,WFLAG_MEDIA_END); - log_printf(SMG_LOG_ALL,server.log,"Call Overrun on [w%dg%d] - Call ACK!\n", event->span+1, event->chan+1); + log_printf(SMG_LOG_ALL,server.log,"Call Overrun on [s%dc%d] - Call ACK!\n", event->span+1, event->chan+1); kill++; } @@ -3483,7 +4058,8 @@ static void handle_call_start_ack(short_signal_event_t *event) sizeof(server.process_table[span][chan].digits)); server.process_table[span][chan].digits_len = 0; - + memset(&server.process_table[event->span][event->chan].rbs_relay,0, + sizeof(server.process_table[event->span][event->chan].rbs_relay)); pthread_mutex_unlock(&server.process_lock); if (kill) { @@ -3527,7 +4103,7 @@ static void handle_call_start_ack(short_signal_event_t *event) #endif if (!kill) { - new_woomera_event_printf(&wevent, "EVENT PROCEED w%dg%d%s" + new_woomera_event_printf(&wevent, "EVENT PROCEED s%dc%d%s" "Channel-Name: g%d/%d%s" "Unique-Call-Id: %s%s", event->span+1, @@ -3550,13 +4126,13 @@ static void handle_call_start_ack(short_signal_event_t *event) enqueue_event(woomera, &wevent,EVENT_FREE_DATA); - log_printf(SMG_LOG_DEBUG_MISC, server.log, "Call Answered Event ID = %d Device = w%dg%d!\n", + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Call Answered Event ID = %d Device = s%dc%d!\n", event->call_setup_id,woomera->span+1,woomera->chan+1); } } } else { log_printf(SMG_LOG_PROD, server.log, - "Event (START ACK) %d referrs to a non-existant session (%d) [w%dg%d]!\n", + "Event (START ACK) %d referrs to a non-existant session (%d) [s%dc%d]!\n", event->event_id, event->call_setup_id,event->span+1, event->chan+1); kill++; } @@ -3572,7 +4148,7 @@ woomera_call_ack_skip: SIGBOOST_EVENT_CALL_STOPPED, SIGBOOST_RELEASE_CAUSE_NORMAL); - log_printf(SMG_LOG_PROD, server.log, "Sent CALL STOP to CALL START ACK without session [w%dg%d]\n", + log_printf(SMG_LOG_PROD, server.log, "Sent CALL STOP to CALL START ACK without session [s%dc%d]\n", event->span+1, event->chan+1); } } @@ -3612,7 +4188,7 @@ static void handle_call_start_nack(short_signal_event_t *event) woomera_set_cause_topbx(woomera,event->release_cause); woomera_set_flag(woomera, (WFLAG_HANGUP|WFLAG_HANGUP_NACK_ACK)); - new_woomera_event_printf(&wevent, "EVENT HANGUP w%dg%d%s" + new_woomera_event_printf(&wevent, "EVENT HANGUP s%dc%d%s" "Unique-Call-Id: %s%s" "Cause: %s%s" "Q931-Cause-Code: %d%s", @@ -3671,7 +4247,7 @@ static void handle_call_start_nack(short_signal_event_t *event) if (woomera) { - log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event START NACK on w%dg%d ptr=%p ms=%p\n", + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event START NACK on s%dc%d ptr=%p ms=%p\n", woomera->span+1,woomera->chan+1,woomera,woomera->ms); if (!woomera_test_flag(woomera,WFLAG_HANGUP)){ @@ -3691,23 +4267,24 @@ static void handle_call_start_nack(short_signal_event_t *event) } else { /* Valid state when we are not in autoacm mode */ ack++; - log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event: NACK no woomera on span chan [w%dg%d]!\n", + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event: NACK no woomera on span chan [s%dc%d]!\n", event->span+1, event->chan+1); } } else { log_printf(SMG_LOG_ALL, server.log, - "Error: Start Nack Invalid State Should not happen [%d] [w%dg%d]!\n", + "Error: Start Nack Invalid State Should not happen [%d] [s%dc%d]!\n", event->call_setup_id, event->span+1, event->chan+1); ack++; } if (event->release_cause == SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY) { - smg_all_ckt_busy(); - log_printf(SMG_LOG_ALL, server.log, "WARNING: All ckt busy Timeout=%i!\n",server.all_ckt_busy); + smg_all_ckt_busy(woomera->trunk_group); + log_printf(SMG_LOG_ALL, server.log, "WARNING: All ckt busy Timeout=%i on tg=%i!\n", + server.all_ckt_busy[woomera->trunk_group], woomera->trunk_group); } if (event->release_cause == SIGBOOST_CALL_SETUP_CSUPID_DBL_USE) { - log_printf(SMG_LOG_ALL, server.log, "WARNING: Double use on [w%ig%i] setup id %i!\n", + log_printf(SMG_LOG_ALL, server.log, "WARNING: Double use on [s%ic%i] setup id %i!\n", event->span+1,event->chan+1,event->call_setup_id); } @@ -3715,7 +4292,7 @@ static void handle_call_start_nack(short_signal_event_t *event) #if 0 if (event->release_cause == SIGBOOST_CALL_SETUP_NACK_AUTO_CALL_GAP) { log_printf(SMG_LOG_ALL, server.log, "WARNING: Call Gapping Detected!\n"); - smg_all_ckt_gap(); + smg_all_ckt_gap(woomera->trunk_group); } #endif @@ -3734,7 +4311,7 @@ static void handle_call_start_nack(short_signal_event_t *event) 0); if (!woomera) { - log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event (NACK ACK) %d referrs to a non-existant session (%d) [w%dg%d]!\n", + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event (NACK ACK) %d referrs to a non-existant session (%d) [s%dc%d]!\n", event->event_id,event->call_setup_id, event->span+1, event->chan+1); } } @@ -3757,7 +4334,7 @@ static void handle_call_loop_start(short_signal_event_t *event) log_printf(SMG_LOG_PROD, server.log, - "Sent (From Handle Loop START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [w%dg] ptr=%d\n", + "Sent (From Handle Loop START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [s%dc%d] ptr=%d\n", event->span+1, event->chan+1, server.process_table[event->span][event->chan].dev); pthread_mutex_unlock(&server.process_lock); @@ -3765,7 +4342,7 @@ static void handle_call_loop_start(short_signal_event_t *event) } - sprintf(callid, "w%dg%d", event->span+1,event->chan+1); + sprintf(callid, "s%dc%d", event->span+1,event->chan+1); sprintf(server.process_table[event->span][event->chan].session, "%s-%i-%i",callid,rand(),rand()); session=server.process_table[event->span][event->chan].session; @@ -3781,7 +4358,7 @@ static void handle_call_loop_start(short_signal_event_t *event) SIGBOOST_EVENT_CALL_START_NACK, 17); log_printf(SMG_LOG_PROD, server.log, - "Sent (From Handle Loop START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [w%dg] ptr=%d\n", + "Sent (From Handle Loop START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [s%dc%d] ptr=%d\n", event->span+1, event->chan+1, server.process_table[event->span][event->chan].dev); } @@ -3796,24 +4373,34 @@ static void handle_call_start(call_signal_event_t *event) struct woomera_event wevent; char callid[20]; char *session; + struct woomera_interface *tmp_woomera=NULL; int clients; - remove_end_of_digits_char((unsigned char*)event->called_number_digits); - remove_end_of_digits_char((unsigned char*)event->calling_number_digits); + remove_end_of_digits_char((unsigned char*)event->called.digits); + remove_end_of_digits_char((unsigned char*)event->calling.digits); + remove_end_of_digits_char((unsigned char*)event->rdnis.digits); + remove_end_of_digits_char((unsigned char*)event->custom_data); if (server.strip_cid_non_digits) { - validate_number((unsigned char*)event->called_number_digits); - validate_number((unsigned char*)event->calling_number_digits); + validate_number((unsigned char*)event->called.digits); + validate_number((unsigned char*)event->calling.digits); + validate_number((unsigned char*)event->rdnis.digits); + } + + if (smg_validate_span_chan(event->span,event->chan)) { + log_printf(0,server.log, + "Error: invalid span=% chan=%i on incoming call [s%dc%d] - Call START!\n", + event->span+1, event->chan+1, event->span+1,event->chan+1); + return; } pthread_mutex_lock(&server.process_lock); - if (server.process_table[event->span][event->chan].dev) { + if ((tmp_woomera=server.process_table[event->span][event->chan].dev)) { - struct woomera_interface *tmp_woomera = server.process_table[event->span][event->chan].dev; woomera_set_flag(tmp_woomera,WFLAG_HANGUP); woomera_set_flag(tmp_woomera,WFLAG_MEDIA_END); - log_printf(SMG_LOG_ALL,server.log,"Call Overrun on [w%dg%d] - Call START!\n", event->span+1, event->chan+1); + log_printf(SMG_LOG_ALL,server.log,"Call Overrun on [s%dc%d] - Call START!\n", event->span+1, event->chan+1); isup_exec_command(event->span, event->chan, @@ -3822,14 +4409,14 @@ static void handle_call_start(call_signal_event_t *event) 17); log_printf(SMG_LOG_ALL, server.log, - "Sent (From Handle START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [w%dg%d]\n", + "Sent (From Handle START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [s%dc%d]\n", event->span+1, event->chan+1); pthread_mutex_unlock(&server.process_lock); return; } - sprintf(callid, "w%dg%d", event->span+1,event->chan+1); + sprintf(callid, "s%dc%d", event->span+1,event->chan+1); sprintf(server.process_table[event->span][event->chan].session, "%s-%i-%i",callid,rand(),rand()); session=server.process_table[event->span][event->chan].session; @@ -3838,6 +4425,8 @@ static void handle_call_start(call_signal_event_t *event) server.process_table[event->span][event->chan].digits_len = 0; server.process_table[event->span][event->chan].bearer_cap = event->bearer.capability; server.process_table[event->span][event->chan].clients=0; + memset(&server.process_table[event->span][event->chan].rbs_relay,0, + sizeof(server.process_table[event->span][event->chan].rbs_relay)); pthread_mutex_unlock(&server.process_lock); if (autoacm) { @@ -3860,7 +4449,7 @@ static void handle_call_start(call_signal_event_t *event) } } - new_woomera_event_printf(&wevent, "EVENT INCOMING w%dg%d%s" + new_woomera_event_printf(&wevent, "EVENT INCOMING s%dc%d%s" "Unique-Call-Id: %s%s" "Remote-Number: %s%s" "Remote-Name: %s%s" @@ -3882,19 +4471,20 @@ static void handle_call_start(call_signal_event_t *event) "RDNIS: %s%s" "Bearer-Cap: %s%s" "uil1p: %s%s" + "xCustom: %s%s" , event->span+1, event->chan+1, WOOMERA_LINE_SEPERATOR, session, WOOMERA_LINE_SEPERATOR, - event->calling_number_digits, + event->calling.digits, WOOMERA_LINE_SEPERATOR, event->calling_name, WOOMERA_LINE_SEPERATOR, WOOMERA_LINE_SEPERATOR, WOOMERA_LINE_SEPERATOR, - event->called_number_digits, + event->called.digits, WOOMERA_LINE_SEPERATOR, event->trunk_group+1, (event->span*max_chans)+event->chan+1, @@ -3905,11 +4495,13 @@ static void handle_call_start(call_signal_event_t *event) WOOMERA_LINE_SEPERATOR, event->calling_number_screening_ind, WOOMERA_LINE_SEPERATOR, - event->isup_in_rdnis, + event->rdnis.digits, WOOMERA_LINE_SEPERATOR, bearer_cap_to_str(event->bearer.capability), WOOMERA_LINE_SEPERATOR, - uil1p_to_str(event->bearer.uil1p), + uil1p_to_str(event->bearer.uil1p), + WOOMERA_LINE_SEPERATOR, + event->custom_data, WOOMERA_RECORD_SEPERATOR ); @@ -3928,7 +4520,7 @@ static void handle_call_start(call_signal_event_t *event) SIGBOOST_EVENT_CALL_STOPPED, 17); log_printf(SMG_LOG_ALL, server.log, - "CALL INCOMING: Enqueue Error Sent SIGBOOST_EVENT_CALL_STOPPED [w%dg%d]\n", + "CALL INCOMING: Enqueue Error Sent SIGBOOST_EVENT_CALL_STOPPED [s%dc%d]\n", event->span+1, event->chan+1); } else { @@ -3938,13 +4530,15 @@ static void handle_call_start(call_signal_event_t *event) SIGBOOST_EVENT_CALL_START_NACK, 17); log_printf(SMG_LOG_ALL, server.log, - "CALL INCOMING: Enqueue Error Sent SIGBOOST_EVENT_CALL_START_NACK [w%dg%d]\n", + "CALL INCOMING: Enqueue Error Sent SIGBOOST_EVENT_CALL_START_NACK [s%dc%d]\n", event->span+1, event->chan+1); } } else { - server.process_table[event->span][event->chan].clients=clients; + //pthread_mutex_lock(&server.process_lock); + server.process_table[event->span][event->chan].clients = clients; + //pthread_mutex_unlock(&server.process_lock); } destroy_woomera_event_data(&wevent); @@ -3957,20 +4551,20 @@ static void handle_incoming_digit(call_signal_event_t *event) int digits_len; if (smg_validate_span_chan(event->span,event->chan)) { - log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event DTMF on invalid span chan [w%dg%d] !\n", + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event DTMF on invalid span chan [s%dc%d] !\n", event->span+1, event->chan+1); return; } if (!event->called_number_digits_count) { - log_printf(SMG_LOG_ALL, server.log, "Error Incoming digit with len %s %d [w%dg%d]\n", + log_printf(SMG_LOG_ALL, server.log, "Error Incoming digit with len %s %d [s%dc%d]\n", event->called_number_digits, event->called_number_digits_count, event->span+1, event->chan+1); } - log_printf(SMG_LOG_DEBUG_9, server.log, "Queuing incoming digits %s [w%dg%d]\n", + log_printf(SMG_LOG_DEBUG_9, server.log, "Queuing incoming digits %s [s%dc%d]\n", event->called_number_digits, event->span+1, event->chan+1); @@ -3999,7 +4593,7 @@ static void handle_gap_abate(short_signal_event_t *event) { log_printf(SMG_LOG_ALL, server.log, "NOTICE: GAP Cleared!\n", event->span+1, event->chan+1); - smg_clear_ckt_gap(); + smg_clear_ckt_gap(event->trunk_group+1); } static void handle_restart(call_signal_connection_t *mcon, short_signal_event_t *event) @@ -4013,7 +4607,10 @@ static void handle_restart(call_signal_connection_t *mcon, short_signal_event_t log_printf(SMG_LOG_ALL, server.log, "RESTART Received: resetting all threads\n"); - smg_all_ckt_busy(); + int i=0; + for ( i =0 ; i < SMG_MAX_TG ; i++ ){ + smg_all_ckt_busy(i); + } woomera_set_flag(&server.master_connection, WFLAG_SYSTEM_RESET); gettimeofday(&server.restart_timeout,NULL); @@ -4041,7 +4638,7 @@ static void handle_restart(call_signal_connection_t *mcon, short_signal_event_t static void handle_restart_ack(call_signal_connection_t *mcon, short_signal_event_t *event) { - + woomera_clear_flag(&server.master_connection, WFLAG_SYSTEM_NEED_RESET_ACK); /* Do not reset WFLAG_SYSTEM_RESET flag here!. The flag should only be reset on restart from boost */ //woomera_clear_flag(&server.master_connection, WFLAG_SYSTEM_RESET); @@ -4067,7 +4664,7 @@ static void handle_remove_loop(short_signal_event_t *event) /* We have to close the socket because At this point we are release span chan */ - log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event REMOVE LOOP on w%dg%d ptr=%p ms=%p\n", + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event REMOVE LOOP on s%dc%d ptr=%p ms=%p\n", woomera->span+1,woomera->chan+1,woomera,woomera->ms); } @@ -4110,7 +4707,7 @@ static void handle_call_stop(short_signal_event_t *event) /* We have to close the socket because At this point we are release span chan */ - log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event CALL STOP on w%dg%d ptr=%p ms=%p\n", + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event CALL STOP on s%dc%d ptr=%p ms=%p\n", woomera->span+1,woomera->chan+1,woomera,woomera->ms); } else { @@ -4129,7 +4726,7 @@ static void handle_call_stop(short_signal_event_t *event) if (!woomera){ /* This is allowed on incoming call if remote app does not answer it */ - log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event CALL STOP referrs to a non-existant session [w%dg%d]!\n", + log_printf(SMG_LOG_DEBUG_MISC, server.log, "Event CALL STOP referrs to a non-existant session [s%dc%d]!\n", event->span+1, event->chan+1); } } @@ -4155,6 +4752,12 @@ static void handle_call_stop_ack(short_signal_event_t *event) struct woomera_interface *woomera = NULL; + + if (smg_validate_span_chan(event->span,event->chan)) { + log_printf(0,server.log, "Error: invalid span=% chan=%i on call stopped ack [s%dc%d]\n", + event->span+1, event->chan+1, event->span+1,event->chan+1); + return; + } pthread_mutex_lock(&server.process_lock); woomera = server.process_table[event->span][event->chan].dev; server.process_table[event->span][event->chan].dev=NULL; @@ -4163,14 +4766,14 @@ static void handle_call_stop_ack(short_signal_event_t *event) if (woomera) { - log_printf(SMG_LOG_DEBUG_CALL, server.log, "Stop Ack on [w%dg%d] [Setup ID: %d] [%s]!\n", + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Stop Ack on [s%dc%d] [Setup ID: %d] [%s]!\n", event->span+1, event->chan+1, event->call_setup_id, woomera->interface); woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK); } else { - log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event CALL_STOP_ACK(%d) referrs to a non-existant session [w%dg%d] [Setup ID: %d]!\n", + log_printf(SMG_LOG_DEBUG_CALL, server.log, "Event CALL_STOP_ACK(%d) referrs to a non-existant session [s%dc%d] [Setup ID: %d]!\n", event->event_id, event->span+1, event->chan+1, event->call_setup_id); } @@ -4209,13 +4812,13 @@ static void handle_call_start_nack_ack(short_signal_event_t *event) * woomera client being down, in this case no * woomera thread is created */ log_printf(SMG_LOG_DEBUG_8, server.log, - "Event NACK ACK [w%dg%d] with valid span/chan no dev!\n", + "Event NACK ACK [s%dc%d] with valid span/chan no dev!\n", event->span+1, event->chan+1); } } else { log_printf(SMG_LOG_DEBUG_CALL, server.log, - "Event NACK ACK referrs to a non-existant session [w%dg%d] [Setup ID: %d]!\n", + "Event NACK ACK referrs to a non-existant session [s%dc%d] [Setup ID: %d]!\n", event->span+1, event->chan+1, event->call_setup_id); } @@ -4230,7 +4833,7 @@ static void handle_call_start_nack_ack(short_signal_event_t *event) static int parse_ss7_event(call_signal_connection_t *mcon, short_signal_event_t *event) { - int ret = 0; + int ret = 0; switch(event->event_id) { @@ -4262,7 +4865,7 @@ static int parse_ss7_event(call_signal_connection_t *mcon, short_signal_event_t handle_call_loop_start(event); break; case SIGBOOST_EVENT_SYSTEM_RESTART: - handle_restart(mcon,event); + handle_restart(mcon,event); break; case SIGBOOST_EVENT_REMOVE_CHECK_LOOP: handle_remove_loop(event); @@ -4276,6 +4879,9 @@ static int parse_ss7_event(call_signal_connection_t *mcon, short_signal_event_t case SIGBOOST_EVENT_DIGIT_IN: handle_incoming_digit((call_signal_event_t*)event); break; + case SIGBOOST_EVENT_CALL_PROGRESS: + /* We always assume early media, so there is no need to do anything on progress */ + break; default: log_printf(SMG_LOG_ALL, server.log, "Warning no handler implemented for [%s val:%d]\n", call_signal_event_id_name(event->event_id), event->event_id); @@ -4294,9 +4900,9 @@ static void *monitor_thread_run(void *obj) call_signal_connection_t *mcon=&server.mcon; call_signal_connection_t *mconp=&server.mconp; - pthread_mutex_lock(&server.thread_count_lock); + pthread_mutex_lock(&server.thread_count_lock); server.thread_count++; - pthread_mutex_unlock(&server.thread_count_lock); + pthread_mutex_unlock(&server.thread_count_lock); woomera_set_flag(&server.master_connection, WFLAG_MONITOR_RUNNING); @@ -4329,6 +4935,7 @@ static void *monitor_thread_run(void *obj) SIGBOOST_EVENT_SYSTEM_RESTART, 0); + woomera_set_flag(&server.master_connection, WFLAG_SYSTEM_NEED_RESET_ACK); woomera_set_flag(&server.master_connection, WFLAG_SYSTEM_RESET); smg_get_current_priority(&policy,&priority); @@ -4396,11 +5003,12 @@ mcon_retry_priority: break; } - if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET)) { + if (woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_RESET) && + !woomera_test_flag(&server.master_connection, WFLAG_SYSTEM_NEED_RESET_ACK)) { short_signal_event_t event; struct timeval current; int elapsed; - gettimeofday(¤t,NULL); + gettimeofday(¤t,NULL); elapsed=smg_calc_elapsed(&server.restart_timeout, ¤t); if (elapsed > 5000) { @@ -4415,7 +5023,10 @@ mcon_retry_priority: "Critical System Error: Failed to tx on ISUP socket [%s]: %s\n", strerror(errno)); } - smg_all_ckt_busy(); + int i=0; + for ( i =0 ; i < SMG_MAX_TG ; i++ ){ + smg_all_ckt_busy(i); + } woomera_clear_flag(&server.master_connection, WFLAG_SYSTEM_RESET); } } @@ -4535,13 +5146,19 @@ static void *woomera_thread_run(void *obj) "Version: %s%s" "Remote-Address: %s%s" "Remote-Port: %d%s" - "Raw-Format: %s%s", + "Raw-Format: %s%s" + "xUDP-Seq: %s%s" + "xUDP-Seq-Err: %d%s" + "xNative-Bridge: %s%s", WOOMERA_LINE_SEPERATOR, WOOMERA_LINE_SEPERATOR, SMG_VERSION, WOOMERA_LINE_SEPERATOR, inet_ntoa(woomera->addr.sin_addr), WOOMERA_LINE_SEPERATOR, ntohs(woomera->addr.sin_port), WOOMERA_LINE_SEPERATOR, - server.hw_coding?"ALAW":"ULAW", WOOMERA_RECORD_SEPERATOR + server.hw_coding?"ALAW":"ULAW", WOOMERA_LINE_SEPERATOR, + server.udp_seq?"Enabled":"Disabled", WOOMERA_LINE_SEPERATOR, + server.media_rx_seq_err, WOOMERA_LINE_SEPERATOR, + server.media_pass_through?"Enabled":"Disabled", WOOMERA_RECORD_SEPERATOR ); if (err) { @@ -4655,7 +5272,7 @@ woomera_session_close: woomera_set_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK_SENT); - log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Woomera Sent SIGBOOST_EVENT_CALL_STOPPED [w%dg%d] [%s] ptr=%p\n", + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Woomera Sent SIGBOOST_EVENT_CALL_STOPPED [s%dc%d] [%s] ptr=%p\n", span+1, chan+1,woomera->interface,woomera); } else { @@ -4667,11 +5284,11 @@ woomera_session_close: woomera->q931_rel_cause_tosig); woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT); - log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Woomera Sent SIGBOOST_EVENT_CALL_START_NACK [w%dg%d] [%s] ptr=%p\n", + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "Woomera Sent SIGBOOST_EVENT_CALL_START_NACK [s%dc%d] [%s] ptr=%p\n", span+1, chan+1,woomera->interface,woomera); } } else { - log_printf(SMG_LOG_ALL, woomera->log, "Woomera Not Sent CALL STOPPED - Instead NACK [w%dg%d] [%s] ptr=%p\n", + log_printf(SMG_LOG_ALL, woomera->log, "Woomera Not Sent CALL STOPPED - Instead NACK [s%dc%d] [%s] ptr=%p\n", span+1, chan+1,woomera->interface,woomera); } @@ -4681,7 +5298,7 @@ woomera_session_close: failure */ if (!woomera->index) { /* In this case we really failed to tx STOP */ - log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "FAILED: Woomera (R) SIGBOOST_EVENT_CALL_STOPPED [w%dg%d] [%s] Index=%d ptr=%p\n", + log_printf(SMG_LOG_DEBUG_MISC, woomera->log, "FAILED: Woomera (R) SIGBOOST_EVENT_CALL_STOPPED [s%dc%d] [%s] Index=%d ptr=%p\n", span+1, chan+1, woomera->interface, woomera->index, woomera); } } @@ -4709,14 +5326,14 @@ woo_re_hangup: woomera->q931_rel_cause_tosig); log_printf(SMG_LOG_DEBUG_MISC, woomera->log, - "Sent (Ack) to SIGBOOST_EVENT_CALL_STOPPED_ACK [w%dg%d] [%s] ptr=%p\n", + "Sent (Ack) to SIGBOOST_EVENT_CALL_STOPPED_ACK [s%dc%d] [%s] ptr=%p\n", span+1,chan+1,woomera->interface,woomera); }else{ /* This should never happen! If it does we broke protocol */ log_printf(SMG_LOG_ALL, woomera->log, - "FAILED: Woomera (R) SIGBOOST_EVENT_CALL_STOPPED_ACK [w%dg%d] [%s] Index=%d ptr=%p\n", + "FAILED: Woomera (R) SIGBOOST_EVENT_CALL_STOPPED_ACK [s%dc%d] [%s] Index=%d ptr=%p\n", span+1, chan+1, woomera->interface, woomera->index, woomera); } @@ -4740,7 +5357,7 @@ woo_re_hangup: woomera->q931_rel_cause_tosig); log_printf(SMG_LOG_DEBUG_MISC, woomera->log, - "Sent (Nack Ack) to SIGBOOST_EVENT_CALL_START_NACK_ACK [w%dg%d] [%s] ptr=%p\n", + "Sent (Nack Ack) to SIGBOOST_EVENT_CALL_START_NACK_ACK [s%dc%d] [%s] ptr=%p\n", span+1,chan+1,woomera->interface,woomera); woomera->index=0; @@ -4756,7 +5373,7 @@ woo_re_hangup: } else { log_printf(SMG_LOG_ALL, woomera->log, - "FAILED: Sent (Nack Ack) SIGBOOST_EVENT_CALL_START_NACK_ACK [w%dg%d] [%s] Index=%d ptr=%p\n", + "FAILED: Sent (Nack Ack) SIGBOOST_EVENT_CALL_START_NACK_ACK [s%dc%d] [%s] Index=%d ptr=%p\n", span+1, chan+1, woomera->interface, woomera->index, woomera); } @@ -4982,7 +5599,7 @@ woo_re_hangup: if (server.process_table[span][chan]){ log_printf(SMG_LOG_ALL, server.log, - "Sanity Span Chan Still in use: [w%dg%d] [%s] Index=%d ptr=%p\n", + "Sanity Span Chan Still in use: [s%dc%d] [%s] Index=%d ptr=%p\n", span+1, chan+1, woomera->interface, woomera->index, woomera); //server.process_table[span][chan] = NULL; } @@ -5184,7 +5801,9 @@ static int configure_server(void) struct woomera_config cfg; char *var, *val; int cnt = 0; + struct smg_tdm_ip_bridge *ip_bridge=NULL; + server.dtmf_intr_ch = -1; if (!woomera_open_file(&cfg, server.config_file)) { @@ -5291,19 +5910,93 @@ static int configure_server(void) server.base_media_port = base; server.next_media_port = base; server.max_media_port = server.base_media_port + WOOMERA_MAX_MEDIA_PORTS; - log_printf(SMG_LOG_ALL,server.log, "Server - Base Media Port: %d\n",server.base_media_port); + log_printf(SMG_LOG_ALL,server.log, "Server - Base Media Port: %d\n",server.base_media_port); } } else if (!strcasecmp(var, "max_media_ports")) { int max = atoi(val); - if (max > WOOMERA_MAX_MEDIA_PORTS) { + if (max >= 0) { server.max_media_port = server.base_media_port+max; - log_printf(SMG_LOG_ALL,server.log, "Server - Max Media Port: %d\n",server.max_media_port); + log_printf(SMG_LOG_ALL,server.log, "Server - Max Media Port: %d\n",server.max_media_port); } } else if (!strcasecmp(var, "media_ip")) { strncpy(server.media_ip, val, sizeof(server.media_ip) -1); log_printf(SMG_LOG_ALL,server.log, "Server - Media IP: %s\n",server.media_ip); + + } else if (!strcasecmp(var, "udp_seq")) { + int max = atoi(val); + if (max > 0) { + server.udp_seq=max; + log_printf(SMG_LOG_ALL, server.log, "Server - UDP Seq: %s\n",server.udp_seq?"Enabled":"Disabled"); + } + + } else if (!strcasecmp(var, "media_pass_through")) { + int max = atoi(val); + if (max > 0) { + server.media_pass_through=max; + log_printf(SMG_LOG_ALL, server.log, "Server - Media Pass Through: %s\n",server.media_pass_through?"Enabled":"Disabled"); + } + + + } else if (!strcasecmp(var, "rbs_relay")) { + int max = atoi(val); + if (max > 0) { + server.rbs_relay[max]=max; + log_printf(SMG_LOG_ALL, server.log, "Server - RBS Relay Span: %d\n",max); + } + + } else if (!strcasecmp(var, "bridge_tdm_ip")) { + int err=smg_get_ip_bridge_session(&ip_bridge); + if (err) { + log_printf(SMG_LOG_ALL, server.log, "Error failed to get free ip bridge %i!\n",err); + } else { + log_printf(SMG_LOG_ALL,server.log, "\n======================= \n"); + log_printf(SMG_LOG_ALL,server.log, "Bridge - Configuration \n"); + log_printf(SMG_LOG_ALL,server.log, "======================= \n"); + } + + } else if (!strcasecmp(var, "bridge_span")) { + int max = atoi(val); + if (max > 0 && max <= 32 && ip_bridge) { + ip_bridge->span=max; + log_printf(SMG_LOG_ALL, server.log, "Bridge Span: %i\n",ip_bridge->span); + } else { + log_printf(SMG_LOG_ALL, server.log, "Bridge Span: ERROR: Invalid Value %s\n",val); + } + } else if (!strcasecmp(var, "bridge_chan")) { + int max = atoi(val); + if (max > 0 && max < MAX_SMG_BRIDGE && ip_bridge) { + ip_bridge->chan=max; + log_printf(SMG_LOG_ALL, server.log, "Bridge Chan: %i\n",ip_bridge->chan); + } else { + log_printf(SMG_LOG_ALL, server.log, "Bridge Chan: ERROR: Invalid Value %s\n",val); + } + } else if (!strcasecmp(var, "bridge_local_ip")) { + if (ip_bridge) { + strncpy(ip_bridge->mcon.cfg.local_ip, val, + sizeof(ip_bridge->mcon.cfg.local_ip) -1); + log_printf(SMG_LOG_ALL, server.log, "Bridge Local IP: %s\n",ip_bridge->mcon.cfg.local_ip); + } + } else if (!strcasecmp(var, "bridge_remote_ip")) { + if (ip_bridge) { + strncpy(ip_bridge->mcon.cfg.remote_ip, val, + sizeof(ip_bridge->mcon.cfg.remote_ip) -1); + log_printf(SMG_LOG_ALL, server.log, "Bridge Remote IP: %s\n",ip_bridge->mcon.cfg.remote_ip); + } + } else if (!strcasecmp(var, "bridge_port")) { + int max = atoi(val); + if (max > 0 && ip_bridge) { + ip_bridge->mcon.cfg.local_port=max; + ip_bridge->mcon.cfg.remote_port=max; + log_printf(SMG_LOG_ALL, server.log, "Bridge Port: %i\n",max); + } + } else if (!strcasecmp(var, "bridge_period")) { + int max = atoi(val); + if (max > 0 && ip_bridge) { + ip_bridge->period=max; + log_printf(SMG_LOG_ALL, server.log, "Bridge Period: %ims\n",ip_bridge->period); + } } else { log_printf(SMG_LOG_ALL, server.log, "Invalid Option %s at line %d!\n", var, cfg.lineno); } @@ -5506,10 +6199,10 @@ static int sangoma_tdm_init (int span) return 0; } - static int woomera_startup(int argc, char **argv) { int x = 0, pid = 0, bg = 0; + int span_cnt, chan_cnt; char *cfg=NULL, *debug=NULL, *arg=NULL; while((arg = argv[x++])) { @@ -5590,6 +6283,12 @@ static int woomera_startup(int argc, char **argv) return 0; } + for (span_cnt = 0; span_cnt < CORE_MAX_SPANS; span_cnt++) { + for (chan_cnt = 0; chan_cnt < CORE_MAX_CHAN_PER_SPAN; chan_cnt++) { + pthread_mutex_init(&server.process_table[span_cnt][chan_cnt].media_lock, NULL); + } + } + q931_cause_setup(); bearer_cap_setup(); uil1p_to_str_setup(); @@ -5675,10 +6374,11 @@ static int woomera_startup(int argc, char **argv) usleep(5000); woomera_set_flag(&server.master_connection, WFLAG_RUNNING); + if (launch_monitor_thread()) { woomera_clear_flag(&server.master_connection, WFLAG_RUNNING); - smg_log_cleanup(); - return 0; + smg_log_cleanup(); + return 0; } fprintf(stderr, "%s", WELCOME_TEXT); @@ -5692,6 +6392,13 @@ static int woomera_shutdown(void) { char *event_string; int told = 0, loops = 0; + int span_cnt, chan_cnt; + + for (span_cnt = 0; span_cnt < CORE_MAX_SPANS; span_cnt++) { + for (chan_cnt = 0; chan_cnt < CORE_MAX_CHAN_PER_SPAN; chan_cnt++) { + pthread_mutex_destroy(&server.process_table[span_cnt][chan_cnt].media_lock); + } + } close_socket(&server.master_connection.socket); pthread_mutex_destroy(&server.listen_lock); @@ -5753,7 +6460,8 @@ int main(int argc, char *argv[]) mlockall(MCL_FUTURE); memset(&server, 0, sizeof(server)); memset(&woomera_dead_dev, 0, sizeof(woomera_dead_dev)); - + memset(&g_smg_ip_bridge_idx,0, sizeof(g_smg_ip_bridge_idx)); + ret=nice(-5); sdla_memdbg_init(); @@ -5763,12 +6471,17 @@ int main(int argc, char *argv[]) openlog (ps_progname ,LOG_PID, LOG_LOCAL2); if (! (ret = woomera_startup(argc, argv))) { - exit(0); + exit(0); } - ret = main_thread(); + + ret = smg_ip_bridge_start(); + if (ret == 0) { + ret = main_thread(); + } woomera_shutdown(); + smg_ip_bridge_stop(); sdla_memdbg_free(1); return ret; diff --git a/ssmg/sangoma_mgd.trunk/sangoma_mgd.conf.sample b/ssmg/sangoma_mgd.trunk/sangoma_mgd.conf.sample index 625cf63..6b95992 100644 --- a/ssmg/sangoma_mgd.trunk/sangoma_mgd.conf.sample +++ b/ssmg/sangoma_mgd.trunk/sangoma_mgd.conf.sample @@ -32,8 +32,8 @@ woomera_port => 42420 # If changing from default values # ensure that woomera client # UDP ports do not conflict. -base_media_port => 9000 -max_media_ports => 899 +base_media_port => 10000 +max_media_ports => 5000 #Used to play Sangoma Rocks on all @@ -83,8 +83,8 @@ strip_cid_non_digits => 0 #This must be enabled on chan_woomera #side as well in order to work. #0-Disable (Default) -#1-Eanble -woomera_udp_seq => 0 +#1-Enable +udp_seq => 0 #Timeout an outboud call, if call setup is @@ -94,3 +94,41 @@ woomera_udp_seq => 0 call_timeout=100 +#Enable woomera sever +#native bridging. On woomer to woomera +#calls between two SMG gateways +#Asterisk chan_woomera will indicate +#to SMG servers to pass media directly +#and not pass it through asterisk +#0-Disable +#1-Enable (Default) +media_pass_through => 1 + +#Enable tdm bridge profile 1 +#Note profile number must not be zero. +#Configure all parameters for the bridge. +#The opposite sangoma_mgd must be configured +#with same parameters except IP address +#need to be swaped. +#bridge_tdm_ip => 1 +#bridge_span => 1 +#bridge_chan => 1 +#bridge_local_ip => 192.168.1.251 +#bridge_remote_ip => 192.168.1.252 +#bridge_port => 60000 +#bridge_period => 10 #milliseconds + + +#Enable tdm bridge profile 2 +#Note profile number must not be zero. +#Configure all parameters for the bridge. +#The opposite sangoma_mgd must be configured +#with same parameters except IP address +#need to be swaped. +#bridge_tdm_ip => 2 +#bridge_span => 1 +#bridge_chan => 2 +#bridge_local_ip => 192.168.1.251 +#bridge_remote_ip => 192.168.1.252 +#bridge_port => 60001 +#bridge_period => 10 #milliseconds diff --git a/ssmg/sangoma_mgd.trunk/sangoma_mgd.h b/ssmg/sangoma_mgd.trunk/sangoma_mgd.h index f2cbd15..cd4cc4c 100644 --- a/ssmg/sangoma_mgd.trunk/sangoma_mgd.h +++ b/ssmg/sangoma_mgd.trunk/sangoma_mgd.h @@ -56,19 +56,21 @@ #define WOOMERA_MAX_CHAN 31 +#define SMG_MAX_TG 32 + #define SMG_SESSION_NAME_SZ 100 #define SMG_CHAN_NAME_SZ 20 #define PIDFILE "/var/run/sangoma_mgd.pid" #define PIDFILE_UNIT "/var/run/sangoma_mgd_unit.pid" -#define WOOMERA_MAX_MEDIA_PORTS 899 +#define WOOMERA_MAX_MEDIA_PORTS 5000 #define CORE_EVENT_LEN 512 #define WOOMERA_STRLEN 256 #define WOOMERA_ARRAY_LEN 50 #define WOOMERA_BODYLEN 2048 -#define WOOMERA_MIN_MEDIA_PORT 9000 +#define WOOMERA_MIN_MEDIA_PORT 10000 #define WOOMERA_MAX_MEDIA_PORT (WOOMERA_MIN_MEDIA_PORT + WOOMERA_MAX_MEDIA_PORTS) #define WOOMERA_HARD_TIMEOUT 0 #define WOOMERA_LINE_SEPERATOR "\r\n" @@ -106,7 +108,8 @@ typedef enum { WFLAG_WAIT_FOR_NACK_ACK_SENT = (1 << 17), /* Call START NACK was sent out on this channel */ WFLAG_WAIT_FOR_STOPPED_ACK_SENT = (1 << 18), /* Call STOP was sent out on this channel */ WFLAG_SYSTEM_RESET = (1 << 19), /* Initial System Reset Condition no calls allowed */ - WFLAG_WAIT_FOR_ACK_TIMEOUT = (1 << 20), /* Timeout flag indicating that incoming ACK or NACK timedout */ + WFLAG_SYSTEM_NEED_RESET_ACK = (1 << 20), /* We sent a RESTART */ + WFLAG_WAIT_FOR_ACK_TIMEOUT = (1 << 21), /* Timeout flag indicating that incoming ACK or NACK timedout */ } WFLAGS; enum { @@ -203,6 +206,7 @@ struct media_session { int sangoma_sock; char *ip; int port; + char *raw; time_t started; time_t answered; pthread_t thread; @@ -216,6 +220,8 @@ struct media_session { int skip_write_frames; int hw_coding; int hw_dtmf; + int has_hwec; + int udp_sync_cnt; #ifdef WP_HPTDM_API @@ -225,7 +231,7 @@ struct media_session { teletone_dtmf_detect_state_t dtmf_detect; teletone_generation_session_t tone_session; switch_buffer_t *dtmf_buffer; - + unsigned char oob_disable; }; struct woomera_message { @@ -283,23 +289,69 @@ struct woomera_interface { char session[SMG_SESSION_NAME_SZ]; int check_digits; /* set to 1 when session comes up */ int bearer_cap; - struct woomera_interface *next; + unsigned int rx_udp_seq; + unsigned int tx_udp_seq; + struct woomera_interface *next; }; +#define WOOMERA_MAX_RBS_BITS 4 + +typedef struct woomera_rbs_bits +{ + int init; + unsigned char abcd; +}woomera_rbs_bits_t; + +typedef struct woomera_rbs_relay +{ + int init; + woomera_rbs_bits_t rbs_bits[WOOMERA_MAX_RBS_BITS]; + int rx_idx; + int tx_idx; +} woomera_rbs_relay_t; + struct woomera_session { - struct woomera_interface *dev; + struct woomera_interface *dev; char session[SMG_SESSION_NAME_SZ]; char digits[MAX_DIALED_DIGITS+1]; int digits_len; int bearer_cap; int clients; + unsigned char media_used; + pthread_mutex_t media_lock; + woomera_rbs_relay_t rbs_relay; + int sangoma_fd; + int sangoma_fd_usage; }; +struct smg_tdm_ip_bridge { + int init; + int end; + int span; + int chan; +#if 0 + int port; + char local_ip[25]; + char remote_ip[25]; +#endif + int period; + int tdm_fd; + call_signal_connection_t mcon; + pthread_t thread; +}; + +extern struct smg_tdm_ip_bridge g_smg_ip_bridge_idx[]; +extern pthread_mutex_t g_smg_ip_bridge_lock; + + + + +#define MAX_SMG_RBS_RELAY 32 +#define MAX_SMG_BRIDGE 32 #define CORE_TANK_LEN CORE_MAX_CHAN_PER_SPAN*CORE_MAX_SPANS struct woomera_server { -// struct woomera_session process_table[CORE_MAX_CHAN_PER_SPAN][CORE_MAX_SPANS]; - struct woomera_session process_table[CORE_MAX_SPANS][CORE_MAX_CHAN_PER_SPAN]; + struct woomera_session process_table[CORE_MAX_SPANS][CORE_MAX_CHAN_PER_SPAN+1]; struct woomera_interface *holding_tank[CORE_TANK_LEN]; int holding_tank_index; struct woomera_interface master_connection; @@ -336,16 +388,21 @@ struct woomera_server { uint32_t hw_coding; uint32_t loop_trace; uint32_t hungup_waiting; - int all_ckt_gap; - int all_ckt_busy; - struct timeval all_ckt_busy_time; + int all_ckt_gap[SMG_MAX_TG+1]; + int all_ckt_busy[SMG_MAX_TG+1]; + struct timeval all_ckt_busy_time[SMG_MAX_TG+1]; struct timeval restart_timeout; int dtmf_on; - int dtmf_off; - int dtmf_intr_ch; - int dtmf_size; + int dtmf_off; + int dtmf_intr_ch; + int dtmf_size; int strip_cid_non_digits; int call_timeout; + struct smg_tdm_ip_bridge ip_bridge_idx[MAX_SMG_BRIDGE]; + int udp_seq; + unsigned int media_rx_seq_err; + unsigned char rbs_relay[MAX_SMG_RBS_RELAY]; + unsigned char media_pass_through; }; extern struct woomera_server server; @@ -358,6 +415,28 @@ struct woomera_config { int lineno; }; +static inline int smg_get_ip_bridge_session(struct smg_tdm_ip_bridge **ip_bridge) +{ + int i; + for (i=0;itv_sec * 1000) + started->tv_usec / 1000)); } -static inline int smg_check_all_busy(void) +static inline int smg_check_all_busy(int tg) { struct timeval ended; int elapsed; - if (server.all_ckt_gap) { - return server.all_ckt_gap; + if (server.all_ckt_gap[tg]) { + return server.all_ckt_gap[tg]; } - if (server.all_ckt_busy==0) { + if (server.all_ckt_busy[tg]==0) { return 0; } gettimeofday(&ended,NULL); - elapsed = smg_calc_elapsed(&server.all_ckt_busy_time,&ended); + elapsed = smg_calc_elapsed(&server.all_ckt_busy_time[tg],&ended); /* seconds elapsed */ - if (elapsed > server.all_ckt_busy) { - server.all_ckt_busy=0; + if (elapsed > server.all_ckt_busy[tg]) { + server.all_ckt_busy[tg]=0; return 0; } else { return 1; @@ -399,55 +478,55 @@ static inline int smg_check_all_busy(void) #if 0 - if (server.all_ckt_busy > 50) { + if (server.all_ckt_busy[tg] > 50) { /* When in GAP mode wait 10s */ - return server.all_ckt_busy; + return server.all_ckt_busy[tg]; } - --server.all_ckt_busy; - if (server.all_ckt_busy < 0) { - server.all_ckt_busy=0; + --server.all_ckt_busy[tg]; + if (server.all_ckt_busy[tg] < 0) { + server.all_ckt_busy[tg]=0; } - return server.all_ckt_busy; + return server.all_ckt_busy[tg]; #endif } -static inline void smg_all_ckt_busy(void) +static inline void smg_all_ckt_busy(int tg) { if (server.call_count*10 < 1500) { - server.all_ckt_busy+=1500; + server.all_ckt_busy[tg]+=1500; } else { - server.all_ckt_busy+=server.call_count*15; + server.all_ckt_busy[tg]+=server.call_count*15; } - if (server.all_ckt_busy > 10000) { - server.all_ckt_busy = 10000; + if (server.all_ckt_busy[tg] > 10000) { + server.all_ckt_busy[tg] = 10000; } #if 0 - if (server.all_ckt_busy >= 5) { - server.all_ckt_busy=10; - } else if (server.all_ckt_busy >= 10) { - server.all_ckt_busy=15; - } else if (server.all_ckt_busy == 0) { - server.all_ckt_busy=5; + if (server.all_ckt_busy[tg] >= 5) { + server.all_ckt_busy[tg]=10; + } else if (server.all_ckt_busy[tg] >= 10) { + server.all_ckt_busy[tg]=15; + } else if (server.all_ckt_busy[tg] == 0) { + server.all_ckt_busy[tg]=5; } #endif - gettimeofday(&server.all_ckt_busy_time,NULL); + gettimeofday(&server.all_ckt_busy_time[tg],NULL); } -static inline void smg_all_ckt_gap(void) +static inline void smg_all_ckt_gap(int tg) { - server.all_ckt_gap=1; + server.all_ckt_gap[tg]=1; } -static inline void smg_clear_ckt_gap(void) +static inline void smg_clear_ckt_gap(int tg) { - server.all_ckt_gap=0; - gettimeofday(&server.all_ckt_busy_time,NULL); + server.all_ckt_gap[tg]=0; + gettimeofday(&server.all_ckt_busy_time[tg],NULL); } @@ -559,6 +638,21 @@ static inline void woomera_set_raw(struct woomera_interface *woomera, char *raw) } } +static inline void media_set_raw(struct media_session *ms, char *raw) +{ + char *oldraw=ms->raw; + + if (raw) { + ms->raw = smg_strdup(raw); + } else { + ms->raw = NULL; + } + + if (oldraw) { + smg_free(oldraw); + } +} + static inline struct media_session * woomera_get_ms(struct woomera_interface *woomera) { struct media_session *ms; @@ -683,11 +777,70 @@ static inline void validate_number(unsigned char *s) } } +static inline int woomera_check_running(struct woomera_interface *woomera) +{ + if (woomera_test_flag(woomera, WFLAG_HANGUP) || + !woomera_test_flag(woomera, WFLAG_RUNNING) || + woomera_test_flag(woomera, WFLAG_MEDIA_END)) { + + return 0; + } + + return 1; +} +static inline int open_span_chan (unsigned char span, unsigned char chan) +{ + int fd = -1; +#ifndef LIBSANGOMA_VERSION + fd = sangoma_open_tdmapi_span_chan(span, chan); +#else + if (chan == 24) { + pthread_mutex_lock(&server.process_table[span][chan].media_lock); + if(server.process_table[span][chan].media_used > 0) { + log_printf(SMG_LOG_ALL, server.log, + "Critical Error: channel already opened [s%ic%i]\n", span, chan); + } else { + server.process_table[span][chan].media_used++; + + fd = __sangoma_open_api_span_chan(span, chan); + } + pthread_mutex_unlock(&server.process_table[span][chan].media_lock); + } else { + fd = sangoma_open_api_span_chan(span, chan); + } +#endif + return fd; +} +static inline void close_span_chan (int *socket, unsigned char span, unsigned char chan) +{ + if (chan == 24) { + pthread_mutex_lock(&server.process_table[span][chan].media_lock); + if(server.process_table[span][chan].media_used > 0) { + server.process_table[span][chan].media_used--; + } + close_socket(socket); + pthread_mutex_unlock(&server.process_table[span][chan].media_lock); + } else { + close_socket(socket); + } +} + +static inline void set_digits_info(unsigned char *target, char* string_val) +{ + if (string_val && (atoi(string_val) >= 0)) { + *target = atoi(string_val); + return; + } + *target = 255; +} extern int smg_log_init(void); extern void smg_log_cleanup(void); +extern int smg_ip_bridge_start(void); +extern int smg_ip_bridge_stop(void); +extern int waitfor_2sockets(int fda, int fdb, char *a, char *b, int timeout); #endif diff --git a/ssmg/sangoma_mgd.trunk/sangoma_mgd_ip_bridge.c b/ssmg/sangoma_mgd.trunk/sangoma_mgd_ip_bridge.c new file mode 100644 index 0000000..1a98a77 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/sangoma_mgd_ip_bridge.c @@ -0,0 +1,376 @@ + +#include "sangoma_mgd.h" +static int bridge_threads=0; + +#define TEST_SEQ 0 + +static void *bridge_thread_run(void *obj) +{ + struct smg_tdm_ip_bridge *bridge= (struct smg_tdm_ip_bridge*)obj; + call_signal_connection_t *mcon = &bridge->mcon; + wanpipe_tdm_api_t tdm_api; + int ss = 0; + char a=0,b=0; + int bytes=0,err; + unsigned char data[1024]; + unsigned int fromlen = sizeof(struct sockaddr_in); + sangoma_api_hdr_t hdrframe; + unsigned int udp_rx=0,udp_tx=0,tdm_rx=0,tdm_tx=0; + unsigned int udp_rx_err=0, udp_tx_err=0; + unsigned int tdm_rx_err=0, tdm_tx_err=0; + int bridge_ip_sync=0; + int err_flag=0; + unsigned char prev_status=0; +#if TEST_SEQ + unsigned char tx_seq_cnt=0; + unsigned char rx_seq_cnt=0; + int i; + int insync=0; +#endif + + memset(&hdrframe,0,sizeof(hdrframe)); + memset(data,0,sizeof(data)); + memset(&tdm_api,0,sizeof(tdm_api)); + + if (bridge->period) { + err=sangoma_tdm_set_usr_period(bridge->tdm_fd, &tdm_api,bridge->period); + if (err) { + log_printf(SMG_LOG_ALL,server.log,"%s: Failed to execute set period %i\n",__FUNCTION__,bridge->period); + } + } + + sangoma_tdm_disable_hwec(bridge->tdm_fd, &tdm_api); + + err=sangoma_tdm_flush_bufs(bridge->tdm_fd, &tdm_api); + if (err) { + log_printf(SMG_LOG_ALL,server.log,"%s: Failed to execute tdm flush\n",__FUNCTION__); + } + + while (!bridge->end && + woomera_test_flag(&server.master_connection, WFLAG_RUNNING)) { + + err_flag=0; + + ss = waitfor_2sockets(mcon->socket, + bridge->tdm_fd, + &a, &b, 1000); + + if (ss > 0) { + + if (a) { + + bytes = recvfrom(mcon->socket, &data[0], sizeof(data), MSG_DONTWAIT, + (struct sockaddr *) &mcon->local_addr, &fromlen); + +#if TEST_SEQ + for (i=0;i 0) { + + if (bridge_ip_sync == 0) { + bridge_ip_sync=1; + log_printf(SMG_LOG_ALL,server.log,"Bridge IP Sync: span=%i chan=%i port=%d len=%i\n",bridge->span,bridge->chan,bridge->mcon.cfg.local_port,bytes); + } + udp_rx++; + + err=sangoma_sendmsg_socket(bridge->tdm_fd, + &hdrframe, + sizeof(hdrframe), + data, + bytes, 0); + if (err != bytes) { + + unsigned char current_status; + unsigned char verbose=1; + + sangoma_tdm_get_link_status(bridge->tdm_fd, &tdm_api, ¤t_status); + if (current_status != WP_TDMAPI_EVENT_LINK_STATUS_CONNECTED) { + if (prev_status == current_status) { + verbose=0; + } + } + + prev_status = current_status; + + if (verbose) { + log_printf(SMG_LOG_ALL,server.log,"%s: Error: Bridge tdm write failed (span=%i,chan=%i)! len=%i status=%s - %s\n", + __FUNCTION__,bridge->span,bridge->chan,bytes, current_status==WP_TDMAPI_EVENT_LINK_STATUS_CONNECTED?"Connected":"Disconnected", + strerror(errno)); + sangoma_tdm_flush_bufs(bridge->tdm_fd, &tdm_api); + err_flag++; + } + + tdm_tx_err++; + + } else { + tdm_tx++; + } + } else { + log_printf(SMG_LOG_ALL,server.log,"%s: Error: Bridge sctp read failed (span=%i,chan=%i)! len=%i - %s\n", + __FUNCTION__,bridge->span,bridge->chan,bytes,strerror(errno)); + udp_rx_err++; + err_flag++; + } + } + + if (b) { + bytes = sangoma_readmsg_socket(bridge->tdm_fd, + &hdrframe, + sizeof(hdrframe), + data, + sizeof(data), 0); + + if (bytes > 0) { + tdm_rx++; + err=sendto(mcon->socket, + data, + bytes, 0, + (struct sockaddr *) &mcon->remote_addr, + sizeof(mcon->remote_addr)); + if (err != bytes) { + log_printf(SMG_LOG_ALL,server.log,"%s: Error: Bridge sctp write failed (span=%i,chan=%i)! len=%i - %s\n",__FUNCTION__,bridge->span,bridge->chan,bytes,strerror(errno)); + udp_tx_err++; + err_flag++; + } else { + udp_tx++; + } + } else { + log_printf(SMG_LOG_ALL,server.log,"%s: Error: Bridge tdm read failed (span=%i,chan=%i)! len=%i - %s\n", + __FUNCTION__,bridge->span,bridge->chan,bytes,strerror(errno)); + tdm_rx_err++; + err_flag++; + } + } + + } else if (ss < 0) { + if (!bridge->end) { + log_printf(SMG_LOG_ALL,server.log,"%s: Poll failed on fd exiting bridge (span=%i,chan=%i)\n", + __FUNCTION__,bridge->span,bridge->chan); + } + break; + + } else if (ss == 0) { + + if (bridge_ip_sync) { + log_printf(SMG_LOG_ALL,server.log,"Bridge IP Timeout: span=%i chan=%i port=%d \n", + bridge->span,bridge->chan,bridge->mcon.cfg.local_port); + err_flag++; + } + bridge_ip_sync=0; + } + + + +#if TEST_SEQ + if (udp_rx % 1000 == 0) { + err_flag++; + } +#endif + + if (err_flag) { + log_printf(SMG_LOG_ALL,server.log,"Bridge (s%02ic%02i) urx/ttx=(%i/%i) ue/te=(%i/%i) | trx/utx=(%i/%i) te/ue=(%i/%i) \n", + bridge->span,bridge->chan, + udp_rx,tdm_tx,udp_rx_err,tdm_tx_err, + tdm_rx,udp_tx,tdm_rx_err,udp_tx_err); + } + + } + + pthread_mutex_lock(&g_smg_ip_bridge_lock); + bridge_threads--; + pthread_mutex_unlock(&g_smg_ip_bridge_lock); + + return NULL; +} + + + +static int launch_bridge_thread(int idx) +{ + pthread_attr_t attr; + int result = 0; + struct sched_param param; + + param.sched_priority = 9; + result = pthread_attr_init(&attr); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, MGD_STACK_SIZE); + + result = pthread_attr_setschedparam (&attr, ¶m); + + log_printf(SMG_LOG_ALL,server.log,"%s: Bridge Priority=%i res=%i \n",__FUNCTION__, + param.sched_priority,result); + + bridge_threads++; + + result = pthread_create(&g_smg_ip_bridge_idx[idx].thread, &attr, bridge_thread_run, &g_smg_ip_bridge_idx[idx]); + if (result) { + log_printf(SMG_LOG_ALL, server.log, "%s: Error: Creating Thread! %s\n", + __FUNCTION__,strerror(errno)); + g_smg_ip_bridge_idx[idx].end=1; + bridge_threads--; + } + pthread_attr_destroy(&attr); + + return result; +} + +static int create_udp_socket(call_signal_connection_t *ms, 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; + + log_printf(SMG_LOG_DEBUG_9,server.log,"LocalIP %s:%d IP %s:%d \n",local_ip, local_port, ip, port); + + memset(&ms->remote_hp, 0, sizeof(ms->remote_hp)); + memset(&ms->local_hp, 0, sizeof(ms->local_hp)); + if ((ms->socket = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) { + gethostbyname_r(ip, &ms->remote_hp, buf, sizeof(buf), &result, &err); + gethostbyname_r(local_ip, &ms->local_hp, local_buf, sizeof(local_buf), &local_result, &err); + if (result && local_result) { + ms->remote_addr.sin_family = ms->remote_hp.h_addrtype; + memcpy((char *) &ms->remote_addr.sin_addr.s_addr, ms->remote_hp.h_addr_list[0], ms->remote_hp.h_length); + ms->remote_addr.sin_port = htons(port); + + ms->local_addr.sin_family = ms->local_hp.h_addrtype; + memcpy((char *) &ms->local_addr.sin_addr.s_addr, ms->local_hp.h_addr_list[0], ms->local_hp.h_length); + ms->local_addr.sin_port = htons(local_port); + + rc = bind(ms->socket, (struct sockaddr *) &ms->local_addr, sizeof(ms->local_addr)); + if (rc < 0) { + close(ms->socket); + ms->socket = -1; + + log_printf(SMG_LOG_DEBUG_9,server.log, + "Failed to bind LocalIP %s:%d IP %s:%d (%s)\n", + local_ip, local_port, ip, port,strerror(errno)); + } + + /* OK */ + + } else { + log_printf(SMG_LOG_ALL,server.log, + "Failed to get hostbyname LocalIP %s:%d IP %s:%d (%s)\n", + local_ip, local_port, ip, port,strerror(errno)); + } + } else { + log_printf(SMG_LOG_ALL,server.log, + "Failed to create/allocate UDP socket\n"); + } + + return ms->socket; +} + + + +int smg_ip_bridge_start(void) +{ + int i; + int err; + struct smg_tdm_ip_bridge *bridge; + call_signal_connection_t *mcon; + + pthread_mutex_init(&g_smg_ip_bridge_lock,NULL); + + for (i=0;imcon; + + log_printf(SMG_LOG_ALL, server.log, "Opening Bridge MCON Socket [%d] local %s/%d remote %s/%d \n", + mcon->socket,mcon->cfg.local_ip,mcon->cfg.local_port,mcon->cfg.remote_ip,mcon->cfg.remote_port); + +#if 0 + if (call_signal_connection_open(mcon, + mcon->cfg.local_ip, + mcon->cfg.local_port, + mcon->cfg.remote_ip, + mcon->cfg.remote_port) < 0) { + log_printf(SMG_LOG_ALL, server.log, "Error: Opening Bridge MCON Socket [%d] local %s/%d remote %s/%d %s\n", + mcon->socket,mcon->cfg.local_ip,mcon->cfg.local_port,mcon->cfg.remote_ip,mcon->cfg.remote_port,strerror(errno)); + bridge->end=1; + return -1; + } +#else + if (create_udp_socket(mcon, + mcon->cfg.local_ip, + mcon->cfg.local_port, + mcon->cfg.remote_ip, + mcon->cfg.remote_port) < 0) { + log_printf(SMG_LOG_ALL, server.log, "Error: Opening Bridge MCON Socket [%d] local %s/%d remote %s/%d %s\n", + mcon->socket,mcon->cfg.local_ip,mcon->cfg.local_port,mcon->cfg.remote_ip,mcon->cfg.remote_port,strerror(errno)); + bridge->end=1; + return -1; + } + +#endif + + bridge->tdm_fd=open_span_chan(bridge->span, bridge->chan); + if (bridge->tdm_fd < 0) { + log_printf(SMG_LOG_ALL, server.log, "Error: Failed to open span=%i chan=%i - %s\n", + bridge->span,bridge->chan,strerror(errno)); + return -1; + } + + err=launch_bridge_thread(i); + if (err) { + bridge->end=1; + return -1; + } + } + + return 0; +} + + +int smg_ip_bridge_stop(void) +{ + int i; + int timeout=10; + + for (i=0;i> stats.out +for dev in $DEVS +do + if [ "$CMD" = "clear" ]; then + wanpipemon -i $dev -c fc + fi + line=`wanpipemon -i $dev -c sc | grep "overrun"` + echo "IF => $dev $line" | tee -a stats.out +done + diff --git a/ssmg/sangoma_mgd.trunk/scripts/callgen/.svn/entries b/ssmg/sangoma_mgd.trunk/scripts/callgen/.svn/entries index 0748650..52b529a 100644 --- a/ssmg/sangoma_mgd.trunk/scripts/callgen/.svn/entries +++ b/ssmg/sangoma_mgd.trunk/scripts/callgen/.svn/entries @@ -1,7 +1,7 @@ 8 dir -193 +276 https://www.sangomapbx.com/svn/sangoma_mgd/trunk/scripts/callgen https://www.sangomapbx.com/svn/sangoma_mgd @@ -32,7 +32,7 @@ file -2009-03-30T18:03:12.000000Z +2009-08-25T20:44:42.000000Z ca4fa1b8a9d0f74e95770edcce98b1bc 2007-09-21T20:53:51.260136Z 1 @@ -44,7 +44,7 @@ file -2009-03-30T18:03:12.000000Z +2009-08-25T20:44:42.000000Z 5dbf3c8b9480d257f7c04e2cb943c2f1 2007-09-21T20:53:51.260136Z 1 @@ -57,7 +57,7 @@ file -2009-03-30T18:03:12.000000Z +2009-08-25T20:44:42.000000Z b7726255f02a83aa5e04d0c08332c2e9 2007-09-21T20:53:51.260136Z 1 @@ -70,7 +70,7 @@ file -2009-03-30T18:03:12.000000Z +2009-08-25T20:44:42.000000Z a94061bf84e9f89de0842eb1249d0975 2007-09-21T20:53:51.260136Z 1 diff --git a/ssmg/sangoma_mgd.trunk/scripts/init.d/.svn/all-wcprops b/ssmg/sangoma_mgd.trunk/scripts/init.d/.svn/all-wcprops index 56205ac..6cf2f4d 100644 --- a/ssmg/sangoma_mgd.trunk/scripts/init.d/.svn/all-wcprops +++ b/ssmg/sangoma_mgd.trunk/scripts/init.d/.svn/all-wcprops @@ -1,11 +1,17 @@ K 25 svn:wc:ra_dav:version-url V 50 -/svn/sangoma_mgd/!svn/ver/175/trunk/scripts/init.d +/svn/sangoma_mgd/!svn/ver/271/trunk/scripts/init.d +END +ss7boxd_monitor.sh +K 25 +svn:wc:ra_dav:version-url +V 69 +/svn/sangoma_mgd/!svn/ver/271/trunk/scripts/init.d/ss7boxd_monitor.sh END smgss7_init_ctrl K 25 svn:wc:ra_dav:version-url V 67 -/svn/sangoma_mgd/!svn/ver/175/trunk/scripts/init.d/smgss7_init_ctrl +/svn/sangoma_mgd/!svn/ver/263/trunk/scripts/init.d/smgss7_init_ctrl END diff --git a/ssmg/sangoma_mgd.trunk/scripts/init.d/.svn/entries b/ssmg/sangoma_mgd.trunk/scripts/init.d/.svn/entries index 143f24e..11c393f 100644 --- a/ssmg/sangoma_mgd.trunk/scripts/init.d/.svn/entries +++ b/ssmg/sangoma_mgd.trunk/scripts/init.d/.svn/entries @@ -1,14 +1,14 @@ 8 dir -193 +276 https://www.sangomapbx.com/svn/sangoma_mgd/trunk/scripts/init.d https://www.sangomapbx.com/svn/sangoma_mgd -2009-05-13T21:19:52.845605Z -175 +2010-03-20T20:00:33.973065Z +271 ncorbic @@ -26,16 +26,29 @@ svn:special svn:externals svn:needs-lock 27f70977-ab3a-0410-9ff6-8a87bf49d3db +ss7boxd_monitor.sh +file + + + + +2010-03-20T18:04:43.000000Z +00eff24c48014f0c89846bf4cfe2023b +2010-03-20T20:00:33.973065Z +271 +ncorbic +has-props + smgss7_init_ctrl file -2009-05-14T20:09:14.000000Z -25c6714516b4a626ac83723a32f0cfcb -2009-05-13T21:19:52.845605Z -175 +2010-02-26T00:14:39.000000Z +105521dbea53c0e6ba298a0298ea43f6 +2010-02-26T02:13:54.825677Z +263 ncorbic has-props diff --git a/ssmg/sangoma_mgd.trunk/.svn/prop-base/smg_ctrl_ss7.svn-base b/ssmg/sangoma_mgd.trunk/scripts/init.d/.svn/prop-base/ss7boxd_monitor.sh.svn-base similarity index 100% rename from ssmg/sangoma_mgd.trunk/.svn/prop-base/smg_ctrl_ss7.svn-base rename to ssmg/sangoma_mgd.trunk/scripts/init.d/.svn/prop-base/ss7boxd_monitor.sh.svn-base diff --git a/ssmg/sangoma_mgd.trunk/scripts/init.d/.svn/text-base/smgss7_init_ctrl.svn-base b/ssmg/sangoma_mgd.trunk/scripts/init.d/.svn/text-base/smgss7_init_ctrl.svn-base index a99b72f..a87e878 100644 --- a/ssmg/sangoma_mgd.trunk/scripts/init.d/.svn/text-base/smgss7_init_ctrl.svn-base +++ b/ssmg/sangoma_mgd.trunk/scripts/init.d/.svn/text-base/smgss7_init_ctrl.svn-base @@ -4,12 +4,14 @@ WAN_LOCK=/var/lock/wanrouter_lock - +SS7_LOCK=/var/lock/ss7box_wanrouter_lock #Set the ss7boost file name ss7boost="ss7boost" #Set the ss7box file name ss7box="ss7boxd" +bindir="/usr/sbin" +ss7box_mon="ss7boxd_monitor.sh" if [ -f /etc/wanpipe/pbxd ]; then . /etc/wanpipe/pbxd @@ -19,7 +21,21 @@ if [ "$PBXD" = "" ]; then PBXD="asterisk" fi -PBXD_LAUNCH="safe_$PBXD" + +if [ $PBXD = "asterisk" ]; then + PBXD_LAUNCH="safe_$PBXD" +elif [ $PBXD = "callweaver" ]; then + PBXD_LAUNCH="safe_$PBXD" +elif [ $PBXD = "freeswitch" ]; then + if [ "$PBXD_BIN" = "" ]; then + PBXD_BIN="/usr/local/freswitch" + fi + if [ "$PBXD_LAUNCH" = "" ]; then + PBXD_LAUNCH="freswitch -nc" + fi + TMP=$PBXD_BIN/$PBXD_LAUNCH + PBXD_LAUNCH=$TMP +fi smgctrl_log="/etc/wanpipe/smgctrl_log" @@ -49,7 +65,7 @@ full SMP product. # FULL # Start/Stop/Restart full SMG product. # ss7box, all wanpipe devices, -# ss7boost, sangoma_mgd and asterisk +# ss7boost, sangoma_mgd and $PBXD ./smgss7_init_ctrl start ./smgss7_init_ctrl stop @@ -57,8 +73,8 @@ full SMP product. # PARTIAL # Start/Stop/Restart partial SMG product. -# ss7boost, sangoma_mgd and asterisk -# (This is equivalent of restarting asterisk only) +# ss7boost, sangoma_mgd and $PBXD +# (This is equivalent of restarting $PBXD only) # In production ss7 line is usually not restarted ./smgss7_init_ctrl start isup @@ -84,6 +100,37 @@ EOUT +read_smg_conf () +{ + + WAN_HOME=/etc/wanpipe + WAN_CONF_DIR=$WAN_HOME + META_SMG_CONF=$WAN_HOME/smg.rc + + # Read meta-configuration file. + + if [ -f $META_SMG_CONF ] + then . $META_SMG_CONF + else + logit " $META_SMG_CONF not found !!!!" + return 1 + fi + return 0 +} + +function init_smg_conf() +{ +SMG_BOOT= +SANGOMA_PRID= +SANGOMA_BRID= +SANGOMA_SS7ISUP= +SANGOMA_MEDIA_GATEWAY= +SANGOMA_SS7BOOST= +SANGOMA_SAFE_MODE= +SANGOMA_FREESWITCH= +SANGOMA_ASTERISK= +} + function pbx_ctrl () { @@ -125,13 +172,20 @@ function ss7_boost_ctrl () function ss7_box_ctrl () { local wp_list - cmd=$1 + local ss7_lock="NO" + cmd=$1 - cd $ss7dir + cd $ss7dir if [ $cmd != "start" ]; then echo "Stopping: $ss7box" fi + + + if [ -e $bindir/$ss7box_mon ]; then + echo "Stopping $ss7box_mon" + eval "killall $ss7box_mon 2>> $smgctrl_log" + fi eval "kill -TERM $(pidof $ss7box) 2>> $smgctrl_log" if [ $cmd != "start" ]; then @@ -143,18 +197,54 @@ function ss7_box_ctrl () eval "modprobe -r xmtp2km >> $smgctrl_log 2>> $smgctrl_log" - if [ $cmd = "start" ]; then - sleep 1 + if [ $cmd = "start" ]; then + sleep 1 echo "Starting: $ss7box" - eval "$NICE ./$ss7box >> $smgctrl_log" + eval "$NICE ./$ss7box >> $smgctrl_log" sleep 2 - while [ -e $WAN_LOCK ] + + while [ -e $SS7_LOCK ] do - echo "wanrouter lock detected $WAN_LOCK..." + ss7_lock="YES" + echo "ss7box lock detected $SS7_LOCK..." sleep 1 done - sleep 1 + + if [ $ss7_lock = "NO" ]; then + while [ 1 ] + do + cnt=5 + while [ -e $WAN_LOCK ] + do + echo "wanrouter lock detected $WAN_LOCK..." + sleep 1 + done + + endloop=0 + while [ ! -e $WAN_LOCK ] + do + echo "Waiting for ss7box to finish wanpipe start ..." + sleep 1 + cnt=$((cnt-1)) + if [ $cnt -lt 1 ]; then + endloop=1 + break; + fi + done + + if [ $endloop -gt 0 ]; then + break + fi + + done + fi + sleep 2 + if [ -e $bindir/$ss7box_mon ]; then + echo "Starting $ss7box_mon" + eval "nice $ss7box_mon &" + fi + echo "Starting: wanrouter start" eval "wanrouter start >> $smgctrl_log 2>> $smgctrl_log" @@ -163,9 +253,9 @@ function ss7_box_ctrl () sleep 5; echo "Waiting for ss7box to connect $i/6..." done - fi + fi - cd $homedir + cd $homedir } function stop_running_wanpipes () @@ -188,6 +278,8 @@ function stop_running_wanpipes () } +init_smg_conf +read_smg_conf case "$arg1" in start) @@ -211,13 +303,13 @@ case "$arg1" in stop) if [ "$arg2" == isup ]; then - pbx_ctrl "$arg1" - ss7_boost_ctrl "$arg1" - else - pbx_ctrl "$arg1" - ss7_boost_ctrl "$arg1" - ss7_box_ctrl "$arg1" - + pbx_ctrl "$arg1" + ss7_boost_ctrl "$arg1" + else + pbx_ctrl "$arg1" + ss7_boost_ctrl "$arg1" + ss7_box_ctrl "$arg1" + if [ -e /proc/net/wanrouter/status ]; then cat /proc/net/wanrouter/status else @@ -225,14 +317,14 @@ case "$arg1" in echo "Wanrouter Stopped" echo fi - fi + fi ;; restart) cd "$homedir" - $prog stop "$arg2" - $prog start "$arg2" + $prog stop "$arg2" + $prog start "$arg2" ;; *) diff --git a/ssmg/sangoma_mgd.trunk/scripts/init.d/.svn/text-base/ss7boxd_monitor.sh.svn-base b/ssmg/sangoma_mgd.trunk/scripts/init.d/.svn/text-base/ss7boxd_monitor.sh.svn-base new file mode 100644 index 0000000..cfd8674 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/scripts/init.d/.svn/text-base/ss7boxd_monitor.sh.svn-base @@ -0,0 +1,117 @@ +#!/bin/bash + +PROD="ss7boxd_monitor" +SIG="ss7boxd" +LOGFILE=/var/log/messages +LOGROTATE=/etc/logrotate.conf + +lcnt=0; + +resource_current_cnt=0; +resource_overall_err=0; +resource_threshold=3; + +hb_current_cnt=0; +hb_overall_err=0; +hb_threshold=10; +hb_reset=0; + + +logit() +{ + local data="$1" + + echo "$PROD: $data" + logger "$PROD: $data" +} + + +logrotate_files() +{ + if [ -f $LOGROTATE ]; then + eval "logrotate -f $LOGROTATE" + else + cp -f $LOGROTATE $LOGROTATE.$((lcnt+1)) + fi + + echo " " > $LOGFILE +} + +restart_smg_ctrl() +{ + + logrotate_files + + logit "$1" + eval "smg_ctrl stop" + eval "smg_ctrl start" + logit "restart_smg_ctrl done" + +} + +check_for_isupd_errors() +{ + err=`grep "sangoma_isupd.*Resource temporarily unavailable" $LOGFILE -c` + if [ $? -eq 0 ]; then + if [ $err != 0 ] && [ $err != $resource_current_cnt ]; then + + resource_overall_err=$((resource_overall_err+1)) + logit "Resource error occoured cnt=$resource_overall_err/$resource_threshold!"; + + if [ $resource_overall_err -gt $resource_threshold ]; then + resource_overall_err=0; + restart_smg_ctrl "Restarting smg_ctrl due to: sangoma_isupd Resource errors" + fi + fi + resource_current_cnt=$err; + fi + + err=`grep "heartbeat" $LOGFILE -c` + if [ $? -eq 0 ]; then + if [ $err != 0 ] && [ $err != $hb_current_cnt ]; then + + hb_overall_err=$((hb_overall_err+1)) + logit "Heartbeat error occoured cnt=$hb_overall_err/$hb_threshold!"; + + if [ $hb_overall_err -gt $hb_threshold ]; then + hb_overall_err=0; + restart_smg_ctrl "Restarting smg_ctrl due to: HeartBeat errors" + fi + hb_reset=0 + else + hb_reset=$((hb_reset+1)) + if [ $hb_overall_err -gt 0 ] && [ $hb_reset -gt $((hb_threshold*2)) ]; then + echo "$PROD: Resetting hb overall cnt" + hb_overall_err=0; + hb_reset=0; + fi + fi + hb_current_cnt=$err + fi +} + + +eval 'pidof $SIG' 2> /dev/null > /dev/null +if [ $? -ne 0 ]; then + logit "Error: $SIG not started" + exit 1 +fi + +logit "Service Started" + + +while [ 1 ]; +do + + eval 'pidof $SIG' 2> /dev/null > /dev/null + if [ $? -ne 0 ]; then + logit "$SIG not running..." + exit 1 + fi + + check_for_isupd_errors + + #logit "$SIG running..." + sleep 1 + +done diff --git a/ssmg/sangoma_mgd.trunk/scripts/init.d/smgss7_init_ctrl b/ssmg/sangoma_mgd.trunk/scripts/init.d/smgss7_init_ctrl index a99b72f..a87e878 100755 --- a/ssmg/sangoma_mgd.trunk/scripts/init.d/smgss7_init_ctrl +++ b/ssmg/sangoma_mgd.trunk/scripts/init.d/smgss7_init_ctrl @@ -4,12 +4,14 @@ WAN_LOCK=/var/lock/wanrouter_lock - +SS7_LOCK=/var/lock/ss7box_wanrouter_lock #Set the ss7boost file name ss7boost="ss7boost" #Set the ss7box file name ss7box="ss7boxd" +bindir="/usr/sbin" +ss7box_mon="ss7boxd_monitor.sh" if [ -f /etc/wanpipe/pbxd ]; then . /etc/wanpipe/pbxd @@ -19,7 +21,21 @@ if [ "$PBXD" = "" ]; then PBXD="asterisk" fi -PBXD_LAUNCH="safe_$PBXD" + +if [ $PBXD = "asterisk" ]; then + PBXD_LAUNCH="safe_$PBXD" +elif [ $PBXD = "callweaver" ]; then + PBXD_LAUNCH="safe_$PBXD" +elif [ $PBXD = "freeswitch" ]; then + if [ "$PBXD_BIN" = "" ]; then + PBXD_BIN="/usr/local/freswitch" + fi + if [ "$PBXD_LAUNCH" = "" ]; then + PBXD_LAUNCH="freswitch -nc" + fi + TMP=$PBXD_BIN/$PBXD_LAUNCH + PBXD_LAUNCH=$TMP +fi smgctrl_log="/etc/wanpipe/smgctrl_log" @@ -49,7 +65,7 @@ full SMP product. # FULL # Start/Stop/Restart full SMG product. # ss7box, all wanpipe devices, -# ss7boost, sangoma_mgd and asterisk +# ss7boost, sangoma_mgd and $PBXD ./smgss7_init_ctrl start ./smgss7_init_ctrl stop @@ -57,8 +73,8 @@ full SMP product. # PARTIAL # Start/Stop/Restart partial SMG product. -# ss7boost, sangoma_mgd and asterisk -# (This is equivalent of restarting asterisk only) +# ss7boost, sangoma_mgd and $PBXD +# (This is equivalent of restarting $PBXD only) # In production ss7 line is usually not restarted ./smgss7_init_ctrl start isup @@ -84,6 +100,37 @@ EOUT +read_smg_conf () +{ + + WAN_HOME=/etc/wanpipe + WAN_CONF_DIR=$WAN_HOME + META_SMG_CONF=$WAN_HOME/smg.rc + + # Read meta-configuration file. + + if [ -f $META_SMG_CONF ] + then . $META_SMG_CONF + else + logit " $META_SMG_CONF not found !!!!" + return 1 + fi + return 0 +} + +function init_smg_conf() +{ +SMG_BOOT= +SANGOMA_PRID= +SANGOMA_BRID= +SANGOMA_SS7ISUP= +SANGOMA_MEDIA_GATEWAY= +SANGOMA_SS7BOOST= +SANGOMA_SAFE_MODE= +SANGOMA_FREESWITCH= +SANGOMA_ASTERISK= +} + function pbx_ctrl () { @@ -125,13 +172,20 @@ function ss7_boost_ctrl () function ss7_box_ctrl () { local wp_list - cmd=$1 + local ss7_lock="NO" + cmd=$1 - cd $ss7dir + cd $ss7dir if [ $cmd != "start" ]; then echo "Stopping: $ss7box" fi + + + if [ -e $bindir/$ss7box_mon ]; then + echo "Stopping $ss7box_mon" + eval "killall $ss7box_mon 2>> $smgctrl_log" + fi eval "kill -TERM $(pidof $ss7box) 2>> $smgctrl_log" if [ $cmd != "start" ]; then @@ -143,18 +197,54 @@ function ss7_box_ctrl () eval "modprobe -r xmtp2km >> $smgctrl_log 2>> $smgctrl_log" - if [ $cmd = "start" ]; then - sleep 1 + if [ $cmd = "start" ]; then + sleep 1 echo "Starting: $ss7box" - eval "$NICE ./$ss7box >> $smgctrl_log" + eval "$NICE ./$ss7box >> $smgctrl_log" sleep 2 - while [ -e $WAN_LOCK ] + + while [ -e $SS7_LOCK ] do - echo "wanrouter lock detected $WAN_LOCK..." + ss7_lock="YES" + echo "ss7box lock detected $SS7_LOCK..." sleep 1 done - sleep 1 + + if [ $ss7_lock = "NO" ]; then + while [ 1 ] + do + cnt=5 + while [ -e $WAN_LOCK ] + do + echo "wanrouter lock detected $WAN_LOCK..." + sleep 1 + done + + endloop=0 + while [ ! -e $WAN_LOCK ] + do + echo "Waiting for ss7box to finish wanpipe start ..." + sleep 1 + cnt=$((cnt-1)) + if [ $cnt -lt 1 ]; then + endloop=1 + break; + fi + done + + if [ $endloop -gt 0 ]; then + break + fi + + done + fi + sleep 2 + if [ -e $bindir/$ss7box_mon ]; then + echo "Starting $ss7box_mon" + eval "nice $ss7box_mon &" + fi + echo "Starting: wanrouter start" eval "wanrouter start >> $smgctrl_log 2>> $smgctrl_log" @@ -163,9 +253,9 @@ function ss7_box_ctrl () sleep 5; echo "Waiting for ss7box to connect $i/6..." done - fi + fi - cd $homedir + cd $homedir } function stop_running_wanpipes () @@ -188,6 +278,8 @@ function stop_running_wanpipes () } +init_smg_conf +read_smg_conf case "$arg1" in start) @@ -211,13 +303,13 @@ case "$arg1" in stop) if [ "$arg2" == isup ]; then - pbx_ctrl "$arg1" - ss7_boost_ctrl "$arg1" - else - pbx_ctrl "$arg1" - ss7_boost_ctrl "$arg1" - ss7_box_ctrl "$arg1" - + pbx_ctrl "$arg1" + ss7_boost_ctrl "$arg1" + else + pbx_ctrl "$arg1" + ss7_boost_ctrl "$arg1" + ss7_box_ctrl "$arg1" + if [ -e /proc/net/wanrouter/status ]; then cat /proc/net/wanrouter/status else @@ -225,14 +317,14 @@ case "$arg1" in echo "Wanrouter Stopped" echo fi - fi + fi ;; restart) cd "$homedir" - $prog stop "$arg2" - $prog start "$arg2" + $prog stop "$arg2" + $prog start "$arg2" ;; *) diff --git a/ssmg/sangoma_mgd.trunk/scripts/init.d/ss7boxd_monitor.sh b/ssmg/sangoma_mgd.trunk/scripts/init.d/ss7boxd_monitor.sh new file mode 100755 index 0000000..cfd8674 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/scripts/init.d/ss7boxd_monitor.sh @@ -0,0 +1,117 @@ +#!/bin/bash + +PROD="ss7boxd_monitor" +SIG="ss7boxd" +LOGFILE=/var/log/messages +LOGROTATE=/etc/logrotate.conf + +lcnt=0; + +resource_current_cnt=0; +resource_overall_err=0; +resource_threshold=3; + +hb_current_cnt=0; +hb_overall_err=0; +hb_threshold=10; +hb_reset=0; + + +logit() +{ + local data="$1" + + echo "$PROD: $data" + logger "$PROD: $data" +} + + +logrotate_files() +{ + if [ -f $LOGROTATE ]; then + eval "logrotate -f $LOGROTATE" + else + cp -f $LOGROTATE $LOGROTATE.$((lcnt+1)) + fi + + echo " " > $LOGFILE +} + +restart_smg_ctrl() +{ + + logrotate_files + + logit "$1" + eval "smg_ctrl stop" + eval "smg_ctrl start" + logit "restart_smg_ctrl done" + +} + +check_for_isupd_errors() +{ + err=`grep "sangoma_isupd.*Resource temporarily unavailable" $LOGFILE -c` + if [ $? -eq 0 ]; then + if [ $err != 0 ] && [ $err != $resource_current_cnt ]; then + + resource_overall_err=$((resource_overall_err+1)) + logit "Resource error occoured cnt=$resource_overall_err/$resource_threshold!"; + + if [ $resource_overall_err -gt $resource_threshold ]; then + resource_overall_err=0; + restart_smg_ctrl "Restarting smg_ctrl due to: sangoma_isupd Resource errors" + fi + fi + resource_current_cnt=$err; + fi + + err=`grep "heartbeat" $LOGFILE -c` + if [ $? -eq 0 ]; then + if [ $err != 0 ] && [ $err != $hb_current_cnt ]; then + + hb_overall_err=$((hb_overall_err+1)) + logit "Heartbeat error occoured cnt=$hb_overall_err/$hb_threshold!"; + + if [ $hb_overall_err -gt $hb_threshold ]; then + hb_overall_err=0; + restart_smg_ctrl "Restarting smg_ctrl due to: HeartBeat errors" + fi + hb_reset=0 + else + hb_reset=$((hb_reset+1)) + if [ $hb_overall_err -gt 0 ] && [ $hb_reset -gt $((hb_threshold*2)) ]; then + echo "$PROD: Resetting hb overall cnt" + hb_overall_err=0; + hb_reset=0; + fi + fi + hb_current_cnt=$err + fi +} + + +eval 'pidof $SIG' 2> /dev/null > /dev/null +if [ $? -ne 0 ]; then + logit "Error: $SIG not started" + exit 1 +fi + +logit "Service Started" + + +while [ 1 ]; +do + + eval 'pidof $SIG' 2> /dev/null > /dev/null + if [ $? -ne 0 ]; then + logit "$SIG not running..." + exit 1 + fi + + check_for_isupd_errors + + #logit "$SIG running..." + sleep 1 + +done diff --git a/ssmg/sangoma_mgd.trunk/scripts/sig_state.sh b/ssmg/sangoma_mgd.trunk/scripts/sig_state.sh new file mode 100755 index 0000000..03d73d8 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/scripts/sig_state.sh @@ -0,0 +1,70 @@ +#!/bin/sh + +CMD=${1:-none} + + +echo "$DEVS" +WAN_DIR=/etc/wanpipe + + +for ((i=1;i<=32;i++)) +do + voice_flag=0; + sig_flag=0; + + if [ ! -e $WAN_DIR/wanpipe$i.conf ]; then + continue + fi + + status=`cat /proc/net/wanrouter/status | grep "wanpipe$i " | cut -d'|' -f4` + status=${status// /} + + voice_cfg=`grep TDM_VOICE_API $WAN_DIR/wanpipe$i.conf` + xmtp2_cfg=`grep XMTP2_API $WAN_DIR/wanpipe$i.conf` + span_cfg=`grep TDMV_SPAN $WAN_DIR/wanpipe$i.conf | cut -d'=' -f2` + span_cfg=${span_cfg// /} + + if [ "$voice_cfg" != "" ]; then + voice_flag=1 + fi + if [ "$xmtp2_cfg" != "" ]; then + sig_flag=1 + fi + + if [ $voice_flag -eq 0 ] && [ $sig_flag -eq 0 ] ; then + echo "========================================================" + echo "Error: Invalid Interface configuration in $WAN_DIR/wanpipe$i.conf" + echo "========================================================" + continue + fi + if [ "$span_cfg" = " " ]; then + echo "========================================================" + echo "Error: Invalid Span configuration in $WAN_DIR/wanpipe$i.conf" + echo "========================================================" + continue + fi + + if [ $i -lt 10 ]; then + echo -n "wanpipe$i: " + else + echo -n "wanpipe$i: " + fi + echo -n "Sig=$sig_flag Voice=$voice_flag " + + if [ $span_cfg -lt 10 ]; then + echo -n "Span=$span_cfg " + else + echo -n "Span=$span_cfg " + fi + + #echo "Status = $status" + + if [ "$status" = "Connected" ]; then + echo -n "Stat=Conn"; + else + echo -n "Stat=Disc"; + fi + echo + +done + diff --git a/ssmg/sangoma_mgd.trunk/scripts/ss7/.svn/all-wcprops b/ssmg/sangoma_mgd.trunk/scripts/ss7/.svn/all-wcprops index 94833f1..b77c2db 100644 --- a/ssmg/sangoma_mgd.trunk/scripts/ss7/.svn/all-wcprops +++ b/ssmg/sangoma_mgd.trunk/scripts/ss7/.svn/all-wcprops @@ -1,11 +1,11 @@ K 25 svn:wc:ra_dav:version-url V 47 -/svn/sangoma_mgd/!svn/ver/185/trunk/scripts/ss7 +/svn/sangoma_mgd/!svn/ver/272/trunk/scripts/ss7 END ckt_report.sh K 25 svn:wc:ra_dav:version-url V 61 -/svn/sangoma_mgd/!svn/ver/185/trunk/scripts/ss7/ckt_report.sh +/svn/sangoma_mgd/!svn/ver/272/trunk/scripts/ss7/ckt_report.sh END diff --git a/ssmg/sangoma_mgd.trunk/scripts/ss7/.svn/entries b/ssmg/sangoma_mgd.trunk/scripts/ss7/.svn/entries index 4070926..a91b67b 100644 --- a/ssmg/sangoma_mgd.trunk/scripts/ss7/.svn/entries +++ b/ssmg/sangoma_mgd.trunk/scripts/ss7/.svn/entries @@ -1,14 +1,14 @@ 8 dir -193 +276 https://www.sangomapbx.com/svn/sangoma_mgd/trunk/scripts/ss7 https://www.sangomapbx.com/svn/sangoma_mgd -2009-07-15T16:39:13.271272Z -185 +2010-03-22T04:38:52.889543Z +272 ncorbic @@ -32,10 +32,10 @@ file -2009-07-17T17:04:47.000000Z -e49bb0553958a083550ebed88fda9c00 -2009-07-15T16:39:13.271272Z -185 +2010-03-23T16:12:45.000000Z +c3e0b3e37fb853d183421bdd8659c65b +2010-03-22T04:38:52.889543Z +272 ncorbic has-props diff --git a/ssmg/sangoma_mgd.trunk/scripts/ss7/.svn/text-base/ckt_report.sh.svn-base b/ssmg/sangoma_mgd.trunk/scripts/ss7/.svn/text-base/ckt_report.sh.svn-base index 9263811..6f6f7af 100644 --- a/ssmg/sangoma_mgd.trunk/scripts/ss7/.svn/text-base/ckt_report.sh.svn-base +++ b/ssmg/sangoma_mgd.trunk/scripts/ss7/.svn/text-base/ckt_report.sh.svn-base @@ -5,9 +5,11 @@ cmd=$1 cli=/usr/local/ss7box/sangoma_isup_cli if [ "$cmd" = "inuse" ]; then - $cli --ckt-report --span all --chan all | grep -c "Y Y" + $cli --ckt-report --span all --chan all | grep -v "Y Y Y" | grep -c "Y Y" elif [ "$cmd" = "free" ]; then $cli --ckt-report --span all --chan all | grep -c "Y n" +elif [ "$cmd" = "reset" ]; then + $cli --ckt-report --span all --chan all | grep -c "Y Y Y" else $cli --ckt-report --span all --chan all fi diff --git a/ssmg/sangoma_mgd.trunk/scripts/ss7/ckt_report.sh b/ssmg/sangoma_mgd.trunk/scripts/ss7/ckt_report.sh index 9263811..6f6f7af 100755 --- a/ssmg/sangoma_mgd.trunk/scripts/ss7/ckt_report.sh +++ b/ssmg/sangoma_mgd.trunk/scripts/ss7/ckt_report.sh @@ -5,9 +5,11 @@ cmd=$1 cli=/usr/local/ss7box/sangoma_isup_cli if [ "$cmd" = "inuse" ]; then - $cli --ckt-report --span all --chan all | grep -c "Y Y" + $cli --ckt-report --span all --chan all | grep -v "Y Y Y" | grep -c "Y Y" elif [ "$cmd" = "free" ]; then $cli --ckt-report --span all --chan all | grep -c "Y n" +elif [ "$cmd" = "reset" ]; then + $cli --ckt-report --span all --chan all | grep -c "Y Y Y" else $cli --ckt-report --span all --chan all fi diff --git a/ssmg/sangoma_mgd.trunk/scripts/stats.sh b/ssmg/sangoma_mgd.trunk/scripts/stats.sh new file mode 100755 index 0000000..cb8ac52 --- /dev/null +++ b/ssmg/sangoma_mgd.trunk/scripts/stats.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +CMD=${1:-none} + +DEVS=$(cat /proc/net/dev | egrep "w.g" | cut -d':' -f1 | sort | xargs) +DEVSS=$(cat /proc/net/dev | egrep "w..g" | cut -d':' -f1 | sort | xargs) + +DEVS="$DEVS $DEVSS" + +echo "$DEVS" + +echo $(date) >> stats.out +for dev in $DEVS +do + if [ "$CMD" = "clear" ]; then + wanpipemon -i $dev -c fc + fi + line=`wanpipemon -i $dev -c sc | grep "overrun"` + echo "IF => $dev $line" | tee -a stats.out +done + diff --git a/ssmg/sangoma_mgd.trunk/sigboost.h b/ssmg/sangoma_mgd.trunk/sigboost.h index 052fcca..be32edd 100644 --- a/ssmg/sangoma_mgd.trunk/sigboost.h +++ b/ssmg/sangoma_mgd.trunk/sigboost.h @@ -14,11 +14,15 @@ #ifndef _SIGBOOST_H_ #define _SIGBOOST_H_ -#define SIGBOOST_VERSION 100 +#define SIGBOOST_VERSION 103 #include #include +#ifdef HAVE_FREETDM +#include +#endif + enum e_sigboost_event_id_values { SIGBOOST_EVENT_CALL_START = 0x80, /*128*/ @@ -30,6 +34,22 @@ enum e_sigboost_event_id_values 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*/ @@ -68,16 +88,34 @@ enum e_sigboost_huntgroup_values 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 30 +#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 */ -#define SIZE_RDNIS 900 + +/* Should only be used by server */ +#define MAX_CALL_SETUP_ID 0xFFFF + +#define SIZE_CUSTOM 900 +#define SIZE_RDNIS SIZE_CUSTOM #pragma pack(1) @@ -86,7 +124,17 @@ typedef struct { uint8_t capability; uint8_t uil1p; -}t_sigboost_bearer; +} 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 { @@ -99,22 +147,32 @@ typedef struct uint32_t trunk_group; uint8_t span; uint8_t chan; + uint32_t flags; /* struct timeval tv; */ - 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 */ + t_sigboost_digits called; + t_sigboost_digits calling; + t_sigboost_digits rdnis; /* ref. Q.931 Table 4-11 and Q.951 Section 3 */ - uint8_t calling_number_screening_ind; - uint8_t calling_number_presentation; char calling_name[MAX_DIALED_DIGITS + 1]; t_sigboost_bearer bearer; uint8_t hunt_group; - uint16_t isup_in_rdnis_size; - char isup_in_rdnis [SIZE_RDNIS]; /* it's a null terminated string */ + uint16_t custom_data_size; + char custom_data[SIZE_CUSTOM]; /* it's a null terminated string */ + } t_sigboost_callstart; -#define MIN_SIZE_CALLSTART_MSG sizeof(t_sigboost_callstart) - SIZE_RDNIS +#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 { @@ -127,20 +185,22 @@ typedef struct 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) +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: - return 0; + break; } return 0; diff --git a/ssmg/sangoma_mgd.trunk/__smg_ctrl_common b/ssmg/sangoma_mgd.trunk/smg_ctrl similarity index 62% rename from ssmg/sangoma_mgd.trunk/__smg_ctrl_common rename to ssmg/sangoma_mgd.trunk/smg_ctrl index d64b096..96dab39 100755 --- a/ssmg/sangoma_mgd.trunk/__smg_ctrl_common +++ b/ssmg/sangoma_mgd.trunk/smg_ctrl @@ -1,6 +1,7 @@ -#!/bin/bash +#!/bin/bash cmd=$1; sigd=$2; +xtraargs=''; cnt=0; max_retry=10; use_syslog=1; @@ -11,6 +12,7 @@ SIG_LOG=$LOG PRI=0 BRI=0 SS7=0 +PROD="smg_ctrl" ulimit -n 65000 @@ -45,56 +47,30 @@ function usage() return 1 } -if [ $sigd = "ss7boost" ] || [ $sigd = "sangoma_isupd" ]; then - sigd_safe_args="-i" - sigd_bg_args="" - SIG_LOG=/var/log/messages - - eval "type $sigd 2> /dev/null > /dev/null" - if [ $? -ne 0 ]; then - export PATH=$PATH:/usr/local/ss7box - fi - eval "type $sigd 2> /dev/null > /dev/null" - if [ $? -ne 0 ]; then - echo - echo "Error $sigd is not found" - echo - exit 1 - fi - - SS7=1; -elif [ $sigd = "sangoma_brid" ]; then - sigd_safe_args="" - sigd_bg_args="-bg" - BRI=1; -elif [ $sigd = "sangoma_prid" ]; then - sigd_safe_args="" - sigd_bg_args="-bg" - PRI=1; - eval "export LD_LIBRARY_PATH=/usr/lib/sangoma_prid:${LD_LIBRARY_PATH} " -else - echo - usage - exit 1 -fi - -while [ ! -z "$3" ]; -do - args=$args"$3 " - shift -done +logit() +{ + local data="$1" + echo "$PROD: $data" + logger "$PROD: $data" +} function stop_all() { echo " " - echo "Stopping running processes..." + logit "Stopping running processes..." stop_safe_sangoma stop_sigdaemon - stop_sangoma_mgd + #post_args=$post_args" >/dev/null 2>/dev/null &" + if [ "$xtraargs" != "sigdonly" ]; then + stop_sangoma_mgd + fi + remove_pid_files + + stop_scripts } function stop_sigdaemon() @@ -108,6 +84,7 @@ function stop_sigdaemon() echo "OK" else echo "FAILED" + logit "Failed to TERM $sigd" break; fi fi @@ -115,10 +92,10 @@ function stop_sigdaemon() do eval "pidof $sigd >/dev/null 2>/dev/null" if [ $? -ne 0 ]; then - echo "$sigd is stopped" + logit "$sigd is stopped" return; else - echo "waiting for $sigd to finish($i/$max_retry)...." + logit "waiting for $sigd to finish($i/$max_retry)...." sleep 1 fi done @@ -130,6 +107,7 @@ function stop_sigdaemon() echo "OK" else echo "FAILED" + logit "Failed to KILL $sigd" break; fi fi @@ -137,10 +115,10 @@ function stop_sigdaemon() do eval "pidof $sigd >/dev/null 2>/dev/null" if [ $? -ne 0 ]; then - echo "$sigd is stopped" + logit "$sigd is stopped" break; else - echo "waiting for $sigd to finish($i/$max_retry)...." + logit "waiting for $sigd to finish($i/$max_retry)...." sleep 1 fi done @@ -161,7 +139,7 @@ function stop_sangoma_mgd() return 0; fi else - echo "sangoma_mgd not running..." + logit "sangoma_mgd not running..." return; fi @@ -169,10 +147,10 @@ function stop_sangoma_mgd() do eval "pidof sangoma_mgd >/dev/null 2>/dev/null" if [ $? -ne 0 ]; then - echo "sangoma_mgd is stopped" + logit "sangoma_mgd is stopped" return 0 else - echo "waiting for sangoma_mgd to finish($i/$max_retry)...." + logit "waiting for sangoma_mgd to finish($i/$max_retry)...." sleep 1 fi done @@ -201,7 +179,7 @@ function stop_safe_sangoma() if [ $? -eq 0 ]; then echo -n "Sending TERM signal to safe_sangoma..." else - echo "safe_sangoma not running..." + logit "safe_sangoma not running..." return; fi sleep 1 @@ -215,7 +193,7 @@ function stop_safe_sangoma() echo "OK" else echo "FAILED" - echo "Failed to stop safe_sangoma" + logit "Failed to stop safe_sangoma" return 1 fi else @@ -230,14 +208,14 @@ function remove_pid_files() eval "rm -f /var/run/$sigd.pid > /dev/null" if [ $? -ne 0 ]; then echo " " - echo "Failed to remove /var/run/$sigd.pid, try to remove it manually" + logit "Failed to remove /var/run/$sigd.pid, try to remove it manually" fi fi if [ -e /var/run/sangoma_mgd.pid ]; then eval "rm -f /var/run/sangoma_mgd.pid > /dev/null" if [ $? -ne 0 ]; then echo " " - echo "Failed to remove /var/run/sangoma_mgd.pid, try to remove it manually" + logit "Failed to remove /var/run/sangoma_mgd.pid, try to remove it manually" fi fi echo "done" @@ -246,7 +224,7 @@ function remove_pid_files() function start_test() { echo " " - echo "Testing configuration files..." + logit "Testing configuration files..." if [ $use_syslog -eq 1 ]; then eval "$sigd -t" else @@ -254,39 +232,91 @@ function start_test() fi if [ $? -eq 0 ]; then - echo "OK" + logit "OK" else - echo "Failed" + logit "Failed" fi } +function start_scripts() +{ + if [ -d /etc/wanpipe/smg_ctrl.d ]; then + for script in /etc/wanpipe/smg_ctrl.d/*.start; do + if [ -x $script ]; then + logit "Executing startup script: $script" + source $script + fi + done + fi +} + +function stop_scripts() +{ + if [ -d /etc/wanpipe/smg_ctrl.d ]; then + for script in /etc/wanpipe/smg_ctrl.d/*.stop; do + if [ -x $script ]; then + logit "Executing stop script: $script" + source $script + fi + done + fi +} function start_all() { - check_running + #post_args=$post_args" >/dev/null 2>/dev/null &" + if [ "$xtraargs" = "sigdonly" ]; then + start_sigd + ret=$? + else + start_sigd + ret=$? + if [ $ret -eq 0 ]; then + start_smg + ret=$? + fi + fi + + start_scripts + + return $ret +} + +function start_sigd() +{ + check_sigd_running pre_args=""; post_args=""; - #post_args=$post_args" >/dev/null 2>/dev/null &" echo " " - echo "Starting processes..." - echo -n "Loading SCTP..." - eval "modprobe sctp >>$LOG 2>>$LOG" - if [ $? -eq 0 ]; then - echo "OK" - else - echo "Failed" - echo "Failed to load SCTP module, check $LOG" - return 1; + if [ $use_safe -eq 1 ]; then + logit "Starting smg_ctrl in safe mode ..." fi + logit "Starting processes..." + eval "cat /proc/net/protocols | grep sctp >/dev/null 2>/dev/null" + if [ $? -eq 0 ]; then + echo "SCTP support already enabled" + else + echo -n "Loading SCTP..." + eval "modprobe sctp >>$LOG 2>>$LOG" + if [ $? -eq 0 ]; then + echo "OK" + else + logit "Failed" + logit "Failed to load SCTP kernel module, check $LOG" + return 1; + fi + fi + + sleep 1 eval "ls /dev/wptdm* >/dev/null 2>/dev/null" if [ $? -ne 0 ]; then eval "ls /dev/wanpipe* >/dev/null 2>/dev/null" if [ $? -ne 0 ]; then - echo "No Sangoma TDM API interfaces running" - echo "Did you start wanrouter? " + logit "No Sangoma TDM API interfaces running" + logit "Did you start wanrouter? " return 1; fi fi @@ -307,20 +337,36 @@ function start_all() if [ $? -eq 0 ]; then echo "OK" else - echo "Failed" - echo "Failed to start $sigd, check $SIG_LOG for errors" + logit "Failed" + logit "Failed to start $sigd, check $SIG_LOG for errors" return 1; fi sleep 2 if [ ! $(pidof $sigd) ]; then - echo "$sigd failed to start" - echo "check $SIG_LOG for errors" + logit "$sigd failed to start" + logit "check $SIG_LOG for errors" return 1; fi sleep 3 + if [ "$xtraargs" = "sigdonly" ]; then + + logit "Sangoma $sigd running.." + if [ $use_syslog -eq 1 ]; then + logit "log file: $LOG and /var/log/messages" + else + logit "log file: $LOG and /var/log/messages" + fi + echo " " + fi + return 0 +} +function start_smg() +{ + check_smg_running pre_args=""; post_args=""; + if [ $use_safe -eq 1 ]; then pre_args=" safe_sangoma" post_args="" @@ -328,9 +374,8 @@ function start_all() post_args=" -bg" fi - if [ -e /etc/wanpipe/.no_smg_load ]; then - echo "Skipping sangoma_mgd..." + logit "Skipping sangoma_mgd..." return 0; fi @@ -339,62 +384,68 @@ function start_all() if [ $? -eq 0 ]; then echo "OK" else - echo "Failed" - echo "Failed to start sangoma_mgd, check $LOG for errors" + logit "Failed" + logit "Failed to start sangoma_mgd, check $LOG for errors" return 1; fi sleep 2 if [ ! $(pidof sangoma_mgd) ]; then - echo "sangoma_mgd failed to start" - echo "check $LOG for errors" + logit "sangoma_mgd failed to start" + logit "check $LOG for errors" return 1; fi echo "Sangoma SMG running.." if [ $use_syslog -eq 1 ]; then - echo "log file: $LOG and /var/log/messages" + logit "log file: $LOG and /var/log/messages" else - echo "log file: $LOG and /var/log/messages" + logit "log file: $LOG and /var/log/messages" fi echo " " return 0 + + } - -function check_running () +function check_sigd_running() { local rc eval "pidof $sigd 2> /dev/null > /dev/null" rc=$? if [ $rc -eq 0 ]; then - echo "$sigd is currently running" - echo "exiting..." - exit 0 - fi - - eval "pidof sangoma_mgd 2> /dev/null > /dev/null" - rc=$? - if [ $rc -eq 0 ]; then - echo "sangoma_mgd is currently running" - echo "exiting..." + logit "$sigd is currently running" + logit "exiting..." exit 0 fi if [ -e /var/run/$sigd.pid ];then eval "rm -f /var/run/$sigd.pid >/dev/null 2>/dev/null"; fi + return 0 + +} + +function check_smg_running() +{ + local rc + eval "pidof sangoma_mgd 2> /dev/null > /dev/null" + rc=$? + if [ $rc -eq 0 ]; then + logit "sangoma_mgd is currently running" + logit "exiting..." + exit 0 + fi if [ -e /var/run/sangoma_mgd.pid ];then eval "rm -f /var/run/sangoma_mgd.pid >/dev/null 2>/dev/null"; fi - return 0 } function check_pid_sigd() { if [ ! $(pidof $sigd) ]; then - echo "$sigd is not running" + logit "$sigd is not running" exit 1 fi return 0 @@ -407,9 +458,9 @@ function toggle_capture() eval "kill -SIGRTMIN+2 $(pidof $sigd)" rc=$? if [ $rc -eq 0 ]; then - echo "BRI: Protocol capture toggled" + logit "BRI: Protocol capture toggled" else - echo "BRI: Failed to send command" + logit "BRI: Failed to send command" fi return $rc @@ -420,13 +471,13 @@ function increase_verbose() local rc check_pid_sigd SIG_VAL=`kill -l SIGRTMIN` - echo "signal: $SIG_VAL" + logit "signal: $SIG_VAL" eval "kill -SIGRTMIN $(pidof $sigd)" rc=$? if [ $rc -eq 0 ]; then - echo "BRI: Verbosity increased" + logit "BRI: Verbosity increased" else - echo "BRI: Failed to send command" + logit "BRI: Failed to send command" fi return $rc @@ -439,9 +490,9 @@ function decrease_verbose() eval "kill -SIGRTMIN+1 $(pidof $sigd)" rc=$? if [ $rc -eq 0 ]; then - echo "BRI: Verbosity decreased" + logit "BRI: Verbosity decreased" else - echo "BRI: Failed to send command" + logit "BRI: Failed to send command" fi return $rc @@ -454,9 +505,9 @@ function show_calls() eval "kill -SIGRTMIN+3 $(pidof $sigd) " rc=$? if [ $rc -eq 0 ]; then - echo "BRI: Show calls" + logit "BRI: Show calls" else - echo "BRI: Failed to send command" + logit "BRI: Failed to send command" fi return $rc @@ -469,9 +520,9 @@ function show_spans() eval "kill -SIGRTMIN+4 $(pidof $sigd)" rc=$? if [ $rc -eq 0 ]; then - echo "BRI: Show spans" + logit "BRI/PRI: Show spans" else - echo "BRI: Failed to send command" + logit "BRI/PRI: Failed to send command" fi return $rc @@ -492,14 +543,106 @@ function parse_args() done } +read_smg_conf () +{ + + WAN_HOME=/etc/wanpipe + WAN_CONF_DIR=$WAN_HOME + META_SMG_CONF=$WAN_HOME/smg.rc + + # Read meta-configuration file. + + if [ -f $META_SMG_CONF ] + then . $META_SMG_CONF + else + logit " $META_SMG_CONF not found !!!!" + return 1 + fi + return 0 +} + +function init_smg_conf() +{ +SMG_BOOT= +SANGOMA_PRID= +SANGOMA_BRID= +SANGOMA_SS7ISUP= +SANGOMA_MEDIA_GATEWAY= +SANGOMA_SS7BOOST= +SANGOMA_SAFE_MODE= +} +###################### +#main start here +###################### parse_args +init_smg_conf +read_smg_conf +#Set sigd +if [ "$SANGOMA_PRID" = "YES" ]; then + sigd="sangoma_prid" +elif [ "$SANGOMA_BRID" = "YES" ]; then + sigd="sangoma_brid" +elif [ "$SANGOMA_SS7ISUP" = "YES" ]; then + sigd="sangoma_isupd" +elif [ "$SANGOMA_SS7BOOST" = "YES" ]; then + sigd="ss7boost" +else + logit "Failed to specify sigd!!! check $META_SMG_CONF " + exit 1 +fi + + + +if [ $SANGOMA_MEDIA_GATEWAY = "NO" ]; then + xtraargs="sigdonly" +fi +if [ $sigd = "ss7boost" ] || [ $sigd = "sangoma_isupd" ]; then + sigd_safe_args="-i" + sigd_bg_args="" + SIG_LOG=/var/log/messages + + eval "type $sigd 2> /dev/null > /dev/null" + if [ $? -ne 0 ]; then + export PATH=$PATH:/usr/local/ss7box + fi + eval "type $sigd 2> /dev/null > /dev/null" + if [ $? -ne 0 ]; then + echo + logit "Error $sigd is not found" + echo + exit 1 + fi + + SS7=1; +elif [ $sigd = "sangoma_brid" ]; then + sigd_safe_args="" + sigd_bg_args="-bg" + BRI=1; +elif [ $sigd = "sangoma_prid" ]; then + sigd_safe_args="" + sigd_bg_args="-bg" + PRI=1; +else + usage + exit 1 +fi + +while [ ! -z "$3" ]; +do + args=$args"$3 " + shift +done ret=0 +if [ "$cmd" = "start" ] && [ "$SANGOMA_SAFE_MODE" = "YES" ]; then + cmd="safe_start" +fi + if [ "$cmd" = "start" ]; then - start_all - ret=$? + start_all + ret=$? if [ $ret -ne 0 ]; then stop_all fi @@ -516,14 +659,17 @@ elif [ "$cmd" = "stop" ]; then ret=$? elif [ "$cmd" = "restart" ]; then stop_all + if [ "$SANGOMA_SAFE_MODE" = "YES" ]; then + use_safe=1 + fi start_all + ret=$? if [ $ret -ne 0 ]; then stop_all fi - ret=$? else if [ $SS7 -eq 1 ]; then - echo "Error: Unsupported command $cmd for SS7" + logit "Error: Unsupported command $cmd for SS7" echo usage exit 1 diff --git a/ssmg/sangoma_mgd.trunk/smg_ctrl_bri b/ssmg/sangoma_mgd.trunk/smg_ctrl_bri deleted file mode 100755 index 1edd6d3..0000000 --- a/ssmg/sangoma_mgd.trunk/smg_ctrl_bri +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -cmd=$1; - -while [ ! -z "$2" ]; -do - args=$args"$2 " - shift -done - -__smg_ctrl_common $cmd sangoma_brid $args -ret=$? - -exit $ret - - diff --git a/ssmg/sangoma_mgd.trunk/smg_ctrl_pri b/ssmg/sangoma_mgd.trunk/smg_ctrl_pri deleted file mode 100755 index d26708a..0000000 --- a/ssmg/sangoma_mgd.trunk/smg_ctrl_pri +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -cmd=$1; - -while [ ! -z "$2" ]; -do - args=$args"$2 " - shift -done - -__smg_ctrl_common $cmd sangoma_prid $args -ret=$? - -exit $ret - - diff --git a/ssmg/sangoma_mgd.trunk/smg_ctrl_ss7 b/ssmg/sangoma_mgd.trunk/smg_ctrl_ss7 deleted file mode 100755 index 8b086a4..0000000 --- a/ssmg/sangoma_mgd.trunk/smg_ctrl_ss7 +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -cmd=$1; - -while [ ! -z "$2" ]; -do - args=$args"$2 " - shift -done - -__smg_ctrl_common $cmd sangoma_isupd $args -ret=$? - -exit $ret - - diff --git a/ssmg/sangoma_mgd.trunk/unit/.svn/entries b/ssmg/sangoma_mgd.trunk/unit/.svn/entries index 8d587c8..873578d 100644 --- a/ssmg/sangoma_mgd.trunk/unit/.svn/entries +++ b/ssmg/sangoma_mgd.trunk/unit/.svn/entries @@ -1,7 +1,7 @@ 8 dir -193 +276 https://www.sangomapbx.com/svn/sangoma_mgd/trunk/unit https://www.sangomapbx.com/svn/sangoma_mgd @@ -32,7 +32,7 @@ file -2009-03-30T18:03:11.000000Z +2009-08-25T20:44:41.000000Z e8adf3e68fa2c003f6c26435d80a7be0 2008-11-04T17:41:26.259250Z 133 @@ -44,7 +44,7 @@ file -2009-03-30T18:03:11.000000Z +2009-08-25T20:44:41.000000Z 6b8a364fdc2ecd49b28cddb2088065c3 2008-11-04T17:41:26.259250Z 133 @@ -57,7 +57,7 @@ file -2009-03-30T18:03:11.000000Z +2009-08-25T20:44:41.000000Z da36acc78b83d5047481df0cca63d969 2008-11-04T17:41:26.259250Z 133 @@ -73,7 +73,7 @@ file -2009-03-30T18:03:11.000000Z +2009-08-25T20:44:41.000000Z 2403f191bdc1959c4cfd186b5030b9b1 2008-11-04T17:41:26.259250Z 133 @@ -89,7 +89,7 @@ file -2009-03-30T18:03:11.000000Z +2009-08-25T20:44:41.000000Z 1cd822495b7d97b8e089c93bac1ad71a 2008-11-04T17:41:26.259250Z 133 @@ -102,7 +102,7 @@ file -2009-03-30T18:03:11.000000Z +2009-08-25T20:44:41.000000Z 9668b8143e896bd7bdf1018254b07e0b 2008-11-04T17:41:26.259250Z 133 @@ -114,7 +114,7 @@ file -2009-03-30T18:03:11.000000Z +2009-08-25T20:44:41.000000Z cf46b0709ff7dd5591725a4da59ac984 2008-11-04T17:41:26.259250Z 133 diff --git a/ssmg/sangoma_mgd.trunk/unit/core/.svn/entries b/ssmg/sangoma_mgd.trunk/unit/core/.svn/entries index 4d341f4..7db3c66 100644 --- a/ssmg/sangoma_mgd.trunk/unit/core/.svn/entries +++ b/ssmg/sangoma_mgd.trunk/unit/core/.svn/entries @@ -1,7 +1,7 @@ 8 dir -193 +276 https://www.sangomapbx.com/svn/sangoma_mgd/trunk/unit/core https://www.sangomapbx.com/svn/sangoma_mgd @@ -32,7 +32,7 @@ file -2009-03-30T18:03:11.000000Z +2009-08-25T20:44:41.000000Z b44cdaa52d96a86ef2bade78a42dd98c 2008-11-04T17:41:26.259250Z 133 @@ -44,7 +44,7 @@ file -2009-07-17T17:04:47.000000Z +2009-08-25T20:44:41.000000Z 9050d35634b210c79b1488ee04388739 2009-07-16T21:35:39.883599Z 186 diff --git a/ssmg/sangoma_mgd.trunk/unit/tests/.svn/entries b/ssmg/sangoma_mgd.trunk/unit/tests/.svn/entries index 0fad427..48a8adc 100644 --- a/ssmg/sangoma_mgd.trunk/unit/tests/.svn/entries +++ b/ssmg/sangoma_mgd.trunk/unit/tests/.svn/entries @@ -1,7 +1,7 @@ 8 dir -193 +276 https://www.sangomapbx.com/svn/sangoma_mgd/trunk/unit/tests https://www.sangomapbx.com/svn/sangoma_mgd diff --git a/ssmg/sangoma_mgd.trunk/unit/tests/1_loop/.svn/entries b/ssmg/sangoma_mgd.trunk/unit/tests/1_loop/.svn/entries index c7d6b06..97ee66d 100644 --- a/ssmg/sangoma_mgd.trunk/unit/tests/1_loop/.svn/entries +++ b/ssmg/sangoma_mgd.trunk/unit/tests/1_loop/.svn/entries @@ -1,7 +1,7 @@ 8 dir -193 +276 https://www.sangomapbx.com/svn/sangoma_mgd/trunk/unit/tests/1_loop https://www.sangomapbx.com/svn/sangoma_mgd @@ -35,7 +35,7 @@ file -2009-03-30T18:03:11.000000Z +2009-08-25T20:44:41.000000Z 368ac47f0d475e8193a8b5fe67f77a0f 2008-11-04T17:41:26.259250Z 133 @@ -47,7 +47,7 @@ file -2009-03-30T18:03:11.000000Z +2009-08-25T20:44:41.000000Z 1c6aa9d5f5180d14dd68e53a60f471a4 2008-11-04T17:41:26.259250Z 133 @@ -60,7 +60,7 @@ file -2009-03-30T18:03:11.000000Z +2009-08-25T20:44:41.000000Z de9a64e489c567028b6b304bb2fd692b 2008-11-04T17:41:26.259250Z 133 diff --git a/ssmg/sangoma_mgd.trunk/unit/tests/1_loop/smg_unit b/ssmg/sangoma_mgd.trunk/unit/tests/1_loop/smg_unit deleted file mode 100755 index 77ffa94871a8cf70bb1e1f12339678a30e213d58..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 109121 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 Tj^@HP6l26kwmswV^V0tT;qaog diff --git a/ssmg/sangoma_mgd.trunk/unit/tests/1_loop/tmp/.svn/entries b/ssmg/sangoma_mgd.trunk/unit/tests/1_loop/tmp/.svn/entries index fcd0c43..75588a0 100644 --- a/ssmg/sangoma_mgd.trunk/unit/tests/1_loop/tmp/.svn/entries +++ b/ssmg/sangoma_mgd.trunk/unit/tests/1_loop/tmp/.svn/entries @@ -1,7 +1,7 @@ 8 dir -193 +276 https://www.sangomapbx.com/svn/sangoma_mgd/trunk/unit/tests/1_loop/tmp https://www.sangomapbx.com/svn/sangoma_mgd diff --git a/ssmg/sangoma_mgd.trunk/unit/tests/2_loop_call/.svn/entries b/ssmg/sangoma_mgd.trunk/unit/tests/2_loop_call/.svn/entries index 8cca5fa..2a35f89 100644 --- a/ssmg/sangoma_mgd.trunk/unit/tests/2_loop_call/.svn/entries +++ b/ssmg/sangoma_mgd.trunk/unit/tests/2_loop_call/.svn/entries @@ -1,7 +1,7 @@ 8 dir -193 +276 https://www.sangomapbx.com/svn/sangoma_mgd/trunk/unit/tests/2_loop_call https://www.sangomapbx.com/svn/sangoma_mgd @@ -35,7 +35,7 @@ file -2009-03-30T18:03:11.000000Z +2009-08-25T20:44:41.000000Z 422e76641ebade9abefc76756e986cef 2008-11-04T17:41:26.259250Z 133 @@ -47,7 +47,7 @@ file -2009-03-30T18:03:11.000000Z +2009-08-25T20:44:41.000000Z 1c6aa9d5f5180d14dd68e53a60f471a4 2008-11-04T17:41:26.259250Z 133 @@ -60,7 +60,7 @@ file -2009-03-30T18:03:11.000000Z +2009-08-25T20:44:41.000000Z de9a64e489c567028b6b304bb2fd692b 2008-11-04T17:41:26.259250Z 133 diff --git a/ssmg/sangoma_mgd.trunk/unit/tests/2_loop_call/smg_unit b/ssmg/sangoma_mgd.trunk/unit/tests/2_loop_call/smg_unit deleted file mode 100755 index 735feba3dbd5de8ba51e7ace8c9e5bd03371d6c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 109343 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�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 c8{W#~&XA0iZsp=;+=>*) diff diff --git a/util/wan_aftup/A101dm_0040_V36.BIN b/util/wan_aftup/A101dm_0040_V36.BIN deleted file mode 100644 index b9d6f68b204168a714c1cdd89bd35cdf78271813..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 212392 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 z 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

    Qg(wmd#IvAg;v@yvKK)BPdLW} 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

    |51)&9A#(y`e*UUsILc<0ZYDqvi|_* zUreK!D%LZ1h7M!>P}ybM^8`Ko6D@%zRpGB`-HkQ)CWmPqtKz%@atie{65}xpKd7Yu z7!@4m)p=@M;(TMT+%=%ceR3ciuSikxCpW0ExecvfMg zQ24O>0zF%z+e=q`2S=N?J>FH_Chy3fO5dq|&#QUGN+n;cY%cs+mWm^DwND<*awihr zP{@_zWFzJW!GT6szmP5Bf#w4gKK&i$@aVDpknrIty>V7}U#j}_`P*;9<-WLicK6do zzu;B#&n^-2B=3tU0P^@K;QX?@^&n`eSv!srU+ zX@mV<7k(U+ctpD&&u2pp&aR@*O0~52F5(h@j2XBE7mCBBrd zH)M2*{`1avev!)sFa8)0jvm)kkC&-OK*R8y>yk z(RqArX=~TNEqv@_d*Kr~$u#Qi%;@~T9N+o_n%TK`ejaX+=wCYeS6+NE_J6$n_Tuq_ z$KhQ};AFsmpx-Ax@0ilQ_{9zPUUwazDcfq)-#WkZi+C!ui@l5NXM7wAa>fRH&^j&C zsh!JoerxXPV0@2>pO5bX)4#^|HKw#B+{DlF;X~x_xq!ax+w4>?Rz1a&CiW$oGl@Y` zFPF!8$MpS<=6UeMH_6hB&k}Xv+3KhH*=qP1=TbV$jD3=w>P_fTJQ-Kxnr#}>F(>;X z+xjCL1f}_Dc?~(Ov<7zgV+n29@%0})y955x9doI#km(G3meE8S0*$)^hV=V42B$v8 zI4cel-zNN<-)bbci-(8WQJZPkT(O>ZesSl4cW~B_HviL4-+5=}H6I#yMqE1n3~n1l z5i5zIe-j{)gk=J1FSC{UA$OoO6ul)sK*x zpVy@IJMdsK^FjC)o8{w|2;;F&=Td9kjcHu8*M!LryG|sR*}~+4lnGo%HPBNIjq8j? z0yna_1TEvm4;NfnalZoJCUE>=XUx_6Q7&Z)Pv1c3|g4_A&Y&X7&KU+ppQZ> z?r$T0+cOeDd5%AZp@U3Pzmt4}W_%HD6peFse10>MgV?aH0T8nm+&2V{20mq4jEf)B z+kj>o_|uDHiVyAcaMj)PFy7OsarM2owP-n(E8vYm`JOY<{K)~B0Y1Y?CWxK^v$Kb9 zGLo7(2h@-jo}xn$a%a$X(767T@6E`_^D?pA@nf9*Ucp7jEJWx+if}WQjj);bm&0oakLKMCn{J9E&G`A#~%sjnxMubC>%t3fI`ju#5ew;s76OEq~p`|VG3X~TQ zzb>O!bp0>?Cs(MdeA;J#6plu+qY*8B>5Tb{u2c&&uQJgwTA)|(U5Wpy7f!0L%dm+u z5bmHLGe>VmaV=aBqP32Yi}?Eh3No@~Z1l<)tWudNfj4KN>1ckvU&1NLt;U`X6hYvC z3jO4YX^y}e5*QhHiW+tUtLvHxz|>}Npg6XT*#6g<4^8782{ss7Vw)i04+W)n2rsjG#xpqWrrb z#@*K9FVrl2koSFgVcQgHc;c%fch|Mh{{QXIUGy4?MB9Ab1r5qQezt~9d1e9%1Eit? zUJ<2_Rgr)kh0G4%RQ9XV(HT_h6v-SwiPf zTaAr%K7C{ctTEXZbuIc#SLvzE^$!w%wX=IIXW=+EV$o8#hSeB zWjNevXi_RX(hR2qpR3H!5V`jUx<8VbX5EBhK@X;~M&&MVFnKm<0cNU?Mj6b>HuZAu zF9#)e0psJS>1mAlk;w=D9lu*J=sO)vzvqyL2IHLUfg1g|2K& zT3nxZ7~}Imo8UYQ5o1VkoQlI?g8hQt2&sDaH#b1;DGN3D+Bw&eQyNHJfMydNI+~Ve zBHVJcA3XynNEFi--X7s({Juxc(GUv?^nGdzC*CXUc;F!%TmDJVy?}nKUUo|9YcTtx zSPU;>{1c~3A4Of69L;0CzkGC<{uDJHF3-xl4BtqOs+jM=4EmHP`rlKmkFyTnm2L~6 zT@1J@GR3cJCoQ&aSpd;UP6wty4I@F82wxB@eKMYh8k#Hhn>FCC?4{%A+7@a+d`3=vbR7mRq zC6(75DuuW6DYUkXYXr)M<*7jSs{m1n7Qwk!K@)_+wE-23fy}?FNoG~}gIf~KM6E-v z8t;csX<9RV7{5|6gH|*%@CTvkHP;J_n(MH=guSVb^q%VQ|v z(Lms$`f)Bz=mKa31#mIyN6gfV6t6y< znmt{zet^U?3DtT<_y~(4`fF3IY2}!{mF#q|zxqvApyu&gW!qD&Qr;Q4x2_K@Gtiho0 zRATxBy)wWuAq9Mk1^6A&YWNc6AQZ<-Wb!B|LVU6&LZ$PrAuPy~{o zBmNuk(nykK1eI3@xt!hKI?_57=0TalCT)6)F9NN=%>pPBHUv6}$0ZQP1PO9L(@ACv z(wvqFtBOlHAQ@L)`zgXD-W}+)m|UrjK&GjF*$8YVcM_uk>%gFvxI*HzVm~j4D-Ec~ z0C(&%NN%`k9%L|JGjjsKWkQ`WhP=+2ImGubs{)rxVYwA4Wi2e3!34l94P2InnB?V{ zDT66HO3Ab&=&bE>2}4*8WE_o=iW70j>htTA8X#?vQcRTC9wl;9u9ShZ+k$MMcGRhX zXGJuajO*%%#fl=U5>h!dnXo1Dm(r`2MrK+rtyeeemlj{DC*?~s+TzrnZ8KLATXpXG zlei;Vbz51oJtZx-gcE(zbo)}v)Ra#oByF(w!I@x6Pvl+URXD)+uKwbZ+0iG(he2=9B%D+KD6tMjk1>Ly(p z>J2$V!-c|t5Vv=lq82?(=KCLL78%e$ljL7=d1@;AUJ?!UDwdPp4S^nUO19|9JQj=v z>XGud z)Y*{K4e=9-%7iP$^(1XpHpEkULS7Tq6OJg+HN`1?RRJg?6NVJ0{GHOR^pyW!6Lu42 z80#d?IHf0~j;I|Umv4oJMYuKVUsamNzGRIW`n%3)KxD!b4*}un=92I1urMe z-v)P`S1O)b!);=*1}7Wpg@5mPgn2vz9MW7D7lDirN`-UTFklXTnPvhtZL-s3>~KTM zn|p{Q6>&JR1ScGPkn9Q(kW|3~X@r|JQS=kjw8zM1Iw%lu9)D4;XEr0-FRcePkvB{P1{4qe)!? zsTf!q|GiJZO0);3JvpK3RdY>ddV}7a?j+Ypr(b$Kh>O|KTEJSsTEJSsTEJSs zTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSs zTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSs zTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSs zTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSs zTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSs zTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTEJSsTHrTe zfnz`2Kf3Y3!ztSQz*4k1BT4<*^Vc#-s`$IrZ&|*-9Bqnq6HPP`Rwh=>?;#pmS!9{A zmVD%|T7Fql``8J{@1(!(WRvza{Z>$;--=egkBxL4B}uI&5cyH(d{e%2Hktf2%6qGn zUn}cGMz8gn`=Sj?4zLy%)cb~`K& z>6|ih94E#p=lhNaAW^9uM1mBQAnHf;d%9^r5Oq6a2s?mLw=vIiIaQEZkF=pHA`_L~ z49A$tgWC%QqqU$P=F`lm$el{})MlRI_;s+xyV*KVdhq3;$raF?eg@ZK!@=8>5SguRTV_f1Q zLvhb>_YG04L{))ycn%E-=#5ICm(j1C_<5qke}9uBf!)jZbSvh03xh`!J$gCgjLt2F zB}^SfbIJ!Mu>iV6os13qj0XZ>B(V)_7my;(4~g=sdw_MyFNQ^0Bqip+Dq%b1M^LXq zBBQgYDtm3dj&$Ufd5+PMjy=hBB@j7k-NH>C0lNzLh(H>JBeX{utc3b`Ca!N!FD)Pk zd8}7SBEKC-+^2~hzS<7mDWk!HKPYo~hSf^ImlQZmA7FyRERr*g-t8GV`nokmu2kJ|1@-{r4R6nxaUXam;-~ zlMiv|Q`szprY7XO%-jYRBzQ%TBaD;nH0-NCBy}^5kkXD)T*IOusLO5A7@O!=D9j*} zxnUAPau--lT2d_!^K##%zV3<5E-Q33~4?bS@nE}Y8^s&rBs zJWCL9+n_%%1@xL)f~761tbdbE#J#Jq*1CPexST$J*Y?z3{l=0>eoIewCD*Y<)qYOQa&-DrPgD;o-;Ex zy=8zj1WS@i(u}Hr*I2*}q=L2%mO|h4`jvZsUQDp^o}BSKj2?-&D?Ly2d#IPB>w+Rs z6Iwyjh(S9|v*j^o%9)l_ne{c;JhVrY@ArI~!Q4kp;9_uMWI`XaferNuo*vgTDZVs$ zIqvZ}n0c1IRIL3)1y|--nZHq-Eeqs5_GucCr#@SxjluYLKQni=_d@peVxOc(KKb0K zg?y2MFkj48ywImzbG~;P*YR9$Sf_I#vv~VRp4(RG^`Gv0y2g{I@O%hDKs~$~piacU zL}3lR>Z4w^XsEtoeI9^p+$bdEA7T?fO(KxE(2rTW`oIkfHs7;m2gq; zDq8|ADi4Hy#rJ45@J2lsO#}R1fWWVGVXSx?(LM;mWDb2a0OkopzLfL{W5|%gz9A$7 zh!3k!uu=+!$sZlAxf*;B%;siDP@HHCT}WbZ&4>!ICE<}3aN3M5mo@?WoI8bk9m8e# zg&BJqhA`tEJqa?B3Y(|N~bG%pI-EX>l($J1*E&Y}Yc3N4mU7c9wS{Yj_zo&7itSn9_zdV%M=f9`= z+lXu;tx|u~$tFz#e!CLtx2w7Dy|u2RBt-$js- zT&Q#V+KBY9$QBS_FZ1!Sn;eD6o*j;w2Hr77*iKNlQZzM=+lD>e0@O9Z`G_(+-Fpmn zC{8=M38W#16nv8q?Tk%~bypElw5J=ioI;u$FuXVD9#cdYL+?G#IP0Gqo0vu&T!qtV6fhrz*7kuTwDN2%G@BJ5|ied&29x- znlfLNbN~fd>mJ;ciq+LVf$DK<>u6#XyhQ&Q#37cqn4QX3uyvSxv*xQKMEAcLZd!Ov zXfZ~Fjkop|P--9hAF9~v^R8)P6Z<22S(xNK_!5`~Vk0xee0koh4<$Z8!=k%m&Y5ml z1?4o##}vAmutkLLQnK#>8wZn*yecyRD#o{1q91bo(Xww5^}w)eLS2C6|Ad)3kbdg# zhIBvC6aN>(5#`HY?NEH)_#XS%_xS4HZ!v8vU*V9_7!r#RSVbG;-}b?esKmmS5^I`U z0Gp&}9i`x%6UVBFn#v@-#Wa6{cg6seohs!4OAH}r!!`M z4*!SVu=ul06p>y(=WSZ@Egr!=v@+@%X`KR%4CP##Be2d1k=N_lb^9rbAYKt0tPM!$ zl-W^;U^h=JWa+Mi=PyW|!Bt|e_~vFlqu{WpLZEgru%RQlOC5DzJudaZ1ylqW6iF|~ zti){&UA79`qce2)&il_PyLMRdsBN8F}MT$C|7>_3ImIhx%F2LYM3 zL; zmkzDt{D`+N2&D04GRLZ*1&gYBb!ec)J_l-}?`yu!ux;CeJ_=SBzQ=x{*XOGK0`T{p zVT$0hW_i3mxKztQSGX%?&3xwN4;8Vs6NoqYNcb50s4MijoN1Hcm`I{kjD40O+mAs#5Un|<@y|tQ&WFkXn2p-1h$0|GoERSTlE&MMPC;} zp{mfYjR2{XgK^<#l8~)k?UpC7>??8$dUDFu{k@gT%EFnVL6CYC>=U{LrgaJ)&l{(iJh( zB_5-)*wr;%(B6a|7}w0?# zu+ZKSet@9$8p|LHT>mOX!aU+EL@F&5{Z_fPY0^C(~OysMw2tMgPC zrP_AN7mIcBcHdUSD5K);S_R{b{8^0E5?WNE9R2~^qA`N-PT@vhMUUrZp;vA$oU3v6 zEC&XHAT$o=(2;DbVu;@`SR~P|_1*_6VJ~%!^zA>=wH@uMn6`zk7pj0B;7cZkM2YSU z@%xA0hgr}Sp1Sb?xSYcCx~LnjK*V%cKzeR^bC=%>hgpvZX=b#`r!I_x(?>RoAPhHS zj$uuZITt{$`A956)hAHrWk{tkY;?*c0rBzP?692x8AP;>{pU60#V0 zOu2FBXf_r8P=gt2h?gZz_bUZDAcztB!U4D}kq1kmi17I5B~S)wZ~}u^NeQ|#7@V&1 z`&BGbcr&gODv7&eE=fW@gz=AULG&>X)@}SjHb;}tM9E=<5(XO* zVrZGel(>a4>R8Tp_14N(Mq|U^GuU6LQ+f_C+sh3qkKOCdN+*C@)EBGME^~*t`H! zA4f<_P{9LGpkqjx(rG6#-Q@tl7%oE-N5HOCy@VBFD*(LGl;Rv2XCsZDFkC5(A~B5y zF}pcGKyFgCG?GglUHNgY@t8w3$(B+FP(C%q05-SPqGck$R2!fM1wP{M5_iKp-4w@0s0|u4I{izrNq=k#^3UGiT16ot>GTIXkzx?nGE(uLUuBE4gGdO%bhKvM58xne|@)oY01I#D4)>{;QbK7aH=d{~~n~ zkR-2BMpx>;N+tdOVn=SHLj}9K=-X&VIk}7$G=$82OusKoQ+i^3)mWcT9_x)UmpfGs zm6OQNG)&1?=c}fto20Xm-+$x~$zSjX;d4A*4yno^O&H+ZD;%BT-7sez!6kFr!XL#$sWa5pBO`JWb6kgnV25afWFFUd*h* zGwq`HXlJZyS~)??H8@ikOy#RMG5y~0!744YMpFvxo!2K$5AoRPYYiP@q5u@93xiJ< z?7V(3%6HJ%t?lgwzgv4_y_v#E%JI0l4tf~!Lx}04AwLz;B|D-MzTubb(X`2z@C+_l zrc$f)#QLUTLE)tt^b&?YSh96kPRQ3}M|!4Zt0qce`N>GGCbBEGFBzpZ8dA!MVL(Uci%$z0vyXA9s2F1ZFw;N`M zpyS94I_AGUjw?IHGnAb`3T_p)fVgA~0=Np8^EMDC$2b;^>62j{3r0AWo52oAi}W;p zOut*KC{(YeX*rRw49mhbVtFN`;DVJ<2&+L=6DP*IBNi%EVf_{seliT%Sbk`Rs?g{e z%#bY2;2Ik8TbPfcic$JB8tJ(xy%41v^fu*Gi*ekG7zM_P$Y74Y0~k9XBO^Iu2jT^$ zVQ-uspO@hyDW+L$5+HvvkTVj3h@Q!h#o~0P`P|AJ0R$#|=Rm<0&NqoOQ+33-ng4cR z(jgPeF=l--$`n*#V_&2%=10kIi+C`PK}V4oE6{N|4+KmzXcLBb`g|u!cpr%MzZ;lM zPFH1&?PWY0ze@C^(fo{*po->trl_7{wu~a>m=V)6X0tpnPlk!=!fZIJ#|<3^k!^iy2LHE@hz0^>ESPtwlAs3w}~#bdnK_1SLk=j4;?QqI2KB)Xk^J`P>vZcH(Q6|o`UE#?%Wb($^9 zw^7DzMyVN4PP5lM&t-+=Ei^(ngIz70Gar#$N*raJXW@5-uxU=og|tHR3}HGJC*e=! ze^)UHDZ5%SDuwy8Lfiu)JT3368C1lL@vK58W6o3`)3alH@>FJ~pG^NWet1fl#maDM zujAB6+aw+H-#3;~&KSRMMgL!CoH7t~g2Li@eOKj%eaMKaa2UesAj~-vzbk!JLYm`) zRg5TF*>-#n9wu!QbnMi(Dm4j?b5f-pu_$h6Lslx!H>_UW<#xGDc6E6Tj3ZUV@lE%P zjrBKFe2!QC_~lCf^BL=nHxC^?ObyTaqwT-`^~2wItM2U9R+GMe|92Qa-|&1xd!@fp zdA+_;sjt+xAAg?u>+9PSn=6Og`!nr}7PZ#5_dg;Ye0W{|-ei6MrhD$$#QBaVx~2^q zu^sOYy9H0;`b7TF>3LY~%!4IjUMQ_?mvkEjLAlr*CQiGo5Jxhc&iNptn_Ma(57T{g zZ!6Kg7UTF}eMZjY*@b~6E-%7dj^S}yco;q4_tCD_Nn66A-+QQBMUvYrf*J7B$8LV; z*kd<8KI^gk=F+6NG*cCvMOoHm130&RGcio!^rY?N_cPA|1Z`jKqX zfVrIb;9RC$E-xxGwu95&GXzxDseJ6KGuhGfOdsjzM%rGXFpQ}fKm2f9)@#WDbEoU_ z2lCu*na_B0>t?ip5c=EqCYe{?e#RM)nBe%4%3em5iP6fo%IoA*G|j6VCu<4qrT$fC zpMB&=z0tFu9;Re%vbHjDm=a48bGo`-e%WN43^v`v(f0a*OnW`+4f*=RiTl0xdi%STpsFT$Zz9z!VcfgO!8cqKqXf4AXl z=b+KAtL{oH{_a7?3nzg~Q4QRHG#z>hCDIQPPX}`~(IE;<`HYjGr=W(GP=bC;8Vw)t zqCOO+1*Fi_kc4vu*rEN2iJE_$3tob}GR-BFoY>Qfi z<%Bm#2fxPg1VKOBi{`TAP(mCctw32zv|05fXlU%dxnTUyf=HgIfs1uCR#ALqG^aD> z`j2VmMIe}O*@QD9+m4tRx;g47!&XFEYml5OO+9fDw#01+J3}mGd$u0Z%BCzj11kbZ z9OzUmouF>TG0hdidKHsGk+4mvq$Ow=H=Ix$dH*1(20gS2B*(cBhB~WARsnXT$C)IJ z>l?ykCO$`IgQLENO4D zde9pNdwLx#Ph6V#G%^V35Hx(=j#4Tsh&q)WX{^KkQ4L+ryxU_W0ksG1M>Pe7VoJ6|_x)t0NXddGQ zRGC&aR8TeJ7a@Nkg-uAno}Bj!^s7K|DS(0r-$wv51F{uzPF_`Wj;5V7rxJ8df}|7) z4c~_J1=>E>0ceRV5!k>14z~$L4;dymCP#9VFQc}Qpb+UnW^V)$3rDFWDJugi59hlr znfx>AZmJP7`MdH`YT$Icgn(<_D`iNn4Nw&|7=h)*4tO*|7nAKZL2XY%6S_B&RdbLa z%keoc4u#`=gPxIdvYl|*-^VF&-+OBgMC!23&>eIvYlGyKJBmk7E#OX$s(zj&|XY_1*uVwUIm?XM(WYpj`sGUPP8f| z9kPz{Ey=NJ3*;QKmFN5r!(S40a@1o!j`sx$-YQC#Pt402`Fte$0$*$T+Y*NqJZliq z-=U9GK@A%Lk;U9iv_Bu07qiML%gRLmBEY~{K;9Na3KlyrcGf6od=PzZY+q_@6G_Lj z9Skpe{?P`s+z#j|S?U(l$=B|bH3g{tXk(5xcF<1CQ&d1jr+xSqm(d+oul3P9iEeK> zIw=ZV^mDPumX;9dgNK2As4Oxm> zk&omu?wBZ4;tw|$BwirBd^VqL&{>K5BU?UXE%Wp+qK1|hVvsdwW}@#QFe~k5j42+K zYHcdo$m?y%OQKr(;Ko|q3}x}kmJQwpqNRUSx)5ckVmM}|ojP<28xnOcZU!>n=aqcq zK0njq=RMy`IcyMf@-WUXDJpZl&;q(Typ(BB@&dXty&nAqP}&Non@&mFWv<>**<1Zhaj2)Yrf9WnL;Gfzqs}*{Q{#P)lwt zrAxKYmj{y%Kq*91VRVf%B=EUB4hMFtZL%LX(uXkYWj7U5)UZBhf%5fdv zr8>uTYkX1El+i?Upi@JKzoX8fV&++&n~x)mXHY?QqvL!9A}R-$r7U@H@5-WsYCF2m zw$*&U)^EDV>#TF6rhn2cTio15>qm>O(^(|ygjIFk@_Z*eoF7L8dqwg*f6gtPXv4Pb zbmel?BV~T&iF=*%yHD+eF?=H0hXFVzpKBW}+CYTNbRp!-66z_Qnu?x07WWgMMNElk zRWg!~aYQmg1saI*;fz9QlRr%IG+Mnxb%i7_E=derNB||fC;#FYjkT3`*tl2Cb$wXMB)aE+gapemo(^)7G~O;BhJS(4|uaeWk3EZ zhXE54RN({u5`*jO>sN2CA86lPzxn10ZLU8sk5+B2X}|2iy#5sfr?7{yzr7yL!YMyW zJ2Mcp*SEug2-r3-fYA75*gwienkTz_x7e4bXV z;;cu0zt;FRzfqajMTys6?W)Xe*uTGW{ERa;_g;3{>eVd6uwupL-Y^#0G3!a5l|iS z)LsFroem38ELQNZ@@{?7^cocLA<^E@P7Mq(t5;9? z9xIif{pqhh`LjQbZK#%wdG8e`$VRKcyDbLuZN3e(#v+}4h$Dnc2Opx*3J(k28S5W) z*!S1_c^14R{UbH`vGBSMq#{eV-%D=5p@i2>6)1 z8LT~H2gxYi(+QSS&K&Rq>6&F5^8KemgVEL&&l_naugvgN=^&q(xSoRLjM+Y5QiBK# z9AsK(2cb!4#9#!3=K5fMD)Ken6=G|rmWJ;T>WHD+NW!;h^kF_M4LjN@1|KD-i!fcl z_ZZ5>zDtw?c}GNf-Y*IW*99;}$piviwS<*i2`1P{(CQ!QDL78(B|1oRg9No6^m;YK zZbNAG;<9=HHw$>ofDcWsi6LR&DJV2^3@2iM4qB;|g4MyrHD1DQ#Re?agOYkBRy~lG z!FDNEdED_k2?Fr<< zDjrldI38309OM$h?&f{LIHzIh!Dk7dSwbeYrQy1iWNVUAqHPSb+fC&)6dHASX3r8J z6DSuOR#+jxvMtmj*9o3C4B6Q-^jghISIs@9&QFKuXEL4;6i(nU1F8|EIK22ExdofZ zE;Gfi%h4TFAao&B*Xyyr;B&MD3nTUa6zaC){O)P z3@)kAdUI+RZ81^Kh&ki%eWo6($6homMZtW|Ou3J5we=u+Scj&-LcVbydi82f(<>2- z^Ma#=6r%rW@KJs|rQzl2(a@R6Au7$3?0y@KnXIC6T1mI2Jn9i#0pq8SkWg!{HZYVQZWQ&&dj)g+17W#+a9mIycFBJ;zp-h7K&x zg~G^<3zJZysn_(1lqrYhl+(mlpbtmbVf=u*T@Lhr8WdQ!#{Dh*b?Dq7$g3GA+Af;N zIf^GRw%?3&l?n>(5LrrX*y#OYJH{M%%3vCibhBD^d&_%nLC1N) zv!3EkQ7$4NaXVkSXgmSV>f~~FwZLrsAe%~5Lmh0s2Cm}@Oj5_tcDO@SA6d`biTd)A zsk>6N+_65bkxW6_t}TB8`N~ar!cAxr3eKz#La@o0r7cgaLYXpv_CkJXbXk6g+dDc0OlMC@m5?E!5qWW5kF)^p~m&cKh>J|Z*aj(?m9~Q@Yy+OC;i>U zcyqgxoYAyr`x)xEQOZtQkp6_fz;kft$t}!bpg+f>jrq<^t&|0aL=vb^hLQa*NQvd(7m+m?3JwYVN% zX7o*11RpcZs0)W0?c-kL;!o5;1;FQECg7IHpyJLSwI%NKA$U^ZMuYZdtB50kEkBDk z2y1OECV z8Mb-Dk92|co*^m|S;N|}rZh~d*5*W`PH55vjqZZS8u3vC$P9?m71Lnq{3KU$O!J{A zJ_X5Q+T=kB4>r>Q8W{9UJ~K_4j7mhangl&*1*_}8?&czfDW8G&|dj145;+K{u&)VHZU;v$YHn?kt5MwdG3wM zK>zb}d4g(?%Re?yL%;nkwtj6(CM&PIwCt5i7wcv?<&@Cpc-dv)zA@(U2|gmeyI&nLLRX3lg4{3;WK5LkFX4i%I6$_psG$n4iu?})@Zd4$KxN?>YFipCNA-C z6Z!h4`5tEqf;N7{sUtkI!up3sN+43;KhDN7Q_QnIh7%darvB2n*k-%Fw+k9 zL9&lBE`J*4{gtypFva9{HUK~3z}y=Ehub?lZ@A&gDuf*~WhS_WsJl{SR-trx$Mg{&}y%_XrN)&;D$i z{#5^``Zw1g&nc&zQhCkndaP8AHHZ&9ynhZh1@HasZ{gvbw~h8!>gz`L|F%DYjgMzg zrE72fw#}8Ga@E;q*ViZ7+cBbI&m_Zh0zOc9A7|`)0^#{Zd!2IATS( zjR#WkGiAa7VP% zEOi@AZ4~Z5)S+HDZ)d84ovh~(oiqBdtR+kdm7uJV3=W!f9fbOFsBg%VLKX(p29Ag@ z9FhikQNG9rxvn+bPw9A*ILh~IgjEiD08eFspPh(qy*+D908Qiw*CN=Akeh1%~M! z_yBAn(m+~~)RZ;8Iwo-dBw>wdTM8BEC}~&(L1AG5nGES}3&m({LldVcR~p)hirwb#&dF8X%>*IQ0N4&brmyCfIu)ym^+c= z6T`G$CkmDXS7p`=-3EONCzcy2ns}p5LzDhj_^2Bo@WzByxJl?sCOmihHK1Fa8$Qn& z|Dqr@d@fY}gI274u*7KAYAw!A3iud>PQ9A!ffOYz!fF_MJSMKq;b{pqy?P^EXIb>n zD_eH-V2@gAPqFbf$jONp@XaG?dE^C^8s^pE8wEZ`(k}^8cVXc-@o@;TiBQ5L7+Aqg z*!Cfn98P<>In^zDx4>Dp%(~-#PQIG?a#N&%a65`krEKVJ~~`=S8L z&8myq=2+^fY_mjJYGs+Sy~tK0cQo^UQQW)wwxgW<{RNNx;DcCKS}vBwdmNc_Q0~+4 zuzIlZ|324~plf7`G}_qF2Pg$KfaTHYVF}4kTTy8F-!RI{zWHDJ1E0DjEw*3G)1RMa zpFMo0O9x*lz*qu6*(9jRn8yyWChX9I4H`@FGfNirtyx%Dh8nUjwPoLJLZ60Hpd4=!jL|F*Sw_JcB7z=utX_)`~Vc|BKy}ZnEW>=v!Y~sAgI6OexW(op=d- zP*C?#S_*@e&MGsz+72I?gNrEg$_idbEBa}X>LIVchIUdQQg)xK31?ky-ZcU;_&YQX z3}}g;Hj%y#54;XyU(+~=6m{-6bRN;if)=$sLF)D^3UwLe3-0&NW=o#xb?6UY6u4>k zSx-XG-#=>A{vRmr`+rA`Bi9pMMwc#iCItPIoZFKH2t1|q@1Y$1EYEh)6wUS45ysWU zOf`7MJS+;U>xB;Mc0m~}sBySNV)!5$dBFKj0(=3b?JC$%W0O@=LEY(R0}#dxO@ks^p)9pi>`eM`MPzA3UDx6%`K=;${aeE?YkRJ znA4xRNzh+D-C(VReC96-^c2RUJe})aM}p3aIQ}!}pHR#(LXi<U_-m3brPG=0^X|w=6F;FLL~=@_wnM^O8jusP*5vYE9j(SDj0@erEI=G(R=Z$#;yl z)p||-J|C6ZnOU*GyYPD-%>B{sy4uBS%Kl?dY^B9(*8Md(XL`=4Ra@7I?fQT4z0)#h zxnIAqHkEQMweJFF^uk+cftyUa^0B@}H2TD7I=M-u3o7-v?s{<-I7 zvNF|4Z9D3c^i^kHOUZfg!lv5w+S&T=E8!1c2p?c(bdmodUr^027s{6T3%AyJX{V#~ z5#ir>?M1GiX}vM;+Zk%e^`I0u{9Tx?ZzxfE$tg58)Cn^=-n?)V$DbmUs(%*9G~{Qs>e7OkGVgHNfBoo+|(x zTGQ!nppT%g(D`_;qpwVYUWUQImlqW?+=A9(kT0oCJwXAWUf_I9dfE7rJ|^n6pLYOs1O2sG?bGoF0}yq;)7D z`HTP-W+V#uTTB>$Q-SNufbb-zN~XiQ&%*n!L22;Okj>mk0b4jA<$nzY;R;(?o2tSs zD$H4eJj+FGT0;TPQw@*xM4;n{WC6e^Puph$IUDoc@U%54g;R1cDuI?9lXz0Yac{`N z2yB>!foOy!M>)V$KE=I}n*(!f=5aL2=KwPu?|%d>IXMO_B+Q(H<)(q0Mn(FCFkGaW zWD^+9j9@Ck*fK@VkSkZwy)@Sow5@BK675~{wmI#WCHjT9yg!lXUs0dzUv%ui=9=f` zb#?Wx=<4rZG|)Z(Ps@JzTV8wZxAd#hQW37{7+Sk;f}`(rg9;edI{x z$UuIeJYN6ovzcG_U(Q8ecG>X+W0q&kz;OoGU1x`}C7Zk2EA2w`3F?A7a+a-3Acm<5(DHI^Orl<;~`gWc@71`ywaz zUK-zLsHZwKe#V)5E#p0xjLp8wC`Z8U`rU!X+$4r-zhKO(#w=T6)$k!!gN}-u=6i%d zU)1p9^f=E<+U&us(s{zZ$2j8*Y;2jqJ1H4VPpO=`?JD#eSvjwNQTr+Vi;_QHB`VM3 zud+>XJFTDNQvDeR<{g-a{f-B|abTNzz2>>fJlZB-uVMfF=JV##il@7KRXo=pw;dp3z$jYAq4>wtk!L=a`ZL=N> z`i47|uBh8^ayT!@!v{AQ$%8U_7*zx7;rx-_9TARwsdt66#mlu>K^d+@sE#_x1UZ3? z+p3fSmBBiQjgU2L6tOv!c+x48jffS12ztl(Ww73HT8S%|I7BtAHDgaway*=h1iTZH z4%Hl`_PK;bR7MUAvX*`Xy^J|Ch(tM`0~Rt`Ri z)inid1xXHOg}K-o`Hu+&pM*rZg0dL1xU!-AHdl~pEkvdFf{AqkpZ9bK8FKXnaF0sZ-Q7p!9816-G6NKsOTTaw?1L{a#nk+9Q{!5UGljj3 z;|6gcSuBR%-3Z-J7slx&OMSnM&gegiyoa{*C9%L(NV*N}SOo>YG@of35%$3yg6gwhuDmWYp%u+<%CoTApOd|tZQ8}TOBT@}6 zh(ry_M8>NvykG38p0n%%xLD=QoxqJA8?iwx#ilrYS^`C;i=@FYzDI+rfJUkPHY*u!)T$OPf^dgu>2R$Jpyls9^&dCuBU)h7BBGQeN+hntc&2^ zLg3RZz?tBgM&Vqa4o*g*!cT`CO<8a)jjMnupwSI_kP6)xocY!@8z>>s(v-&wRD4pc znQOe@*o;MCk_Picxc!e|KzGZ?$QZ8Uv(Byu2T`F{IE`mti@extQgfqEh4CiWDxjaW z^)0;)XSzdB?QNgLopMjG#zs5d{sNo`*dIF#^M5Z8xgc0(3exaBHwAdDfo`!na2wQF zoH6$5abkH6f;`7G2QfIm6UMwtf$gJyr=!vJ0&FWTXcge22dD+5(NVp*s5H^6>k}wc zOs&_EEd+d-1o!ldxNd8t*||k%rDnlx$1*3u>N4nj(sQ3)i|g~ZrJCC&h(5c@vuZXH zeGSj8zOYg7=Fq>m{Tj%jS@U>P%BEI-_~-bVw)H};fP!}ZfI5<7>l9zxn;>=Zim)_? zmf8hh-!44rg+9CQBVmuC0`~jH2F2Ii&^a{2+%=flYv?-7(mXKf)dve_)nfcf*+p!P zb?BdwcL~>Lu8CH&9;ak7R|jgCDUm88f7Lamc z-A7TJcC1WBSxOBr#k@x{wU=oFZyEh*U8b=x$xn-Gf^oiU8If{L7 zXLi_5?Uv7Ob!nf%X*W_X<@BT%-QrN1&g~ri&fAoT3Tq~f(5tON&mZ- zCa<*ZTW$9<8$YI;4Lw(E^io@|Y`NNjR}-5lovXaXwP}C8vw*(wFu8uRcFTr^lv%Xp zg1TlnBT(w2>BVU`=YBQ))f-&r19(&V3V+L@BF2HyS}K1wvpb*hbBpPMG%2@m&%J1k z%i$+3JMKlO%M8(X((plkHJwSf#MltfgYX!#m#?Po6;f zERgfinep%yNLkosX$4xIC#&&xe{*SE<>rSN=El$DRrQAJkffhN=y&KbRr}xu| z=HmU!^@2X{Xs72r`^9_lk;u+PggtJ#y4*Ex=BrDwT6Jj=?z23Wd9*lBExxl@EZ(@- zYx7W<=*}*l387jx;G2>dP*?ggy{u@uh}hlY6?({Dvs;@M@MuS8?fJfQfplGdWFsD> z47Q0j5G}HCYo|;e=cQ?cr&Ihn0D#}&IA}ffuIJaGb-)bgcOiusoU|Lr9qlB4RDxg# zTSHZ3l3|k3(<98t596jy+U2lqN$}9deApI(jmI7)rW7N2nqU(xG*SagH70q0upG-w z*O`_$VkJKA9ARl9G(g4jU;~s15nxQ#P-M!JsGOM37Zw1%t6w?;#AnFod{0kcsOrwm%-nMF8i4&&J@Xsf)2C1^0R#+(P}c-gKI zS2zG#O&Z5VL5(z@2#7AwF2gfH4?=;bk{Xx~7~nHv&}@sGq75?=n=C3~#t3=d0VLV9 z62}aP83AAGsK%6%G8B`{Q8;6!abU57=Q;u-BpT49JYoi86h}BAs!5?4~2z75)nRTLS*zz_B+!yfX4uS*G=GKLGUT$H^Vc+?^yGf z>MGRU9^&vV>5z_jF%Bi*U5R{r4?nzZ+qQ4Fw|33jT*N-DO8=j+CH z`@l@gTz;$ib^12*5i`K?mn*Ns+qmio(1H3d9-#+`>MO79-~V#u-@31B-`x6(!3##+Sef#f`ZAnq6;`F0`u{o+HTNyl+BK**^y;~#_~otlz) zJH78J4f+{0%nkjGUWCEqv?zTNgKg}KPA|5Jb|Mo0~vnr zdBD7%J&-x&lz~&=>VLYw-v3PhVaGWSn+m4j)XK{*Pf;h2J+hbg4_Cp!{!yl|clg-e ze0%*{l?U26_iqyT#6bkB#LJJoRX0{yC2GZ7Z?3ljn-VtNQ-1B?3Equ4&wlvu*ce=u zhx@S)GS0&|8rG^=Cym?2=SOst!5WXg*ayfNVWN^^)*q8a}KVbQn(6s9ejH@VzlbMh2Lqaa#eUB0JMfRYnfFE;D z#MQo2PA#1%a#GGEY$0H!!H&2lqPq>YF)D}Yd>3F?OHU(Ag)gYbOmH-gLb%jN>_pJa zite|-q@>|Q-*HrSxQ{W%Q#OF6gPbmujrst_S~Y@INMi_=16P%Am{kE%r==U3c?^mJ zWOf3MFlhcZpAEGf2sJnO2|b-0Q_uW3LG6_2rX@d_`*cE#dHNu!q)7Y&J#GC6`+^c< zWqJzlNw;sOu|X>lB!VSmnp6NafWU+t$jR93k-)G8y@334e?lvRl|M?*ec0p(?O~3@ zlBWV#-m10G+toNW(uI74!BhWOlPKd|x>aL8;>D{uL4~v82Bb_Mxo=YVBoT;*Rtk%I z6`YN>t-%sXA+!+U{Y^!~h%iIJ2ML-t8YTieZ7IH{C8T>QE0M1G3yq0DE19$uVNEZQ z%^D$0WswTYP6oDd&YM)0RP4p%+Ow?eP*$?HyW7?y*fTXOI2B1?YV7MOLkO%GKq(=~ zwpE|h+!Oh{kdAq`70O94Sm>nISgVo#=dUd%)r_|CVsS)gG0snXjyI6$kyouc1*}&J zv)0D<9qaH8AdM^`@c{fL^CudXVjm-AJxWRFu>+I^40rD7r>!SC`;uFjqVg+6q5{Vb z(NGq~On{yVIc9|N9K&kNC5bvgE7Qp$sVi_QziKr5qi6z)9H_}nU;__i^>NMtY|+x~ zsHvS#7V1_Oh_3W@-f=1RM?sHs8_r8jc$Nfzr_kW=W#Zf?z%}f{(b;pB$^>TQUwcU= zHd-nrRm+!fjejDM4aL`#>0=<#0FU3H z3Hy?}i)uMk2QP$meAK3d{1K^-{o=G;Nx&VK|6#aCCo&F4Ljj&r9#~v&1U0yLV!FCOH(cLA)zjo z4W5N}!KDCKVN3^#0o4>jW9zl}$XfYYv-f6YzNN1A^SJ)w*c-N;)EJ2tJ&2R#b{r_K z_ZD3PI?Mf&+siHH^C&g^-Y)}I>N?@$!qM4%n)P(k2xNN|loVOOgWDKe^zKJ66AIHA z0XNK}q-zUBI*Rsl|1~)pzi*@NtMLf?E0^w0ShOSCVOEa(`mGgaca!57(cqP`UlR1J zS|;kDclEkHs=Xa%J)pNgcnv;woP>LbEPR|{bb6s7bw|(thGzuodiS;*c5?r{=V+Yl zNVn(y5;pDVAy>0)>q(%pH7Wua;LwK|UOdb7w_h8FP)Zu(} zvzfUNUNzQ8)=#3|W_VNjWq?{`QN;zUjLN`xi;i#UubK<$o`s^Ku_8 z>8MWD)D}oD6hdBp9noKYKUEW4BlO=1_&$;S=)rT`ghm0DM^xZFs?~OsNoa*c>>caN zIf{m`!_bmLd0JzM%;yB`>9cyB9FCL&H+!^Y+l0Be@Sq)zmtu zvdcwN8ea%m-zr;s&gmZRwg=@E)};#He3aQ%xqG2o%4DV8wg#n1JBt-G4dS_4mZZj~ zkp_hn8YX+5v<4S;YZBhzvJtkd4JX^^?e6_@)~8&yH|_Tl-PzZs&tz+Qglt`py5PRb z!WtJ82;%_aJ!@P~41K^Jv{ftaHa)26yP$Gio6;r6wVS9l4?e`Dun|rI+79Xp7cLEF zXqxv>?oRK^__Z71RdyS_`Lu6q-M$-2qwDrqT4R z*spkwtL~S5?uI{KQT{4^oMo$^KZiS{=w0+z9(9O}omrGewSBhu5M2cm{pOT>2p1Cu zHWweimG(WZslRVw>3rOOnrgr4U{m6ScyWBRpoliw1Yajyoqvt99VI7ktFM08{Wx8L zy^r!Sy4f$*jkd0PqTg3|be2bF9z`ZVk;(>R>gShu*qfiX;3&qo5{kZkO6UBKw`}-9 zq4SNAof%5EW$+%lbx5&H7b?Z8E zw7_>RTyTCz3*Fn`xE;7*bU5!6<*E171+!rj|Efz#9rsIIjBzUdQsY@|i`BD_SVu;Vl;!ut(@ocRt^p8qfo< z%M%T4%Y0v4Q@H797Zp8rE;`LiH(qFa9T=TE@fffJ_1f9#OP{^J)ak)PiR`?gb{4>0 z8W%x_t;WV%Q4zG@=`gPfF+&3fkDELNv$<_a&^?Z{6mt2{d7&9^;oj%GmW2fpazRQV zWU;LNg=eEau5$&H&+kxMl(Ot@R268OTG>>grPCn zM8kYxXQ+}u`7o-AF-sKn2!NylvXn+>phty|hiHvC1!=E=l!}*19ab6nd^2(CHS)vV zW=~|Z-5q)`wBoajPYJtXKf-+M$0E{ILus9@Sc)u+6YAnVY9#YM; zL@L{D`xsx1eI_u{TzlHYOkM?PP!Z_pO6iz!QtYT6q}eB=bMrD~Ge&Y@5chTAnR=FB z4K@-D!OlWB1Cto@m~FBFS3{cwrEqSPm_V6m97HtFXRdZ=x|2*$a@p5H?`jfx@HSf)u zH}Cm|E`J65DP3-Re?7KJg;3wVx%c?-%5N$o{LRNo<*QgDGG2X4ef=B1?V_%(|AuDq z_5h#a>*4nw`4>7k`_KNAbxgu$_)LyfUVpQKk3;USyu$oV_k8&VcqA)3AAZ^F-I|m| zr0aq=A31PfRr0s5eCS6vMbac%Cda*UP+Eu)05|E^3Sj`gm^H;F}_E< z@@P{7zVgT)FMV@uWkqGhMCY$Q`KwQU?^e87y|=z*TmSI{b`baWZ{9XACxact^?Q>8 zc<(y=y_ers9=@Qze#MF|7w=*pe*Fz@%UlBEfy~rz&rFqiVvKdgT+UZ{6Tb{~tn%`g zZ$KFXtB!0#gBR4EfH!jQTl{-9Z=%Nm;yf|_vr7Hg4dfkLflowIFxr z$QSVgA4>-2&Y|aCTZ^IV-GPqoQoxhz*4Rfkn6s@w;`(4}5y+qpugQRR|0eW3J}UB( z;48!no9E#UN&LHgd~FeX2Z;1fwv1uyYsB5b(aVTX-a4eCJpB()1O0*GJ2}cxcN$;h zgC?KmNiP8Mdr&IdK}U@qFhhAxe$Sve2YiDO#vLMrh^9Mppaqu;j-WY%hStta#1f8B zS;kZ{>GxsfixnXAnfZ30IZ;q+C<8YF>q9QR-0*jVE*FY_!1EgT9ubE7 zBqbu4{-_*tU}vN!k~h3}9Vqr!lp$`Hu!tu7N>MTi3XZL$Wqz;0#wtb8I6e6yo+>Fi zgk@>#TuKC3?m~{AG!1fw8g}$)?QMrWHHV-H4y3sXA-9*ehcSW@WvT-E!r*rTNn;6_ zAe%F`iq>AtLrUF)twEBW?xiJkG4sFlqlD5d8!!blj-adwwrXA0UrTRK9t3CHp_;%< z5M*?p4!E$l0~H$W1>6`d3vYXq)Cn!A(YA;*8Y&Pj95s1aA+m^QoTZkMmBImm{{Uiy znv?A4!o&mh8I{iY-Vrd-##}>@BSTT_zDQ`hd4vqjCMl~MMr8wNCYzm!HYBr_;y1sY z$yyI9-%@NIX46uPaK{lg9(R9g&^HxurFPpb{q zKlAgVs|$kkB{!d08WebdfzHv2l7F$W?l#0xC%(oi_TnC#R}3f~{lp3ceU>QSZ2)`ronTrKWso3ImIJ=7038{uiA5goN|( zGU*wS>gIxg|x;3g}0|K=f~gJ{G-Xzt#q-r(Rk1A0MATP9DE!z zL4_oKTVgew4jP^@0JrF+nrq%GAPB0i0m$jVY9`1rb%#%>aH7QFDmDwQH5$fUMafH@ z)6q*aj!oUDJBQjFjg+hhC8LSrLAd zD}^m

    9I_#iaG1T~+I7KBoLsu>r!{$cS6q@4uN91OT|-+{e+ZU%&7)jde~zGU~^ zJPa>EO&KC|P=3^fSLFULa-PnDzQ34I z=vq5kaWRWDB91oE-y%O9Er`-Me17>+ys*3$-`3DngWr}3lD;Wza<((QY(t@G13EN^ z*d`1N2RGcB5;P!1*Ax9PeXRr{JKeZASW9pWKIz(pye6)g>EJvniOOS*bwgNUpVj!e z)X-YEPOq>}Uxisi)2fDYa1H67^_QmS3MosJ9BziH!FDl}qjq)HkkGcW_{JoSZcMGMm6_V5j+JkB@%gLT@eS+ktx7GfG>( zS6eR8Eq?fT#7?C~GHI%PEPZ-Q%F5hoqq=a9EV{1x!e4)vtP*{z=Z4V~U%*lBr1`1p z$vLjXW=x_FZAh=jc)fmEu?a4@T6aY!WQ$sCSnjwN?rw21qn*24uimMo2m;>S)fMzoFdR;w^OR+(mx#@`V?!*h;Qd*W7FMoYm>BcX!viyN`P3(uTVA{6e%n z!b{P!I_oaX><~_RWKO3u>Uifn8UA3ht2Q{!yq2BoRqf(oayoXMi_bf@P{x~!-@QKH zUD&a!*^}<4FQS&ZIyj3j=ydQU$;<{k{}Qg_)BgSS_)x@flf zO?}FHMLWL$O`pF9DT{cMI|dy`3^r{9F`sXMNa{o!&Cnu=2uN_eouVhS{dS9~o> zX_L%N482QuW#k9T)g;hq^SBu)JTT+47tmYe%K(bw@S)jezK`dK41+-Uw`k2^uVJ{t zF4*9(D-MNP9cTljY_FBgVmGKPFo$(x0)qY_l{6XIYUuGpqB>MDG5I`9#+>Omu8Wwf zxlF(yPLj(c-OVBTJA9o2mV(2yjdGQgq$QiI?&QkWNYVH#=Q%L-QCTNhkTXD)tNKWR z#0l881}c49{p3B-2$O?dh~2&8g;eO>BldCsu)~iX*B_J}%9}DVkD&(GUT91W)cHNhyKR zT4_0+kM>|@kMT)2N|s?j2DD%Z;>rT>-jl%;7|XT~GQ}5S3cL}XssKjRU=QR7!zDKz zVCl6n#sPq4b0A46$sB+*VHkvR$Q<#ad{Yu}!pYJrmmjE9Uam-+YOJ~5>VEhs21NV6 z@2zaxn|yQPi#6<{VA!U@cd9wQ2*AHFbJ$&juR3nJr;C4$x(e7g7?PoXZv9Q=)%r?%|EEB(ixGuyHt4|KXOAB_ za$xTSd?}ST@$0?#W$nr~9Dq&tyoFlOXsb=Tx@!KQ}OU z32i&Jmb$hj9+|44I2K;KkmC6CquO6*zNr|!H*MnZJ5Kr*qi;8Ie&a7>pCJxIV}%m) zjs7bCVZ6VQeUALQGru4E9HHCDR@oE!A7ejcm7TFZ;JC~%FH}T+%Gl1?_di0e8Uofm z9rXQ;xnuX^_pSLDE|~tw5;|(oF=ih77>(aA^6f=_O7>O8$2VDS5?V2Bc^lBn!%h_A zSd`0{Jka2ERiiiMLol`@mW%V2s$}18|6?rAoi*-(?f5nL`ZJPb(W&*9_n+5Z5fyP> z`&$(uGWZsN`Eco^AF%=6#Ba|%Fz>bGYxsr$`xW;l+xHK&zuy0|KMjB3<$JgO>`%XU z>-TQ$?9{vTuGjEdGe4&aj#NxvVK2gLP&p>(Xz^&VeF?tb`1Zt~EABy3?{{J}dJdxbnUfKNSBSN_D%RgS!HNcBEtVR;`7hd?* zwxW9d1fL}jTr&YT2n?8Qg0ZGvtWHDNzRb1)4B=M7jP=fFfu-8WeKo@Tn6wITg3#u~GDjamY>dVLZ~mef&9b~Ua#r!y4I9YRw(e!p7D z5p@psKPqVthN;N7fbGhyyX<`4|2T5}9~=8|x5Q*jLrCL+KV!_+q4%AkG>gbuqp4Rs z3+#wtzM&Yn6SEEyP2bSYHpCzd+!>Z*VK)w>90mO&{dDptOMfVS+g}IvI=%uwXVJQwc>VcbsYc6l4Iw zunCcp7>B2jNY?aXU5T2gu>;(%)zDEG$k$b=}>mbQ#R!FOLI+TK4nTQ=_N_nT1ii-K#ZYZ{OG_CyWgqLm@a?D%CO7x+g z0;puMw}HCt@H0Gcyx^_`zT-udfob~3_Yj_e3*G#NA7@ohM|!Z88m~UH!v}`FN`2$t zr}tnyCay8eU_BvE3qHm5Rl-INM;7n{HWNE(djW#kEo3!A(z*pf2#Qgkb;n9titjik zdD2RCuWY=SD{Uu+H`>yGyJQjm2);vKkLD*x{Ri|&T%1>n_$?0$`yqvzQ^XD*Y^Gs# zsR@66@81#0pBI?acVLpd?m0wW@$fA@d@$>kB=b)O87p4^4lMjV8a z0_Vr4k=aY&Q6u&TGv8|p%4h%{mh0rF^i8Nu`B=(BdHR)W?g;Sy``8)yee5yNFBF&J zJ9ldA{sQvN1Qj#z{13}FJ9X6if}r`jxwIfi3P?_x#>v_u2q&vH8G7$EVUI&Lbx>=>~$QWzB8ouuWcX6#f& zy6}1~_Gkxsd9FZW5ZA@YIm;x`u8XrqetKZaV%F{t7cq7ma+WqbowWV`Y&y$%(W0li zH);odR(#?j&ZhaddkXOgC^D65 zUdTSkCusyHPXSg(Vk!39TFUmYIY>B*%bRd5@_p$M4}CVTKltCCMoq4~Mt>1DmtNcI z+%^thru13Y$)Glw=hMOWUkifPcl*w11$eHyOVpZ&-*DcqPrK$5!w#S-@$_NXn+ZzP^Ffe#K_5<#LX0xSB7#z# z(wKWIxSv4&<(QXRiScp%4{Pax2K4p+m$UbQZ>zZS#m~LElCQ+IeeIa65)^0TsGv|2 zt{p>^pu{5~z+?keASQzUN-ab^^4#cWm96IWu!+=G^&v?wJ|Q zPJZz(!Po;QIm&4uq-Nt~iIF}^gdz+(L_ui5v1+R^eN+7 znQhy(CN3yh(i4fkWKk&9*RA0$8V4(P&5%^wz1qoWZMvhEbP!fKT~d7Io))^cLo-aH zA`?AAjATgNSAxWFO(xe7-x-e&=JZ|h&Sb8yxKbC?p>iEYmy=M5IhUKQ&T6Sz?Ktga zl`mOrNft9XtyR+ft5&MkmW(tFj9MAOQasZ++G-kQGi(s_Hp^h2ENYY}$!sY>`iP2m zkUlDKLT5x_@g9-LT9!T%AbMz|M{)X#ZY&I8({AV1A8vj5Rnllx-{CfQELj9 z$vkL8BaJJd-Kd2R+(u(fTMs@4ij|gp+TOaf@s8{1`G=mpb+&M}=3`%rRhJqMt*Y+i zkLdl;l=x!$!AK+BVmrg<<MzVP4|X*k~pLR zTAZk{dHT*X^!dCAzwnA{L)#h;l5SS=!@~6BWI%h+I99j8G@StqZNpCIGD?Y?9tp+f z=FiNZ)!CT7fc7@pmc4m49U!YKBCXUxr=>CKI0G@~yr$4H!!&!k?d8i>te6|S!ZN#- z<)Z_Z*%V2qqV~LjYt62Ky^WBv+R@D`?(KnYY*RF9S=U(I_eHI_b34=d_Re|XpIHlI zod>N$OM9HX<|9_rj<)xtwl+nQ&*ISA*pk?VDJ!X4=P!4bSrpS^=BF)9L@g0r{&b_+ zr9pcURXo+zgf1o0*<_7Ty3HOSbaxQAP`clN6`QXqWs9Dc&L*R6tdq2X9vi|#PwZ$3 zizqpR7js6T};mqLjXRC8MrqK@73m0vUWdk{fCyN%ImRgaP&djjf;?9Q51E8BZBg1hVgbNL zW|M@JmAI%_I@|jNE4!jg@!kySPoeM}6x|{REID&>1b>xsW(0ax#e1<9D-O}INykto(S1$97Gm}4mVM+$V>CGq-&1!+(*OEzSUcQ2} zD{X52#w-01`u*gVzxkU}!p)3>`Z@J7)g!oj`VpLc$iLF2F|NBTtNVPnaK3S*b$vQvT{_fNA;U(wg#kbX; zwS3m{RZpi6pFrv%K4eNi?=$h0>DDk4{qc5|JJx*}j<_xlA~F5|nI&N+v*AGdUH-L2~_%9&pqr<1`!1n<7vznT{*GvJ+Gv_f2H^JoM* zl+(Jx+b7L4Jd)d87p>$`{GeGp`5Z~KG(ergAJB;E_S4N5o^9^`pkE6%H8(eKJA3!t z3oe*k`^qb)hx;hEN}%6@zMxua=&nfY<}eDAH59#jL_(- zF#P?WfkJMPf!#$;UJLC|Oa`(+HvSkKPAY^;+J~7FHT^@x>wt@@SW&Rabn0eNuv?im zx5qPJI5vkPS+ij}1Sr);rm0{amLI;qm~tz&A^z#$a>^fpKRw(>x+S^PkPGO6lg)38 zUpDULB4D_rvCSnCwO0_3a96$Y&Sleu*d@bbs&)+WKbS!25@m78wb=!j=Bx}{tdCEy zMADUR4WF&DW!yzT7mLU5Uux3#VFxuPB3#(K+%SseBN(m`JUIU5OF@WfYSfW~aW69* zW(FK13GIIfg*>RzKDo~{g8OEK_#;-KRUE7Z4Db}|iVR7VixvMoR``ro9Gx47XR_=L zZ@@;PapW1{Yhc)lg-y0Z@2fBbBC8yqwLWmPR?=VwVulv3kn(JnyN%i;d2=4& zc|cTy>9F!S!m~mK#Sf^%7_N{=9Kn?V(^LUWBI_@K!}wszpx|s8IIo{Eq7P0TzmI{! z41DWfc2I2D`y)}FO(#!E5;m@Ac&9vK7jUWd_X=X8%{Qe+_xr*Oh|7wp>0&>_bQA#b!u{m@R_^s?_So;zvFFUQis40cY+&3 z+0q%vn;-$&h(N;uwrPEx1{A?zTYw)|G1&?pyLf^8x?G%5Spj}L4m>;w#CUlCjazXs z!tDqbvPJ=gfX>H}f5rsqNg#*NI7LAOCpbN4!`~@*QxD?XG@%dE83^uH8AlkxPlvwN z;2UUf?$B)G&rG%FpOm~xA&k83^nf4b7WOXclZIX#+C%j1wq%g^VQPGjoD1y`x|q6A zmTJMm{S90(auKtW=&#&FLE`;uQ4%3g6`mDI5|A}Wg7a03Uu}xJAzMDBliYd~@6IKS zgP1`tAg?hcX<0aQ@86ExNrlpE;`Ase-O&(9^ZsGYMUykUITttLCNdCz%xmD@`|*f* z56Mr3wUj1D$c<)^oN+1YiHcN1qU;6v6OX;+*HPIQPJ~7_U?T1gS^v0!dqPK@^D9~v za5rO8j?7lU^MWqNG4(0$#CzR0#yQqPu3ssXW;U4xup=(ha` zsxArdyANd44wa$4BMs;?g8Wb9*rMPBhlP0djU>rmpo{x!K94R%=b&PpAQ@8y&|J`k zDz*rrB!pH-FT*hqY$kZFC>+(cImQEcE`O%D|C4ZyG)@8(j6ZqTJh3gz`uxX{GXcL;ls zLs70ainC=5!;?Q;6kuvSUi;@+PeTC@beuT_6LOYF0hRwdPfEyn>8tt>N#^K@0%Z%J z9m$Q}y&77uFShA_{W+xeF9zswnnUs{Fr)^?oDO+L*s)I`Xv0(8ok!6BnG<_d7Ia<= zXHO4(s$1f&D?6{1N~=mEM(5yFy4;yv6Q|6oa8YwIDx4hA3>__=B1)nvQAfEbsO|Er z_VBjrT*8XmTD%Wx6Zl(KLolKV$Nj--;8;>u{S1hHq;;cn%64FLW zt!+w3RU(Bi+*oY#mxPS;hoMJi)d%jLAyMpW5t2DGPE z8I;&ZjX9fM06fdJ>{@d zS>d~sXN0}Yu*RmwVY6rHwVSsk+J?j5scyV=wlQ2j*f-X=%Yv#LuAea$KEElSkKIny za|h=|ukX~tx1Be-FM4pydcN_(eYaKZ+t0%}i+LJe>I~Sn9bIbOI1pY(;cZ_F?>+N^ z&5P`%cND{2j+kds&(b@>^tf}YXsli!Bl2t-9*R2AaHNut*fa&KnW!i*1^Z3@1f(j?PVU@DemMLueRn}oN zx$Jm&W}Oc+&6c1;ybbAd*KuI1EDb2&LhP6RstQEgZvqm?KMhHdMb87V`Jl9EY_ zT@)#kAxomH7g}fp>XlC=Jbg~1l_HT5_*V>3#&-v^XrOGEIXQ}%it{HGs3?re1_oPz zDB%H{0}cSdt+;2=1oI64MRvkdu?gV!@uy%U45mOBKLHbY;D7v31!_OG*Fa&SKnWy( zByvcfby=Me0bgXrz``iM(BYtpTG{YoCJ`3UhB>xpNp$!{G4Q#73`bzv+$P{Jk|-Z$ z6*R)c$WD5(O^(#Y#X^__jfhEEOni`l^-$mJNqbNGlUE0=G|@kuPcoZ{Qy^b&|4 zAqxcb;*aA(X41UA8TVWOo@d?0|F8c7w$Frf&cSUmfd0j=y*3licH9f|E`)?XhoJE_ z?96`W16)1xd)99R{QmdAzlX&Ez(eq3J4aa?c%}KG$lA5@Th@Bg$KJ(Df-Hp$to>R3 z*T;XBZ)xdWymsy6mL`IpQ6P|mn-;eei3f|Fp{M*E5z~XPwo1=HlmmKI`4! z^gS_*KaShn*IDOxu(W+iaDEF**;)QRzoohNdI%op7h!-DX&YMCgBiC z!Go>gcgZ!k4f?M*sYY`Jy#l=`;H`65W~d;6;Vu&R z0-KG5cz4ieP`tXJL8; zM~2xw1GtP3gn9wl=DW+j9$`tlkKJ17#iN{ae5PTZ6!z0DH_2{u*MFzGU~bg!V~!$s zD#a#2_Xg%i0EQO!_xTU2;xK#;O&BJ5d-$M{RRz&LU;{)5Y~TBd;O9t#}C zz%R$$fUZ+Vpuwdh2&d7d!3n?c2AtT2fG|9K1_HVappYIG9!!KLUV5_uRkH!y2^A2_ z5Cok?m$V;4pO}8&La^gDa6q#0bMaoQL6;*RaerE5^G^WM(kO_4<Rw$s#n55HTq4={+neP+Jog zMhx;lJOjjfpl`@_gYIPByak(=gybFgjfP7)(QpeS0SJ-jxz_Fx9Xe6_X_nGx&4n(7 znp}>MqSuUx&Ya?(i&*Kk)L#VrexbOV^CSg^RSA?=`5hc~VoCaB<((nik|y zkh4jy>cF(`{V%dFUf?f%a{|VUxIU83L(Lp%rftxvB<>s;P+kcGe4zq#dGXd=Sb;9k zHUUc+blB8Fcwn2EaIx`fKlBgHV^DJoc+>t+bsU~avPRxvv%v4c{p$CeI2wROLcjOR zSSI{!JR72BN(Tt}0DZ$PbS@Y{_-fY`2AGw1Xtq6pgn|=h=~dv5+|jofC!oIga(4*t z2={Olfb?o5n;v-U5&bbeK%PPY@aGyqxk0JYO! zz|;K&st%!78ywERNt^l{}ZM= zcD7NFRMZMrv=I_ITp$U$0T+Q#fsG$=>s6NP^(?RgBtOJdlK^Ri(}v3B8*%OeF!&7Wx2CHo*#bTw8(ry$GsAtvHy9lWgJ zqks&6PB*Z_SRBup-FeB7rd?t_OF`5-)xaL0!w>lTP?efQ9K~&-L`8{>%61(3iO^as zwv{N3aVV8BFM$^lbakMNh4m2LjWLdw%Ky|Fly<98JS(!1< zjjw9kBlT=4o-dudhg;C);;K@fic}Frqv&Mxwys?1s#X?&;6&V<-eRxHl-h7>aW0o` zjZ^Eg!TS?VSjl*s8D3~8UE571l7Gaprm{)y{_6$>OAz8!ct)rb|x2IF| zDDI$nuqQ@)Y}=VjO~crgU_W#FZU1zzIw#+L=mE=q=1kLG-gNDDCIe zzxg{jL(+Qw0N!+dZ1^_Z){TY^<-67}Dy)HV(}KJa;VRf$j9Z4Y5xS+33OHe)k^4a( zSZY265S(H@-395D*0TYJGxnurbO2YB&si0f^N!rJtk-H?zAH7fEj+klG!~oNc}4iE z?VF;rmPZHPoqcs$vtsG3TdmaI1L52qkjaJQMVt1lnP;UscR^NYInREO8lwvj*&Qn* zo3c)OD!t;23gfx@vK?~w24b?j&tU-%X zAq;{J%kR)-JKUsf+%ba=$FY$`V525JTB0%$CQrlQAHy_%P;ourZkxZbIE1zYVc#+s zDRy5#qYgrt!TPj%)J&ne<$?(f;jo`RQNY2Qpe+w5)JQvY*9MG^Bt)M_(dsa9OO6%C z#0vV$)H>SDbyd-!R+Ipi&JIJ|gp?_AiCBrJ0T}_5IRye0!rF+UO1U6~HyuEzl0qAo z+;@@SoGbhZ@j)Js(it=%NLNYD1($Ss0zGv0qU9&SE7Vo z)#y_D@CtOK#f&2AW?7L3I*2mYkclbqf=s!53wz^tidW51WK{qkOj0Y61>emxZw``# z0aGMQEcWjAUnRRSl$g*hwoye4Ts0>8Jat)*=;c1SD90E0)GOI7P@`CCxjzBIRnrlu z$bLBV(VV2huqt(dKAYnUBjoEex^Yl3ksi7+lmMm>-YiWR_+EIaPXgiVmX9>OY}I^b z+JUM&)kj2R8O0OW=lSTl&6mAORdMyzy$-f4K84*UDx zzvu0ThAx*US9vF}m|D*I3-25`_Md|HS)Rzil9V3?50rha#wEdLiM1Qo23qDn`|R=8 z@{^N+-eyQiY5+mzYt?^;_M!*4#iI_*kYsM}XBVU>lgD|dD(_kKc8 zp4ddKJ2-ju)l+vmPkCH+gb$ z{d$&3avTOv_xIggj_ti?a@&BGaK}FemMq~G<+ZA{EX7W;yKle!v!6{uUOyQ` zOW4kF>QKKaicdX2qX613G%PPnqM3brakn!$`E0$I)O-(onn}HaPYH}vn6*j%|0uxu zjrj7NKi$jMLrh>`FiB4gm0F znq}XCXPtf>p^hOH>Ihg^2 zYvyh?eZU|q>UQ5`0oO-COOmmM!s8SOdoF{E3Miq9Ddfsra5%3*1+2c|;#^|fteJ7o z+@XYL?p83^pACkKIX-nx<$@QQ)4e~+elDCC>pe6-ynInvb$Iw)(uGmyIQ~-?Wn*r+aFQ!^opNrV!=(vEz40bZpjyJpK|%!ifgaN6dja~X>te6z z8HNox;sTd_ABrNI{4Qg+m33T$5T^aTRKhl9?{yBF)A@Ti~RwOlf0M#vt$~c~wYCfT{utKjr<& zfUCjA6FMkOaAsKyz#zP*u5`h#2w7|)Pyj%94gow97sGnf zEdS+tu@IwLvv`K4dqzRcMAq%mKoeE9==huzrT(=~zjFh!`@1n(Q-Jv%?!|}(NG;gZ z6hI3<7fgq^Oe#$HV5NWP*IBqpJFk{{MWZsdGMSR;R!PsI93gZxCp3-J_z@Mb9)pJ? z@2bKG=V)XB=pUBiIO0dLLjqWuJ)!Ft592Ny(G!v^mz%JeU64zo0EIkWk|OAeKo06s zU=892;C~gzM2YWbZ8zkvfEPJ|xPSv!zR^Q9aIqDBG#v7R;NI1-*I6n7q=!f43!r8F7L^OY znaBGBtML0ckcKjv@j^y&dgp9Y?u2B--ql1dlu@`d90PZg$gl!M3`9(&`C{}>r~(1= zuul8>Tuz3yN84PS%oq@)AL4JpMAFf8h{lj7W<*)Err0X< z?8408suwhS6dZ8@7T#HzBqcmo{lRVZBS8LzjTddI@?oTT`PL{t=}-tQAb5jqtCtI^ z{N8z}zg;GBDZv9|G0#-M9cr8XOVV9b2Fz6~m;0Wo&OJ_xmM6P}dZJY|I&w2Yx^2op z0MZKP*C+7yw1Rsy;NUJ9ZPVRcnX27qs|JdN1U0E5+$Z`hh1`R;Tz}3BkhTJvNn{{> zydnJh&}cXd+Ze&X;TRTqPo1I)VY$8IAIt}D04M{mrX+p}KfG1S`xehN^^i9PAfMOt zyU=IO-rurlBWPW^kL2+UgwJ%#sbLsFtH&IjwkpUjkQ3Axl>a4-2{}4 z{DpH*5LGw>C&(&+11tN`->imzUQ>agEH+K2%x2D>v603XB4dXObaWpU$xlI-Popc* z4>h^>A^l^XwyrXrIhMpb)_`vS%fA!ugJ>9sr)m|&Z@hxcLC+EZc`~2@@(zZFf1Cmz zG-c;ca=9h8-E;r)7}^TgXHY^|uNdevJpQ0HC|;_zsa&ySlngClB(f+&E?g4l8cuxh zazh)C$kZ8->BlNqBYPKh-mBSOmVOh&b|(n2U!m#~Rj_4VA3 zJ!Vua3cZA0IF5>;D2IOsI;5EdK_hcb&bS}q$Q(gqNlQ>xN1%kRNosYt>^XiTal*AG0Kzcn1T?eN&Pjl)j<(ZQ$?^PYaDeVcWiy=ZIfS<3Hi z&mXv_uWQ-CI||Q#i}Ke$aGkO4)_Hqko$K}#Z_T&;gB-f{dVB5_dB^@5<>%&UT|4v? z(ILqY>d9C?4m1=zIRFSMa8v3dqp*_jj>6XyH(!Q)qc?+K>li-(HhN0@td-EeikLT; z%Tm#_HP14a_JupQ&inJ8O|kzg^?`FBw#V3N&f7cdhk?(>2A)i(Q|ZCj+;ptdOmCvj zdFkXu%kGZE=31-v#>VCzlyk~P=l`;^X8PQ~vTe((&_JqvZ~L-tt$f8D&Lzv%FZ;KZ zeP_kyY+7W_9o+lt*~`1n8?ckN%~`nT(5}Vp*3yODeS?=BxaOMp1#>rD(6l0zZf`fQ zk4EjmX!ML|`rcc2J_#kyP^#CAEKJ+?9<;2^(V=w8v|>%8=FiM%QzxaR8M7=Lmr28d zyLmtwuNUKr12_JCK)LzLB`~nffHA)%&A|9pj10CeaGf}7LIyQOS+c zAP-ZdP6v)883R2OvZB(+@6MxEvcwVf+9D&4TfilPOO+wLlhlS>BR&cg`V!E9)6(=g zvAGDEtvt7C!i7i@V$hOa7U=afff6Y~Z;K7iQlLZEOp+o8nHqMz_W>8T@R((={6Pa%}<^dI^>zN-hTrAS+o>)C@4~lx&va zi|}^UA9j8Ez?lh&XAO#4-R7C%6c;-_gqfm)&YzgiQ!v?kJlA|y|LQGECTD%{!S4^4 zXQ&g9JiLG#WfmV_wcV35sxkkpN4MO20i+MO@`e8=CvTrzeD! zv-$oDzj9s6o&3Ymqmx_w_k7k_=X6Ymue~`B0XxoZ^yG^(&TsT7R|24vv4Clv{OiDH zKq@p;4~GdO1H0?y>G}74PJMdkJqli}p9+;49bh%(PWy44^(BC?Ty}wXj-%7D#gjGW z^40RmLJ^84oOAPJXO&q>1e|#kn47#sWpLpX9mlcO!ftH~c?&m-M!ykV0o9)|w z^TX}yUU=a*zj-2vD>#nhm_i47j5RoCvUkflliP2uySe4CeajNGV!(O`NqT#?d*1}u zT?fuN+uxgUKI`_$NxotJ6nT8Tgb@EvZg1}GeeYy%GlB$Od4;!c^9hq0J@7z1jxPOP z(vKDJ&P6mjuyYi{SyIQ4h?fBYqi6bzl!7X{ZRk=MSd~i*hp1=5&|M*BE6k7-NAOLE z5;%TmJxd=lA)g8mR$+DcHqMQ7#Urz@Jk_MY5ka2+bUnVwbN68h%JKGU!`_JJ#rAqd z&Zmn#ypy3gILS#=`(CsE-`J6RQgNw&}iUKn})`@Rq@B z`l}jjT}m>(RBlP~pjoQi2QSi$9SR1hBR$`2VUrB+`B9I4FCWvg#~&pX;#+r1HX(uW zWs0MIHh?%)##qAifWu*$;+a_L2spL2-5Q%yh4k#+!w)C5BD*v+O5vf)((7jBR65E- z*B;rP%kGFvz@E&X&NLi$pu!(vWd(_~^v)~iRLD`1EU68BceWU6d$12Bj z`K}rxFe`8jgpQpOe#(JmEw?QaCh=hkg-2$ovWDVkFkdg=>y&MQbVZKv^r}WcDXhZWz zX)cF51wh823@PDZ)l5Gm;{k+^ifJUV_fR1`B(uo6Vk86XKB&5dO>PwWgp!>33pT3p z2hh>K#fwYG8Z!l~1D&r@bt9?sRFXuIWD*l+_g_hFKur6VJ9T=xIQu9y-3>DP;y8Da zX`W3~tvd9NFKTq>2H^FcQG@9}ih*z|29^MsYU`KO{T*LKnXZz`%ULJvo13_9MRZx# z2Q;O$m*p97E4@I$0`{x%VwDRU@xDYN8AY-{xGec?4uWtBo;Bb)ic$mxj1D8j(Y|_d z4_&+(dT@!GOe!$B9$-HV4T3uuNsoLwO?ly{z?V^E;gI;1L+YO=nn`zvg`)VlkKc{> zr8U-LqyjTDrH=&QFZ8Crhit9#J=PXXl8Tq8;SEI>yF=7Jq0umOXFvh6&2EW9$hp`i zo#D`|W0H$#yF7_SI1Jwl8wvq0wWnW)OQ2pBRzObT$XU?PwsD?SKygW>z%~~NiC}HH zhw}l^OAoVq3Oq(bSp>41xKrTq9_xc0>PEbh!?!L3Ph`MT?y5(SbqhtIMJ4d3nF=`t zolb)ab1o%)B|#A?7p62u2808se3lJsA&(J78b_L5CJ5KJ+Be$86Jxkf zJ}${FK$?-F8?-MV4xyP=Y-C5f0U0EbYoiMh-9^#KWl@V=w3Ur!9ybJ$tO}G-X*59) zK(OXmBL-YmhCRMrKe+S4xPH=tkhBEJ{cVS;{{R zGx&M36UoLX$Y=Nez?8Fo)r!oZ5iaPaiTzu>)`+vfNd3@8rZzz^<$&n@7*Fwq!W`m- z5Z?zj+RP)ci@op{#oS1?*bF?xy9MOA!9+cjpo4fy2P0u3p3;WM6fAB`<5z7p4`Ex&o9X}^E$Q2a_`VhRd0bzpO>N9yH^k^&Hc@xq&8?wYa z`je+xLr4>4zK`St<}Rq8$UpH#$~b=ksa4EufX}_sEu;s)ZOgVojR{`5A4Lec5 zn4#{bm(Y%7P*LONR&L0bK5`2wTxUT#9RP1RZjaqgMhFM#p(Gm@U3@0kt`Ae74eui? zsiAT|=#I&T0{B2!I@@_Ru|E;{4hMuVyV)w$ZCf5`d#)zZ=nFW@%WUI?tBb{cpwHRrU zRJ6-RM|8wNYe0k32I6J3whm3ioesN5t2@A{Y{n|d*881~Zo4hfp|uzZsl-UjLo$;$ zP-7wC+?jCNM2T=!aaXimh@s^k z3>V`gS!T{M%0S~_lqz)QU;C%zJd~Bco zV0cYo@!8dn&P@j&TiSnSS2}p@{#(;eKWSWl-Cf(lhbq_K{BX5pp7V@3Y|jl3 z3_w>8@+VZd>Rt-s?wi{lBOsbc>qHd4la5t)M-X$^@U>XSL(lQG3V|*D}6>wLs#d(9do~x`kB+dVa{3e<~8&`7TYw` zx97Xd&WaBvKOH?|IKF6k(%h83AK$Ig|H%y9+Ov$3|GxU&c^6r+MfQz7gI3#M@}fjE z)|hHKHD4_WcsP0M=7vYb7* zb_T*mhw@ltRB&npUN4GiLcJ`;mBE3^E7{-{dIG)#+aPF!vL>a1=c(Xucte2A40z%r z0JpI=Dtc^$nx2Yky1B<@ElUb1p^ZxAbPTm->>PqQBaq^wX3_}0RocoKn|mMnmI@_0 zs?v}O z3CbZUW8sY4nB;XZ(pD%7^p(|nRwPuDYHn74F)T&d_6{S%cDwp3G^LZHGWXYN-5@v_2#{ zs_9*&9$mNgIXw1Ir*3s!stTLsX}%AQ80xu)fy_ zz7x3MfG+b7@skN;S^wzt{84};@wS<>stRqAx6%T#NxyEdpga}GGiQME?Cvy%>)H!T{y;A*nW@j%`wAxZZ#B^`e3>J{^seu&=>snb zvVP-|C9K_u)-r!fKewGF!T$A+TgRcE+Yf!mEsG&JJac>V`uR-1cmDR~$=6=M`2sHh zJ462$ej2fqaZ3X~@de-muZ=*@n8Pui^&y|Y9_=lj79~snS&q-&0d97jNoVrc$LG&q z-#i(ZF444&)1G`~%l~s;^Es^Th_rB1_VM>7$H!loDW}w>nvTOZUslMIY((Jhilrm9 zPL2Jag8W5M0*hbL6hRUo(7ogLry%dq!zXRTm*UuAPQb-+WwMqICdnv=fizDAKR+`n z5Y~u@XR3vs*+e)82kw(B>x6kW$5uQm14sd@fLjvJwKas$u$ty@KjAh8f0y%vQ4)Rt z%w`mG^G(k+_3#EQDxq^%z)8(_C#L+zxvgor2G=ve!no&EnF{bR7M~t2$x9+Wm3G$j z8F%EobWH7X&zzP)*2(xUd^&w4;G@wlmGsQriqpqrrH033!gK2+*n&kJhYQjh_4I4; zcC-lK(VSN&`7o0bMmR46N-Z%_1`Hvo#0cSeNW*fK;7@Ww zkfAV0&@^~<7YlYT*B*Y_<_D{oIQ2?XR7I&6BTC>J>N^P}$0YS@L%%J2 z6-VCF4sBQ(1(df#S|h6=?Dz%CD6V2rMHA#buEOF;4QsJTeuCs@iLR_(E`ET9PWla| zgjjL5;aH_@Om~d*f~K0pPFyU6I02(op+(Pj^$tC{kj}=2ERU~x;cxeDz(nvcbi%f3 zY8Ose_yIH$uFUKDe$uGAe~cHju?Y4f!zA@h%>o@ve6_Qy8eL^Ndei+NHTUQd(DRmd zY|exMG!we_?H;;w0L3>Yk=U9vtDC3840~3AV0DnC8!d8Qw*~6C+Yq$r>8>tP%@qH^ z>wLe5EoXsQE>Rpw+hB}dg(w!0%~Cud4R=G+m*aSy!m<8-97@udc8)9{G)Bx=-_Xer z1@&c!nxsJjad-(PA!G8srUwdM*EL)JDXA;njY27sU;R|%yhpyQs~wlv&ATU1^5hM= zYNS<&+YFJE<(jlWnRE{&NmslmlhA;8Sx4ttK#(cG+nE9ZF$@`RJCMyGU)o0GK?h&Z zLwiu-Tzv?Pe?gDcm&L1vyogyX47@_`83aqz)QFMYR%lIwc5yha%u%91C&Z4S0J0Op&!7pH=qRXa<{q8yFCo%2 z%q(U2Kn{0>V-1oGB0$<{p@(sgY7{S3aDgpR62RScX2%fA8xSHOeqzuqzI1hmK#?eO9tCoqek-IjaiB)l-uhQwo5Vh}y z>OkN?x1|DjQ7+QepiZ0D&S!>&1aYHg_B57(zcYXl_d}oHhLLr+{W@J%F zS|$2c@2mdQi)8-nA@6(?5=c|cA9(4gy=9j@PS85W=a)d zi~Fnu2{NRnwdk}^wm5p5s3b>G$a5>Ujb6Hv9WAcTi4sJrt>Iikim@VOQbRHUL)jMNbH?`sA zMjl?Z+`JXX`vqyaU0GfoYs}BvMbA?Ett+gj+tc|uw!QVS+n3*Zu*II&*poJ%-+W8k zae6byrQb=WoYYbpgnHp{<~AXdC6M=opN87w9mAF3+roRT+tS_)7;8$@J_k2U z+o^V3<-KAodfiu+E?d`-CiZk}Uz<36%Esk9xwy?wB4YqaTk zTx8vO;aGZ6i)Hpi^PN$OmgmmXddw&`S*N3^z0q=I-l1|j7QSgIj@z?hJNG8>sv%hk z-(wC~D4u1u59aeQA$T%|xyy(kri1?LdPIm>sNHtKKuz&yLf~DMD@zR|aX-VllQvA{ zpyxzoqgI6`YmLxm;R>KGK`8}JS1=A~sFdi;biFFd zv}`P;6$JY1IF1ikZDzEmGW&Ct6fRG5G(|nD^?Y=!B54fgc5dJrzoQbXqJ?%TQ&v(bN1|I)XqVPJeEQW0!`BFxLwNPUf%{_sLf6wk!CjLroaW;>L5V}~ zPhwgoVKRw5j)WMslgi5TQ)FdK96I4mc~xB6m~(WS(JvuVMw2;Sb78tj_~OP@Fo(8i zZ3h1``4K~cNn;~=)iJcrVE&(;zVfOTXhJ>--<8`)73G%`T3mJi&AkyT;9 zdl=^(;Mmt(^bs9%-n<;amkL&;Soo4vwinrn-xR$2&TCVG!53a&ea6LnzT-H{8+=$2{7mlr zj`|67TNZD@nQ|<@IRE^P^WT5}*N-m7d5nCEFXV#_kebZzozIdAKQ3!Q0=H!Q;@5V3Au=Ox+7~#MlO2)p?&q1GkZwKB z%zSzFn$Be%Pu1x=b3P;PsQ9Hr`lp2Ru|Cau`C&3oT+XWxbva;Pt^U;IjO<&JH6FN9 z6~=18JzPC(JxpD;SU2lplAA^7{vJ;P%3KZiR68)?5!gE$8mFdZ9b$x1%SXTfgPGmdSpunD4FS69JxBf7xaI z#CKRT?t%-hzFM8IB1V(1L(FY~C564vS=`dFrGf1nZvCwFv(RdmupF|vVex{=Ex7mP zz2=5@0A5S-q>ft}^7(3Yd;j+S-kNNF`{FH2psP5aWse;9IRJEv=X3jEDJ8d|cl;~3 zL9+Z?PK!%)8UE__dI{0e7Ej{d9LE1<648D$360Ffe%}E7VT9AsTgw`^;3SNtEL%K= z({YKU;IR~QJ#=dm{&|jH_X5p$V6hATX}FPPd05P0rl%pkTcd+bvW5=*NP7gP!l~KH zml^&y&)`#xh~2C+H=x+T?T+Ue^!0h*C>ec=rd(9xI%4)-H`4C9owig%xO#i+UcQ1F=vL%Cw39iAjTVaOLoG#lZtSMvP*XFex_)D)cCkm# zs>whNj4)on@>IIDe4?E2Fn4A+pH!-!&AH>gqyYQQ-VLM&emGh5^(Rfu<c=G}4syiWG)p&70X_`KqZ@E}F<*yc3$#0lUS2?V zUv}Bc+A*|CS6!6oj`kos&itytuo)oy)ZkXtBCj#SSx-QoJFNWMBp(6`0*ap05 zmmB!0nsq0XB5C|6c6B3TFvn3TT$&I$+)+b8SG)u^ym`Y!2U3*Buo3qJlCx=KR`q*G zLLT94xf7J*EY{%|U7hPt>?*qCdqpGZg`WzC^rO5_A?T?m1tD;b&)z_MFuFh|Om$7+zsXhKTcHY<{}3Mqt+SE))M zK7!;l&_(V_90T-R#E1SC*CUbPlCs-S$QBR+50W6m6;J72ZSy?ZcsU2f64De0se?cv z*k0ol-Up`wa2c=g1Jn%9P6jRGJQEeLrgk@yMC#;bP`7!WPdijWQ+wi$P;t0i^g6hX zon9=Aj@Vd4EW^P!0t8#Lb1I*vwlI~i{4%0H^d4rTL6(C#;#-*PK7OvY=wh%Y__kvq z$JBHrdS$hM^s-n40B^RgjQ)LrRPptj)Oo|8_bC!t&QtZ5qTvKIiS=l~MmnK4jF4JQ zO$$#3p);6Q4+SLh1I-NTBL#YN1V2psRRH<)z$A4OQO-8i4FR3xTfB%tZ+7w$bb+v( z#4pJhOo?ZJO$Yf%+S%0&4(eWkLR9oEHwEtgmzNm}UNF|N>Y%~Qv z&}3kLH>rsW5zJw^lNOqQeCKtI8U#?Hjg|A-?>UKm!(`s8UJua!tv!8Z8~iqWKLjOS zO|?%9;jQ8XL!?_NX!MmJ^o!rXQ{;~#=>Yru14N@;@!>z_F&XqG8oaUN@z3Gs3 z(l9vct$O6whp;rM=|vJf4aOoOKKw{7&AT?_!Vn0&MIrS$O=>~{37F;toF0a;pd&h9 zbl|f-%IgPGJ&-K=vmryo!O~1vIjC`i3wx+dM{+5#GGyv9YCv`bLDv`+1S+!8IE3O` zI_)7i8%78UX#(v-&ibl(*X|J5Bx!5%7^@f;?^7$0LNYhLRA@9p(k`#mrIyH2-#+9+ zlgZGdB*PF3ZAEgAwDP(_SKxEhSH&`JXd?};xwC=-zy>d7UO7SuGPp@{!2p_*bCF?N zq9XzepsqzDaC80`kpCJWv=_m1p2>=P8f0>S&TG`&v7H_&UiKxh=7-;W5bFHll7FtMRWryrKI+QUk0Vu$=tty=*LGL z{mx$^92K$w0dxO|WhmTFe$9>Nk9-#GZK0&QRGMX8@g~D08LD6)jATDAI%=ITFij#;DS| zMQ5p!Eb8$=h!0C7ET^@DvU;hnQq&{*wK*PMZQhIBQPl6x<+ArBjC`Wg)^mAE?A4{n z_T{NBp3#fCT$stLc!oxjs!bMi!cayo8J0xxZX;9a3~T3hQb$=D$${9y`=VImw-$>f zp|8rdXx58d!SuDU z)wr1^GPrpqmH+9R&UH9YG8JpF?kF_P3078ApB>mRkFNixptkM#>R8kBUn@9=#;jqe zfl-K_chJ<)8^*>^_aPax_zU}?VH6aC2xA*JhwvEcQ)R5q5ts%19LMgmmpPRcNqjoQ z2<6~@mM^5}`DkN$UNUB%mmW+V+LJoubatkD3+a2UCAX0M-Je{T?wgxm6Fch|HdM#v zUzoPiozdBIqSu6mW(AGgo($XjTcd{|&7ITaG}3=g&u&l88FlTbu_qlnLFvxOwq(OK z3$42@TpIkzvrFm3cD zoiC*yo;|SJHV=rV>)HpYExpGXfFRM_+=E+n25{9=6M7{lh8^^%B7=~J=M=W+l46#i zUT|y5JJuPPMKG^CciPa*82a2iBLEzgo009ActA|eI7HCsNt^--YPL~DEka)pJleb* zqB4^vM;Ykhp{7?hgzwIa6bU`zl&O(+M57f0oi*hXRN^JLzPDN;U7=nUzF}yNi8du2 z$BgSy94j!KS5Bd^8)Vd)82T@#s5DF;wsFPAT}^#Fg&0LDa@6ZfZ6`zcB#1r}%@UC? zG89n_IMcj1^0LRYNS`VgoF5Nj$_oYerz?5HX7D(q@eGu+PQzJXgkGUBq?Y3xmTxqHziV58;$L}Yn4>^ ztWL5pN2;d`sjF&LR&%S0N{{a-=FsFXty+7KJ9PTYPE8m18m5qlB-$@YRuT#W2VA-i z3^Iq;Vi;J=!O_AiO%kv{YLDkfvc+Iu6ykJ{q846xK7kPg;o)iq2-pQPgWDfyNZ26( zL_ou2zgloFS-5R>GoF9QB+T4ZMFW-^N*L-E%#4HH*Z<(SQB7pC;>2Ni=a^Um#mr^G z8rN06XwOTT@RlS(kPBX6$}V8#pFiz{tr%qbwF3J7^$=V@Q47ssJmApyiF%ZoYy)rq zf6CqmKC0@>8-MPdo5`K=Cb<&=9dN)iA<94_y@99!1DwPZf*W-}P~;P3P;1d2Z3ndr zT9h-xhykNJSlWV1y93phvRI>a_k)V<<&R2RSFvv2x1Vi&jnKFCeSiD0RM)z{{>$(C zoI5u}ba%T?GUxAe{@(NNe(!V65lV9f3U0t)Xkp}2b+g;FVPw@4KoQ^Ieb;Pv;!b?p z$h48T^Zk2%_l1#LSe7br-ft7@fjF}4)`Ho5zXjumBhB6yfB$=6hLCqW55fUB#||=& z0h+>cjsZb>aXiqLOIDq?>RKGzIBwJO_1CVycGb0k3|GJx;3gwK<;IIEX0COvpIto* zZzQoCXW^a4l9tzAb(QzU)9>$p4(CM9#-3^rxJH8WAOF{v!Xnm=tO5?Se)fA8MJ_^G z>;E*eiOxIcosovw3%EUvAxECE1UMt_yAEXngAN6E+%prJt8$7@{-yGY9PUH7p&P`< zZXfpx(>Xg2|A9Q^*)o<-hIu`6{$sU-RqT-kSop4Oov%?`^h_;`!5$69;h1{8M!{y_ zj5t#=H^f+7_zOd_y%+6|b*$C?qB~)_fcrEuOmVf-Wo|oTv=biQI`8)#d7w999 zUknJC4h1WFTmPS_VA=iJbD|q1-zg=UAzEQ?qy}sD@ ztCy0j%L~+W%k+hF8xy4M7JyxK%6f|m-Mo*LEJohcu0KrcP8Im7;?ZAYu=hI~8?fyzNT@+0Ym?wSiqV8=A2 zXEh6NLkg}F4A!x0(~yEoLyBEFKY;nhPC4uv6gYUF4D3+-sTAQ?N;OqB2aa455&wri z+m7*k2+Gk9$yYS)#;b2MOftK>P07yx0r|l`X>2j9y#7CVaW(a@-$O^oMY;ehp5$HB zwU-@>ESef=-aKy6QH8`Wv;6<}hc&~^C!1X{j8|<^$GPT@8>*m%;(|2!kQryR!h33 zuME_mLPdhyfX8C#9!)CH5CbX$Z+F5FKpg##w!tbNYpxw}IZl2yWcY~FFY)DIA`Pqr zD=mDHPfJ6PpbFmair}h2`Vt{6{4^mP>$W=(W(kQ`+eN@pRTZZql(38v1_Oz-;1W>+ zswM!Uh^B_6AF;l%u@2M`Lxs+6pq$d0Rt72tsVVQ8gEyhP6W%wJ0$;S!Ml47M3_+P2 z3P6AJGp%HXqGqm-+>BoF_Mrc4i_{2}HYhp6N?r!==dnFj{UcbO&#;R0={({;maqS0 z4qzAL9U%I3ClsfMt<(0A)qNGX&+PvE#LbT(U*B{ti?8s28M=;IXEf63oNR=@dJ!99 zDhTz>4;#gaoBkdRaHLhQ{}gKCdcQ}mp_Tek&zkfX_h@=4SoGBll%^Qib8d$G%75( zUt@|mNkHWDPUMtH%Y+>G@%eaJuss{ak}ny!yYy!$gP*fEBU~^2(jKS*{9EZ3(~g*Q z*z{T>3Qf+Es^z4KGD*Y>d!!vtLR(yO7^Y!V0H_vKRjUkOlq43F4SMaDH%lHT7jXk- zLfmJFqtL{ww{9I?2to0kA zyVo?K>gx@!j%A81vS&Vyybk-xi;JIveq_x|1R;BR&hSw;2KvZA&Y&0l-i_n1_WSvX z9AawcRk8iI5%hS-2)b~Jkb7Wi9GRudr!C62?xNsIg4IE){#C99qyqoibFp0TCQMkr#1PRn*A& zx|mkLc9t4N6!OGjQ@ntJ{Qd>gkzn4pBq8hgxgwof)H^lqY_)RO_!I*`gKrtV!ZYjtW(^X3w z4J5X0H43JrZQGT!%vM86OBOPKGja05AfCBl^ii=1r$8Ewh%go}cmsu0(QQu0;>O}q zSK26AX=N0a6bpl~cA?shtSws1rL=u9kVShLP)1>Fl`UOMy=8?$rl-ef?nxKh(mQwe zq{)qUL5N-P@Dpd&DHgi_+rB zuazspqL{K!^QdP{3U6qHwj3M4x&8CdCdykn<22p7$D26b-u0k;eP+V*u{ljnV(_jZ zd+6~S69+r)+Of8S^4HDD9CZGg9<-Z=GLO@aW!HDO?ceY=Y>2f!Z+rts4km~Ct*0}S zx2LwaE%$t5%H12>-+r&NH}$x?8yCyWZQI}W zRkyuk`3hzx^Dm0&)+lu zKz(P{B{%;a*PV3TvpeGX`~fGsyAma*;?NKp3XoS9Z4_mO6xyLWdY=5>@CJ1dS!r-mR-tCi0lfFLXiSj2S z&tAIwC-c9yX7Fj#v6E~2`>g#=fG^&Q$P9hU-)WbP1D?twt1qXvrJ!4_1hOUU6^#^PRhG?#iY5^ zZ+8rCI0(^G=TPS%y1;gpNFR%ThRRG>Q{bP(z*P*a^Gd ztxhoxuEk*#U!g`6j}?-2e3$GdT!2ERtTzWuJLA~(rlPoOp;9f|>{RJNvaq;OICur@ zgaLG*%T%4Eod_GJ8-XFB4VK+{sU(C2JeoBCSa>MkJ(#!EvzQ*73MBE0$z~;&7UIU> zgi?MwMcvM(ee@J2B*S>7aVRm9cGrt8!V1K->XzDOo3MM6OJ@~}MRjqF>E%qPvy+f5 zPQSw%q#6T7(TyHWT_8^=L5-eEL^SLP%z#pkAu+}bW7TQ|x}=bhX9U!LNgLmqP;}no zj;;g_1MAR&*MkGM&_joHf6SB zM&5Jt(nb4K;8X|tb3Kx*8?Et@XT>Kg{dA{Yw)=XLcDZz=efyQt0@zB~?szXI6Gao{ zZG#=&hUshKako-WPf8YT2K7=mKF&pv6^XhaJxHSHrd^MYQlsAl;m1uv|2PN}+a((W zD^(g(eXg%#!YYFOxGqr(z!g`+2nk#)Urk%EllBu1*dWiake`Fb9@&SCI;6o(5Fb$B zFE|>_PnO^hxhP@=cHttcmRfm^jWpdnG%aeT}U1OVrmA` znSqOW8EYt`9mLOEL9_RKHf!SIYQkO;-UzOXE#M-8{AfS6YZ6tR%M{d!)`be{1e;Ir zW&Jdby!`V1{eg}ImXb;`b7&^ z-Z7-$&Y7^t>(@hGYxaT#xc%dcY_3#FCFW$AuZC$lOwGVRxg7GNVdS(Kb<(TWLWWP@ z*TQ$UJQ*|^#trLLIQ;7Ly!n4*5bo^wayO^{G!LnjEpntA8(drfc75A8(3}bNqxp+7zlb9 z<>0b}ansr5^v^KQzG(T9*%$qQ-hc1SS6+SNxhMYh<=V&}Mj|!i$0rP2)$%5{GQQ6} zR*sd~{67Bw@Q2`zCbk1R9LG4~`dYMcp!AFra=V}X!R!yXAARudg7sm)VJ!c-=Vp)i zKJIG+5B@NY8}w@Ai6>TNM@}2GT(X=aN6YSzf4nw0`EeCu=6$#r`|@4n0D9IQvLS+F2o8Ucp-yD!AbfBN7z*bL6`GBr%wWdC6Q zg4uNo`uf6}z^EicbC<^GUapQX>}uhG>JgTFfs-q74x}$y9(Jdb;S2eIN@GbZNNm^g zl9qW#j4{dq7UT)y2rxjrD6)Mt86Ynj?tCm`gv9GVw# zic~_%0R@H@Y~h0)2+4M>TU-2$V*3Ntz0(iB#^H>cYXiMCB6U2rkzjtZRA5JlDs5-o zC`Fal3H4qj&94J%hcF(Wmm_?a4RbOs74Quxq3v6$_Hs3!_;kp0Cfe-+R$!Y||1F0z zHta2BtbsHYST(F@VAJ(3Lo=oYI%Y_cjDu-9_JVb)0*u4;skSF#M(DCrel7j@_2EJf zu)GMPcqfnIVMTm(^!2I!QU9nq_4-tJ{mdu%^T9euLxpIrt6x zkoK~v}ToJsPlNe?v*dpVOMA&pp+o49ZiEU>SK1S;q(^5!Tk`VlF`Vjj<_2KH6 zj4TR;6;p)qEJ0npu9ap^S7Bw_m}pEZ6D<$KV}LXf7^F~)A{z)nxb52(J^@4KuotJU zA}V&`5M&9c%t8nOyAqaUKS>ebmt{6q5pm0+8ZxDRH(`EFLSdQh+Oldrqg9YY#2DZ# zoQ`zUz^vh7Y9{0Y**QaM)MzwH%pW+vEmJ{uVm_I232+hw6W9YyA|G%Hx&YI58WtXD z#o(y zSN8G{vf^EeN`!w1K+)f?Lq7KQMzNM9E7~*j*PPn;UMOThM5i9k@ky0Dzu%_>vR7GW zxEG5?N1;Y95Y(u0z(DtU0E`e1=zJtI;dHxt6urfGR{<$QJ)>OZz3|vWrh1z4d z6VtUOp9D>1Q%*-@$QuP?-?x}p;QM$x%3$G+DgqaiLcOM-BW99%#4$R1OM+;k;l*m; z7(F~|%4Up?g|~8MBjo%aO4GnMbF(D!jX*d%3qlLEFr7GH#`qjY5?(`X2Kl`3#;He; zy@fqF)CsUl%3WEC0c-Scw-qAxA!~p#t0>mKs*d-qn)R|7!+}Twq`{S`*v1Ps72WSr z1#K`^F(p5^Xboh@SUB~hZk3(7$3;AdNvvCKqA-T&1XoBhG~gQuOa0166@mhy%ek*& zLvO}TRs`A`yD=!!N%vI)xUe6Kpd8T?ji@T4<(MRDS(8(dMQAU#P`q^-ve4TtXum-! zmnQq5O`k>}VMuCfNH{3E}doXlthCsu2u#k;pCZO>A{P2uHCa}s14+mw4 z7Ph-P-!tS_ji-rzjaO!4qfMU$&#?N&1H<^B_E?iMa0>D8Kw`HxVYHfdUk=}14C$O- z#2A;oRmBw-(4+SwJpFk)>rND?i+XqWg0HY7cY(fw^DVsQ+=%grR?o-#z;b-nODSX~ zdO|t~-UNZY&u+dCSruzyKH`bkfQR8hmG0i2lLmh*%38Q4o(mDdea^X)`A^;VYaFfS z!{MV;w;XW{ber{`#be#lh|d+^nb~f%!aY-d40yDGR;KHu_Z7s>4J0+-9Rm#aUu z4&%oDwxWqetonO5(+$@>FD9JL=$_x%v)Zf4!Qq-RDU86%G#7pwR%IMttMZUzJnGgJ z*AV^ahi}Wx(@~Xex762UGwRBiG+wyXP_>6|CS;v{^o&uDPbg09aE>wcrf8N-|5jk1 z!w7?X*I>L+*BKN)QkYo*|D+1-Rj6qamiQ&KZ=5voC^Lqx6M+nztXU{gZvQndZAcYu zNK4kb6yPLjW@EC^lgddL+q%-&%1kS}ETem{8BZ;?R9XeM&+Z;n!c4b!rIxi5uCwS8 zQchRs)d^WHb+xSM+t$<8QZyb(2+<{4Fm{*Hlub0@ZW(iXmnzCqQw&m*nU)>fsMIpJ zByCXp<%+y6Rm!Ff)!c27rEF6rmQ3opybG{r%M#@+-fEO~T3xoXx-2;cKbr?`P92}XsY>Iv5tuIR{K_kAzYl^AwcJ2V) zaaY|#sg7H{_O=<`uIuMMAAkPlf6UN)cf-8Fww>*c^NsUwo^)MD=dww2zQ5%*?}B~e zk;(RqPCE2xa`p4>_FZlJ<}bgk*X&H?S8nfg>FdsvK3X{O$;S>E@r&qo$Ft|Jw4Z&H z=5^Gs$jtfrHcHmb1%`HK=duI6k6-!S{Dm7khIVgo4^A9PtnKKx59AM!zzKR&hVof& zL%)}r>^ki<6gzO&q%7L7bD;9**RRVBE!~+#Cvu`Oo-=F8acD5aD0WcKq6`(GNi(4( zXN_bc_u198-niVwOBVDm`1izyHn)BSDky2kVud))AM6!}npSqi|9$+$R_d?3%dukj zmTkSL)0)?@?b7*u2dRE$=al#VPi0BNq&Wj~ChbgiB*!c2s5$SU1C1U1KYAp#^X^%9 z*=fIcuyf9i`c%__iHDqZsg4PEi{!-T2$^@xy+$X}i~!ynE6V+pXKtF@3dneH`WMTrtT(nE@w1 zs_X{L37)0uX$p&Twnej87S(f+;gN-wb@>`@ZXFiBvq1ylWt%mS8?+hSa7<=nvg6H& z0Cx~E(86yhW|0i`>#)6L)aOx0i*gNN7G@PMx;#!7%enJtF)BFbnh(4Cm9SG6Z%uTR zd33j(xy?pOn$%ZJ(@>lFWNeFR4epHICo^<@vcr_J@epQfDo7V=jD6;uN89OQa+{Q* z5_uP3ZTEe^r@|>;P_7%o6eF9HT~tS=Y$Qjan6Y|+@m>_wrtaI45K}TkyB#4f7rmRm+PUYl&dUNH(}7~ zn%+d+^iYnlFtCiWvsU)I(kNWnQYt@%yV-1#R}5fB9meNWNdi*O8#snmF0m#qR`j6z za3)n5L{c*VeNLSk44?USnz}xZ)qKA103D!=!i$p)QGmm5Ik1Ho-2~ z-fg%7qZ#$qkbsYZ6H`W_x-)EY`3(l^DNL#UVk2YS+zlKSR^a1CjV-|;Ab4(5+NS+v zwNj;12%L1i6`3pG`zbh+!gf0lK(sD21oIC=Op@>2ei>Z=TioHs-|jggCYuaQQ*855 zLf8c>Y^st7bX}gnJt!KSF_KrA#%Q84#=>$=e9P_f5q(X;M7KzQ;dA7`JH!u z@r##7*01N2?iTPZ79)`2f_#>a2YXN7d;bG%W?9JvKr>mUi_PqRHeEZiU;(>vQO5Xf znl|lsUtqTd-pKDm_XP`_KARNuks4>F8F{x!3L>IdPpy{c%r$Dzi|zdSwb z;OK8h?n$iA|04Ouv2l&X3`49h)N%f{#wSA_c4ssHdeIp|^IwWFa=ME9wP?f#)-z+L zLI)}H5Bse(57J@!*`v4@`%&D9&A(D^NISCPkD7Qu$k07>4+I6zZCL)`v=z(|s%re6NR8xc7r-MJ1Ze*2px08dLQM!+m4&@OU#s*)@ zDCj-+2bg84yqN9Oy0seb7R628pk~#s^@pnNdzlOF=4+guA*OceaModJVGk+?@wR?% zyUwSzCCo>V{!A^0X;ft8=C1d(?4A6hu*?Cen-KK{z%*PjPAp4Dt>~)9Hq$d9~O>9j#Cdsj~$CF zoJv2YA8&3Rjzmt;ON*xZwZ{*C`PAmxqbDP@>!snu0|+STsE&<${4nrFKnu;0TC|$L z6}z+}$2Wg-Gxf+*M&&qNM(~r;_am=g7Lk`le9r7K-N-mZL|uK{pNiTzRnnJ2g=yNn zF!9Y2kq|mw#O)f8<+R;&CI$e-O2Y-d#Id6XVL^&Wv07pqhtQO(>a5MIK>1|Fa9%JJ zSwcDo5T<^ZK9NNw#nABZPM?Yn|8Swq1Ftx;PVZq(Qje{W{qudI`%oV$y3W3D9rE(; zF#}60df+X;elM~z$;5uM9KgVIyK-`bfvH_pTm?BwZxG4SmXkLVPYd!b$VcVdA*Uqk zD?6z1lin2dZSdn1r-vRuw$)z!wql&`8xyjSF~e9wd*OZ#>J_VAL^@ZME$j zc4Fc(V?plC3e2bT&gLb^=d4D~zZ(^B4oKH$)qLfBFhnZuh3*>LkM3*az*K+Qh$;nl z)TjEbA$ec=W5|f@OJ`UZX2iekLS8l+ZzD(OX1>S^HJRnj+U?R?NbK!Nxy`E~t%gTm z{@;ys0;tC~eUBvc%>uP>{K)iXfO}t!k;Gd=lSqGFVuCc)J<98ckM z1l0n5tJriyGy}$u(tSk)H5AuANiuU41sC zvK(v}$E|VkF2?a36X!w2xF*TkVw!4+N>a1HGnN%Z-NgxDzc$YyxTs^mpjL_37zp-@ z9K@;6&R)x44`~r^a=2!6Z7iaiYXl$mMJM@wN^Tq;HABS><0Mc;-3I53EYu#g;3s3) z($8aY4}K6#CR7B`X8f2g2Xhoh{#^@P$IKl?R$LuC@Exl<6;5lPX$z!g9Ve>TyjPy) z%Lw-sa@3nhy+VI|j7(X(?{eJHf;z0YGclAkMFe#rNx3E6UORxdzNl|^j~h18tJZW! zK8tv=7hznTOA>?QIYm4s!yuVMu6-|M4;y4)|DPGb{Rw->-FvGE`OB_pl?a(=)2(Vd z(FyyP$(v1D^Wt&j1Nd5`V%Dn$`sdmT?cRrM9eosv#sCPbVF|OEhHP`z)sN9o)YW7o zK7!0Q6?Fv~9FL6bGwZ?Zpxn^-_aVX=Y_L}(6oV#p$8D9vnF~iQCx105cQ2Yp7VQw1 znGdh#23n5Eo5;Pu-vuE|x(3;)tzd^{{DQeo-!SyK+o=S8HD_VxO9@oRK+ue#0rQCOJc%jH-2%y1%;k!bP^%3^@;2p9RGfb6`Ru=!`RVU! zD^PRz1=IvhIVD5drrj4GARe6uV$+S%(iSus(bJ=PF$@(hFW_mmw#uFYu6rhQiImYo ziFVKG+P=86(VkIKmkVV~Zz}YfvnF;oLDCR&h;2_SY){MV#9cHnFo?RaW{{C?F$>*Y zl6K8{xbeC&7H6_!SFJ3Sz>` z;zDH-DkiM5NH0c(B%QLf(yk!tZIiYVR1mRdsik&sK@8recSv-nZp>c5?j2#HiCCyZ zz?FUIR*;G$i)S|U3$~#;@Pn|cJ%k;)&LI>xR&P_Qy?f15vo_jhlexyZGnX{)cASmz z?9k5qGf&>rVehl*W3iNN*Ug>cPPXSI+uNM}?3zw=a_QdLzZLU+@@%#P#d1;&w{3GeKbLHu9-p**(wup1QuczqW32epzSR%iG<9$-WbI$AUvMzqc%PS7N@A$xq*KgWdUa=XUvG&v-XA zseYrqEq?LtZI$@XQuo*GYTej5v2*^tos)66W;`}?aoX$b_+k3{o%L4$dRlXjIAm?; zrxl;eh<$FXqcXR@`Ou2rJDrKn>!0j+=F*i^*Rk>Y!m~e-o8fqFN}$bWH+Y`&b!y9# zH!rcprR+S{;ZJgC&QbD)qm;sx==$>x+;FnA!q9FTd7wkVcO9-3fge`|Rf4wSu^EK9 z(S^oMh<}b(fX0iU9{x;a%cW#WoFK&_YQW7r8`CwaG$#d^g$aJ>i7CoWtccORi_sh* z$-UaKyBFhVk`7L=Nx1{f%H_uCMwL`?qkYJ$5q#{*khKS+$hM&tKw3|cGYj&()D1QW z=5n4SW-^^SZ-PuA`#4i^0MRJ!#x^i`v{3jnndm#50CU6ST5KFcA>SCMQ6m?iN~)@t zkT#6Ex-jh-?n79<}r?1)nWsEFv^plPyGxwN-X?w-jp7oDEI`4+D>W zYYlaw_<{o+)+`_-{cm)iS2xOUN^pG{Lj ziAAq2id=p5s_g8xH~&TKdsE|qj6d+Xi=hk^dG&A5p6HFAzJa~de2N{gl?5XjPho@vrtwcavHsfk-orjp5aTDT^Qn=LoUFyY z8Rs#+vUl&2CGWlWfBnSrSr4ldD^s9@vE=q)AZhZ?*|ur z-TGkZw@r3ue~57{VExqyPT~WF?nrY1n}dqe4N_Z!k(cdfevARKT~+rehnU}39*qk! ze#qgf7$TcPS;(=xqh|1hIOAyeF>W7+JC25}=8v&NolaH9&;vWBW90~mANz6sgECN% z9?B7h3(|l)!Uub>5ttj3AH)L_;*$G%6&-|r@Cxp)RT@2avGm`=9yO-Oa+LUmxMCQm z*4YnK#sW$6R?P}MfZhIEm}A&`f^MVZ4V7N>9fkPL#u_$!>PHuneO`t82*?iSi@mEg2|IRxL z7T}yy>}%$GVOEX9c?7i|e1N;E-$QR=0?x?&29l7pY15zoylK-&t(I*>Z(4YA(B zYxW&KUEXljh@5@Li2UjDRhs*qcSe3=VGlN2z-~(Xe@wN)>%(+2Fgit6ovPk{&Aw8M zsZ9)`zMzK*uS<$o)1LOHMvWZ`tS<3xNJveu9nlZ*c)%Am>KF@jezjzd#s+W73o?A6 z%AVenJ$OqszUFFtWlnc2DWr#Vcf-x#{47N}QY6(_o~qeE%QI%_z2q%lzQbd_o&qKZ z6XD3y=>`Kb9+++{9}@@96{GYTb79YQfn8WWFr&Rj^QhR)=QpymfudLamt@$brvRV@ zddWyYg&w2{iI439R-@(NDmM6l-=Scag`*tKX#FzOAigvkKAP2Px=ISg{(oY$^L0lot8dVPI5a0^3xnVXKda3P8?|;1ybXD?0T}^2bpG zM}?UtmIf7?S{wGoy;yOa@~IiQ(adD^_`@d_vN>|fe<1?&kY3t+jL&VviHtseL4{*- zMD@Ld4c9&LEp-ZVh{xal@eyk7G3oV_$FFVnA$0V>Yl+u()e!Jb#3?z-F0(647}y8H zI!6(0#6QmFai%(T_{*A)c|Syup7fRSj~_iI37HT_e~yfhJVusJ4dnk=Gb^nIh1Nz^ zEYx5IxOQ&^t1?qn>%@$>QeiyATlRoLbqOv%ilhmALcn66f`w(^2QMzLpomo?u~8CN zvKRG=*(?c#o&%iG;^u_aaK+|$E7BBbM_7-R(a97-E21Kv7*^w)K}#nmV4}22^Ft0v z3GP_D*a1oOO;H1TvNtj6~R;#4eWf8LxeK=t# zcY>|4xosu{VUu9+uP7cffDs-sd4xbo{u&nr;>~l`qzU{k{*P&ozL`UWvAp6`b{x(1 z)nE(sLsJ2ZWNlSX$2hTT=Sy_xj%;xhcZkVW!at0s5>O@Z$??4$KLE_0}FyR^`#e37R#OR*ii(nY{`uy zGz!YsFIRZ|`BUDb97dCUlY~O1=(KDQDF0Cmm=U}Wy@>eE;RWh(Tz?`EEYo>at&f5} zruy6&&_C_<|MU5P-&>HbLF1!fpOty+AxU`rblDKiNqj>%$d_y?qnIW%f+}W)qn8ghrG|cf&}I;|DL-G|0@c%Hxcg zLJJ;1wpiG4iu|m0h8rM+@1 zF3-0J^ zP)Hj+Z4!2`y1QKy!-2Qa zo3J63t*C1{4s>GZ%gpZV_*rYy41`TP9S2hDYOZ@*PCj2yV*an}|It6?g19q39={E3 zBRX`kn*`?UIra4`Q^|U(u47DZo9dS325H5% zLlc#~g;rdOMM0|51#qQBLb*DsErgAlyd8nY9V;LJmmr>Pbp=|6M2j;835it5Qw9(< z)e9qIkcQO9@5OERDMePxz>UA=}nH-5&b+(^wkd(~5kXXC4T5Pr3q2;oND_O{%+QSvda z5~#96**Z_+L3rxP0f}5nY4kY)#l&i0tE4tOEQz{-JYgH28Oot6p$*khhARnn2`MZy zKVwj}YP4AityPsspv!Kp3$g_IU%<|d3xr_^gBvJt71{+hpYuWjE9?wDo+6{ZZMj8L z7&UlA!WHQn#09ePxk&50B+FP$k8!w)xd(ENEqKsgje%2<^AzVgXrLe(rPUCf(}EaU^CZG}aMdGy!&a(Sk)9BfLzegcv7wTc?P);$#5!pR-JKG$F(RzqMLf(;a%D)tIR6$?) zP*ne!C6s$CSHrO$nycXy^bcW=N+@w6eqTeS-5tpD8?e)om->z%$ojQrufKj34-4@Q=2>~ekc#6-ibx@_&<{u<6{R}>#!wNsL6W8go+Y~$S^n4a2w7rE;G*C&atN;apS~7H@aBigE z0=psa-zwk?jPd_u*gp6GRE}4_<+Cy1Z!wpo6>L2Pc4JbkaQXte!Y_j#ntP}(5vUe; zD0Vei2}lX(2Ax!a)zWs%b=%q9j=BNnSQ>bZ^W(OT2^j!O&=nFm{w;1tX@+opS^5epfk1qn>k!Js1wVh!C}e^tT?)0S+a1x_=UN z2X`O8fCO8sw`aUHG@>Q+ymfJu}jbiWc5YU`AuL@wMOo5)MZ0cL;~HdKVedA#!^9e zLSHT$N7Gv)s?h6hPhy?)6MeX^fyn|d%|05cl^0zSnKL0G2>z$7SQCBOBssnQTWA$D zOOPmV&j4D4e%Nc4tQ>|03B+fV#x#+@Em(7T)~g0>?!|An> z&Vz`MTmQ%h$R)KIWd&#Z#!nT;0h7#n@~J$^zQ?ouVca?M&tCWasBEiZ)^_|0`onR% zzVxdT5fqaoPZumCZz~;l~Qz)B38@R;XXh)zd zD?Xg5jJ$@V9muF3fX4{~XQG0lZ_`(SdsC#M%%M{T9TuNyq+wu?evZA-j8su(S;rp7%rO?<2*?J*_!OVAl!#QpO{)LHXTjO&bEw4+xK z!+%fep_A*dXM2`UCfu()T(qx1|Gl7j*i`pnG&<4iEXtG0?(33+tAX+S^YFrN{~M`+ z#ZAsHKrrp%$Pe4jrfV1!~t)38>hq4*%QbQew& zQ-%T2mo#%UrMqOQuhduIMTny5y=EW$OSlV03Nu~2yomkSWwLfWJPU{l4&k)aZix}z zluIV!?1Qd{yVJW0MYTOmJM2tplBq24moNfZW)A`9Y${K0+Ce+AYDYP18-v}RHPEwN z4ip8Yy_W7?94eflSa%~P3^k)bklSez7CO>0z>hZYPn5JoEtvL12AJy#%K|WO2z*hn zrL7bsF7W`mNupc`YxgeeQZab&;IWbxclXZ9Kup;*gLon+Sg1@KC_tSG*6get$2R&+DU(HG}+gD#ohKT;Xv~6(4OeVXP&-$`sC}o ziU$*I&(G*=qQoRJ&OhWf&vGg|z0~8`D=sS0^{I{aMZ3PcaZR#y_jSobPImsYUjIES z=RVgOxBKeqX+_oozPe$J1JTD7QD<2+k56QCvb&~;cny#rRDrGvrB{a#*8^I! zF6CNl^OKg@_<)+ihF5ZScX)G>9qlQjoeqzq)X={@ZU0N+>+#O|a(ck-$aKb(XQoWz&wtPsZOnX~o9a z|9ZsTU1!)<$LY?w4Xqwvi4arDpQ)y3U`{+A)c)dT1uSJg;uULlcIQ$@r9b zYVCaInz>DLrewFq>rPDhZM{9+=-jxqzRg7`+z0LKA+Lm;;Jp5Uh?k~162gN71Kc`O z^RZdlqk4z|kmY%uUc2Ctsp4_PVZG$wJ5fknJ!8ougdvFc4`M)thg;OO?b3iy9+|y@ zghW(mUWvL>sWIZphfK?U9w0`nl-+YD4gYh#Zjvw-ZP;iI)P z_2Sp&8NuCs2*++NDeWu0$v#lqIY{X|l@aQ3f@Oh9vbk(xWAP z#w>hg$I#}%A2lVB0sIc(Cv|RM2Pv%viaW-g*c>zn9z3gNgA89=xeBTzb-n894EQa9 zl0HbmdfFDc-r2|2m><5OK*tM`shg|BJBaWTNuY`21RW`3o#K*TlR?#_MBA&lUZ{my z5LQ@7#z!nW z5n%_ZZmn_f;HRx@IYSt!t2An0yD~y&d;ple|7F}X@d2=t_{hk#X}s5(Z<+{xI0X{) z>Z_9{hnNNH0X_FU$R=I_>|{3IfHCsMdhDVO&a2}S=CJ=$-%$S`oGto87K8-u! zH+@Rz7PjfqCmx(u^X8i)Z!m7pSoayupbduY@{bT~)Kt4(-?79P;sTs(Ix>=rp+5nq z$3E-v+;7;8@k-dam#yDKBk$pSNt_z_9Ik@;{qKS8p9aRiX=D?J3nE31@C{&O3|juO7?K=2u5*uVp?qhZKfGmHn5LaMaMuLAMGDR@ zX7hj#aaj@IdHOzXp0BG$&7-*-hPyIqx68!{y2|udj`%% z>EZ1hDX_;L9Znj)=CiAJ%M3o?^Aw9uso>J2SjEa_iH^6&z$Omu5@MMNwvk1-54l6O zfx98XDfWiW8k1rLu`8VqGJDeOVsB>^?$ITZO}Kl8auj9PTW*YY+|8V3bV2Kg;xN$DtIZqz-Us@RPkG_PL8vhg()bXp0 z%DBkM9{;G(b8ckZB6*lzHw;x&TmpOpwkyIyq(=yvcxzFGHXl7^MK&KTe0f{}@x7#Q zrw!q|B2+wDI<7b`L_hc8h*J9!OzZ&iLqpXmv_>Lk?X|~yYI~R|aQtE9xZ-# ztT1U3a6wpC;`s+IyhaL%KQSw|x&naVnN>*0Y;dfq%3UEB&jZzdhC(m6m{uO=s5MiO zvq^r=ft#9OTjLZ8gpySQwL|xU*_h%y4P6_DWXHELfN)fGo z%d}inEyK&z(pbG&@zg`6yk*-biNxufZ3T>T>+BreXFy!?{XD;}JClYz>fR2y#_pV~ zb(i909+}D#J$ppfM>fm_?vAIOZ_`+7E$0YX^AX;7mo1|t+4LD!INNvuN&{o4k}9G) z)hV_i!w8QeK%Q$28_uSio(A7*mO-V>s1+>G=| zHHVd|r(A0ZmRiG3Ms70b6BpiYk1rdz&RAXZSjMD?pN~ZFaE?)Ly*Z30#UkB!I491( zQGVqp(!&B(A-o}ma%hEEB%tg*z%5$iR^BpcosDaGvCdz@W%KUD;dC}}ok&zKG(uI4 zC4Qa~ADBFP3npcny8J=rA#n)LUk2>M@Cjrl1ACy#&|L@n>F1>S!jmaVm=U2HEDbE4%3umdQ|xq76L_kd+UHn`zRS{Q)zmaY{>=4 zQf2WPq+@k2gb*#}5G1Y0I8Gf|9JdSOnuXLd#BN;>JK=m`U$rOv2r$qVwjl-vS}XXM zm{5+f(;3nMEVe&DIVyA4PLBExAbr&_YDGiC8_I{s4Q)34grmy2#hXw>)3}hEMO0Fw z`fQa&=t_k}RUI%bgT*9?=IKdG0d8X$hTt;X_O0e-Bv<)m^k|OMRg|mcU{~S@%E!=P z7SpZfG;qF19T)HgFt06J9mTl9)pd>aE{#iI8OjYq9(NCs@Lc+Ah{KCn&fAo$Wi|>h79*xnamaWxm0-H zIr`}}7%mC<(Uh-dgzV~glK%wU7t(MCnrC~`%pv-VYRnvJ%3b&tv3=`PH`X6rP4vq0 z!_oMi9?didat}^NzK*!&{RsTk7d`#{w&e&8;KD&YwbF8OS&U=D2G!*OyBfoGGsdH@ zHuf!XSO5hT8_9Iw#gc0t`88;vf=*Ad@?q-3!b#7Xxiy&#-8|~a)j&RnM_p(!BEK~_ z`ZZ*!z##GEiMGWr2R>*)XieDNxLlMhNEKOwfXHoO4p3a0cDxs&w|KXed}PId>@|CP z?6}cv7Q1X-7OhY%!24Vvmgz?$}2fg?j(VjcC{wIx+LLtZ#0G`TsJgv*5Km1CSI`a zx`_uSt#KAM)lI2c(Gk7y;H>VuGl!l^-#u%s6J61N;obFl+i=|HAOAijckXN(GXCL^ z)4K6qT(jL@pY4d{J>!{!s~@>{!@OPX|1hb%TumAHhdk{z=w4^2Zm5K8ISwxD4#=`% zn5VmA^%dKFn(FBMSUeY{e?a4}ry=zDcwL_CIy4w=goK$zQ`Yn@#it2xS<^BcTZjt9 z$rg9!o%;R`h(spc2QZc4&vyH-T;b)6c@Vz;#7cLyn(uno0l3?+I2F6_LU{$9*Ioa~ zj%%X(&N(>Ee#Y@H_&H<}uS?W7Pq}!0c~0k?)>Q)?_YA05cK%?rb)z^JyR${g+b~pj z{xxx$(r;%!DS^Svr{uh>(>dR|)SPJ9bLV$Hf62YS=vEcm?tCC?|14=c z{g~i?(Q>_mhCwG%Yk*^+VL;MTe|Ez0me_fX;VHZFLVbb(iUo`eecH?7Gc8{iZ9qtl zd*UbsMzp&RQPs|(p{7DTC=YyGebu%Fxj!Uq+1)0Y5V-|=FCsN>&dsu@JL}b0-Hvda z`!Ge+APC0M^T997Os>a`Q?kRtUJf&#f%m%fUc`e@)O@P9lZ-AJz%12*Yq@0`i59ZF ze%xp-49j!z(E{U|#NvsQj$ssO8$`=TgU2`&W?ti2c>28Au<5@{;LSKw68WswiOq;M=2PojV>Q1CSNhxBZ87{63o9Qyt?v{aJ3E+1)KMYqtnsAEv|3L}bB{7^RH7^6 zR!mdvEy^4~*{nq7yXP}JtVen$O7wiU9uM(F-XXz{%*Jr&lB&dmkH&2>s1+fD`YEI8 zkzER2n_$?3A3tR63O%H=?V&p6$E3#yw`LM3J95<`Evy2Wj^GCu zJ=&F`Ef}Eelcw7waJDH4SI`mLK8$5d0dXBMX-}+1WQ1HQ5XYxw7dkcKv*ayahT~Bt z$0($MW~$TW?M^n*M?7%H&l8+F{#g^sgq2CHhC{%i?aZTnpa;|v8gxnFr@cots+{p3 zHH|zm@&sET|D;irl(DNq6UXN{@~9eM~_}~(SlK|g?YlTAwS#K_neEDSWtnLGjfk? z3n=W8AI1=ars&WbUp1e2Sv7~;0d5!YhL~T-%lfDr-jXyXIc9G)`;Wyb!#&+0M##Fx zEqSjrW1i?RVVoh)hx5#1+?ji<70X^$F~%^z94^RH;KJtrXwP*R=4_quz7RvyeiADG z#?V*{Pn)alF$~B1w%MIYOKpaPC96J%#>Z;1g5qLMLK!Eo((Ae5M1nhpuAO={vjvH+O~7z5$6c_iOJLN7QZ zh?KSlJ8+ClFu|!!eAAovT55r5o7eZ zXPp(E=|A@F^4RfTFONO7>l5A!&&@GoSJWO)(BG@9Wtqpy>y=$o_^SLP?scqyBl)}E zy=|E5o4WBj4G3RH*Q@X;?Fg9He=_{8Da18VQ}?^w@17?4v@96>1_e!-)44M~3YzaX zfYrhTr|3gK!wtXAX2YxF>3<3WN!iGTQ4ot*O@ZM>@;&^_+fxE_7W`-)CE;EoVcxT* z{eNxVw|+c_QPEP;QMZQoIu>=v8|0B2yh@ckYR*Wf)pc+WWcrLkhIPj-VGh*Q$d!yz zQKMJGDdGM|O^}m8LO`b@3hwoo^>~qi^bw8K@*WK)qWqw4hJH-WArEkLl3Ft<sk79}_9@}#DoomE|7{7?wY zW(g!gFn(xrWWlm2ATvFhfC94AvFR$VF-`9sUw;1)w7~Z1dt+r7@GAB6+;nHUb)BAm zWE$>;9H;v1yzIRDuiO0M%iwM<501q4cIH$wzW>DzQGbMD!RG0E z@b>ojk>zwu#OCe0=*W>noQ_~0mkAjo8$JMK9u&X>-6LLNw_t+R8kndK8vB6Tw`clA zL2NHV)HM{RF=AP0qQ+t&u!7LYe8O)d(BIA%wbEucJHuij|g>I2r4WYx>e^H?NunaWo*Crk7 z_8fV|SOI5qAZK(GNyODudKxYfM`P6BlEhcRL3oea1*GQ9-a4~3;=mxU16eyuqqv+m zv0MlFHp^Dl22}WqmOE}fPcUIY@(xaMt|!1t`3{z;v2DOu3!zA?pHYltsb+H9%b+4I z7$3h7>NlW@ggtZgQOI55OZ|tJi1C9rH-``{o1=yhhE-J+=0FCE6{Re?6Gmk(q#4@K z%lJN;5$xG;zGMn09!l0jvS4cEG%PR@_oGY{C~cCPm%>Blygx({>{&>C z91j+@G&JGU!32Hp>)aQ@%yDoelVn!NF}WM@d%%RuG4AvK)24ay>sa|eei>{GTwEd4 zPhj+jWAThlz#mL(Fv#GBQkn=+`M3JL3gaZANp~2S^`!7)rt-ffu7e4n@@L^FIQV{r^M~&wck?5Q zTQtyh0TP~vvz@1U-X@#6MsAUep?O4i3h*Z1dYA}pQfgW(D%uC8AH9!0|kkn5c;b+o+{EDf-XIdv;@cr zNKIiz1XEUJP`bVg%<`&h*~(Q-Cum3-*N{7}0G=OCT&hQ4msXvf!!Q)HFB$y?o%^k3 zzmd~fUq%(+t~8Ckdp-3CJ&iq|!-^h1Tj+1S^eHej(r26%u4i!R8hGyL?)5K17O@TQ zSLIp=bOio--AfoxP$tNU;gV4fCey~=@-axUd5jx*M?K|=pNFCRC-7JPRW-vN6Bvl6 zg&!merY!2w_P{U-n`-FHstS#1qbXcVio+UF!G&(DYVPr)G|XBWZ|_ReD&hNz1P%1- z;WlfV(>^>ReW}v~$8A!B4Y&xqU%UB=>`E&68V5;NfVrZ)1?|P`$_4E@{eZmG&UUo; zBD+wNZ11#0Cq4tV_djGNh32+gF-uD8qHIt1caYsrL%m8Bhy3PrKQ2FLZXam9T1#OK zd5L15^l8Ly?v*V_-=#&NA?+i3`I8*<6-YA5q`z4x&OI)z#uax7Jm6$?s6LQ@i=#M) zAq*i`_;MnPlI$(mircCa&b_U;W0DC1ib%n{AzZ_oz>w5{)AVScG`a=-X!P8b^zaA` z`A?^C9@kB@k=s^EHZ-IXqe~#7eD$2s?G0zI#{F{VF8QWdIin%>95^UcD$P1$U7nUK zauabebIv_(+gG1=wph+LpSW7Pf4i>rd%NkH4U2o4AHH$>np^+&Iq!RvzscIL^G`K3 zKK;lO^OLUo*pfu!nvofJL`BpsojYpXO+B}5yn4>%gNql>Og&ldaNI?IyKVc-v1`gp zzxMcyh7ECVkRD!ocZ1l54==i*)!BhhBHY|*EC;=@;W?tenThwTse#7^Ae8a(309FMdDESr?{Q?zn%PAu}g`Sa$Ngb$XO zCw}f7Orb@T^D?!MUH`ova{gZa~4qnB3%gw1^elY zY@-zo=bZ1}*pACsEfHE9`O?6m9WRq$|U0i&QTXqLwx4!d| z_67JPfM9w&8d#+fq5nj_KrKV7J08vQcb>=F^z|BRnf|&b$eNEycX${PAeD%s>>|P8 z#0*>nnuEg+Gm*))$wHW)Fgqck$#`fqs!x*hZy?gy0`G~cU896-99!c*n5oq3nz&$& zD^0#8N1B8QDV^j1Mj*RGVo*h|GT*z}+8?gF;B362f=9KyrIDuJ=}`QWH-9}vViVrwGN*{^G zw>5m&L#BJRLh`P+2q^>jh{j}B!D2>a#!l^X{IH0SyAVIADwrZS&YFfjf*~&D5NzI@ z+Bk^-D-sSh$04zy+KgZ=GIIwYW>6Y$v9lxJHrn|#dmZV(!6rkC0JyZ6Z;DY$wn@-E z$!vA2amebuf!1NfH%XF?cQNHnIJQDsFqCkV#+wr80^~e`fUq;hI-JvG($fhe!$lH` zGJ7C|#v`u@)gozvN6ehSQv)yW!nAUzT1-*x7`y>+4dA9l8_tE)BlwI>0G57a*++hH zrl~2Ee~f&sJv=2tg_{{Cc^z}+G%b&K6U*>Q%z@Y@X?T+?!-dDIBR=Ia$aArf)>9CL zWLV~IQjy#vpXlblCcH-z-jerExnFg>e>D5qO7q&LwS&{W`SaONl$NHWzpGGrdpF}4 z-g|cwfm6C>*@aE?7+`Qm_x`~h`zPH)s?a~1O*opXXR3GM?UiT4UwG!4d47|!k7GEm z{0x2zzgCLU!1{@Ny^yD;&jc^8%HNClj%+tP#2UHC$Sq7m=$Gk!FS$q1M_}ILX1*rJ z9i6#0`E%*#&wT!g*Pr?PPW;T9IUINNWB*}VV8BwV_P)pJS?8yx;t2PS{m~mUV|bHg z;g?S+lZcl2f((X_hepae{}@WOcE5__^VJgajohHQ*;dW9{`!G6_A?O{|FC9{!elr2 z`T#^c%t)>={PF{TyxtG<@pKdi>k-`v0R>aG|M;_i{>%UPv*&MPOy&k~?FU0y$L?-^ z!71!TgM<6`Px+WKMQxAuIde8i))|56X38-$LoKE8*d6ec#l(wfNA|m)18b!KiSUu3wT}1qu&|tYay9@6eqB2 zVSUF_HN!DH=*f|Vy?+p-Z82|mi-0 z?`1n6ltWCJH(x-HG6^9J7dvu_^ZW-9B@MNcjRrKUppPI(YYqF6z@(e3s{bfm^SDgl z{^o1E<%GYVvF?!xby8PYHfYdIfrbPHk(@*XLA}+186BQcS@Wi=3EDI1XbdI;`@}vh zX7)`FHV23+s6W(EjJ?Y5Vvqlbj>PtjzZlyi4ozQvC@Zd-H~#WPoAo{thYpd#3F7hT zUQG0k%NTf@htO+XGZ6B=z*++n5{WU6hzEdR4TVv9u}&ncxRs^Cl-UNY zAQV}`0Z-c*;%9%^6EfOM?II?PZwI&>G9|WE z$nQl3Fm4{ne<^m$0tmGDXcIno5ffD31~Xm%rHeLVcY}uqCXl@&!TQ?;HW}pI zIaE(;PzG-xA8%R|e8)2OP+>FsSH@~% zX%s-*7b!z3xW2l$KCm*vWxS$CC-Ws(`*p+hmikfICpykdwVRFPMX(3q1dy>NM}4v5 zJkTvT%gS5y8cfsmeQ4)By6I>SlH{|jBke6;Z@mQlM*#?GK<}yc;{e5+M@o9JP14}& zp*#Ss=G!?y5tecoT-G zOqWouKu=f`6qC4yj%@gAL9uz(=C^PM#NS?~?gkczaiLB^SMYz{7g`Qlw3$tohlT+j zCnu9g>3_H$+CgK1l16}$c~aMioI&$pYBWi(5^cp3GFiHpR4;;YHI3Su=xl^|nG`Z> zCndVFH6(@-y^ck_MY`16+L_ZWGAlY(vS{P*)=OR0)vjCHag|tmv9q(icV)KNd0*>r zi@k!nWNs@W(F?Y=OUQ>6?P6=r>TPZvnm^o29mUnc8x~UPOQm;TFN7y`r=n}Lw?I2Z zTNm5JXhK;1TWu-uVi;x*d;s$>Y9l~2O*w3#3i0lQCOx94MPdDi2~Dx-Ac#B zke6u4C2+Cn=iQWW8dGbME6$~7X3lcEoF)2Jx9r)88%m8xOwvxR^6!~LF#vmYpZ{d8rhJ#M?Cqd+h*Rrt>`^_#t(mR!R_<^oc`wSa<0z# zmh%La3sexdGVdt~4wTo&DkM0`FYyG#jh(qpXtDUn0rm{IlKcR2ax6SVU|vY(LMdRD z!8<7=#=JbOaXpAd;;sZrh^_F`;(Cp|JO;_K=C+o*{Z?GEILB#nmb{)>of>kip531X z&$9Xa*{*DB@cbo<`x|>wj=MCqy;-%nU%lDinSNMqo0CrTELqvMaaH&>E%W&YkYS|fId@zpB zSLk!pFsjoE3G!%0K9Ps!wC3AJhj8q~>0B(uT~}Dc5Ns|tT9(e8W7{qm;-u848XZjA z3kIywj*$+luDEcN?D?vUhq@JiG{n~%ykpIgwQ*+aF1GM~65{9@CS6UV#9T&kWXVP% zi4@R7@ctdIqTQk1>n*pK&4HEQ#QP zRaX@i;uWgF0pA=+>%_iv9WUT8MO+HGZp?Tdnlfg+I5g$RfamG#W%4d^x7fwBbh~vc7{)LX&P9tQejIQy3n^u4OM&e$!OnJ-9Xpw-3PD z>CAP+nQQ3A@#s-JF>OR1D;N5e%3${&X&3mE0rB>LlJ>*r}#E zmU&Cs+?5#d6v4{_&oScXg}gkb4}a0y(`;wnpy5SA?9;W_Vn=cXYdyEGpFY{ zvd@U$$NWPB5wB9`aVO?!e@`2JV$}BGagwj7kp=w1z-q#;BKpxU_-CwGZAVbTS_l&s z$|O9q&1Ncj>D8s&v-pismG{W_i2umE$H*LoL&2o!1BS|h!-tta81fxMo+G~zb28!V z*-hP$DaH*1mA|fR;`_C!iRDU%?_aR&_4NnVH)WgFb|<@cy|8SP@wBP3 zjMKE^=+|#~H~yD@`Kec4TKv}Rhrf_aR{pn?O-Gqq-@Tvt_4~nlJa!CY!6fWz4&^!- zadN&+wre01kMX4GV#D)`MrU9!H^Ti)%qgTK2q9)4$n;8qdm95DgwZbpNq-L)1KNZ_ zQxyt-aKEqMlTcE@H3AznU1t2#Kn)>Wjg`k1Gr6QuyCLYajenjC;n$1OgHb+-@1m}P zZp+-ZAq>wkB|WhKdLVI2mE z0KN{ds)MM z9Q8~`XyVYmLs!0Z(LVyCZD0^g)lW>p0tkQ_kI|9w@o_@PCf&+J9;<&5`{jQu?&IWu z{2)XsiT>B2Abw=t^l$I^_m^qBgYNW#_E;IA)81F+?U|>nGAU5e`1ChL>`k!tEKpmqt2sMjUp)I7ghRI=vp=40v%9;cP z64)h(9p)M#d$3vv-frgrYz_?uD4?+%1q+rkZT)zz7K@}@GSIxp<`nVbhRye+4c`!>PttC%?jDXMf>*%`gz{9Vu@0f>le0Adb zGuHes45W|RjaUrkk$0CK=%K$7KHieS6@R!i?DnmAm%CF>A3`nNbInX0&k<$c*>GlF ztiz>}1-c0S^}Ku?%L8i840;;#9^=f{6Nn@P85vHYD|l1WDQ}!g|I!wUWl3X2AY|kq zbNJByf55zD4^l~VDfp^R8FBH-bu%OgXit9NJ|g)eDVikYx)3Ix#n*M|0(guVLSha- z2nEJXo-{3X4|YP=Sk!+K^_)fmWM8AcndpePHqV_d@rOT{t7AB#hcdCWqHS?Mg)mcl zP!9^h*k@xurEy?)kJo`WZj}A5SBFR&03b!D9kIha5o;F5*oW}QY-zy^<$1S$M+#`$u{m_WRLcVFL&fsL`ZpLj!} znB76x^$Bb>I<*C2Fdu|akM_q+qgL)E3H#MH1htS_;a;{rX|oiE0E-{~sht_tO}XC0 zI=YTC2l*@?%+#F3z8x$T1m7ahs7HBPN?>Y)^oO!{D`AzfC6MqRXdB#4>MOyoN}wA9 z*MZ)25Z%!M?jwN9AAxv^^?$H?SY9>icBVJ;*rBNZyo+$k_>C2!4TKleDeFufqdr9P zlRNWh&|`0n-dj%#OFyG8P5W|%kXWak+<@;11RGTUj zEf22MgSbc&W2vK?&!#gmrl>D)9;h*v!%*1c4Q#%a^xl_-8WBaS6NtLlFzN|BgZgT4 zC?cwt^DqAw$Xhx;U&5P8y)B9Nw%-D`F|m_S7TmBfp13A@)Xi!dvw%bdVhlz7VSvmU zFs(^@HrBuW$U~dzx3#yjcSQBI&=u+mH8X7uXJ~X`XQ!=|?p&1aRA>@gJF}gmst(OBO%7*!A-%h^=KZPS;F${OX^4$vg>k zj@OfD*zJss&FvA%?MrFL8fRox$=bGI+r#Jhb*YUHdtalByP=T&;jM*l?bwjood!c; z=|T)ukH>aI;CzHb8m~k_sZena^>iyI-`y9xO;=<#VSZw<&s2sPk?8m5A=Q(v_ zXFBl)|KNGkdS?8h4!7xgT`9s`wW_YsciSP*innK6-=^K#hmmNMF=!T=E!;d#28%^P z5sPgrtZyV3-=M(mL<2|7mJm2TJqZHcmO)CTvM5lxW7yDp=$`XO?A84HrUPNR3$wHUI)(t|FbTfP zA}gh(;+Sg6BqVVH6;$p9r0~ldi&5gNR;wH)lX#0XV?!_)qp)3v$P-oM07D2S2hkZP zzQ2*f)4*k+kJ^w*M1HVVU`_frK&HG525<(oMJMKx|{~J=QI-ndlVgjQ=W%(I0>)S8xc^p01MAIDEjxxQ&9eq)o=zsK7%=wg`9!7s#D7&d>{FXCGoIms#y()b5&vzAxM z{6-D;vFxbPYr~BAkQ^4jx6RWN(=t!8mhTvuC(m4S=7O*hvGLU}FDR%InSRcdcD}w5gW(Dykf1J9F@Ad3xM7@5Rl>1&Ak74Ck&- zf=!=3T)A-B8uY&xxNn&LQrYp;Q=1&fTjGjGJPz~sxajfT%BJ@!7p^~_c0IKVJtBln zYu>ILzG@S#&D~~XG&%nNr=}+z09StJ^2IMX&RWs5Oo+^bi(aX$eX3GkTd6#@lFLC{dpS1JR z`-jq^kk|Or_E6tiwMPM$BE!efm5ILZgtXUsQ`JfDTDrX!O`$(W>`8Ct(usuu*c1(F zp*vh62xo&*k##YAS46i$YRFzi9S`%2gnJQOGKp!!Bi(D#2kr!BB=8>R?O{7UO`34L z);IUG8~nFlV;!-*b`IQ8jVGMBMv%b%ULfd|8u~m74o!i%Bv3MmHY|1q-jPF}}0Ko|XY*m*Q_Bs<1S#AMd& z?S)rttWOKwSf}C7Gn>rsS%Or{J~!3|yj-}gKIdtbu$|2}6Mg1C^Mr6)8;`2GM-l8a zDcsHAc~Cz(OMipffzZjgMw_L@Irw3j4!fr!jzZ3=N-KHy8_9=GX^S$ zRRF|Oz&@$=z#Pj*bl|kQY*aA){@~nxb1(55GZFmZ5KPLVn8sw*Zh6*G7^p|_1|~;6 zWqX9nUJXnK`iwA%W^liA0!eOHzAmDBU|(bBhcL=a=&x_K5WZh0@3Ih)BbldJCou`{ zQTnrE$V;9ZTHab{S>yzN(a7R6s>_JK8&#e|Qs5N^PO~eccX(>oKfk0f0%24p-4t;FMxzzq6*sp^{J%YT* z>qd6)ZB<+(W;DW(fTahTk`H?sjY;#^l*cz<^wYYT;NnW59IH?16vR6;HUu;gdJeZJ z-_a;MOS10>s?L%1^tx*WLjCBIYLen&2_t%FJ#_ur1lbcYx`7lW11_C>EcYuEnWR2D zPZZEP;GR4OisrSL{+>55j2s4TzgG`r9ps|EjeM0Jaq7x%@WzcS#up!H9dbU+eWV}7 z!IvAV)x&R)df-y7VuWv0C;<=HpMkIbkSBl8@9M+K_IF^|PS@Z9DFqtH`YG;nd<`Lk zdBPVdhTq_0mjio@;K|rgFaoT6WLLqBXsd#B-w~t?SGC0Q1@M$1L_hH)w*&W1^kaW) zsF6qX|0~XMhF5m=A&B6S1b>6T)=ydWt9-f)Da*p%UMHk}1I7@Yb<9&FK>RXAqdPc}T^IDkE% zlSS9DsSl+Y7DSEeU>FEtwK1AnU>r7Wcqtcb$jwGNf2p>5|_A_ZdtT*|h@Vw&rZ z6wgcC)@XmTRLD2`xzXo6r*O@vbL$eX;AMWeWo5&yZgXN5J<~ShTf47z?n&G>;-_}j zo>RU-7W2=&X zihL9x2<_fIW|Ng5x9Apf$Xt;>Q35dfoCvC>YvNmuk3NSV==|K$%(NUBbUy9oNqlhz z&$P^#-{$!#Z>#V5zTdXZAJf9~3bW*Zx2nfmx5#T*y1LnS-L|CHoa))=IjgCsM-@i= zp3gs=S#muV?OpEJH*d?zOvC8>f?7hUhNW{hH1uQ|Qkh#anZ>Ohj?K=TbDQ7lXWY_% zy2*w9Mlq*xq2tbK-R1aeAkT=0iZR%Oy@hwZ(frQ@N3jue7P61W+yp)AdvbmzjvFVv zo@55K=ejvGRL4zu5QfwL2#QFdW(1YM zLjx+$Xf!D#5r_Cq*w#o7j|N&}u2pvtX;Anc8fXFSOySZGOf$qV2SOicU+v<)oUug25J0__(@GZ+(6gAhV6I$S;ox)z10&mWFh0V9(TZJ+f|(GdE5@X8myAa&0-0tpT!vYr@${4#L$z`W zn9&iS<1OrivyxUBD^3zQ+qH|fL_cs%($Y@$E)-5b79n7b1J!Z?<8cwCM5^%hP$7^o z$%qQc2_e#}R%nuqP>`Av3V;V928gAy#tH|AJk$WH;WQArs44eauSSrJoznOrejxksj*2O;T0Ey&F+|_I2@0z#iV2iKCS}K zh^4x$3e$38!JXI^WSQwd0+uaXyB4B>fC`1@uh|xgK1MtU4uZ{3#Ro5lf~$37-1-qn zGFE}@k@>rHY{jl!FI@P`#x>2$D?dI^*|F)f5l?0Cf~Vq4cYN?G1c}F|zDu8tG0)Ok zkh}7%?$-~`W^N0_vbviFU*8Tc43!7Kt;AO``pNzms%)7z-nb@{$#m}@fABNUL~#!8 zc#>OU7s@me_-K*5V(8P{-3WeR1k3|8 zygtw|M0~%n$ujPwpJ{#}f05s&spUH|ZHf7c%+IX)a^=brfKXKLwd&8IDZ(>N%3iqgYC9!T0B$}j31d)2l6F$}AA zOubdm7}-D{0;+KV&OT4tt7z6(UJ;9NqbupzQ%Gm%zf~Y8 ztZze*Ir>pCVb|$gskdbAnpk7dkq#M*tqfXOI;^=NT;_zfB9w)6P&<5(tphv@qK26ZNebt}9nBOa z3R^wM2n-mbeW+qc8Yc>d2%iONiB)J}b`z@EgB)R?BqQr2u^i+RXOxYo2Y85y9@&G&23oA=LqotzV^BUXVgkk!~)+E&~72p@q)SL0?n(kLXZ4 z>ZhCs9WoT75QINo+GyyJf>x zF({)}6)vz|o-drBlQb--{~=B%CqXBTz0`^wQCR^Wt>fZo33%maVZG1~qHWXn!Be!g zwQ~sP%Ky4Vx0-{d; z8}kUAGc^x~2Z`(CjJiN9)$Ad(dn3IFhlw3JPuD|!5wfkD^N_=pkX6u+ zdgowo!px(fJAHq>h2!7Qxby!8UOB3szXLD{UX%z>3FRXmvWv?M@{H@L^AJ-Y{1zPf z)QK^T`te#1z6K|a@xwIJvDSlj9u*J@xB~gHfoo#2Y)u+Y_UiL^(#DxRQ=(rZqsf5I zMBxokSy*fbSFnrqUp!&eLD#5Q4rGRhvijd>Ovx*(d+*KT(-#<5Xyjm}w-E9s>7WNr zP!Gz_X$sB zV<7%1(|j7dM-3fAu$71Zb#Ul`{X@Xkla3N5i)9D5ymP>?L)eqEUBCO z(%NzRi~X0DO0r*)YWAqpUZtGQEq%JEI|q7OR(X(k%-Zdu_&^`F{#~PXR_g()BP-9b zv$zFyOAI9eB>Qb^T_uM)RPjvYo zJ-6|UoGo_Wit%R51*fVJ-;i*`*gc2v22Oz<`qIp2flfFZK|MxpgR{Dk59^f^Z5znG z+R8P$kV-&SH_kzRsL;?@0AVid59ui9&>0P2)?uGVT%;*CZ}w+t=jXK zibYFir%3&s?`ClD!13pJUEWfU#{PNk#wL-vY3^Bg^nT{DuGwvwZw~m$gztWHK(40L z(|TvY@!ZS{erh12z0}4hzv{NNQV+O{xz#DY!4Vfv&LY1pz0jHKQ^D)$IY{%*v6~%- zzuGa+lh4+Yzgm=}Kw@#;8=V7pIy!hR2N#QEc+u;5&h@zT4wG9(_!&q}rtJ3rw(*?d zGih$ucW1f<8E%|04O9D0)GioHUExOsgs3_E(*oke8Xk&U47p#L&p?|#OU+0Dn;ATX zg0wpONn)W!MP-5&LZNKY&^27aB*stMW-?M}tN17YS4={THscZDV#Bydl_lmE4S8Ex zv@y_lyb=T;s-oRH)x5!s0}=cn3g1FdK8oT5kYSw@bcSl(D?%7yFgACZ8qjP8#^`LW z!c+-4aD`tS^yf0|B*Eh-l2C%G@f(Jjb&gZvXW2zT8f*>J96;dX0w@rMonehuz|FHH zhDi?BLTDVIkFv!CiW~#1Tmz|`&U%=K>2dwIg^-jGCQXRqN$^k&ouPGr( z^2u8yE0vCp!@pJP4`wjkf!OQKmIIluD$ueSGr;L=sCjt$O0 zeDA&ecp;cAneOq&IDH{RMHbmltuvpId3PazA6UcZ3(f2j;+Y3&czlpEoIHmOGXvYh zPQ&&Sf7;N5xX8@mFkLO*G2$PZbJ>_m41dw^4qeRJ;m`Pyb0zDOIFGw+HGB>?l-mq> zkKj#CLcla14lFlV`(x@zmQc6W`2CnX#~L4#MqM{)4DWV?{e&~w{N23@5xX5J%nA3vM@`@esG|Ix~_Wz2I# zA82~&skQ6b9|K;yX6`%qiEmdnZTjtR*<1^Q7rfql)%y9(l>;o?SR>%LA3~G3lYq}? zGyNu}`h*a{%+{ zL4;17pqC~Lo{LE@A?6KY;l=Sx!2-Mu6G;2pz_7bDWO$f%G7q~pR}kBllCDCuLP>9r zkF(f;Z2i}-V2=trjq!btNaiFMdB|;(h|;9P=?$-uM(D$_6=~>{Ee6K!%u}qxr8@KL zs;xsyU>`xA1XhhO>NFIXHMBAkdNL^N2n|k#E_=Mqai)Js-p|Xg7pw7vj!<9Ybcn9| z1Iapdyk_*_p*+VRXdb)*S_8#+uqb;CzV-eU#@@Q;H3htw>hycks6@y~GBJZT>PIP? z1DYP7o9*r0TfZ-0U;F4HGtutDem8^(XbOD2K1}LIsyNnxXZ|Dw9lYU>AQu|6(Pme% zakQ{{m{mag=7APbPR!c}M2xsxaDxgv;%5cp_4~wh(0U}{yf2uD#pXHKzXx#N8(8>> zd8O$W2U(HrpqDhbpqi*-{M*Z?b9l&%W%vzXeNZfmr*w3T%Sv_b>|grv#R zu68)=j}LARe6hAN31my(h17AV^f*FBusl@)?=or#b-P7ajf&_3A;BNPJ{o5?7r+U( zB?>n{ymi=*%Cw*zuO@XY4Hr6k4@uVBVw9%`lEL*zHK%nFoEN`=PWNi>=(Id=lJNx- zUmoTy$4an^``Rd5Zpf3mXL*78&@*Pe^Z)9L5g**@VtHz5v=l)HZ=j%1g5Wt)-3*hF zH~}OpzXvoTZ@RuLta`NJWAGSGiz43=aM#$#ZJRUEx0%xrrv_=!19cMOS4nyR7)vt` zE*a2H(THUgU{Zf%#&fUATSp^<^V%w}(llZr7 zCxGu0`P7DpDNYMPoRm;RrX9Qjkq``S!CtzS6MUy=|aeX|F_j~Sy9O54A>+`VFI%Q8Nq{=y;uP}_2xzHcyOagEw8?3wJgw=Y< z4^Sd#py3*`hZjky_*?U2MN=_D^wmxYu*+AqenHG@FSpINEF38tpmu>$$vx z8h%M@>`lAi<@u*1RDk|BL?(^#<0{RXi*drTxJ>kC1TBY-M1%E;`sGoE0eRNY;$<_! zYLO$WggQPGLlOK-P>GUh;FcB3f%K;rAffPp`x^=J*?G%rR0UYeTdAH0aV{FN6qLDl z-i+^yuPWDu`#}?T0m0Ed>RQAXH2YSe3;9erEeJPAw9Z|L@u%@$5l4Z#?t0T6aM6Re z9SX-F>RW*D(-6=mb5qN%0=2uIk=%R@O}du_Ku2n?5L*iI{d z6|rfU+-w5VKb=z)>awdZvpPCEhO(sr?>@PuL#ifjevbQa-c?Uv4g z+)!eqEz2jkk;6ov=tIpRY3`!2 zt9xu`PT5_wy4-X5nups8ZJFn;>2V%%5~+t5Kj*gfesk8&=SxmT`3*mu@2<*yHMu(1 zuquD|*W9buJ(RdLlUwBFW=dL3+ml>?Wp|J^{sp%lDme!hABb~lC9jgx9CHYjJ=ArFja z2%7%3;HDPa?$V`gET1?Z1LdL=c(%D_8q@T!skL+?(&`H*+3w%vVSMJ?2`v zx{O}I764eBX!xA}PC}FLfk*eN0#dV3Aug=UA`}x<%+Uv6YG;8m=h7KCC351I1+6Z{vy==o~cLLzQg|lbL2G$(RoTd9qviWCZNs*S(7$ zuUOe^^zGX#$9_|3imktjV)x#AW#xUmc~Loh0bbm_3fz^$SK%Dz8be>c!0;y6%wQ~& z%76SB|8+N=bykF$J$u>OP0rfn?Agm>ufL9Sqw+xPz=0;??`=`3TmXmxQ#oo_EXLb3 z0;dFky-j6TcQbX5H<|O)Z@>LZzWwx$_5ZYk%xs0{R*PD~? zRx0JEG8rbl;l|8(m||8>CHKgUo_`FrdKJ%~ju`&|Swx_L3;B!emqH#S_g(t{HGF{cZmRHF1{RoYJY-Z4> zNMaO6NP*rGb(iY>j?80ZfBa*USOb^oeQTIJ^8;atn)gwH(b^G70tfh)q zxp32;?|-s#`0m@^WxC4SpEYl}vwyatXPp4FljVvl%bEtejV$mo zcXs84?mwrd z-|-k+>G;t~#id{Fckv<*s&)i}9(OQ#n}N*E;o$pf=CH zw;5%vWax|aP|grr6}&~G4cf+J!UjcEJ9vd`2cFw6hF92!fUwxV#yyc5mQw+{V|FnO zPGiJ`zv~Z%Z`#abdd-RnuN35&6l>Nmg2$*$0_+(3=dZgm=l#g&^l-d84R@axF|Lp~ zqA&ZQ+jA{{rFt*nNQ z#;&23*)H(|@LHZhbG(A?3phUEgyj$3V~}P$y^#;oA+Sq@y;bybJb*nsQ{n!{Jv>8! z$7s%6qhek&cz$~ZbRMsjP5}XD!a!ItFq)*s${`*qo{y2FaP$?d3q%oILcC!YFYb+N zkZK*U2M|$g`8+gs?4AQ$eRAaGAI}5BsgCWy;&dFR-@HAW_g%E_$i98k z$M>OW1+T{R-aWBHIHVnbtBKTE2(%_&Py*b?Qh`NR;u z%2K}tU&Ia3(hic@+zyY*8{wKP*~17AOu)cKS61;Msj$NbX$W~8nLeOrs~DuPjg48l z>rrSORA(Xh@4T%0H|G(0W;u4A6$LMK*2${;7nU0pg>L+lpd%VS|UmMN6cGXlcdN|YYba69g@(;a!m zC38=V8aJO!TK!tMw+kOhFy6qPx?Us;xCgSFbz=~1NWve5AM$B)nQ1_KFwJZ%wlGKp zcQPo`&Eu+yFI07uB|u51aJ$A}@rbIo{{*$P=eqoK@WymIwn5yX0yu)*00jV{qrezi zS&bb`LtQ6d#odg2;bPDX57Lvv^MyX})R2r7R@g7+x%=LT`rrGN_|4-TQg{eL~!GGc#GMjEj& zflnj|<-xkyXJw#&2uM>?Am_rqga~#h9Znt5M2{oE3#1=yBfsu)p{=-7kd4=(0Yr!! zO%tG5A%(_@)B~`$W)Nrsb}Kj7l4*OLpw~&^a2s&wIz!_-V_-^HVgYP-0B_^R;hFQH zgu;iI4W47a57?9qm&LA-D3nJXiaRJ&v_yU_g(l#R5`ak(?I$&l<@Z6(eH(Au0PoS3 z;UrW9LL;!7YJ>Tdpuy`y_2%K1-TR4+fJWvO2H$y5d>u1IOr_j=kKpdlPx{vIgwXYS zzJYrNIKnUEm*$KBa3|V?F>r;DItoSleATNvK8AE}=46sH0^au>|6*AUUx2q@gL^mb zg9W2j>$f1q|49lis_y}r;ahXNLpxEeiW?zv@|M^TcGL-!SHSU;hazgt2W({US}2cg zK#*UGu0vPhU3a7lzgw7hPo@!0bi+`<@AP27S+mfLNwTUE^49{y+3VG9UIB(+(9$yW zYbstdrt{nsf>*Fn<2X#a3|kA5SoK`sL})$&uBaeUGezP$egtKZYx5qxk@RH;*HP|^ zpi(oe8*aST6(yA5UuSM>EQi2opdavkiaE)$MfXda%lwgea?8W;`_Ef{B(oqo$6 zvaDXL4M4YLmet!aQnY6F!qO|Og`L^-EJ+e80J0QDgPdxfkCEA<{+3SBRb0@n`kK>Q zM6bpRHP%+ox<*>fmt`e)Q%Cm|Q2|>!=n8OYhCHQm!~Rwzr6nu)z6EelM9CMPpcNf> zqN9U*VyorcCuUkS#8VMk%xdna4z)PF$RwsU-HUR9Q&A05HjyPat#tYdd$gwyVUnE{ zSIivP(rNcumYsBlAb9UsHgX<oT%q^dBqfZ+b;{|Q8WUSz+!=5B5BrSv=qUTIR-7vZ1}; zm*bg67Y}pM*pfK<++50RbZ;ATKTmB>Dh!mq)4Iw-s_IO)dj3zT^bBghfMF1~>^bgk zUj7v_H*TDjz_{jnZBO#~arndF>(cJs1@j;_n2O8>3n6J5e80zYp${Se_XG1jM-HN% zu;*@BX!ob4IWJ;V>RBy%9G%)gZPts4ZKE_lGs9ShhB&`WY9#-3-z5y=_tetyEwrv1S*$Mn~O=s zYnHOjO*Li~jRNnE;2y~gUfl+-nFB`CM;1Ag92#r@{(_H;;QwR@-A~5)*khu#s+?)5 z;p&JKsQ4!lEOZXcf=*@pKo4$PHYBjL(quD@h7->P?XZ{>@Or_33N_?aqmYWcGGg$3 zX4v5aPB??2xl2YfL_R}Xs48fhjf$01g7K!SPOL{}g(RLj_u+st6?4_I@v9{crWt=W zqa`aWqSlVG;lm7E(A~o85yd&V%^Zx=!`o($k3^(Rq1RnBBEdn3ReIy zD2vrv7dD5#=4Kcrex3LMht7)6MI9&*Mx$&PSjA5oGv^vV#!UtZ#&<|5Hv zI1n2=s{_yKCU1Q3#s}lExZB)(VYi?gZ@lrr?c3)GF>P(`!3URT=eXd7tYx0X`|lrx z%+!v_@Vi6NEq1ElL5s?+*|TSNlM$z^e4_E#akr`ReRev2yy-yY?OnSnPrY5~#_QLR zYkc?h=0E$h!5tWI%LVv+4=`BSG#K(O-;MvzKOEmac+F!w-uTRp!NDB|Di?rB)5L2L zI`@sEO~--_=f3iV+RT;gSXEjR z?vyz^(+s{Jc!bq=te=kJWjE#%M!Z6narCOVi2oS5og4?J!@O_J@zwGk8PDgeYkpI_ zQgB&n@aAorNtsThN^!bB0%Ubasgjjy+hycd)uVT#Bc1t*Q}mv>@A2f=BDqCfhir9* z{J?(HfW~Uu@Cgy0uK978{jxs z#?fyEcP!5?&u+hg^jf+y_m0S9(5JHN*WX)za@ZWtu3db)=@cA0_S93mW>4|Mg|#U-28-haO^r7Fl%6aXL--D2VP)n5HMq}_rT|MoG;csH$S@<#$j75_~V?-f$i1XM#$uh0;oLA)N?NQ9XJ;Udjp*fUt`AEc{xh;wp{)E{~#)j=d2zOnGpuuH^ z3-91nxP5USr1P+kv6cz3hG0n6bq#TSkTH3jYTz!54KF5cCdyyP$0l@7FD#83!CgfZ$V8ECrsP^a*K~cJTqJerF$wR>mE}<7-7O)?l|AM&dYBCw*$SL-)VV{%&FJe3_KyBH+&}4kZ23 zK0tV0HrG=U^s%#!FmoM(0ZkghsKs`m%H|X5qz^yjY|boW*eX)!Bpp}|N(-NIv`Z_G zz`IW2EBJBdF+qpVmz&i*7GhPQy9ER_DL^+6a^Ds5?KBkmHCQ8)raedI;nF}~BWEyG zvq&Nz3K_7o@t@RX=>8ri319U;jd@bX?e7@u&`u4KQsBVXLiE4hFW8smYNFZ+M9W`d zAeqY2tYCkeD3e;okL%X8#0wZJnY~xRd2v8V47E_tGEZp0S)|XPRH?`#Wo$lI1QFET)_Rc8i*eN6M;B>8fXh7wc7EI;@3L8p zTh#pkD$b9Q-;ce=M`p(ua*#_CpTWM%GiZ`eV-6ewO=?Xyxp76y80>*xd}`2Hd=c5O zOkXfWH{hn&Tf~~-$>QNF@m|^o>He{o@N(NjUl-myZ=BPPQCz)D3x?=lg_XUyj$$MX zOlTvhrzz&h5`sYXf?=MmBX4}ddmvd}qvakfGa$M6DFkX(_mm-b|A5ZCn!z~ma;}4g zv&K)>+Fe7l8Hg|Zv*7g4@EEI?neS^x4C8}o9v|~s5aJ}Dd$@%S3E)N~+!dma<_AtZ z%){>KSA*~N0i*9w@ixRXW6W)3=*Ae~@$75f*3+}(?a3CvS&^5ee z83X(=*ocLC?0U|{R7(==vBpBKV3HPEDlWp0bbb7oV@T03k@5t_g1)bc z!;a%*7(1mG@iDf^L4}cV(fDptQm4728b&C0M&egD=AFU-YHSZ^ZUGOhqQIMZFU}pL z2L)CRoT>1gmp83opPuukM<8(T+$@%EtrnpM^9!>DnYm#tH6e$p?SwVDg*>bV$ntoD ztUzIaCi8i`2|>6w=lx*kWAaaXFCayilK0r3%4-Gt?Dk6hp7fTdcAfhXukg6S7CT-g zl#}nfWVkFoEy{cG?DHcPXu+s>N>Xo;KHl5A|FW;#QYhlF$-+7KTHwAfKCyI89#^}? zeAX|N@ECNkWy1fUA4s;K*TXjpI#CpDbad^}3h9q%3{(cRx#K|B-_q-FSbVdUbE<=3;(dUKg)D3;ng8 zM~>SmhAi+4*!~GX@Zhq-cbSDB;K)wWM0R^|*BJZI)gix_6}WsD8}zT81P1=3cd`m- za{C^dRD*r8FmW+{5yw|z?@$nopR{FiKu==d8up6v>m0HFmHOc7CAnj})VIj;W;O`$ zs;wpnhJga8D#i<=azZLONLv&Yv~tI2T;iu%_?49Kbyj!`^PHrK?Jk~vvg+}QK&%`pq01`l+D5Qhb| zxCe$)EUaw8g5Gc8e49DVk>+g8i2Vh7+tQlt#*Fi9?o zgDT8Kw`|U5yEv&v`PvaDkx|P}0^Z$+N71aw1~D!?x#x!Ll_rt(JvJ}ybW$(c$U4qh z;DkeO3yckIV6DfYk6>)B7K%%29Dq6)2U%xVx{khmDp0U5_@rld7_v>H3{_CTk{Ms*$zUQYuef@v_@_#IU`B@R+{aLe+Qf8ei+L!@B3fB1;0go%i}*9xT$J=rm(T$FXgsS zm-*Z_`yqJ`WP`$%bj)O5p8fFXQ~j1(MLO{(aysG1Rhm&O-((y^s-=$AjVgxQh@XkJ z&v#=#Bla@#?^S=zPCv{omf_#NY4ts}{uYkqvu=FYcGlhMv+QPBaA<9bb)&;C_lZSX zyn%NCa0rgoltb8NNWJY+qk#ZA&pHeCTpKzWqu3F7=NvhF6%&^Wv*t{nf89 zq*u0Vc|OB?E}`_k<>mO9>6Kr+#xgw5e~338th|=RbJmx`>uKoM$no1!`}p^)fBo*u zPy9E3-uJ(^Tsrijqesp8GoNAImnXJx8lRSVgB8y%(TPM9n_cYl8x)65x4u7TCSJJl zY_nq^(hr|tTMaCSTH$NCXg{Lel*e+%N(Xio@N>N0#by`5S7%P-eF9g6yQox?CD6JpEc%V8)o;+ubSPe>2^^LsLL%T%vc=Y;zL)x!%% zc)v{Oz)&cJJSuc2<3=}0x**_-m;xp)I)yMZfX6^f!a#+A?}}&`Q%`7&WzE2o$V-u# zhpTZAPp>(cQCtba78{%E96$zpQ1G@fG~?pW5{{|n7bBF6QVVc~^y3a=3MB>?biyJ2 zgNHyCjG>IiL%7bM<2VWiN!N9iz<0HH|D+?aHAdqqnro;`=n|ZAiWg~D;U7&Jt^flK zHy%+rNwmfq{Uh3EpA$8&<8_9}74I*zzttRk`QitzDihUSzlqMj68zWBa9G~_9Toqe z4?8h`S>DF-xFPQ^W00O!Lm8L92w|p1(@~bg5alwwp53vgh;|cVy?2h-ynzO=C*EN) z`$)sTX0}22k8#$5SR=N^&G+ZDZ^^^TfRmJS@X*& zFUNZxpTU^L$PwZFC`U5XZfyO2nMFM~w<^vlh&rI!KV!!S3 z*Y@q8klU$%+l*Iiu6y7fVh@!UoHd7&34UA`ueLM~42;RacVK*sWj{4DjHZU*jzqJs zYL!(mAL#&7OE1gm(L9rMFI z6rGQ289Z%9n;aUMnIK0)9`*WN);9*X3fl#YHy|7%`DOSRo})kwLVRI3IF-NoTR5cW z)jZs4Vp1H;W0A^E^!^9vI*@lRIR$;M!v!^awPT+Dad`)QA9r>233}(>+_tFK zwkzv=AZvDG5#1>mjfi@%I@~JYKEZEU9~PB=``PaabEQ+JYF&V3BB3zg?>eZb9xw+h zkYrHl$ZJ7~)#4+M`~mJKAGvW6<+?|KY3t*Ci7R5wctL45+79ikTAW&%lNmsKs`prq zN<@GAU!3`@fkoS75&QS(v={F#o(I7Y+srOrfI9UBqB7Qj3kRuO!4}u=4$yg663nwZ zyR~KKa?jW2Q7*Rc@E#+ciV1x=Z#bz<&-Fg93-Ui*&HW_({UBbOzQN_jWZpdpbLK9{ z-*;ja_T2fO4f@MP6ZAYT(TWcb&~1>*)d5|V=)VdC6%_yVB)N6iZ)I7&GeIQ=hSr!0 zFmoDfhG$6O$7mI4t$~aZFi5lcDHmrc;Dr@e;kt+Sz6E%c71-)AcS_3FzvLHc@GO>!rR?MrXannNjo<+!2gB?#5>KJM zJa#Ukv%ST|C7gvOZz$eW+FB@0W5?pH+=+_9&r@cLy%5TgOaG4iYm01ep05A>U}uTw z5~Oj?r6+y3BeDB!dl}e7q){tdyLO9jV3#(ZT>=|I963>EBRM$pTBu^v4E%Ty%&;>5 zbY`85&laGHUvLrU(zrjQ19F1^k9!AW67SPfgMs4>u}?2xeK<)3gV?W#0~8aUr)9q? zG@Ae{je{H51B^C&KR}@3%OEL+ic=VB8p8)B7yMz!tDq>!1bW*Po$Mz)o_3Cn2A}3-&V`J}~i*Y`Vg!fQ11`@gU z;`W?=OT(pIM|-pmJRLV{TH4i-T@gy) zhxRIlMvhxssiL=yK+6z*+qyPZpb`= z)%k?KacnjgX_ruOc6Dr5Hk5_!Yi=yvJ*ve;OL1)LLx+m<>qd~P#R6)TV8Tz>iCtmB zI8L#Fo@6m=jhX*e$e@zjQZdPMoak>U?wD+?p9q*F(Gn+e$?a52(eX*X$+56uqkOz$ zooYkj76H@=Te7kxy*jDlf!r#;TIQ|f%WD2<096{@`h{0 zg%X8GveSsT5iVDd^`z8bDVx{I-Zoc}#|H~gJl!D2x6?{w};`=ale_HtH=^ z>>3E+3KT4{q7GK{(iWEm@cIUPQ&6NzMdBMF!)_4o<1jmqzQNT9@+N`;)K(<>8ZF7a zry~^*m2a_+K$rQYxuCPDw`BR*C-Lq=)Xpz~yN_ykGh#x#wWdi?XPfybCEFPgvK;P( zfX!?cr`a0(yfvo-a3+ul#FDXv%^nLY3sI11WhbrJ#g6f2W3aChd=`sDJ$q=81;rJ3 zuO&*+WW-@#CJu)HgL&4ksX4)RLKYT+_~5}UihR} zbg&$3BX~|ll*GsdaC67pC^Aumtt&Cx#(@nnh;+>D7;&d#0eq>}QDikYX(bkJx22R4kyw09gSpr zL7e*sFW(5R#PGLjUsJ!|Otz)^iDugvYcuPX4-k#5tm-Rw8r zFzMXVZv;2|jp*fZ?4;{xK{wF+g<&JC04)bF1AtoYikO{yn&Fz zP9zj_-H^jhB$VZbHtY}xWtpK3I|M>86YJ7jB%y4@(1slXp)51B;d(*qVR2^3VcF^3 zy5G?`PCB`1EMDa0`loHtZfDvf<JvTD)k@hT1IKN~G66k+?cK_AI)ZN}Gv3|59Z~ z^&LdH+LEIc(FHYNpvZW2NaN&)(V}QS(OKjwstMWwHVqODb8M*$D4Ax9%-_rhtP*h; zxdO=|a4I3YLARWp$s zcPuG`&9vL&A%eKps3#+dXwdWykjwHrt2&ED!~h1VZ8}lXJ+>|H-eU#416Og(X0vu1 zE1p_&b2x@t&=d}W!$UOg7bn(I9kQ3Q!SjwjoGIrpUi5`7YK1~y$>O1)Ji}5Z1G8~F zGC-&dzC)N(G3^HkOwpq0Q8Zp`+kxft_(FZ{(TWIjY*jr2j6iI-&}W5+G*2gtj~7U9 z!$7BMtQAGwsqD}RY=4*uU6PwI4xxXRCsZ^?l%v7Ns_0M;$#XGY$mJ6o;%@5AX=HBC z&Ww~5#+)eU%}03s2+3SS^eEd~wpb{t@O(8yif9yUDhq?9Bc=*wfF+OTYk~B39-9^u zMOc4kMxu48Zs&7>*PAlY$*2;-FzsolZsWjb9850YgBp!ft9G3LcDSrx+IBs*vp_g{ zCFX+^W5Ad&Mj_j02G}yJZ`^)6DP&6yVT3T>m|0S;&?H@QT!7)YpTHFr-;wFu1mh-xx#gntDM<>2OSg2NLP(OFTpmAwiHw{I3k``C|FC1hgd|=O4M7W zF(K`#*Xq#UY{=TbiC^($C>2djfv z4+46)vZeYJ4`+j=Qm>SNW`JroTosh;Vkb++b%GpSQP}odK~9#hJ)3&mf=df`8nG|u26xa>kMK&n zHos$%o;pYN1T>i=S-p0#_XoKgxxI3y*)udQ&i17re0ZqjuS`nm4GsE!FuB*0LuX2V zLT#tq)*H`W8TWnvi(XKbkL@f?en6JC?vay&m76G#_?ygm{tM{T-pLn+E~U~*6Ow?9 z@g&aqK0^~=Y@F@eM2TpqL_uj$Ry^Q|30w-upQX#t!@chHK}C^5TR9I$4B$_euFVM? z#sH87UXgX+Shd2m3K`C#Vnf2k*yK|HDxog;jnTaLJT>aqt_5RH5!%1MedwC#XaGkx{Bjb-N@f>XK*aZk+SV` zitX5#6c#h4Q9II*j`LCdbu=hjtz+v!x_3v(m;nxc(q+ho}kuFz*!vD&g5 zmE%%qtgXVT4C-%G{mWLaZZ&ZJy z@{RO&Z+?T43@$HlF<~|kYbzFtvjZWAok%Fl4Q<#V5Xv$aI}}-#P&Nu}*dY+gGD917 z2!yiC(1vdz=u=#&d;eNhx{b_ibO|th(R3MJLx*NB;JXs|_TERNhsoK^*Cm>qi)L`t>yRSh@r~lfUt7|khzOI&S`pg}g}?Gf5HR)h9zgrhaSM-BBwG^R1VNU@M*ccU+gRyswP!@rxe&0QfGEeRsKru1eg1fsc{z z%j(t_VCAgLOq`s}bCJ#9KE?VPpszp`tM^@WE&FMra?3~M66c}c`VMt5%7cHcJw!;X zPX@*2MRE+_H4oJNY9FpS(XT4*t;M!3b(bAF{L}0D)iUlvF8QdV(WfH&9HH#}D~NH- zD161^TOFbF5Gg@^4G>h}HdKkuH==_;9_~~fV5k{OO?AjwD#ztBHpb0Z#OCrc`y}6h zZuAUMFHeI zhk`P5EZ3w=>~qR~DdLq>qYSFh?suSHqvculfm^jk7%NkSTZQJ9Nm)0O!;U7G3y^O# zZw0#qIIPMk96~jiq~;k53n=2dx>TtLutdllVZ2Q74es4U&*Wy|8x$0B<7MvS{>^+` z1pB?qq%(|(!nkSZltWQ+0-gk$f(6{igfzwv6Y5(kN3cd2976bJ%6$JBY~%6Z^3^oj zv3(CYMmgH+q52M#BYzFIuM_89tfpfW!pIQyYrsI#;Eo#GC+a(D6r0kamf1(|o^@a9su0J)lBTPr!fvn!Hcq^rS)n}$^6JB(CH+!-+JvOD415cu* zssTmL=)kNugBdbEOQLj!Fx7*KXwK++7xkE&(t$fXK2LLbGq|9~Xju1=qm+TWNK2#~ zkA|UAND?aFr$;?SCjlvZ-{8^h7()_W&?v`O8OotiEE%|S({!mu{xqfk5dBk&3-FH; z&ETGEU}S-jc1eKIfq)1Wejj zoTvO7Zn?t0;^0IjZyr9UhADmnW^0g_a8xdOJSzyZGtce}?gsq}=ys{#e*r6jn}^Gi zayj?98!7L9p6>cAWGlDkb~{V7RH3L_Xd8GQ-qnxt~xP@sGo^q?C~P3nVD@Q+JhBC>0q zKrhjS2|JhZZ7p{mN^IB&0ErtHxsJsL;esu89VcEIc^qlx0Sd)yttCOSvKZ$y<7-Jm zf-SxT15j#LLg!R=ke3N8jvABGU=h6-^A8NS7rOW$Xw0_-+d9T7Vy(CoAK+?Ri`OF_ z)SXDfJdJ`_tSQ11q?Ow8-L!!Z7o){XV9vq>xYvL(r>OQ$Bv8hFSC=nDjo}8+jN{>u zag|7;$|^!DOwEEha18P-wO3U^|L~{KOC}W^ z9L`xl#}s|bI`c4;+*pR2w&lvgVJk(_maioEkWuK2!g#TIFj$_Whj=K3S*{BRY)i2& zNbztYH8=`eh(<9TUgG*#O8Jm9j;EP}R7469e>R|oD>7~m8f{CF?qkugK?DDv7n{HJ nNf>mp6vtsSI4qnN$~4c*lf|g!QT{MOV literal 0 HcmV?d00001 diff --git a/util/wan_aftup/A104dm_0100_V36.BIN b/util/wan_aftup/A104dm_0100_V36.BIN deleted file mode 100644 index 15bbdbd7936c0d9521e0a55dd43a616e5bffd89b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 402936 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

    tdO~OM4a%UOHsHmuBAAI==eR|Ms81{|Di}uYUCV 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 diff --git a/util/wan_aftup/A101dm_0040_V37.BIN b/util/wan_aftup/A101dm_0040_V37.BIN new file mode 100644 index 0000000000000000000000000000000000000000..fe6b240f501a5a9ce04faa33e6c17fc47d2f3cfa GIT binary patch literal 212392 zcmd444|p8Kl`nd#r)ShNZjE|m*(;1NC0T)iRiJGdB8WgGA!ETBG6od>JSQHAL+o7c zuGp}Vv-#taEE_=_G7#cx@{+v4Aur%$gV@d9>%>WDV*^g&#AdU}=DQo#!elr3cJ~Vc z<9KfpnD;x?-J_8#jM?|T`*NhIQ|HvFQ>XrR^_l9PN~Ka6d}!D0E(uLUldeuNZgaH_ z{QIxCI-&Jw;(IP7K$8>6m0Yr!rij)qS(Kq;%=!ZWC$yna`~leV2V#Pb+T^eQK-wfA zNuHFERevCj^#6|;`SlJJ?b@=p(AIKtDV>oEnT44CPhpzY6YHzR`V#V3Z;ZLz>2j!? zM1H1WN})DiEj=wC+Wl7PSZ`boSNnVpB2aPUe-oBbIWhjpyE(oG;cpqnuz?n~;pf!ZIV;e#>}bZ)C{7m!zfo}S zx{)Z~USqemHyeDq_QrZM#WyHtf`{u!sV){Qg|r>D%{fbC+rGK#9m|&ENQ5UM%>1~04X#PArRg-=Kgv#p zC=%mf7JVvxrrfB!xZfGa_Ark1MQY9XVE*Yskp;0%#4uw%fG8~pc16TIH#vY zFB`4R8X#zd*F<4NjZW}Eh1IGl1gcrPZ4$!JBfE)c6!QIj(AQ^Q2YLSQvq?%K$(K6N zf-Dni0o{_X+9Q(Mpp04_>etACd;V6Ilthivx<{pj7St#Dw5gE9^&P@cCLKsaGz7y$ zRL{9uL_%)dCZK(;f>$sp;Y0+z8@>!ED10o;5GBKz8MR>{l#g;#VZz86rXPxw!kioy zz{0;W*@22uPB2OJH8RuD=~yYu0Y=%OER`orYe(rGtSsz^h=MnrG(n|8xGyVlEoz`N zqfL-%U~z;q5%zdaXb`E8kp=DQg|2UuIW4?J_KUPF1M9j`*{e;fWlRX8+$^LaVwnL3 zvs3tl%G{9UX5a~C$dI2BuEcop8|=^P%%UIz3f%cv!l z$x_!4!KMt~(zcM3Y%iY*Ae=8)Yi%UVxoK(ux4_Z$HFbr!z z#c?O*pBgJ3#&bYCR^ucUN5ZBcD`L!&lN3YPp4>bJgqn$CJ~|^zm=3jxs)!Neh&(+M z897_niutF;KE>%X6@aT|atsyCcFcKCGDCAv1G*ZI8!N^#N{MmE2X#{Up+qbfWBNma zHyW*P+1PmG#F#6|N~Y-%X4TM!Je@$ZP?-Ca;!mEv>7AXO72?kzprK)Ku(Ol!eN&~v z83#MhJ(uI14TloL!*-J63@3(P3nQI$aFB*iRL=BAImPmDeBLmC1CL{fW-s z`jxL$mVJmijqp&tJ3PE+&++77XXPgX-$PB|lCxmJ^Uqg~QGV@OmU%w+b^H0;wV7jy zGc)x1>-2bX<;o4)w{KuuF1l#A-{?WW^jibB$b)vaFK#X!rR9rhK;QA$yYwC3tock- zr?^~9x*h8&Fwr(k+peKYjPVKOTpEyzQDTds2k7%{M4z`9$DdkR*2A);cGl?<>+*n2 zIVO$Mx0(E0?f`9rK0stkC}%jQA1GJxc*CCHP4f)sBHE*f_Ano`#VnAGuoc@1p3sP! z4~ua8O<*8ePToi?o^K`S<}ym{QCVCbK3s;uv4Xh#7{~n6!2y+Zsu26?PML$}N<%}1 z_-}Av*!q-?075U+$?K`Vo)gDXIi@iG%L0os~_`KVL3abemNP z+N=gAlbO;DPGVlNzO&L$dHy|1{mPTak0*vJQ=D(faAnw{%BITedn$zr1Q-T~hYgN` z!OoQ{Il5u{zI|-hp~RuY_1E{fhY~AS9!jh&%(nGZG`Ig$9?Qu{>Dg1s#c4{?;W%E4 z!PnZc6M=013`Si-$^H@0Rv>u*GGB60RRI4-Ola&C)?u?6Nc~G%j=K{{tC+8(*$fFI z;cz;A32ffWCN`&8U;if?p_gS7z?vkk!M25b43G;Nbe`qD_75l@11Y+Mo8(gmny4J0 zO`4mjcxp+cLJpugm|og>+?xHO_q0TkPhFB`j{>C}lILWe7W~!@M4T2;txI7O5Zcy|quaFXSuQNqi9zpk|sYCxI zzOi}R&^S-+M{|?g?!A+AW~Xq1)t)Y`zH>h0>ERVat}3FIQ|nB=tmRve2waU{9(`y9 z(pCRbVtuACw2I`b<}^JAoa=$(ygGUgfZ}fDZy>4!8u*RdH?V$$CR~U) zk78hqK}A{?sXOhqAxgG4jtXO(XiOL3Q`2UPJR4oHl~a|}XiKyO%=%A4`SkJy@wy{V zl6PQ2(-mNkpWBU=c2bJy8wsKh+ob7dHl3sY3lh~pHlc~0Y2Ek%`&=5RgV*5Gd&L08 z#kI)W@>6^0Vstts4zGv%Lt}ZN)c9dR^Xck(r--Tt8so(GG>^{LG5Pfmh863CApd`qANTG}dd9;4`#nYqE6g)%bSHB6(DCPW}P!gGC_fUci- zGGLVm>d=?>VNjJ#5ngU{k-S?jZlr^MEX3FqMR)UgB(}b|JGFT=c@J9MDaBXW$tKG< zo~YPyeu4DJ|5O+7I&=;#3(H*&h4yk%8Jn&^t@TgW(UJli1D%9(MMRhWG~7Y0maqIS#5|(G-Cmj!XHb)<>>R82Og3DXf;3% zP8=Z-kuDPXzfVch(r*>YZ<6qSH5DYz_VxD(hi7H;byiLZfhor;l9b5e$pP7C_X)L( zS}m{9ZfuoEXjLTJu%U->FwK%}=b;S3N9%!pa@t`qQWQ8~^B>1C0QkBX(x?ys)XBij z&rnFStI9aV4DF=(J}q4zx&!MkFMhLRQxOiW%S-H@oP1r8L@HXc#}WMR9I+23%+tT^BlHJJWkay*!K&RFJNl#)^THuxNK znf$mUS|n2itcs3%r+*1fs}^wQ(Cxa}jYhLZ^}RyDAIf@OiMk!>7T}1|8L(F4v=$xu_RyubLw@s&U46y0>Tqqlo@ID&fU&GXf?IEX9`Q7i1h0!NTRdx;jEbtR~$Ffu(qMoKm7b_gNG_djwEuG9`@J##a~p;Zg~9HuU9IG#GXBa z)S2jcl6{=9?qu@u$Ju*w?zug4jZ=DABj>~2uyR`#Fzm-KlmMMKYBSY>KNR>)4oE`@IA)<$I$N=`UzRa_#n^W ze4+l(2Z{99FUh{c8oy*L_l9_ee=*b<`aO+lGXgG~{g0t2g!EC zzCNyVYJBCXu?Gnx{q%3nW1rr(U-GT7 zHUryEC~4!S!_c^nguX#81o?3xVVW<+*yiS$%j8y(QbIfVZ z7k`u(t@<`ielPSvsu}Ag-`mUS@EM}~WEpF`;+6!UJvbziKxnWL ziBM>#$_H5sVLpW%+OY4JKmu(YmJ|6PHJCF&GbxB7`$Q~N?1DFN>Beq07gi2wmU0e$I!=tq1~At2iM0#X7J^8>y^ zq%zE7Ow>>P&iAniNF*$85~>nM73Ac|m6?aB-#FaDtK?2JTaduL@qA-ZQ07|}+=X%B z`-u)GY}Q2@Difd?nJ^0Wi3F6x93t4?mQeeHr`sT3xBta{V^OW}thh2-&6W;jlDPpx z!SEl8QD!)14+H0-vYT?YVmd*Mm=k2gN%K%agWJMp1dt}QR6_H-p+-{30|@1fjE%Zj z8VpTRNkLju!qSRq1PnT7N!(Csj+>0VjB&_CvNHivN`|7$G&=4vN)SO(co9%J|`ivkYP*+s)sOq1sj-v=?;Aib_W^v@~&v5m-eN zL1hgTIcz|ixCsOrwH|BXQHyQ|=mcDmMRHSBw{uW61&cira|J zn>sMN6(qEu1l;qAzCl3}1wCr9%_#IPA^CQ*jjl|%Oqk& ze9+$p0yr3WBniro1>h)Wl=*=u@-=ug8(c(f_rfnRIn<&&Me`?%Sa{I-lU_d9i!PmN zzJujSS{K%7qALv1)pXcJWD<@&X&4CNzKH1%fTRj?S?ThR; zEglP>j3rpO_lh9Jy7u3B2v zbCX$3QHzUmRC95~okX|tp(;S?VPl8lIM>&-AtQKBbP;QMkb@aaVhv$shk3+!dLbCx zR9pAvRwbwdZ3@2#<{o_U^A*0ZDL(P9sg=kd^yX4qp!b~q5*3?B-~9|_*I|n2ghj0S z>cX|G9A6Hhydlb`%P`|YFb=cvvmnp5E~k|r!fLw0JI9(N?<=WNYUO_sG;X1V)^qw@ zvoe9y3Lnzgf+J)aeJyMj=9H1vwzS>b3m%S-HG0Z|a|tW|8Is1pBb-6BP1i~4K*dVf z>x&K&jctwxMdyGETP%$fv`bJB!jqPR&wtkxx5X6An_T zRHXHVlpMln$@rNe%$FTJw>?Ne$G0&{Txq7um1(#1UFkWYZ`k$Wcf_)jb#4Oc+JRMr z7H1vGe8ewg-GQqey80tSMQ%9y4i}Sl>z!tds5D*fxUQdfiyJ@o9k%GN>IrVNZ^KTU zyUCU@@nEP@Fta}QO?M)V%$Z~+>tQMsvRSgr$$FRv$e7Wc{0@)jMUs7>UXhR;dI(-X zoQp&r2BcuJn~pAe7?8LEpm{E5hh4#=KXqYGCcAl&PxHE6|6^_!>h&=+lPf%$`VpEw z?`kbjZKK?IH(_1plN>xc6R^BKMXHoOvxMB%eG#SI7=@ zi+XZ(?@gCn){#^`PAit*UVt$;+t@|j#ieD>U+m+ldEbAPKFqc+E?f*#nzVlt9>ksM z&M#jho6+jICKwH~@y|5?_#s+EJ_Z047V+Hn;2(BVp5IP|EXGL-6&`Jd3lm@ElC4py zi|<}dv#|p9q%tVeG>A;6FncDzvMMOSd0+vqyvDH^rC@NzU@Qb74MPfLFb5)%Qkr;R zF-Q|KR#7ZOu@a^*JF0*-s8ZEPHFY{-+A1V##A>XZ7OlpjGG*KvRyF11hz5&44Cq7C zAKKP5%8Us38We&Kkqi+AQyfFLa1R2c#Ih$%R6{k}0vv;hmO_dvp@knMBLx~JOp-Jq z0v$&p9JYp#RkqJD6I5A|9aVZ(q_$$Sl1?V2VTB>0Y;goKjzlAwq-i>jf>x6vx6Zbs znge^OT9C-ffk}Dgt+Sdpvs;G zd$?}c_#b-Q(C^qe7~+P`wKz@%A4bF4!k!$Td0bm~>80bxnKx5Ao_pW5b`=~T6-g^r zRGwTwo2YX>`!td(k}E3Z=Z*eOzrlIOJK&S_;c*OlHdP*PSX+70r&lY-RW>qkjbjG4!np;Gy$kN)P*zWeb#j}IFD%K~;CTp~7XPvV5+wV5x`|7hH>eI=z+ z4Gr5j)H|Kj+E{)(Jo6Q5n4WGp)A;DM`i|Yv=ZP>KA$)wHPY-7dBY$5lHp?5!@G=L? zK1vg>DhMMwI=dLAwHSXN`yFFnAp1J`OkCh+x2PlyN-9*XknLt2j}@Ml1I7F^=Q$*~1dXEX!0uV$;I_ z?1izJVVn-`0Hk!3m-Xn_hY1zWehJ{pQaO9cnN>{o%IhbB>QE8wU5}dVJ1%h>FHs4*InkZEW@u~NRhe>+vhkJgveM8Pai%z_{VfzXJpZ)3$+m9X%4h`<& zd9z{r67Z&{#a#V16ke5Qtb~CK+f$=m|J-9=V%{4vSm8_qpF2F*fG2W*zgA}hE4gYU zwizohmcu+q=8r4pYreq-K<@*buB5F&GF@c3aI7;{%oa)pdp%Pg&M_Li?U$SogGQz} z9_9z-+ZObhD~$ry*t}_wRq&x*R;g;FRE7<=1+N#29NK&+@&YS2|5B}IB|A;Bi?M0C(=%D;g^tYNoi0Vgb1MdjwJ z-N~uA&#+mDAhWR#OG)&=vpF*2XN^H!AMY()CMnM*kd$~LzYj? z`#}Q90qFzOzXTZNncx8OETN-606`L;7y??de@WuS@Bf%~K07~RdRAazkOf4y(NTB= zmoR@4H^6{IkUWsU6J_&*5zX~gMnC=d$3kJ~ybn%DNHR!#0)l68Ul4GNK?i%E`F`-! zkDf(6_AS}}ELe5H)01_Uq(HF%T9a`ZpMDv%@fJVM%dy$Ojg7-VN~p4`rm27{4L*Z~ zLl0wGY1qcMUJ5M9HfxilfykB+QZ`~SNR613w(Z#x5z7(|#F)rKtHd9ag@6H#qBK6Z zxsy6k=7NzQa@f;JDKWmZr6CXl-&}|RPqCo3$kMXclB!HjFy@t`ZH%yCtktUoS@4`n zqbac~U1YkP!; ztBU_;9T-!g9AYVa2-gyY8_=bb%N&VSnRH%D)`_zqQP<@!2q>#O9Q zIO{~}1GrDqj-_vRyjw@%e_L(WC^4hIhSr5}xZ#LtmJ+x)WjwDQ=GXa68QTUCKP-(nvW& z=|*(Q7e@Qw!Gk_;LaQ?8w~dl8sM=0^8kx%o@w5nfi1gm|Dekw_fdOMOwmZl;3KonV zf`9~nQsCk<;Empj@i3j;#9(mN3yAKz_g zPIRL!2*%-Z(B#075?mB$vmbjfvV){uQrsSyU?|KR?lg>J=UJ?Qf$)11kW2+0aWDN# zlKMV9i33>{l;6q)@1{D3i&*FW{T9-Te!3#{R|V2Lo`wTbXpGcRqHV`_) zxWRBQ5Qw|z3aHb+6z)?fchO|2*<;c=X^(nL8(B$GQri6#gk@^g|4Sx2e+U{*a?~5C( z)$}0zHSKLAGpey)h;~a-EA};h{vI{HyiJa@Dy$l+wA40V95=U)DJ)A;^Drmj$@B9v zO^SReA8gam;!*qgvey~8N6JySFB_>tx(Yg_#01hbLam|g<0KJ51F+kYYlBiiS)`n%{VC_YsS=&bMWLxB9lBsTc_u`hnq$_uvLEYVFrQL7ZAHVWD=hL5J{$~nm=$jK< z`v|f;m{5EwBR=dEFLoBTZ+-XHHJ5bDZZU)n@)qCoatpiWX^3DwzYilHUE)3AP}8K9 z>GH4Iv1ScCk}YlrbuY|4)$NHVT4 zoE9HPILEYEBt17;D~&b;s4l@L8(%sx-6P%L_2W|=R+a`O!;;IS%Z5V0B2XIo5;4gL z<}u*dyrapI?M$$*R4a~gXL1A|p@A_|c)4QmruPbL6^mGmvDg`8>_N69+r^4P9HzlL z97bk`DDSO;Mustb7y}f|oLpST5O9yj$;QfwKDfVek&Kdf1PRmTl19K&%-67<>kG`y zR^o5KVgO$v&Kyo>eg@oD_c{IzFRj#oNkRh+y@fd}}T`8~!D!*NcB>(TgLV%qSS0MzE^ zxSQEz_#8~>%0V2?>S;K%$DIFSUYvJ!zH^6>nkB+q9h<8-jX^OBqvl@H8in&+;g{8hc+ z#}Ma>eU9u~tmdoH#d)iKM{~|H))3)fTIYaPj}B-;d+nW5(w(2f#fkU(5*N2+{4g zcX$7f#+7INU#ad}j-J)o**SR8;PK;+KYolb-ZvfF`FO+NYTNBuK)=6`Dr?6%9E8NZ96&cxVvc6rnNVQx|uV#uBImY z$vywp6WAog?<((k&!Izy28Zu@>8tf(g;+^vRVtG!#2{@jg%=9PaYKLlkT5>Let|{K zQj+trFOVa40vkkN^c8MnOr$nozkoy%h7+(dNdk8O*z4*@4*Y4r#bhSU<*|UxmwjGj z;p;qj2n;-Xt^WaAWyZhM*jplf1{gh&&rx>~1rjbOpY6r1udyFCV}GhSSByD+ny}jB z91}uvhW1jgq5DOz)S$=i;gH~@VKf7zp#r`s0yr8$8K1rMC6+f|E|<|>F=)sY!H+?N z`RINya8pHlxjdYQL^}X0JIFJ3yb;1hxF#P-GH^fV2cPvaUfHS*KdcY-~eZG zg1uoRXhX_0)CERrnvtlZpdVxcK1ujI+TOvRO!{ux`4D;>PLt%osOTEts#p%x?C$S+I1LCgBqmTL0@L z%b9eT!SPx!SHsOn_++0*Z9$`Drfk~TiZ$e@a>}c5TqbwtqZ(4p(k3%we*-)dq^O}a zl%ZF$iSb50OY=T^5-tNGIfg^oV)C|yY7&P!rE)oon4~-AQiU2l=n4RQ zdy@up5Z1wCUyq)qIW{*Kvpy-Bs8EJe2KQbGQoe?)D04`-F=W6ac_#y3ik%x2ZikBS z0l$spju-H(wj5Z$<%I<8+h4RcPYKfXMf&5mlQYlev_FXZm2tW)Y&k2>D|66o$s06k zVzom}CR^MO^dqYM(;PEz@~Q$eB7x;%c$tRb<-?~hVX*8Cnjxej!EpH1 zp|`YlEndNqtCo_+e7t*g%Cwb3UDy87*o*a~2t!ge_CXEXEj@TA z)p906D01tX2CES^Nir0uhGsNyxr*P6hjj?2{2Dbz-uD+g04~m>&JCIzd%Gk#@!^ce8-Etn*y@waLi6UH+YLpge7-?#Ava;0*lh3BU>$L0i57_Xkr<>Dy z4BNo;m`L~b_S1PKOSh9HGc=~G0b1s1*^B|`QE$7Ikpfq1Ww%rBzz!isg(Z5GwpvF< zTd8k#t86UXQxMW>wejtZudxQ$;@)nE1VJp7I!sSLb zO2l({_$nJMF}})%Z?YwwwrTmcG4=+Ds&%!y)rhZHck=AH!9^s~B&TK%3GoI?x%TWqm-?S#%)X}wZBKx2!?52sE z=<|1#>`VIJvvG%HE7JDH_jcgKC(er0OLwAw;$sgkNDr#cEN@V)R0?M- z(-`)DO5VE{lYev8C%U-g75;k~BOMj->fP@aoDVK;rZs8ri(mc%^0O-BCCV3Cuv^BO zlV5fSNA-=nCn_te=!E){iW0$8QnLp zAsuH&a(f^XFWz`%=7tv1<)h5`U3MdG-C> zJ4#q4;C%p)r+~vsIeVjjC1mCe=z|-pFOl15oV)o!wjxNTT zi+lN&<`%P8fIpGH*}`dP*L92V(ozBMcf?Uk>V`|t!#PRcdn7+be%A%<-TWfRmhSeJ z8|dvrcvFUey2biFv;jto$88YhA!5iTtZY9p3JQaDzCJP=p}RHGC^fzM!Tve2yt zjE*QC^VpTiCDbl}NDIfg(H>=}wlZiL^QsZUnx;aLczVRrb7OuZ9lw( zdv&5yRHhz(e1Z4US62%>WSVnq4<|2^4N0+wHa$6ymUVXW<|~5f_L}i7N4lf*vu}Lj zu_NF3#5d>sEdNA_r#kx?jW01eix~Rg)fE`B=EJ_r$d?(*mYU*SKlIi3PUH7R*7DfK@a$t$bP(GB+UHGr4Ma45DfvdA zc~WzFtqkHd?}d!t5BbC!>6n}Ep}u5%m9f8&=V2H}T06GRoF2)rj@VaezR-@8mXOXL zhJkR>nE13#L(U9>Y4V**JC{}to_OWh@#DwNt-SW~linbo)7@16{Ml<6^M|EFe`96S zr|X8zI~E_udmb_EItR~M+qua(GXKmo*A|{^_|2b%CwKYu?sq@_PanmL2~G#Zkt6%M zH*8-!czh|!Gw@pFM8$RS==V7#Ro{Osv&onj`Xc$!>?zE)VYte3RT89kczO3=88F!;-o!g9&jS%&2O_;4$X zF!q|-B0ZFAX9hHJfb@W4z;6UdodI}5Bl?*8D$wYv7XuLcgT#x8w*EToAOfbr5An|u z0sM(b=P#5Y&&4SafH8O}*>~8F(5g#BK1fcfLb4i*BZ?CH@T|3f!LU0yUu-_~neTlj zf!z3fi4r^MfEHK&TY{cd2e?-A_XWlQN&8>i*EY#o60`);fiaQk19E;D@8u$AC7YHw zOeu?=f;&|!PEGQ&*!u#jNP4FgFy~~DibuT;=Z{E|B;4l-gv#qBP$A7R<|Ds@1(X5+&w(j+AUusU12(m?fto}H&+KVYf=W161aim{96ZWWP_2=5 z4sf0ZKR-p*3gA|6sSy#Aq2!e>44DWngBBt>JxKyxqFEPQjSt{`(azmmlU=0#8AG5s zP>G_T#tX{nl)jblZmpupZvqo#KgdkzW4m4+LOwy+sarw#EN~8yxpTHO>OF6N5-XL{ zm#WUKYrH8mUn}YR`d&b@BmR9FU$GjRrUvpF z=OOQX#q4KTM=i7(3s7h8T(Bp@QW7-8tEJ-asY4i&5q<#J{UI81zIKdwL@ zzyNM_#3YKb%T`vtf~!-RAL@x9;QGenE@l!vA`lOp4th!`Gr*&Jb*0U?$o}jX$X9mo z)C#vi6NG^PGUL~i)Xw3qPhr5~j3W9C z#=wW{YOE(u?*l$ei==}m8u0+Xormu;PUn_!;gbVxV5{FosXX{d0 zbw224A8=Q+LZ4{5V5LXu?tQn0xlLHsxLc*GFuIjvJF9@vg9EE@sL$SpA)qz2J18&H zUlO!>Ts!LiMm#%?nWMb%CioOT_s|$Mjr37|+#ZJ)uC+bCv`w~-U-e*ne=FWiu%qw( zenGvxqit!6M$fSNtlo5QlkExGk;Z!u`z)@xEl0IYBM&HLk0}9p-RSwb>(DhqEcLGI z6`Jg259X*pXE!ObaI>>5H=;)@cn@>Ae#|p*KB_;8=_Jtiy-FcMBz0QhfQ0Q8+6AtM z{e@+At7?Le@vUC79JNSXSYQg-TL=zT=Ol*NpI}H z+ZWtB((X;R^=)xkwgexelfL9K)%5K(=`>|O-vUQpS66z6mxe2OO?UGIjz6Yv>XuC( z{+Q$4B$Gn~=b{h4qw8(C!0~p}H^1j0c+g5do$;NIV_D*NL5`&T@1U1On&hPy6xbrX zcQWm#CB~^EmGr%{oB~So&q!s4{49Kpx%TAp*37Gy=TH8 z9-tn3>fo?+vmLT%7K;|V?9yq`lY8Wn<=^FVZTfx$Dx)cKo+4zpLxA9rh4@@#Wji-L7@k4rj;sHe6EOF1N{9 zjx$IOo$p$i>=`)mNZJ0+efx9nzkaQGVv*Bwj`Mk2`_6k8)wiU?ublC`YWb|a@LC*_ zbU)nftPwJE15R#tQ1chzh-|_2sQKr@S&vacEoc~cAm@1(_}t0D_v{RL@-Fh1yWX9i z?R8O$6iym1S?PiglVtZ{cwO;gUYyc|t`MSe5)#nEptyyMZ%p&HhRZSo5t;b9!+SGN zgX_3{6AJ^-wh~D3ctZ|>(^}Fz(a!Wzvd36~%NbMKIJF(~gs_o8=vf|);X=$0UvZ<# z*0>jb4J@Uj(9I0R<&c2(B9VsbRjPlLR(Yv84btO^Bd*1R1%R>*jsh2Y5Jj3$k-=p2 z^2*DGoCLbcnT{!lLItd4P>xIez*rOv?c!TXaN#ZMm5PLecNbv(Bgw~Bi&BX7vFXaf zKEZ%B0*i!Ka`B)(ybAYIpRxHKvIu9kAt(9L%N>jQXb0C>rw9wM9mG`UDzZ8}IRK18 zaA5{WpGGyzFiBEz#IiZWl+vWaTp%SlS|j8VKuHq7(9XckWFp#xa|I*JRDHEcrd4dL zewdvRzuE`TE75TF%x(!w10Uk3m&(NQSj{lFz?Bv%4Ll7jVw@gvW{im_s<7EZqN4=L zfl@LO2e6J zE5q=P9$7g&{1*kBI~x=SofCtv9UrbsSZn9wdR>L-bB{Rd_}G9pXCA+K4XwK03@!|? zcNre@`E$n;F~5r0w}(&ccMRVJ3BMrxfw5mQOhb1@K1%ihvRv%nd>4G3tS|IE@|2nT zQ}s9UyC|b_SvD@q=rGTGVGY>wAQ_ zj@}1pe1z>hmqVCKC;J9aoa7UQJm+H=h^AFY z%?g8Q`mDjh6%A(|Av#()>q&cX1&_w$;HC=&9i-PQ3v$DCJw0pZS32?Tg1Sl%JNuXp zefQ^{dnoaf1#3Gi!Ei$*;IojSACNIaTuN;`<|XSZ=Dm-Q`uJ+998)U>`!ORV)dJ6NaZ4$Ew~WpLM*_uX>KOO)=~V;EvPe z*QY3OG+|?T?r0N|eU87t+-BVIQ)4eQCyky`FZSHFE_{9krxUdcwu_;Fi;j{edfGvJ z&QXy@o<3|W8Qy#XJts9HH*|b1@qyk}@af*Y z93mZixVZt1!0Fa=tY6q)+H1W=qlzH9?c(}BA8}1b&*31Jf(x(>H>oN(3N=r6(&YU91e8GoJ1QypFe|0Q0ZB?y5@iDt ziG4595{|t1s{lSvD?qvPuLR?JaBKZIpt_xE3Hbfc^ABPYT1f9lDyd$wQR37AK|ctN z9RSDa#U_d!Rbq=R`OQs$z=X2`*pF0g3q@Wp?#swRd$T!w{~LiOY#6lUCep;R%(tZ0 zENGjl!kC7s=(4(%1zn7$A*V(!zb96(Av$SFI4?sLVsa?0s6j1WD$g zC?E!9LIlM)0-P<)7kK|64o~1HL+dLKQLvgwzxZLYV3*qIyYC;O%)_ZrvLZ#gs-C`5)_Xql9`)pREXd&^yztBsq0JFn|GmS8aX#LYLfN*o)}$slAuCYaV-A_5~K@ z$cnRe5dW#Ycda@NBVuXNB4xPt&C(pm_ z1$!Ugjgwk1iDzt=+EgFliw8^59FRmIebn6UHOS(fyiWafB zCq1;kLhBV+#>N`2a@_4ZtE%a;Z@Cc3itYCfL=6#@*qvj_%_#%#6d5jF)ej;m% z2EK-73Pwx`@KgbC_phFN!MR++ERm;eyKV*Zysi$7(#KnK*}B6*{r%*tmNQBZexyu| zPJm9qZ`RBKo?b1~SlhrEpG5(!zZjspDafw4IHd~opnmtdhQU{;14dA}i00mnZhe7u zjbe%8x-?me#5VjcNCy?)?k+UVVZ6a83h#R~(4h<(g0VEojnSel_HB0P0Bd*QU!c#j zKaCF6bx-*kt234UuBoml1Qlf?ZANCD!k9El5b%wo@WAO1-^aY8A{G~TQAP_GEq`aE zbf%0;ObMqJf6>@ne*@gIS4}4BkZvEnGc}gSP5-ictR(2gq02MYOjwJxDmjg=vTZf6 z%B5AAfehR~I^VDCjKW(AaVcvPZc|hGetQhP&PQVlXY|_RTDCukU&b6Eb+4^d%Ndp) zu}5jlZqn4)mKV9J*=F=zqeLdrj*AZtBGQA7<-lLK8d~$zW_hDn{o4nwleRoh+VJ|f zW$d=roF{Vr7+Hn>oM-h@Z{u~gjaNK+O%EvYR;!MV)^wY0)xJ_!_t|z%*q)Vc7nX&K z8N3#1QM2k74h~?FRyj3*QDzT}S=Y7MSK)+4+p0@)t*AC0?r69Bqjuo2AA?5Q+8D}? z=TZvqS#)+}9p{dYo8G;<h?#KR=KHt)DGnKyaiEcZ6(HFdL7W@S_C*29BeuC12>FfjPk9ixD zUcsLz&~A6=JDy+g{}u)mQeC@!j3ECKN`Em;3!F>v+t-)SnwAzXyDLfZE?su;9hNNG zlEkAJ3KO&lZ$tK+Z>F?gCJBq+qM^?@AI1=C?xwUyj}e@rPS*o&*{i1qGj8|S(#pBv zn(vHtUtHfpeSdz#B@?UYJ!p;k>@{Bx8)T>eoEJC2ARmj}M(?Jyx47APH{OIe=5PF% zFPAS|Z^O0Z7Z$Cu9eYi^d)Cdnv3I|srFrW)>p#D0k$-ttSE}WPOQtT}?Oc56$J}jy z+||1v`n?DIx2MH6JSB~DrgwB*=KiJYT>4z9 zd(m=;wD{kK-4n74zIF7z`|qxBZzBJl@bO_ql1FsbF zisaw>L6S88<$9qZ!sf6ZvUCv`-)5Ncj*X&)i3;0@dL1m|68xx+j4>;!zy%wqNe8V% zW;MjCI%CNwX{0K$s;V37H=eHN+^P}!L>adCvKgVQqfm#U%|^rj%XbDOx^WdmHH3RV z?hO@;HmUF~mI;}03DDHrY(bL7b9jrx!<5sCP*)OMjuxi5i!?Ik>x?}lA;+>s#=fby zr4a{AV90P*&09vu3?Whjrr0nu7aW0#BPU}gbTo59&9@}{J zHq$vH=ye$gZvJM{@08C2=GE&dyab2cQ4?*MI%%o*w4Y)YLbW{=+MfQHlW%NGnxi-&F8OmdCtZ2 zE{M02WyZe8*f&}Ag;xEO>|4V;uGTF+>o|?8ZKC43S2@+l&%W{JkN(p){(QIjG!^gH zJg{qE_Sr%H?H2Rqw(I-)$z4aMcIy+n zj)uQ0jkAqsb~fa4e>4Bc{I#9%H7>yW7}bfopDY+&+0dEnJo8$dEB>|UWfmYc^7vc#}bC`4UQcD zm(GWd4IZoXxHndA9A@5Zz)DjEjqS1KZ(t+9OM()(6pHX!BOHR}ay~N^o?px?#rq#syDlWmv)0bK38d?S zy#&q?$hCh4g$gCr$mtedsu9#2=j&wzp)axnZKA;$6Zdnqgspie!oYVJ212^@y*ADH ztNbV*v;IE;XgB&p-=n%akl;KP`XbA;&YXWF5&y1sp7!REE|jfUZjNgTGv@*wGvk(! zW(Y{sBcOlOfqZ&@@N94(_%VGiAbQcjm?%tebaOlD+8-2&SoVNlZk+T zl@-<`uwkWrPX{Yu@Uw0BfrKS^LR%z0vzMNw&DwgF68o2k-Ya#Oy&NJF2GP~U0VVM% z<1-7ip*4JYiknm#tl3DZq_lLn9TeCMph;}rK)_RqxnqqXrzF;Cdq>LPLbP>-g3yaG0 zsgb5q*(>f8n##9juPDRa{_ucR7{qTkqusAq;?cd9)vTbW{EAO%BTU&v?rm+*(|U1v z>@c=yWqbZ7&w%2c{g#F$<$Xc1H@zaN|hRUu=V}fqnmMk!stxc=Me&%`E)$^j@6#E6<~OXw14+Cw$o@ye0D{ zv6Gi{AJKL5>jWs(TcM3mQ|KQ zKXd-o&O8mPsF=0>n1WQ7x}fxakYB!{?K8fh0lagwl*GI^=j!a}KV#um)b{|kPg+0* zuSZ0H&X2)s(ob7Hs0B#I4$B&;!*C+WuY8;$o5gz3{)d5sr_RH4C;IDkU$Uti>~%En zFe<|S`6{SttXpD0tylhe;MgOQ=v~Io{v34bM>r3R0=VUVi=L2u&J(GG%4Zybapbz~ z!PQ~{LtBwPyb5*i$1}bRP)h`8P-*6ni8v)o|W9AA5BG;zdEq#w;Y)4$7E>9;c__A^Q4E*2)+^)V4O|F_3 zj^jYX)}(WHCct(=Y}A>6bIuLVw!)?ZIm<>J?%hd}+vHsTfU94I&5Ld)HG~HD`#NsB zp6Ke_czR0#HXMGUxs;(MaoEdmgZ!4Aqgk~8-*p_dRo~5MyYhOv5&A~{w%EZJhexjy zZs}HBuqRqtc;}$6TBfcjAfb)uE5Pbow)?Dr@3W6Of*kj~Wx?%BNhgk92+pnJJmJ`gY8B4CUH=bd?*kuIb>|KL?wvc6oAD;O69Nu6gp)AhU{iWSf($so znJ`M^P3=HQ0bi6s-9FgIJ{zso=wdlDj1n>0LD@do#l8d8U1Ym$w6-rQ){EFmTUzP* zw!5EgU9;Hj)BW>lX>Hg2w6=M_zjJ3o5Nz!)nS1{I&fk0fe(&#`lkB@n7#)q&Ym{W8 zmB6jV7!5k?d`glqcRRV(PAcQRntWc)Hz?=PL((9$4$H(5lS-3SY?Cml0i|$pt0Jp1 zD^@9$?T%K%Gh&sF-F>*q7;^Jei7C@9%C*_H>_BWCby}3fX_KOpur=F4=jlp(RYPx= zGGz{}M|R|L!d6{U;;>3!5|V5oxW-Kq-pwT8uI6S`LHlNG)J+WEn!cSr;xs)2E61db zTL&BO&fpeli_B|MOVih_qetUS^ho-dMB?DS{HCGj<4-NQ#cR5?@w&T39Aa*c-PD}# z-{*c|qi}>fbJL#4(8j!z*;?`LTDmBaU(z^pY~B9uWy$y#1`m(*&i1?omG~yt8xpA_ zI0`+#F~4rO$*vCDO?$6-)T3up!$~)t>4g)ehL#P{{KD)b&Yk_AZMXA~&_hKm&{*4E zL^TAd$DykM>$Kye8_uL0iwG18P^F;?dB8m#NNQ36xooD}=x#{AH9zk}ZpYUSJ#%kD z8ERtH_CPe`MLc^38Us0A0nltZoo}CI$GwDmTO@x?dPTmuce7=tAMrBjnB(^UuR=o1 zq6>^zepxE>L(fd?$S30M&QEuF?!IO51sl4yu8My=J+|RLhpTpac+oeUYyE6LG+v)8S_V`aWE`92f8MC~`*>w5vnr1IC%eyO{ z-@WBKuJiHk%;B}^jp6onH+z}3naxXW`?5xAcji$vvpk&$FB=8Eny{NQ6hE?bANMu( zCgjEQ6pDr1;Ywo28}d>D!lzyaSSlAL*ASNw#h>4mVnx=v%ZD3O9xluY<` zumEub&Z8*XWwxQc8@%D1Kpx;=k3++YZbNUui;n{GgUKM4{(@QX3_^wp@~S#kZ7b^$ zGShn}MaSzNmCi7{;tL>B4Drn%7@*f2rxKhXrd!|~riB7C7JT9|(a&_l?IG2tUCCk< zoFd?6%L=Dc2aAA#<+-Ln{>_6Ocujr5tCGkhvs#8_G6;qt5dU~nF?irlvQ_Har8Ea7 zu9B<*-UrGspjuMd+f%U9K|=Ll4uaQg^#noH)6a#C-?&rDEPX*W>r%r%R~opmRog- ziE$JE%@q#s^5or~E#U@wCA4D2V9x}$S+~%3dvnkCr>_8la>pe-Sc0`&vbpDuPQGDg zV&XNu-+E3gy!boe35|IG3kfk2!pO+r=F@MUe)H|OdwLd!Cp14!TjUcLv48u-2~GlT z(int@*dc;g6TM4zK75 zc6H=dF^jqR|3sd-g4T1#V1Q=+ZRlr*e)X%j zPcSlxn{GfjGvS(gJbz4O{x9K+!1?&39xZ(1D!pj>f^mgbdBPr0t~Ty?f`H|@xcQU} z1t-;w<%A%0W6k};+8-I7R&(fWNrw?&l^CpgG=C>PNs$y(sRe(o;%RqDOEkYcm*M<2 zI-a~EYjTx8I6oI`(Y*@2sE9%~CAs^2d^u8VZEVqM4rRmUE}b8Ckbygv`)p6fOKgU# zx|B^k2Z0&1MyqlXvkdGd1z2Sg?FdZRsmTe#;Hmg|UP@_OSFwtlGtSF%9?O_uOXb)6 zS?6KVR*Iik?PM{jz+|e(#&ZbMf~u={((%os)4pz)lD;kH9-RxmxK96ZE|)4w);RtK zy$Fzxekd_AB3~h&PhR}y^~Y6&%?2>R9q{&T|J7-V`S1@FsG&t6$Ry)DM+$Y)i;@iU zN?ze13l#3Ngkmjv1N?&{-0?E*0>P*5T&SVJ$9hxtnRGon{b8E>`D33anu~gTJ!13E zWfmmDfvwNZMLPCv%ALzLX`^_yX8VvbG^K4ATm~S_2tbyF41*9RaiD+{nOj-WR@H%` z5Xc(5phe(tMz{m}gH1rJnsBr5Zg>s|{;;xyA+<9+aB1;mjSGZ5yNCtTJi#`$fDgha zRY?-+A0yczm<6Dc@KF=aCBznxo<4;rEb10e3sdyfQ;7Heh`e_t*67B?TR4%_+3>ijlOxT-j4xQQ&l39Q<4Y1f5E;L_QB6X#0L6FXr4Ys#7 z^h*wDYlm5o3aKSa4Urp`@m;$BTd99Yu% z>IWz^nG-IS>G^`y6d!;9tOEvx?f8m1eI6VnDs_$*fTkq8tExnRgchcRJBl?I)`Qoh zg{dkMg|`HUBp&ZViWz)_&+bTyVUf})V)I$#?6@CnN2zpAfiXY%0ec6|(>yt3U$6^U zA|MD{Z9+#MD~wv>yk0A0IZb(AHgWKwYGHYUAEKBqXP9s=h8U-O9~!5pLgOX!twbwD zTPd<(GV*x@{FSOkL_wj`{rvGrWykv+T;)YqQriMaz{rKs4)^KkSLT%q2GQ~K|JW%^ z+IvMJp+-#kgEfiF1-pP{A5XW7>*3+#R`f)uFf8H%(Ncb#LyBz*`~XQp^(3`l8_s#$ zfS~UE-Y&C2;9lw2i_Nq1SXo|br4&Mzwsm!R2y)HFeO)2=+!_68BnP!>;+p8K}OEpHusHxzgexxa!$D{09J$fssoFf3pZe7ezDK_Siw+stLLmle|J@QkrNsRZv6FL zIyN6E7czoUwb`3`h3UNY2Sd z+l;6nyBHs+c-ajkXLcBEySlU4j)dBi>La6=ikHfjED(^s)uLRsiq=v~nDhHm7=HGc zyl(Bq^rEO*I|P+FgxzZtjbvlR>sy!07K=r6g@#O7IHECYHukluH8#El&^p{K)Vr>6 zL$+v`eRf%>`_`#qF}u)68odUNq8MG1EE$HW#KJZ6&1_bc8&!v8t(}>m;%=uGpptEL zIv7HWs0k)IEiMF!e}$1K#j~Bg(69|z_?=A(Z4b7<e$IbfkH9*nae5jn?5z z=Bh`p-DZt_GVIQdKW{BcETjDM^BdbgM^DFN?h)_lyPv7pX&W!T{K@2AYrgeJ+r9@M zy)AzCSL`)$GVMhFog`*D?aOddVj}M~ElphE&32lHzCyFSClX8DFb&&>y_t>HEw{wo z*=_M^(~$FM`tb@NOgmF(Po=$0+v3(UsGo9Q)u}Yz3F)}zO--nmO^^k|!m@9m(41(3 za7r^eL&X(v$|$5|^LV3JciM2S%XfJjoPx?9&RExUSsR?Cv+`co@ZL?sjVHZp{#QER zOWv}j7vP#`!-;2NuCwja_O8_RPWOzD;pQx{;hLA?BbPautGbV-o#n&n>CMB@J*oTB zA6)zU^mR+~U-+WaJ8jJ~dn?hVYg0~aw0lmvH-k;+%NqPN{lBwrb}oNDZ4bxqT<#31 z4|=N;pG};~%(UIdGsmsUuCC8iGwwV}?6rsfa@4-j-L^52IqckN+fRL$P%*Q987DaZKg}Tv(#RawlsMILjLsm)7@DYMeKzIcFO7QWRFX1`8HJ&!(%p!v0iCjNNV*ny89Li$`Q^YU>vJ9(0!lF^_im3pO zE5JQgFe~kDlT*T3A4r4}%z|lppfEo92grL@H#OBoz+iCbtZSCuh(=}EI+hg}zmYl1!-gC(t*nPL5Ip*86*Z(eR#FiQ zqX>-|o;}tmjH2Cz5+W%Byx8ee4|ty(qY@<^vOL5YvpSGnd?Q8@82{}rYyK|qaX7%o61BOzqI5aQ9$N8g7SZ|syM2cu7y9o^DIDLklLCJdNXn)Rx zn}B%)Z@s?I;B5|5$mbMlLR~YphRs6j{`lh$@&QJ`8FVLbmrG9z#2!~oyz=_%xHUtc z@E9P4gCJa^fghj#7@P#jr$Mq!VCEAegYzH{={sRQ{P`8LS4>|qed6ah;qkXX7^hHY z=g0_S7X!UwdsL288H<_OG}&(F;!!>(Bn3bp~XBNir_^UVdYBnZ&ch zoQzDz72Gj%cAU)5egoVP=Q?6eJGqa$X0PQNv1eQJvHYWTCLbUt zu1i_=@z>$M{s=ClezWD0!P%!Xe0JkM|3wJ(?ak1KKit#uT#Qe6WGwJI;krB{aQg5a zJpqPz?X|z`JT&q4+k?Oc7cWNr*0PEmM?=eIEMCv1p1IX(3+}(pc!f{@%c$VIEG5&P78zUvlK?CVy?PCt3|ML$0FIP?DQcjvYpwtwZ76+Lgi{rZ1S zY(6$|#~m%7UbN^FY>h%aQ^eZ*M;=3ZG~xlk3jGmQ?X2cw$}GtDv_GHi4A|f|g@S4d zJ2h#KCXH#V68I1p@`csoVz0I@?Qdm*B2`yii0f$VV16U!ERTyq@s?v@!`&RL2(2jp%F}6H9j3V3ktyXVld? zpMs`=FFK>j(LWA(db{7cy^i{DF8gp9JkTM*WOZ8glZjgAV0 z<+i=y^Kj(ST)3V^C`Z`a$G(?>YIN-m%yOqPt$Bw;xJ*ooeWq!p7>LnkG z=~q?u7$aYZ0`b0+e7H}e2I-s9rz^tKK3v&}^|t1JC#yDdKZQC6DJu)+Dt-pi2!-uS z5IR&Og>=g#JMb>ysu2U`WrA<8gW!bEzMN;6kxPXzkwA9gM?kfiE1ZK_7B*(9s;Ho& zx z0;?fRQR1)CLYeGwlGLP*96yXeI^_aj1L`wJ7ui*~-SjQbuEK}mQADkQ#kd=hFjwc2 zkT8aQDk$U`(jdC9>D>Qvk+DCF+E& zSWVs7JU5Dat^JR7N+c;HAi6k?aH==L5%`DSdNpLhBw_N-!=oI>qeftN6sG7@ko40| zsbs-GCG-Yf#=D_$@^C+~LjEHtIj$ZctaEi`gda^_P3>(9%t#=@^lkwDGIlz!5;c~< zbmloOxnj}F|7r_ftc>9H)H5XO+TB=!t5Ql>!oxaBi8W*FqU26U-zbD^yM6o+eJ=@E z7o?#qxc8GS1!|y&mBIu8;{xmzmPdR|fg7O?*qJCGy!k0fL`F>F%svdaXp#^D z)-bY!YbPlAF%lSifGT1c1-=G;AQ@HNxe0zfAgP~c9*VU?tt&$i->v2G>~%oEdNum4 z6m|_7Ayq=~ZJ#wE?KD{?Na186&6g2z+rj7u9@}Ed$VqQUj_ZLi>;@#6s(8ytoigzL z3NTBQ1v<&f_w0Gq4%CmTX{-Oq)}iHjIHOb`AZtnjyIFu9o*<`iei~AGFcRpyd@tgc>I8b&==D9_p14CV5WY43{-pr7D{!Y3o5?Uw4pJ&g-;@nMW}y)!AK>r=(s4Gq_(q{V6P9$v7NRY# zYtM!I=fXw=>2PqLHBy>_0bLU8rOm~WYQ98=F+*sD>S1z~qJ0tP>OkX>Ncb;c^rYh2jcdq*JV>8zIDyJ;l9f?}#`ejMVMx34DM5Qzg zz!=#^bN>8+XxWrF5mNO54oszrk3@w+X0D#0z1+oSD;>gV>~)QJr)yo>(GO8M zwa2t_M%gqBuQ%JN?wfCz9c0?pZVTW{9~qaLko4y>3M^q$7P66T9$~;hy)$BcXvbU@ ze9|QH*k?t=x?1fB zZXnsT@y@()gf=x}gQ|7J8~!+La+{lPqv5?>kgZEMEw^rc-fn;1jff?g_GZ!ibb{Ju zCBk2uU&(E|?MVCGP1jzV+BW>e?Mud%C1%|@^R5kT!&{?Gvj^_V#gE*0_h*wlV_xtEqk`v$&4<4*1S4@B!1VU|MXYs71Vlo;l`QQ{%?QDk#xFg zX^opf~h)+d@~#-sMtv$_wvM_hTiNtyP;p0gz0;d-Ua z%hOA((ey`NizY7ElF2O1cr71jbM9VJUSw@pw;!*Xa{b1-Z<0hUJ!?o-Lr zJt^Cu9!Zov@ehRPupU>ewM3sHR7F@Sj5sxp40kn-X(V0d!g+{Ag_tA70W&X10f8$~ z7G7MtXbaR;D}ix0i6a+A=we#XYvQYfXoEnNLEQqjy+xOtY^2FF-GbwQAIT{Lhs0&> z__V~WKsH%UJK`2#uaSZ(CsPcAg`WYRby9T=ROIyhB#Tq+5mS!Ms4NzQF@rfwEl31y zaD@03xf;66h;BOI@z8&E7(bzwdd}m~fKPrE+Vpfugjd zyDLuham-peRVn#kpHt~8KXwJgrl~jg81k7-?8fN{7T#lJ3}dh$>cAg4B}4(i6?~-V z!0&2OBoqovDx?CTgFK{@H69z7IB_+aw1au9Zb=9PB>3@8Du!W$0Pwm|Cs@m{cR)fb zbt6ZKUW^oaGwpzA0RuQuR?TWJ>*bH9e%@P>!F?g&J!V-^3qYKtaaKdkr zgK?8=#3`>}<)mN&Z`d%9fNSlfP_F7Mp=xLG&_{2=BA`;AfTsSdCs@Jmoh062F=pMU2_UT{!ilrUz=)t|dnx4G+$3K4Hfsg$e z8%`fPgP&kq>6E*2f;)2p&_m^WqWQ;i zgLRyc)%t$y;O>{eZ8AKBTIa3XX+JohkyZ2}lW_;;n*aAGPsw3&yo@{YkJEk@_Bhs_ zI`6$Xe4h^P&T$o@N14c@oZoklqSV!@aQGd+C#UYI)_berO{?!!ezArnY8)|%P6EGo zlEyS)dA>WbwLdhVI@SHFnjNeBW9-olTCG0!v0ssp_|^SQsz{F}sI_<%%m(?|_N)i6 zdl2boU3chEOAAgvrPrETS~f%4ar360o|q9c8ty>7n^+vaJQ`lSetjT~h%*LS7=v6N z`t=~>7bkvmXi)F59$W+b{vEV=Gww$^O{d?QI8Cpkxow{IYn-7SMmw`-EAFfL{|}KM#twf{XUXdG6!Aso{fr3`y)+Z68mKjl(dPCDvtQ>| z(BL<(ZXOF^FEox43GR?l(gc2_xF4Ud{k;f2Lkg8wwqk#Rxg)I6& zW!N7}U|~`a!Bf*p_D|)&fM=|xYpkhXhkMmX(ounzr8dr z(EF)jrqEo*6e)5&T_1_iG}dFEHyl7dg+wi?dW0cBkL+@sLT`q0HyEL54L-_gk|a)W zV}qn|pVh)kKF#&#e$#*WKfXanJ5gSLh=LvpWnxpCga|3|bVN#F`tW5KvI>06CqoH+ z+PGnteO_PzDukc}{D40o)yjK8!G@^?Y(}K1c;Y8a3r{N)HgU?1K`Mk52={kJnUo@l zwDF1SK$2vmf#AR|NWfC!2R1@ELW2npOsZxFtJ)$Kk@dq_uunawhLa}2W&T4u?}-P|s@m(hP!NwMOHt7SshL#D%SqC=<>v zq{yLKy*h2MYQDge6!Ke}%Xo1iaeY@6S0|&~no*A=_4h?~glV1Yllggg`)DT_JGP?! zi-Le(@ieHo~g>VP@8@7X`>?q|2D9IhVgEy z7zJl+i=n=a)Vwdckm%xm8Tvn9re*K~6|g~rLIdg;LL(=W#(*bd(P8`S%7lW{G{lKe z4G5Wy4f=_~sj_Jb3D)lyK?w#BD69lZ1)F3kGEK&n9Hli1UTrhq@ENVHMQyCT1+)mf znkdW)zRW5zXTxjTOCf1otb4~kctOGqwq`^Z9|FkTYN19)No}~#lwQgfDL7ehSNKES zwmo1Rl+BP^fSJsbI^Ee3j8`M%t2lMqsf((ZX2!%ir*-zJt`k(!z7#d zt{iNZ6j~KjM91ozrl6oEG4~WIaEc4f(eV(DliV8F%jh4`)olf2!-^XcA(k&D`=SQw zQrRHJV^4Y}3fo0*Jx_+f?Wg-K6T(5qiT=M34e-eKb-YLNc~F+P%!etB-)$(@6xRoW zH>V(Rw%<5vuAfK%(sW>_c3oiWy4hDj_9UQ8g?Deo?>(UOo#S-uMq~z=Z3v;hAoIwx z4i8rRJ%H4O(B#o5;1)WFwd2`TGkcE08-jeV;u1r7=g(p*u&F>SudK}8#7Z`TKh(vV zN~90pIFw=0sxnrU5z{S~N$hOs#pp!0MKE`)7&OBkyQQfBsZLon+?UXwMkA(t0O8@# zJ)|LR%;KPKzO0uOyh%eYBH97Hf*A77A!tsT5!|6Jp`!zESKl%mg;W;Cucv4#MUwo3 zVI*)43Kxfwp5cZT#x|=8d<}Qm47G3A>eNeEj|PY{+Zkn?uvqNaN5$&dlLR z(~(g#56sf}+M|x;Y~9zm3>Q^58%r`29dZxOe}Br`a1{0bIZiJNBH;`v^0O(K4G z>)2SO{n2$|hFqO@X6DP?y1d%xJ|cteh;Ufevi{4ba8-P3rhd&F@bU3%T&Yd=-Jrg8Z)Y_A@^`;ogVUmnz=ghy2=&o~8UthAUF~?8 z?sUI5^P0G`7uR3)rW+F}_Lp%Bv?o0?wk`c15t@~0c9(yl+szEmPS0?+TxC$#j7D$q zO{Gyg<3;mFF8qf~;#(U}sP;`w-sO4oN3+c3N9I28`{T=>Oyl^vO_rUArz1;77uq?% z{auRz4ni=oeMZW&Qfb#-lK9lp6e7gr$DQeAGSkWlYbJSbw=Q)yd~l>a_1_!f`NXGg zYfjv4yZhW_Mq9ee+2%RDTSwFBwB52Tx~Vh2)rlu)-_7~Vuo`I|<2~lcDZ@;x1eJxm z8nzL&4n(jEXLU@W!ki(lWTOp(O0$4wyB=pgcV86Yc*gLxTOtXN10s=hIzUx#+nZ_zQ5oPO>yxhQl=N@&hY z1`8~ohlKJ}-I_z=9|&UEH5D2G+sR`RxvIpe%W_BX(JRGY9XWDAR26Eqa2>o!q!(X- ziiB?<2u=qioVd!uUMAxZAHSx`UP^^4MUK{CVgfE4-;02tak4Ut&=4Xb7CLI+Opni` z`#4;rgTrG69S_o=!#8>Ven6OMXcBk|Uu*TFma{V!2yE^d6X&8v0`;5Ysu3Uz4MA+@ zt1xhX%ZlmPAxhiD&wn@ZpVOybdMR$>)iPL`36F621%f2k89cG(5a1h(J@RhP0Ehe% z@XQr+R?OKvXM2kQDaW7x{Dl`LUdO#y&`(UCXt=x^_~Z6hUYXdweF8gCA%Dr|-398z z>#%*9WASqnKhxnD6yTb*DBn%l&JKK6@YnACFTG!tPu}C~B8wUun9Muyl)9d4 z#TjzwEUZzxv3u}iT(K^L$a09RXSX_@SXb+Ib$@jnu>y>de|7u1`cC^go>^C?^0B@i zMm;{A7ETYUn~uvxy7;~g?+qo>- z?!97<90ueO88hS`Q(a$|Q>@!nP}}P~Q{7lU)yx>B_5=QYv={!VceE6oTZuc&Z^a$v z`1Rg;{(eMJ+)ji(E?0=+V+WGy|rDuFuiBu zw{&`pYv=mVhjO3DZ`Oz-P9y+|&U>fl#6J1SUv{3D_}K}Te`Kji;DysB0?d%Tz4fIp zvBHE_wLX`l7&c~WED?LTF9}?@&8&dzB>aA9;y2rSw)bpC3*@!~&zkp55c6Jyd+aZP zgtPYd8*HNnO$XXpmuTjD?kDKbA+~>aGUb%s1$WKj-JF-%!ppEpEwfBtq@3EG=^Nn} zSYgFS6=e*sjv;=Rdsc)KnG8yxe--FrwY;L=65MGccrZk7Ch24+W10Rdb^hv1HWL+R zaD0L=Xd(U-;R7S$lUWRdrW6*bnFhx@Z!AT?K>Zw6gKS=BKn~Iuwxtjtepq!_A@r$6 zaBgMNfc3i*dfv0jggMXSBd{vdZo%%rNINv&Lo%hHp`%LRi9OURRa2qBe5Op8pJ>ES zE4F`ljV}3|A+<`z_c$EvIAtcL?{_FR(+&Xxu*Vn_#b z`w1C*8W)c8sXe8cY?r<@NP{OcdT9%zjN*k#>`=eOT9;2jD5@LYwe zCqmOuGSf~SU8&|OteE#>f(Ebt8$P`X0Fi0#8}Q%YzZ80jLjT_3AMgCKam){?@lIbN zq#dtz#t8>Mo*EC?ojOLOCv`^sHo7N4}6Gj_Vw;-vf#vu2I2o4nJ+q_`-B`FM1eD=6)3fxb9N;+6_ z0UIzPQ5^Iakuem*FizajBm_p&O31^|XzF4_&K0{z6J?TzzmSCm4%>k8MxfT8a3U(y zP3Kkrrid-6R$0+sA&vVN&g1)Qj=e;4?+@volV3@5M3|wq+V`)J0lF1w;3<5QA!y(G zfGMdg$?|SeKk+r4sXmzL&Y3$d{>nIS>d$wRNa+nrW1zxjVCV>rs z9P~Z#q>%l}#hlNp-v98BAx|dBynhF&?;7zAurwh_OpeBmO>a8Vf%}YsE%~V2>@ptw z3gZ@lN6kYM&MIdqAg&+kp^b>7*M_}9ok@EIxqU>ZAE6qQ5z;QIcg9q@EJb_ zPfQ`=^g<({WO8`7yiCb*#=)j3U<~w}o~fjZ;f#erA82?J(P4S{(j`crZ|^_&LwqTM zOxvpn=E5+tg!h6D!)ZWTLCvZGAJYUutcDUHpB@C86RFZb=Pz;ZHTsq6orY;t(bfup z{Rw^b_!tAYkj;T_qhHbgTuLXq3J#xjN!fz3YfIaMhKId0#>mL4hFXOw8FyLve`E~W zq<{YL7-ys#Rv&=s;5QH1txYQdy`p{KzG>ZFLgOL2*BFPPPVBW_HqiQPKgG<(8ga=< z9Cml%5H!`j^d0U1uW4<58I@)yb|xM}apR72QxI)!kml@F%xA792{bX{qYe;V*Env{ z-@u!Qnhv|~3Vk*%3w#@TiL9+?2yNI$IW)rkb0l2JXg){kt+V!D4{$*?3&B(B`s@Vr6_UFO}4ee5=Pm^nRaGJXST$j&x#PBLPg-0 zT++ofSuOGwvd=#r`o7U}n>h4k7y0gjoT<_8`TIL;m)=rE$j*(bCco>WKnVGS%B}>!$67fg3 z?H2KPdRzX+uQp8|OAk*^wz@QwzN@*n*-by1-?SW`FYM_yeSDBMjBRph$)-ndy8W4} zvGM!q*|!aMI>R?g?CuA8xM#+Z=!Qh1n%R~5yXCWN8w;_hq1b!NMEGT$<`H2pQ#O}BSv`j>6$-L|hW-?we{ zWqgl({IP-lwtb^5sm0G*pIkb!>~QqX_z#??kIuemL)*y<`ocoY&bq< z(S@6i#7FENBYtAZ&}a7~GZ!UBR7C7*Zl2|&FOPKXT{>{-R!Pgdn%zv(h%x79Kk83k zb&Z#x<;@up&VOKMQ)YBwu^$qS%}rf@d!IGSNlx!-RN$~1GYi^gK+>?yN^V(TVaftY zhfEq63o@G?1zd>wfCmP5LT8Ukljo<#UbuL0*EcDeY<}#n&(^sS`=u*_~!>+JAv~om9 z)Za)W&|i$r#Uoa@OhyEshcFj>zn$%;atAnEyV;W#(klcw8GuI|0dXfJf}SR@fFU4R z*PKyNd}wjN$xx!LWgK>lLC=sl13C?R+waL3~_ow#SfDKc=V&N^Qa16Ov+Z~( z^OFvZ?S3*_BrmB3=uyfQO}nJjtPx{w;tg%$bqSc4uUT&2Ab zzYeu9f;WNDQ7OU25h2N`V?uuggIP;2nEnveMl$YO^!%9$y2fFI12l}H~l+q)Co*v}cNp3wr4$Yid$CitD`InlFSZr}X<=@Tmk-y96Rbzr?Y2uZ1) z592Og(DoH`JHKnb@=6S+H?CTR?bjf-|Mf%^;TW{NW%C@CyLt_G?+M-CKY?zCLjpIOywaUQxp>C*BUz?+lz4 zS)-odTA9GKz30oUS--BQ=XXX=&m!yvpOVFMw1bCwP6WRbJ=#%mF;0`RVWOF?ZZJvT#G9TO3#*&XFTUID{beWabe7nseXznpGwdJSpL^vUlbmEWiT|c`8N~4n>e(9>n5M| z2nhYT6*$Rs@U6kga|n(dTeRp7V1?X%XmD{D*D)>TJDGwzV^|iFWqp4UJ2av1m;ola zd64majpO&cFfs8vv?S24!(T=Sz!#sJ&={n~|EDnq$pookFE}%m5l-Avqwj-SANoLN z?n6KN(XVnX#OFTNbH+aE`gc>og7Jh*soss_{PrGtaqZHJHg|Tn6nKE06qr=w406TC zuz-AEfi?BUfC*0o1baEvto^|rD*e#3LaOG-W=sPR9@#lzg`_xfwB|$#YgHfeiwg5p zxa%~*T;R09pJc0mEL5&@iMtNP)==RufP>y(i$6?YpCqY6kVZ^|5J-7s(xI+NY0bzE zRs3gL_-nTdodyq~`prt9%)ocRA=ltN| zheVFmcPYG(E-0Nqa7dJ(p{Pz+c9}eVq-w3-)8$upt*L^&nUG^NC`+acLk`71Q~E)) z{9>3GYcMD*q?IK@z>qaD*|N>JDMh0~=t{?rQnl?yMW(b6IM{qrh7VO&d(7Q=M>B`a zv$pflc`d=`@}~+kGz;lbP-}bzH6@Htir4mJ?t-J5dmD8LI7W~6{okEwimZ(+D8TwN zFZPp)v8ORJCy>ObTEL0dP|6TycRpRcV?HplP;S9JLvXmHZKQMy&P|&;AaW4o`s&{n z97QBpelU-pFdxAPO6s%6ypsA0$Y=c*3|!X2yti-Irq#YhY*gLormQqh9O|WN0XHZ3 z#Ml7je2gyNAUPvWO-Mv@R1D0JRoWA&;{I!nzdl_>80U~+8!%PD_eP+_Q@o=LxZ~0}?(l%Omz6X~! z$5$h_t~sMxQIkef;L`c(aXwg-k5}|f?vD+r2y=HCQ7jZ=k&;O7NHbDxQVuDp5ZtMY8KM4B z!U6YtDMwoyNgRGUJ*U7$z)TAe`>&qKOCwVkvIa6>?6Q|x8*6H&Yy1c@>^ZtfgH zK8>x;jx97GQ^(0)1N$E!g9S7y;SRhEE#yqFFB*5xAUOFLG=A@+xql_q|LiHFPr*E^ zZNUQtsDsnZQyu89o;2|Wxu^|VDM;Nj?^b6g%H-Y`7nxk9ygsZrRiJ7VLEmT$t#bl28H-9u2Ks;C^bmU{vhE0u~t(Iks%2AD4^2W7IX@~ zcOzdrsJ}1ht_6m1ZNYvN-mNO4B9a_PsSzP^x#Ij0t52C7uCgFEQxb+4Nv7O<7Hvhv zbBYWP>rYX(&CW*1;7iqNTIJSQYoj65!q)BqsjS_d1AFonvD97L21&$n8{a@blI_Kj ztZg}~&BQsZs&~`)YtcrW%NThQpx_@ zH8E9eq-;m2Y;>>AbsD(TyzikLt?jdmv2rpoAaUn*GT)aoj85R-R-@~hF?bcVI>3u^ zCbT&Pg$<17NUiID2|eP}GISX5ZL&9vJ+hCSn;_)9Z)S81y_FN4X^%bsh}-ov#3Mz) z-nWd>d-Kj^ZTp69Z~WBoowwUSMjrjtvcpH5bliLTtIs#iZly0gInuVs8F1TYKbiK@ zo*P}#E>!x-^d>JR=65e0tHdew%-(2vQ6}XrENx3~JMv&h>qa+n(+5@cy1he*Ez!eK zO3NjMS)Xe9TITL$8#a8#ah@J6rxS;&N9c;#;af88ZF^@q6ua#3lIt9|X+v}T*}roS z=heZ=;LOK1VNs{n>_4)>OWfsAWvB|28TATu1l7N)b;O-CLo58mKIBGitAg30$HH{v zb)!(c{QCfWd0xzUed^;_?-%?6xmQ|6z&nTd?yB$|711=4V4#;nAKOndii%fZ|~g&p<= zx@>$N395(AK4%?5eu7ny5L&?lg`mred0Fr2Dq~ZsMIDlKO!}dB3wvvH8y!S zL(sue!nQkm2Z%<&T8{PO2@=7gD=_dAah#!`*05RB%Il09bSn5^A`nJ<(-RJ(f`nQI zvhJ3m3L5Z;98!bZ+i15W%nnRLM)>=76hVgR2s(Q-(|HfLjyvLv0y`Ddio0`4_B>2v zVvX*s&R!~a;e-f2t~Mths^HuMx{{t~%O?4S%(AH1c96;tvc2hX`_|%+yd~z92n9JX zk5kxp`^Baw$^ES2QBF}T$1YpkQfz}iRGkzixA>%o`oc*O zXp*ewqP~yK_gb!<-*Qb7gNKceOG(t>oNNRvW{IRl4alyr1uCEsj)5I@%W?&^dC5}P zE|R)lz_OM_gf9yzW&sKK*YV+wpQ%z3>^8!WbE)30&UM)g*+iDns=KTp$ZbtAPcYD^ ziEpBg&EZ>g84;<=Y{B*u(aXR1#S_?-y%_RaJ>5R!6<=#&FTVpS*@GXGmT^2eak4J& z$g+@->6-Qeq%Co)_1i4p$b6gEZyyBDbV!XM6%*S}Ol*hn)#f>jpM0t1lO8aV+PxW@ zfz{KAxQ~ z1OePqQzenQwzBE)rpl%#wVBJrKhsbGrgNVPbJc#-Nrmo)^_B8_zavKbNuKH1IGm}n ziyBKjH%e_Yl}}qR?paH&#v!5qL;l$h^H<&_i=4K1kb>FZ3Cj5hh$5tUx67W3x;<30 z3C8YOW!g@B&g0*Pf<4xY!?@w4cIV57J~Z;_w@>`+XA{_Cy<){34ZtUBbVoxABHKLe zwO1x)-+?xT#&Lpg34?<#oG*((+V1%I)HY>e z?2tjXbY3r$mIiu7rc-vCiL-rRu+F@A?Q;Id5XxNPO*Qsfro^WjW#GM`^|^ZQb~1+5 z6yz1DQDFrI^}F>@`2E35U!HD-d~y6cb^9Ci{np3-EUyPRGH%rJj2!CusQ^B~BjS0| zs|aanv`nqyD+b4>>0Lvsx!05!@fee&U!*8ppmn^;0hRUh4?TP?0Yf$zg1O4R;ul~* z4l$*m$rS@RNc;X8W?1`lg|vE_Z{&L(_>3|Zk^%jK{Ta!JC5MYspRzJgVK^_n#C$lh z>YdqyEvJKcG$RA=ml=RPvXMtJr8Vb!Sb$FOQpsXRpZ}*#qgUV1dif0*>5H7t*I9Pc zcy#oEABMuKUOuIrEC7ARtEE#vZ1}ogJXL8}A38=MKj%{@Cm7ZXk%S7l%FwX}V+a4Amc_*qeELx>CC4uK!!o9q>;&WP3qt6W$i zF=Qc0(00~b5Geb8H!@0-yQj^d^mu}d85=26aX^2JB&XUabF@D(kEnf~o`yI6w*Lj816_ zJWV9lRqzjj@eCn0up|5|8WuM#T@C>Y+WQm=4NWmXbWv`>-pz0D{%V8)VoU(<@(XI1 z=-Eyw4{R|g+8MDQt3YD7lSWsfBe)-!6XPMckJh#dgk0k5`W8%RPnYghgoSaHoD$-B z{9i`;d7tt7BA(vM7&T8*8QyjsXpj62(kq`Z3jbFD)94pfr~&*7j_;LlroV0+f3VPj z94_or4vwZ$W}$y4zy`sj@zr#vIJExUM-rF?Rzvo{_y2qR8q4%3A zl3ktRP<%|Y8Jo-^VJgPnWb*+OZTbDCd*G+ollyO2d=wNNqk^F9be$$Tr7{0MJmqkSxd^qROFDG&)g=gqaUK-qwwJrMubs15HW&y%ZKx!I`W5|eouMg`D01tZt)2MvIK zlEfC2;56gKP8bQh_c=(q7@lvys~?G%Nf?lRe-Rl#qDifm=-1GESlUE(%yIi1H0LrE ziq@Y2!k=C=VntJ?fiq%BP+Y4hDG=0r+ZvasN(fqTc$l?WYH7XA2mk;=(uEi|R(sR| z*P`^WV;08u(|2XdOFsX6Jw^0vJE6a;;HM40RE6+t0_R$_n9GxjI6u7H~wvrr2fu5u* zcZA5<(uckHg2QU#>l>tD0K*&y*ea;`w>Lmea-0$^_+s)3?oC*E38FpOI6A`V>br?- zCCM_}rUryh)e=Pu3lOaP&pUn0v!uJ=!mUK|fT?!=8_KQDzII83f~Uc?j+L1Umu`we_=2DR?ElX`>TxwXB9rp z?s|4>IWT=BQ2l|kexyZo!MG5vU)!D(tMqq1F)xWa6zQ$zZ%Cr&9A=ydlib_m_mg(-t>Ra zpQtN~RGy9g>$AKW`SyMZO5<0BIMz;c0=I!V?j#D!B4SSuAD{Gf}+5J7>mH9T3C z0&t@85+)qEDfN2sQMWOIRlPNU=>oHQAQ9k+6 z9$M1W^oZ-FhCk{g8Y7V=r+L0RxEXplYJIH(OfvtiwE zv*WpUO|L%lMTfqSr?H_t3)4c{o2&?hbty!+0Wj{^q|2%Rox~6QAYG2UV%w4BLJGN{ zqF=bciydB0USdgp;aDb5nZ!CEj+t!S^D?n?{_1qww&hDM&m4|kbE0+lksmGT=rarX z#@TcFhc`Nj^urhaKkb>zGs(6K(rM4R<${f|pZ0&2+MEkFT)B7Sl9^8a$;;CHc6x5( zPxOWf#X%@^NlMN`-8ZNF`nJ>xR>rVDzgpL6P#}JY3ezdiVb^{|Ri|}o<#-nilB402g2^WjO zhbB}>_;rj9mhCof8<{D%`mM%v4Bv}m1rIk)>9#Kdg5-7w(~|N2VDztwNMKDYj6U!mx}O&6Waa>ll)0 zQwC|q*e;L~-Ya0EhNXeqjO$+VM{vZ>DiuOsgf0%Mnxv_%e`V7yING=69BLeZZjGVlo_W1B8C zAnpl*Xk4&BfDZmpOF=kJ8*dBTSX3e$9FfB5g+54;*6Rl>K%7b)kbp-26f||h>4>Kf z9Xbu-6YC(h_2KUrt5$6nb@vu}>#YE>n3z}uVhr#XvDlp8u3aFJ>nAiCIVbjsPkc9e z=s94Jzyvo>$|p{oe)CO@E55M39?S$p9|uQ9aC^lBc83Ck1oGL#a$k%&u32-%6>RtO zzkOj2ev7OB8SJ~>KFIjX=JmiUAxl}`^ZonZOa=C!F+i3>46wel_ev~q}GK{)9z|3~_AKRUE4~_0O&!l@2W2|SPrq=J-e!Xp5oOu0p#vC_;I~-oW{#kD@R=YcXPEQ>bT|D9NS*kxfv7%>1 z%S6j;NJmU1%sc4!zkl}7RJ)qq)9QPvB^W>zI;B300#djJkH}niG2lS$!k$12lk&0= z?5RFZ=)DlCq8qvxLmb(3OC!rO9(Pm_jya^mk!v2-_S7*=W0Jzf^vO`Xxe7h13i>^S z4+{L*wfn0FxJQ%%(=F6ls3%!g$`?2mEwEQA@ZZT^rxM!>48}G$tGb5)G1>dQGkyM__xwL~>R0bMN3o)zEJ;t_QG1vwE+9zN z+>6Bic$g?w5uUZFq}XCp+zW5(C#L$<%)B1^AeE?=)R|r`nu4Z{xh~^+qc^)b%0YJn zE<3-hoji(?YlVP&bT(ds149U=rcHa9jvWgO(GY3wtMm|y8U`ey)BTmg@VrvXoZv1fN5+kcoFisJ@^U=2k;UsouF)bWG8 zB#DY}Dne&>eDKz)RF+v=D%DF7iI2uG>cT*1GOJQDC@ORb7o!tcUZ7RiQ6~nqx?o)+ zoRMXF=Sxan03pBra>qdH;RhQDR9{VJyNb$M)Tx@{KsEvGM10$?`l&rM45C+G3;(__nH%7Npo7;>l(fiCHZNAPeCN~+{~FxlzX1dxt< zxk~}jCouuXRS=ixBz5E+9L94|eoSu|$E@sXf-K5? zWSpeZ2JbuXE{*6&I0WpI{(^F{Rp73U9wT=@!XJKlBdPxvaG=YtIUqrdo#IV{bj6)| zKLx6=nEPoqe373zVTxxO&(|HM=x1~e&z-ZT@Y6B?w0C+=XDS9B0c|MXf6c&MCkJo; zS>Qc7_L-}MbUF>pY_c(D!&m5y1~Q^G!^~x;?TI0Ukv@vo1-eQ@Qci56p_9&71#E5_ zrxc(W^pI`Hk3o9Dxev+a|ro79ErHWFx?*$&y& zepUL!JW?;WQ89<6ig#;2;ApSo37ON~g4VVO?Y1hECH2b52&ujO*G@Ab)}!fZl|#Vl z2CIb13(>76YDS%OW41A-)3WjIJrBH#<%ytcjlZN-vPz+YCyD;~2~vOkbJSh+Kyw9S z;(r>0dUXThS82WiyO5wo7$A`EadfVLkV~fY>mmeiq;n=UxM$iw%!Z^uiXO*!hTy7a zW2J!=u9zee6Y%>PC-y9Td$W!T?^VSd+)v!r{6+z-Z=##Dwc)5|Y|@8w=r}=e1>t_6 z;fz(Vf%sTula>_nQ(aZm*No$B`-?XtjU)CmL$J@fs;6rx&rQD&=R9hcU!`?lWFYDJ z@ebnr9ecLB@hFC_(ruDh9fjy-tE!WjDRfJ8lESHUaIK5MLS&Bh3{Zyrd%4(GsWn2RNmEN@NL(! zPb;WihQHeQotmaFhSC4gAGwk=>f;i6jSLlK_MTau9hTF? zy+CV%>?04EUh@lccQl0#!AdvMqk5BNkh;6sOl1umM(E&m3%9z2g_hRuMzfn+Ls6&e zIcw|X5vwh-nNnR{l-}Botld{WG>F^L4>pON*Ey?7D@qr!H^)g8iAxQzMTpIAq`b*pB=RYTTn{badP?qA&PD zd$jR}ufJHGZgzfpbNfN<$>~qrGovxxtUnq{HH#H#%j&XU3_jyL=G-`#-8zzgBAd_W z`P9d1Y%6wK2lF=i5>Ik8m}iX(&aZwrz67ekuTH}6WloOuW;&0`F^b_D8gp)Jk#m(D z>6x2$(mi`Fx;{Fv4D+-J;=9&6;&%KkMem}+SM zf^E%Q`DV!e2rDI*`gU&NwOZQl_Qsu4v=xU)@ zvw64m%>14hrJu0Y;q;5p!hulEXt(CGm|rJkRk6!ncpU-2nTGzMEUl+rY}OrCxMtwR zx?~f^m#1TD;9=u=WAalB3OR5R9LVor64nsT#%b2^@K4UYoaX9b8-N!kH(b#J;HX`R zh*fzw8xojKb-W#gb<@T}3}}Q*?q$(HX*;+!rRYGmG(pvb?Zmt9GgqPSDM9_Rt*Xq_ zHX$*NGL~$zW@@y}RKofORb?8b!=5j--R`z_;!%$WN;Ww_hTzRV35j~ zxqOAJ9%@s4IM4yFDYjEqy1H(L$|Twn`dsW9-e!nxy4{C6rgGGqLwVxLj!V)%;}uxs zu`2S>;Q{1XAcvq4eTrMJJ4ir*&CMj8ibyD-q5{?ER z{Akmd0jFZ05N5I$p}gfbfI>@Sc8pSLSIgO{-5#Y;& z>1+vXAQW7922VpoDf}*^Hu%o$j&wb$71OuCoreS}Rr4LN;`sn!H#KCU0$&2sasJzH zd-KtxEEe26!UAIQ?%eg$T7Yl>%mZ%7W)8s8R9n_;Y1q;*kKTTpca%@ZNini+uBR1z0)He2zNtCt~iM7Wzg^ za5%;t>saC>p2+7wdUAw**g9@lck|>Wc{)D}U##<=iX+zJ*8QgHb-y~+SofR65o>qG z)bq&xoL4445BNI##63^F`2_BkIs0eaR`CKt!FiA4b@PYn0@z@!oAnb3HKofjwphm* zr^ffNM)-ODH4ipVwb#Qlj(Ilr$aW?#Jx&8OHQjo6&*LnG<6szISVH*_Qb_Z|NVy;8 z!ugpUQTM(_5=L%j#{ z_^J1K-;Mh}@1kb+U=U-X!NVOPH@x@?e+*&#O#`ZV@!Q{h`@0;6!}_}WOXhWN3r=3e zSmN<>$OLw5xY;1epIvML*Z{Gcnkep8Q^#WP3>_wWka2uP7x;mw5t z6aNCnAjNP!TwQ-%u)Fd2360Z9;17ka&z@c&104m^B90}Nf@gV;zXn+}xF<=)@%n@O zC`5$*5&~$hsDMY96v8M(6}7~$Hn@o6i~h<0JBfaOPdN0AwIIyDQa68?ei)qbq(y!~ zz#a>mJbi~uvWqP26l^z&P(AY9X}galmI}x=Q)QIHYng{U+Z5$>Mp?A3hkmVl{tc4T z=cBT;qS(cQ<_aMkAB!hBQ5Q)JumB7dV>M7FVn@*2p@ZOiCy`dg!l} zfX$%$d|jVhi@;aH=flW!*vCBn!?FJoV3W%V0JdY0R)qfQcNLw82V6R_EHLWAVH~Ry zG~%=|6(6M`71%d6dns;|#%mz{0==2TYo@)#5f#~zo8BcsLH+Q%OWcNl78nY+V~24E zHT>1Iv9ZaVxqu0kF*_rVxK-fz^rNK@Ed@V_GP*d}5hVz?yV@pdtisG3PU$-3xUzC(R(GFI`j8-n zWEx5v;BZ1=tj`q?7P@rl8_3_@0tGf=WYg{5nr`VYHZ@CBQTg8+n~QkHs7tn6j5Xf( zN_oam6xcyHXZ8U^pradUi23~jhrj`&Q16kB{6OTf*xZ1kgnqNxay9Tjg{p#XoXT5x zTnQ|v;>Tf+zJs`DP@eQb9K!@RoaV({+o!tj*|19H032pS0So|zn-bY%Eg2_V+oEGRDRnnU^zow-0sHYYn;DvVy;CX%AY8*q?gJlb z=32S!4hypPoz!ImWViHF8lCqRRrjIlrqM>BUL_h6dE^b1);SJfA9eQ@++E~VRm z_jscQ3~d|`OWvUo3No-DuEc%~r^Q`#9`#|*^Cf^1q%E6E^N(Q4zZGHB5ERQ0_u<)) zz;b>Z9e{MvLuf!LhmxA+76HPiZBjpQmGkg0(eMtm1B$gY8AwDJ-yAEF(E-Ozl2Qev z*eIlYjLHlV87EOHeFoV>kh{ZcLbVT>GAWJKz$gDL1!h!*N9yOSP5P7aT+gZGT(panW5blw3ioR_`(dldC$E~Al8sg~vVcBH zGj5>%;tlZYv;!ZpHFf_N(QEA_D&KK_4&zUk;`&@Xl_mP9k=5vMuWc3o3S}85r z^N5~`mNd*?bDvKi+W`A@`tSqe*d_QxgswRWzI&%rDULYD&OLvwp$lx9ue3KcMI1u;ubrtMKw2YQ1Pysi@BuMF2 zVmTh^k5Wk)1vM;m$Z6??>*A$Ss~(l{9SgUY@C52LIw@l)tC-M+E7E9dD|NQUyUVML z#G}Ybc*QWl&_coJ4dbO5)QHIqrzvQtce15~i;&zk(B*m3X2NsI)6Y1JM&I3wqTQ)Qn5kx`4?}w zXZPltp4$3|bJvLW`Gp-F*MIhsyV|qUZ>?H0n{)jgw>38Ao3f2xJ~SMAWESPujhML8 z*(8WQHVMjQQN+C9a*CW}4OBbx(6{)iLl7;uL*`w#eA=8D%6Fqa>920I8=F@(1)jP) z-FSr=zCQ9@I~~n@YT$}sDt+m|LYlh>f{456fPS2;6@8ICS7wjj{jmpj|HFVeY|e_V z9C4Z#_J3hGTKV)HfzBU1a;R(YxE)D#gySFCnx4OK@g0M>%jWm#J)etij9PQbTVi({ zJlJ^CqHBgP?^=9MQ}ViGp(*)j%KoSRwRGo&$#gW`v@ZXJ4yz~q>_-Rh!ZCL%V&4!^wK7sw&S_BO#qswMnjP&$$3#h zz}ufCJOkKBem*PVTm5o*6@g-Xkkq7;LQwAy0}f<+6^f}vvM4PoK~qXIV8%(7RK~!Q zz*958PBlSuC4C+5P5p6+LA#*W(5eoZJEdk0s6#eI4~2QpMWxq}T**5_bghy&ztM5x zxC^au&tNn;Fep>KWF*N{s*(+5a;>V@HmaE6`!ZjPinx{Qqi2Eh5n4TeHJ0OO_n@4a zNuyvI%>mbh$)83cGfGqvF!}QJ$hLBjRf3bI;&7ir5@rOi6T+T@u=cI>5qJvxd>a5q z99dMiv4_CRDOkiT)3&H#+@FOw0=96Fccl@Pc2xE6$4tP6cFt_W?K-9qhFP}l^Een> zd*}fz8x>is%uU*!2?EZHMWaYc2;n8p`Nh4~rbrMZBxSaWd>})%?~Bdu$gPk_8_Z0( z^s3}`#4TdAZgOVj)dJ|Tz=SP0L7(cyK3&SX4|GvCSL=o;`fq=`8aLfCX7S5ktz5aq zuU()f%a(mLxO$$Z&1>O%a;f)!|DmA))4AZVOuzfxz4In;y9{pJ#jP^9c!)T3ufZ(@#J5&AWg4IsfjNseWD+ zD6X$5=RJCkP=FEom|ac2(Q~hxx%;2_V~o+m85vvTec3g=julSD96dMqdpv6TWVmzi zt^2X={g{6(TpinF`&4t?pXDm+*dv>LI{WS!0Q{NZnjbWUpIZ-m+WBl#|CD~knB7#n zAGY2nr^Ix0FI*j0^nBn}#}p?+*5n({aIeWiau^R|tfe`X7n|$;zR6cr;;b3s99jFb zch9&}Ir?TuE55K8_qdR2niK!D*|uJ-P$h8vF|J|A@g3WtN-Bk+3QiVZlI;9G|7VVXFT zfX5SVh<}?gM%*nk?d>mp^#!=V2ON+Juu7;~8W``N_#OVfgTLCH*ZD0Ui%k3xe>JL? z5Y~J7#~42@xc4N6`R*AGRuA)`xwg;N!T=R zWKDIZobPl-x*VY3g*SGogconucv5lvzxFXgS=i324lS|kpE$nO=3P+$7v=lo)OZe$ zXEzj&dcAZ?n(Ol!8e&(?2>ol1Vm=SaT0Ud~c9Pp!MS8mLE+_N%dZ&U%+bFB-&l=MT zFa8WqJ`+1hG!mY(^3)GgB)7s5-xoDHg~pg2Gw%cN32j`!9tec!XdvKL-GDUpj81ap ziP2Y8k$(BAn+cA^myEijSLw@uQEK!f0E~zsGg#y(l>xg9PKaElel*QnJ(yK6oi;Y= z4h8qlKJQi9=i)XRWX2sOH%xDg9$Rzx)du6c=Z^`TK}P#d{t_7Fd3a$qM};B4T?Mo( zp6dR3ZCb-a%iP(A<46zxenCw3bV-e&$I_36To`f7cEAOI!ag5PlGkW$c(jAJQXI}; z#Uq$>VBrC(Di|bTJs?b@XqdvqGC`1{DhbVQG*yXs9Rmu6i%~-v2-+*L1>1BrT`(6H z{FNpVN@vrQR06UD)2VCg&ZKCQqW{P%GH9Mp*;@%xwxhmKcP|Ki!q%B70W0qokJTv z@hpl^KGsc-V<}O+@Yl_f=m74C)VhMQ?y%Mjc{uA z=4OGtn2UPzTp_J(u46&x{5{A=5GV~_C$^Bf_L9v=&8|J3{1OYe7_3y#M;j_IOp=&1 zp@vRZ&!nqm<4s4yBys*YD(4lNjddh5y;MIAJHE=FS2XFc3%=uc-{|aj1nzbT3Hn4S zfR2xqK68oy0y)oiFnJB8?(Za80=FR-g&psYZ7X1W3!HAxxPymi|2v0)P7vIU>EqZ|u95i2 zs1E-eVd(+qNxGEKmJFhtHx3QyFg2h)J;R??hQ0>>4Avp?J5=DCe|$pc7aBUSu!Mu7 z=8AfCH+vLpPIn28$`ZZb#@C-sdesJ+8q!-`vmn)dWKyI;a_*5FRr;4f?$LDG7ZZk_ z?wW0=VoHuGsnPx&XuTjmvb+K-X8KZn-Zi+F`bTg{@9 zk!rOee#936*d+LH0=qx818wuS8xeUj^Dz5_FJN{8?39@hg#OB|9}MM(ouXP#;Clpe zXGV2=+PPl&6$;^&pyDy#Q`QA|J{8u>Imm}=IXvoUdcdW>Esvs|U5IcYMo5!p5beM7 zXQ@l~|Eos87U_Y2gP;dyc_$I;=B3!@E~(l*yba+pBj%@h`X$P#G7?w4cn!+&^q3ns zcFa3h(jNUw?iaah`)-E&zI>}XjQ4aGQK8u&`NSW5 zmN7mHNrZmB0h>SG(n9#uLUy?4W{Hkj)^^4?y&0lRau*8!{khFYIDKqAANmx=&dZNn z`*$aBCeaVw=ISLxdy#KW-&v(6)z7Fp6GB!Q*e|kmYm_T_3>PB7zWt3)Y2e$|Ky!CW zIOE(~4A&HJQO7*C8D~$t#Z9>oKaV7<3@^@daimuT&$8F^o(CQGHLtVee6y-Kd78vr zh#y)5)=pF!&=kD8u8?Hxkjo+QL)-OX9kPuBeFhN46^pQtLIbQ9G{eMhS_>VMsJ~m3H)%icVI>OR0po+UU$?IxA{tcSH+kIwPfI zuPL&HzD|8DpK2P%36*Unt+!LNwY~%{1CDD3J`2Y3h+o$ zo(m6vDa4CSu9yzP>1woB+1H!v%u0&#Cauj6giPz>IH$&1G-shT)691K?bhZmw*yGh zhRyc53p+bf$?Ef4DW;-g81lQ(TQn;Y{F?PtWG#LA`VO)mp3(Kxtv&m3?RR?5Be#De zg##wn-ZFD{`ldZ$kv1EjbCqRvZiw32s!-)daZBF$X7t5M`nh|aGgqe%eKLRj9;ZsrSw{QA{5c&L zX*-y8^Lc~!2{6p+4FJmFo@*wPulfeDI%S5jvko2=&UHBCBdfZy5FGsJidmnYnZ5!N zcim~HgR*HWeH)E@I+Y%9(v2~k5INkvq3m3l{q&)Z4$(DdxF3>#`fjHwX6g$&V(S(h zvifIGEFFC`pBAypFX<7G7mb<+V|T4@pJ_HW3A;NMk*WTzDW^T2YD+&BynfIWkKVYT zxoRg5F8<6fzVPF=FF$@H;I(-=YdS`itjRf{{C%<6&5OvZ(c9RbZGQrHno*Tib$Ow>yM$Z=S#x8LMDg zZTJvu^++-_ClpN?s@oYcownuDq05a3j(W@4p{wlqkLei<_Jy}ci<+Ky9h$a&aVAIPg@fvPCxM4f%HYS7AsE6ann-M@lFpTeN->s9^OtRf)~IgmGjG0OhDSYXLn z+RjiOb0+EY@5(q5?V{$J#chUABe(a`NE~rDSvlZuSO!^)0MgzJnjWo@&UE3elA}V^ zv3VY5ODfnhFrm_GKb|GeMI84}5SAiHEsQ?IiGmcB_+z4HGZI+Mld;6}$e|MnMjIiz zXp$1hD-#D46bdH~7g~V_B80G$M{!Ot;6n%R0$!kf5YNWqSd6i}vve0;P^)(C% z-pjVEpXlK5EI;YpmtjRNxM1?c$;CKdlK<{-ew_XNcYgC5*stIlt?P8zWfSq$Tkg2y z-FXwvYp>PI>G>|6DAw(sUSRcHV-1+y6pWAeJlF9>Pp{o6?Z@T#{88G+CMPk#I=<-f zKfTwQ!%fOTvKg2nW0O;Vo|~xQlePG#(?WXx*}dL18BMCrO4j8Z|3chT;Zif5E~9Vv zRIU^ETy-)f42=DLzw;a!+dBi_gR2Ui8Q!x^-7`G}OZ24FIS%-+yrhRAP7PaQzM`NpHu0g8n`pr)kn{Yzt?@13*I%OdaryP^_{G5H|D#;ryS*&1 z{)LHCTh?pyfO~>!a*u4BBrngN?erb-Z1vr7M#2@V**KMS%@I{) zGMX#Y;lLb2cLiSQr;rrb^NeM=f_PBAku)`>14eL8qoVRfdGE1qlZ1}&E8Y#Td*-~r z0_JB_`P-E%$UYw6bYwvTPJ`V_lc_jfd`Xxzk<#e~uggE?J$DliB=*Ol*-_?Wzvk_& z?x%ltY}*k1zF>vNRA5dQoaHH`d1aX5;2RZdc*FH=wJ`j)B^2DvzuTT<{}Nt4ebU4< zHYmj^Fg}-)VX(PIuY2~cfMgv$xg_hdw%588*y-_0>{6ai_th}R?GcF#l7XL3_4s(3 zfkUb0T06H9wiTXxuD0j9t&UxeGG9FEz!!mx@I#Xk*^XxL)VgAl$E9was!KnpAgUUk zJD1Q7G#>F}bB@Y!ca*oE2dXS7Mb#zvj^_p6E;J6xq`gWS_|Zey>3uAKJGsZ)hBvrvrPN)0nv0|F8pb#vA#WVb1zre4 zjkpOwA2xP06Fw*<5@k|s(5X{v<6*%Z?J|}l_~SribRr15`(rWCi<5ou@{a9pUNX_F zTM`H;5rE5pw`K_z0m9|q{Xx=%E`rQTYRXWZ3h_#Wh=xIdG8Qm|B?^8_IwvW-F$L9g z;uIu)igi5dUC*pw=Dd6Ijz~=5br4{S9|i6;GDO|PIu^}L%2Ur$xak$-_9VD&-?w2H z(>NI+misYVJK-lG!iTb5^!XK7MTTEi+cqHlc_SW|tA0t>O2C4<-x(|x?*t%t;pK4G zNSv7aMICF(y?Xzpfjq$8z-^>gQIkh*CpQRC?#Sk16?v+@Yc%8Y2Pp5~8vmPTErp53 zd|JyjB+-%f=dmC|jv~e6hicn5NY0uhhIs6jZWiX1iMS`oqV@&iQ?pcJ(v@7Yzr z_T43gIA26L-_EM=jFS;Po3BV6y2R2Gl5}e277U=Y(`uA({lTNWeKHga;rxWk4zo zWi=tcsW7b$?KSfk-waG@e8kV9$PFw};CJExZrnsN`}<@`VGl2Dy0#7A#Y-6!ffvAz zM3yn`42cRTBL$CG=HybIa!rAXVbY?$&NVQO&Imc9(}bK=b?KGnw4-Ozpemh~Ps7_z zx2@;EQb+Mn4-UpX2=A1fVT6+sT(t`U`{F>ZB+DG;Q2ztqZBu(jX=(-_eUcbAxvPS6 zvLvgBY)efYpn}jBLj?L2C2YJ{y~QhjFnkiMOUHG&f6RqYC8rP?rD5F-pgu$BPmoB# zO)hXYG)b7|@#aKY92kJY3xGg|^&};-1}S6&#Mu=Bn5NofQkROO$Vh-m@wy8@B7SEP z%8CE8sL8im=ELaIhoiF)%c%ufgYfw6!JiJMjUq*zHO=WX8FhyQa< zIzrnbuz&wnspl|=%wJH$rW7e|JQ_sDL3O~~Fut1_^57Rx?8L-)uL9QO+gJsE^nRgG zF?A9JHsNSQ$ZDAe0+h{6tFcQJc9Nm6e!8xbp?FbbEY(vg%1mz&&MIRTAXZb-i_*BR zfDPbsd83iu-bp5y$8tC@a2CC2Hb2t1d!*@E zh`>D;E?Z55Tk%~ewKd(C9=^V7cXOjvSv9LmyJQs}O*6k?Jg2vcg$v83`OM{Aspe#M zD_$E9&A;ll=lZoUgotl?KK#jS%6_r=>w`^O&D5QhMfrtYJ$DU`r1n1V9(jEGsg6=l*P}dZp97v+0raaP`TEwL`^b zskMjpSh%r!Ro*&?@(mL1WILFqSrsm=*8|w#^^XdT5TSai{zT-tqdsiPg|oEQT+45| z7WydUpp2%}taK`bC)I=O?2F*J{J|A!`5^ zoadXSTL*4%DvfGx{Fxc;>tf|(c)roGt}WJN*ulZ5T5HCiF|JH5p!v>Ug`HTx(_Psz zt86-{3m0rQH2|Ap;f#AOc)sh_gFPJ`twv8d6L%zDhqTTRX3 zaM$h|%`2?-$nR|$`5~_90LIrQZI~BO zt&KK>`VbK}k$oKFyKT=%MQ;wt^hiaf@~SWTjqb<-}Hy8Hx&|n#N%ml4#1aLPC$!QWGv!VL%Q4%R>3QInT;fij|c{DrAXeJ7o z5^AsF%$-H1hT1{aY9#6g=mJZeNvoPPAv|FfhyrtgaE2@3+#r>u7XY6L08Z*;G>7(a z^iozB0?4*}7T2UiIfw}W4DWB-f&;}Y&b5GmUT+4%sJmpBW6i`z?!hd{!A|PC8cT|3 z(A-^$EMHnwbf9-Q7%6LcnFGu#D~|Y#l^F;@Sg+?=ED4HK7;e6Z+ZU|)WJg`-@uV$z zZH5CjNjL&9m3@_m6&nI`qTuGlZ~*8I?M#Z)PYavkTnnY9IMs}@j|;_w2A?#VGI+XL z{TfDvC6Xj;Bbmf`wg@T!1l~qwTkp0;M71rW7&NFqm_Q9NM9s&TF;kLjR z6)f`;sKXL$2?}3?B0&CI$2gW4xCn>Q3par=s{&>M)r-}l3%*!qB;h8g|0;pFPmzM^ zKOO~QnNqVE(*z%s=Yu5J$uB0GxTv;(!oM0THk~HdUF_Y*d+&`Mkw_323h2%6-TN`t z>y*s`|G4p6U*TBmv+J{$U%qnX;w`WJgm*;=E~-aq_-Ww=;Ai%+Jj^H>aw zk)H$n$8L)+@b`nVtd{47o!US7$@^CX0*hP1ADdUZ(`G%NS{L;1xrtx)z3`8J{Hv!Y z_`JF+)^9lo3T~&M)?=&Hk?)XXMfK!AO6+z;_c0zf;)QnW9hb$8g}`tkQVW;9uEeeTz30# zs$-2)@y5Cv$C_&PaKVHx+sNkrT3=**(ZdoE-oqNF=-z&9V2{ja(hqU#W=?~N<9mF- zYid~bWBUil!7>g*tb*SM$iF8Wb%=fG2d>-y;{(?{hacM&ejkcGPW&+(2BNpL-oIGC zd}3nd%9T87d_sJ$&$I3PiHn1LhYXwf%mMWO3&OvB<&{7F5eOqrG+lqe1V#d5_B>A3 z19OC&?+aUSbJLbJzs0EGcoRV78Jp+W?4qsSlE>9eU*VDD@#J$4*7)NLVWt76oEKRg z@zTs@;w|f6xEK<{6X##D7;=v_diPymjRM!>fKEXa5+}U=?pL^W{`sZoZ~^{cN9#T$alieSB0(vAGbQ}O|q)WFO2Z7GFP8CoGa{o zQ{^9NcHQ?(U9oivhPWcZE}89ICve}jBFXmBVaYkb<2VA99~(Z#I2`aXz!K^c1kBQR zF9m9t;$Qo^{2SB<0+>5}|Dz2%1&258^Z9q}Bu)?Azn;z#FFnW$csd^UHL9nt*bY}e zJWl@d@;Ur%CE$){oUh}#T_Z1?Mi2Sj5_C5(-*+4A)r)4v({r9zlGj zqBiCRYW2x?pz`h1-*ypWbZqpY$K8|D4o_2~0R?Q(wOwJ**pX@K4Q+I#d+e35F*^{T zu|Qy~;gvXcw5yYJ6?i^$c+@>MO5=giaT;Yr@YV5Y$ASctpvMX8y_a7u9Ok>P$6Q7n ze>65~c$aMBNe(O8cAWDQU^7U2sPOVXLf0GC03cmvkaSNRVZlc?kUIe;s5C;y(hDnp zs9t4$-(ETP?SPui_34qS=~K06FJcrW-hVYe9js0~63Tn3qq9oi0c$3ZE_ETGB`KhE zEFe0`@Ow#C;&MfkZxmEo6iWt_ggQ;|zz=5K)rAIKsm`dYyJWjzQ#o)K$Q>AZfdmi^ zQ#1Lba}-3e>*?bAHuxCu@zzb6hHuF(FH;GegPojHETY;Z(nU7CS_B^fR<2|TC)6qy zcoMxDnnyY1>r+)J*;CU*AfG1Ce z1{yux&@*i0F!H+dmb(Gzwr;c@-;DdSd$D&Ld!|vq;y&=@W#3=7B2W9S3T@+6O}7?8 zUa1)Zc6jG&CHhR4H=-zi5w~pfDS|%)oHRo%xl@x52J!-D4`E5z17D8>xPO4Kmb9cq zMZ>qEZ6s7Q!W`f_vB3~jVMPK}MZbnT9Rnb)BZJ2|mZS^ABpV>Zg5W&rd;bv~^ccRb z9LZ_&pEC!#P>)!RQr~K&lY6+e6I*)PmY})B%{q-8rmdNAl9_6&cu48)F}%~^n>hXR zxKzd`fP*06fJi~bp+W`zcHq#q90ZU*YREPnGL`zZ2p zSf8Ds2=7 zj{B|Z6mRdw-7?tpNk>R3UC3ktdAH48S<`YC2`x zFa&}ugh(b;=(>B6lRrgh z3(J;V=02%3ARU5BKVre@^W08^BNdrY6<|)P<NO)*wioq=HRYvg71ftx?e~1?`y(BA2lJGOO?$CJi z!EHIBpLO$|OGn#|G>>532)X5F_-uv-sz6E>a@ld)eiF$Ab@OHe`qOVcqJ~x|H2BRA zzJiLXey!+_tp*{O4dBsG zlxQs){wgu z7b=r!i#HmX^2|MkFv_hY`-L;GV&)u!=7@E%xE5Ddr!}Y7q=Zx@?cq#G-?>ASuty6LsIJEXqc6WDA>%p(scMYbe5AHS>4K~j*#gi}YkBDd2Ub;2*{FTF-H#d&l-O=1( zHmx`q{Yp-lH&ufp80RR_x@_LDbF*Tyf__6IS?r?*;`rGwQlmWBmE+ROHvv$~Ny6H+MCgz*7G;BndMt*C|>F-**c-5k{S6}+gS$2;tt-(I*l(k}acQ4AD zo4VRV=k582r$5?%q0_WI5&c-l!FwJ0A6=W~wwuw|d54@>@|einK)>{s~B{WX4DTLo-9UeoluQ+(orX%DJ~Qu zr_EF}E9`~A`pL$0WJXcnj-FcH!}BFD$PlLZsG>MRhJ8TC=iu0E6Kb9B{3B{lcL5po^_c2=tNwWV>3ob@b;s-clk zv_PZHBP_~RmMi!c8}RH#PLXT@eZ62S%yS4ySJE~xhYj7y&1fd|{A+9fqp z&zZZf!wztHEmpZ<;4mYE`jCPTf^DP(GT~IE=tItyr;szOo#kAKyWB0JuhHB-3wS!d zJK)R~!TG+*uZ}c-yPqUAX#z8t?YiAPT?xLEL;+FzM1mEMc#6SKz=9;(F!osLKnPSJ zrqWi*`BK^N2U~ohL@2B{l8{}rIq!&VCyiASQ%zDim_Z>Ai3t<@L>4Tg31Ef22*`0w zdRS)@65#~AmjJlSAX85f1~uj567=ri`eF@Jd`;Yar<9N^Kn-~21fBWg@?!McHD8BU- z))#MC>BW5wc!*~Qe!}vNY{ni>mT>gUi#=C=k2LT5oLCQ;N6=#IQ=P#5ye!}4?FGd? z>Xj=YAC_J00Il@Gtj7($6VoQ9x6qb(-wT5$q#YT9Tuh5wE{I$Zxgwaw9XG!}_4`v- zfVOxT=W6TAaRF=RM?E@16G@hw2DQ?Vf9|#!%HYV$80N71nXa z3eIx$ngdNz_XBnZnYr6W>h?+fBFtNmVN}pPoDolc_N!xvj0M*5#(ErI-VyP@zaFk0 zre9e?;>Y%ScsBnha7e}~pJkr5&3t2%@x64IALor_E^*H_?zldPyRH%5C!F_KHzJA#SN_F>1H{{2ox<2XQ8x-Q(o_yuxbm1MfFqywv9;QZ9?r9tMrxzmUpRXN$B@8J> z#=xgF#+U=UO{G8n5xsnZez+JoJ#a&uaDY3m z5$E33tFwG^Du?AWRDbuod++5l5xyFHFW7=4Tb4nf^746?^9cq#RxYsLU-R3!mj!~(AUoX#p^z)BAeQ??D z;6L&HwDn*8Qu@@ymd}5F;&ZE)z82V$nYb5t=DRKbP_K#8wa0HvhOWrENSUnT>odp& z)`;(0=nP#vV_{XQFcq;K%Ut2vvw6($aF@C$>B=^CG~n(SmlEQXw`ZDWa(F2$7n?;G zd;DGrOK?Rlrfokxp7p^?@e~a`o3M?56tV!T#(7{dC09@np5ut;Ev}Kix1YlE-&hOB zVW$#4TxvOiH*0)&Q`yrvngjm1B!w#TI6rnkL6QpmGxfMr>trW8Y6-pI>Bw(_?X)Ap zb}~pN6?D0?k*#zeWjYmfS@7L~Aznfu+MC$%x`6v$E8w!fCWm;zM3ktw><+BYB~K3& zc-SF5ewc&Yny!{76x+8;4hK@~uTjxOOF<9#`4L$Czz(X1^DI*om-B1WmktXP@!Ag8 zy$*ZjXnP#~{Jef##o?pTiMl^xohFyuqp%&7OO79tM+2y@aW@_ac#T2E$-rTXb4$@l z3UP5LxJ*^1y%NBgeGk2>jmiJ1om@h*$Fa9;>5w~n6p*6!##;dV0>{93_PC(eRxOzp z7=INp-%!(BGEff^RRMCBaZf)6R)pWE`^MOK205MQ`^shf?gl>^8zp^=w1PqzkN}hN zeke6Z6Lt8X^%~N( zO(DuyZ1VCC$6oPTMwvDSib9P9f{J~>ms`px!U$7P<5iAF8q(3LxEYie%+gR%t}_#x zF!`Erj}+9}kF~SN8wYx0fZ1skk2^AA2TiStY=a{_T6r1eGdU|ZtU-Wv>fdMvnng9E zm@-FJ$jy=3posn>$4|`R=2JVQ%A!Z{-x@ZWJFeOW>#8x!qE5mvMP*AMDru8ry=PhyDYJXAuq!A&@FE8|_K{1w2?t@8?c| zw?eq0fC62b>C#?<+?4)AGsXAzq16JU)GJc(O;kp|WuX&ki0A;HclGJuqzQ@f(D!47 zmzma?$r3g;SFx`?sq{oZvlH#OW)$gOG#u6_(TTTFK!5fI)PDnjHBHY^FhHBiL?y`! zEbNe4g7Jmk=aXUdpzOL!!baIOUD|0>NKpX4!WQW#?efjkhZj1p)7GMJS!&ee@9!UO zz)S6Yd&yF-5m&t`Uv>$(KPIa>8&ixcNt&rygtR|@s(&$CBQ`-g<>&VY^*(T7}Piehc_2%0GIkf&M`@w54)f~%^ zK2#>VLE;cH0B420)ZB7hqt`PKM{7-y#9QKvL)fG|UL4->HsoYi_5VLVD`45soAaiJ z!d`A3iJt=ijBdx%W5!qE)G+2;Rz;n>^)ALTig%y3X%^Znc38_JtWT@C9ce_X1seM#Uv^Ty1gIrkU;bE z{2}49lwsZ{sCfW=-2B&WKBK`5SG#X+z&wOywJM@w%u6aU^du|nZlrBsL7f$g$NNjK z_f2~^f{%bBpK6wHUV0Gnq*+Az{-YG&`RoX#_;bO>pRL&2lBn(DJy$J@U{IPni=Ws8 z`+;u6Li_kF2AEtAsTGjxeyfA$MXJ6*O_alv>|gC>XzuR+aXUNLs9Mt{suLf_2m)6L zGP+7qwo;}L*Wr8}Wp)*`?lzSW35hvoXGvz_y%~(g#*8oqqTR9+BT9>}DD}qWs^xL5 zqVGt^0Rv)>c4Aej&|T^@1|(!A!B11GDh7IXvNuueO!jx~C_xxUb))0Qarbqim_RNP z?GiWB?yg*?QKtbwJ5HLe74!ueQ>D8)7M_8v;qA3O%*1gkWHJ zf=||+ZQ5S6n$Kv3xLnOKk6~ zZ&<01hG!gH@RgC+Bhko>J=UQIodwa`^LMYUQe{tU>yz#I*xChUYvh^zpI;PC-L!6R z)7@q){Y2iPr-F8tg%r^%lT<}}Zl@+g0>w2)@PzvKVD_{07<}R55#*fF@gbd)#Z_8s zfy`u0BO(?qU12*`zxBs#bVVv1UE5Bx(lY=brgOM`CY6dNig~NQt!3?@d}>x(_(7}O z5$jn%3%k178e7*onn(-=QiskW$Z>a=MJJ~l9W$u?iQ$fx zwSSk`Y{%1|jh&xrOJ(oMC)drwwc3|{tm*i53$p+FbE#*J{~_P>U}m%XymdI?7(+Ns<}lU)IJ73K7NMoc zxr8(za_offE0Bh4!brgS-$EZ_ay<340=Sf`2LuqC5LTYqmoPp7E-8+;5crOr#lj51 za(E{;qjNJtld9EG_~4=o!_g{pdPG%=3W?7ZhS7DPlCV3KvclpXyRW1=or0o7W7fXT zzP(Qs^#KveDkG)xMOunZF6~Otjy|BHWGOv+P5Us;PR(F9gwj^z(AO3ExQ6Gsqth4g z{I6&Z^=N2V4by~A5aY9NC%up22!2kg74-~1@Br^Kh$m}2Ofuf|0DtgP>>|DTGZmIE z01%;5Zw1v|=J*0@l$6wVWv~);xW<|(6IGftZVxJzrM~@2Qb=_c*XHo}arAI|Y14sZ z$sU$y!a|829+Wp5Y9t&{%{dgw*@(j-ZB+GV@|u)r*GPyORhQI9ZnU~Hl(DVbB~l$h zC%1~+?ld2Yv?M=KIXjDxf0$Td7l)s~Y@eiD-Ncbk6Ia~1D$iu5AOk2b3D4-OFk!@_(p|k_QNHlBM4WQmkm+iC@6(}Rmjg5+Y!@q_svmKh<6s9 zc9=|{t6Z zyuC{8V|%%p&G5awe2ya9Pv9X8?D2x%fqnw0WHE3>z=;j(Cw@Y2z6o@NX*FO$T=G&o zYHniQ%Ja|vn})@X=LaPiyx;=oj+WpAc2hIP*b?yX(OwgmU~3QBFLG)NqIuxiM}6m0frYkGu%|1@N6D+dp*p#?Ci(C|1`$PvYDJGtf0L7 z)b@lk=IG&;z!}-heiir;!q?NSc=DHZOcc&sC+->j2M%Xaf2}{U&Oenv{{^vL|D74* zOj|t+V~}i~lz*(**{zNPP7TXk{%`KD_W0D2_VS4@Pk5=HOpnb>9u}H_4CPtBS{@XF zXyigYi-6$aeA9^+?v>LC2x7y#?~QqK!R~ABYkohR*TQ<8j8$8fnfuNCzx&d+p8xm% z_D$gVe7fWMX$bsoJq#owJ-iX85v+kDPA0%PjWvS&;q>*{l`ALhxbON8e|Ymv++yAG z6>DCE$IbA* z%U|K*J$-|}*SckN_2*kot$y++k&cdumh~X)_y$d^U+f~7F8Tlef`m7hD@bjVCWR~g z`>gkSPb9FZOtMPgIUuOwhm$*()Z`UE<>>-q;iM_d)Yo}-i-vs66Q3vzyod~IMeqDb zYhaS!pD`Q{5HLjaVeifxfE6jW^L!sRM7C`y=3yfzY`ua{dPtYX8+G`rNJZm4-(yyH zJFrqu%m<#@pGoX2?+hk!6a~w2)^V4NXU7vb3EL6R!tNMho~mT0Vng8;&nuxV@pMZ& z%DN`&@{zQiLun=5=hvm}WPVy=2Rr{~$io#`!TcV0}jCud-yv)!_f!C8tVXbr0OAApxgzPFc;2k(%Jnp|j+e>(mZQ}>y7@`D* zj?gp&__m=1fN2Ej+diM;RAqw;EHAil~4%4ym zm)9s@ic9DXoHwc5Q9u;0jZ*;mrNci0FhOpp_l?;{@&4xm6IFmZN3~^i_{5*I73n?m*5!O*Oql(1}4}d>Qcw>=B25CvO83|+qX1v535+EtyO6=A}YMfmh zx`I`au6NqPK)N;-omFy5*a-MpoVvTRm6oqX1WG+I@Z{c+oZS}?3MyScY z8h8U<)N@W-Gj|vWXCS30(tEH4>&;9jazr#3v!Q} ztR-lyTfC{S)k|3+FZXG{zWBqH-!b=u9DV=a-+^ku3hgB1Bl&XUabN-AFK&3|{!b#> z6C|nvCC9}JEa#_CjS{Vg*$Pi&2a+WRGv^jAnr*YpnL#_Z+rWS|qYHXfYKBn;f~FzF1w-;{ zi6%X1ox@$iA{Xcd33=KlezRyh8=To1j zs!rAaJXNJ0tXrK#nGsB3SiKy9A!>Av1>HNp^ff`3VjfQto@3%d8pENag4AEXgRqoR zUn$}q$TIy6U@yuZ<}my412=B)MV*k%XUti1Fgd3)9f@k#idcjG2O2g(>LRFzSSNa1 z!f%OTb*tM|QbS71ewYSgNNxmmg4;+14Pa?ON^tqtKaVoEe-S2-R0@rUj*emWjV|!# zrU2Nz{PO3~IEeyt?jH=NrojQ^4-L$I3`tvUZZPv+i^?sK9Y^-@sSC-qC@v8)fgf6C zGcvJ_fkC2CKAZ9fsBN65IH`aIs8A4E!wi1G!*)cT%EcxO={f7 zvt$v&8GD*o5_EtI(M4MLg;rpcgLn?9;1%Jeiln7{VlsK_x}*xlkhtzPr8Awz}_}*a!|JjGiDzH#W+!Z z7(s8VFBGB45m)DlKG*UuLjM(#Jn9cM6f)JQHD1>HsQhQB2jwMkv=($$dbWtEzVU;(Xm)EM z#Z!$E^}{7T4>gqg1l}>(0D+j!B(MlAG(J=}SDu35wr@^XLBTQ;ONi*_K6{a54` zEm`Ds=ybeO3~$X^^V)}H?()IG&i)QaQK$9PVI>9!Eu9f_^kPjTanZTOYF8b4bE_C^ zA1DoRUHU^ffW#DN9@=o1-2n;CAs(chZvL!=wYcrOWGzu!rwWDViP0Q;IrA+g7*K)u zM_M2mpeop4$Z_NGQ6O!}Q3#hiaqkZLtNreR7L0_>9Z>XBdUS`of5BhSUpaKQ=Q;UQ zqjL|n-8JHFFL*nOjmulQX?3Rg&Tr6d+n2}VjY+RDzPI?@Oq@0@pPd0BREW2zwUaY< z-SyDssm|;Hn4UEo}w zY1}b?#q_7B?TNX|=8nkBT}!W@drxBO!uZI7+w00VPkw6Q&e?Zd@VO~7TOW&;k_G2) zlEqQFp{@SzBrcX2eT?>#-@g-W&UZ2Nu;f$;Et=xf$yQ#xtL`bub&@RZhhxEXE3O=c zCFNqi%fk0jhfdcLKZP?t=QQKf{QTZ4Q!^I$*eB~PiL2S}e|LK`3w?K4V#~S}j^8&w zRaCua_qP3GCgFP{cczNZ{BvPN&!6@5*3(XBE6vZWUHkQA({A%_ozd$rnLlS=Tb%A+ z=K5_5&rd9D>)z>j3&!t1|FPzb+a^lwGnTa#Q!{(r&P>MNS@vhTnXX&s2MZI6ocTxQ zZd-KGtXtapGG}el#oKy&ojIfXMjgj~BIElw1@ngqzw5qa=@vghIQz~^wCeP|5OPdn z29lVw7?N?x6v{~gqcOjCxa6oG+pVVyljytXaG0QgP+6m<`MUwd6kY)Qw+<1l#6RKg;p%qbbGF7tiTH!&72WF58 zD~=}_cx&o@0TW~y(HK3w;?oL5k^if*y?iNS8KyxlI% zhETZPOvCNN-3Vk2XO*<_aumtU^YnmneX^b?<1iDeOhxIcqNLt3NjwS4B0Z}YKM&Ug zIXSoheg-L^b+c{D)+rlujVShBP0GkZD&IoogcD2X8vhdfFvdy2#HBG+!^SFM=Hlzn zY_uWd%#aOEZWM+*GIa-YjVr3c#4e#pt~uDHtvQrrFAT0w9a~CrBs{qrL--MM0Onxu z=MmydvoRg-Fge2_jXw*c1!KndBZo^WALp0~s1HwDq!BD6ImqE^D9B+S(-*8x#3)I6 z5?(Nq+{7z*P7WXQs>NYDm(4NjR#f!yh&&6?@WeLiHKs`S&PKV5po^13~dy*$Elk(JN$xr|@Te3%Th1)I%JT>aqkC$3(8@J}9p^890U4_*88dCli7U;gN$ zLxeJn9GsAQ?CU$455xZZU$zhb_ll_r&9%pmFeH`*oVYW34xfw2ekcE!H*{Ma=#HaD zbVupj5X)nEM}q-|=P~a6gU@B0z%q}g^;}?~OOGaK7QNV7erqc0-g?PiZTZ{3TuBpPX#y#B23^~lg@B8g#-sl&ld@B3L z#1o~lm*ddLE5~8*Mx2YlvW{%#e3xK1dKGVE{?Z)apa^4r>|Rq=3Ya6L$~v^9m85UK zBtg5^RgQ_J-+iv~oWPlnm8Gw|{L0JNgK3&tU*CCkeSOoKch6d<`udi}=!von+u${^ zm+N8%ALKS3_LFPScW(Fn+Y^=D-&wN;7~(`*=P*srRYoh%9iEVzTspUD>2srVkD2(W z(){B@k3s7hb8Kkf(@`12A3yeQxgY=d`~Ef8tXXPQuzMj9=#jhvG^!^%6Q&ZTA_B@evx- z67CO&`@KVsQ>%+jJYAyMcz5ExsM*9~0-z9C!n!(Utbc$~#Tq+a#p}cqoo7EUF-`(= z9Fz#`wZ1*bad@rp*T{V?7R=H>Q0X8rN7#qI6y_(#&jF8?Ir9-MOP@(>Y zs-;$2|F4?hJqecli(Nj`AyUKClDil2l{a&;S{h{r?fM#A8^Wp321co9L z!}y&0>Xr%Ge(sH_1`O+ej2RfhxhZK)+PVs-z}Js<6mDQFa@5*|UZMYFU=ie%NI$=d zEa{_e6}{8W3E~al`K=IeqV>z6yl1-&lvelQD(VeeU_an@t-phHn-}hS(}7PrY~OuA zB>O#JAhw1lZA(JASODK~sQgGCnA6+E`dy2W&H=~-#=9xmjf;*8sQ1pIS@_%+qa^B% zwtoUyKv?QW<~~Ze@CO)%0t@t)+NptAUh46DeYU2z#;FVk29pZg>QMM>;Gc7Vw!RP) zWK5g$mn|Km$t1jkqfg*QZ@`nU$hkG-=P~} zi%3NV3>?N7VB;z_gVN9(3?ps*fR|x!4chte541Sa5P8>crG&U?#IooUzL<>Xf;`0n zyhFf|Rsh!m|+naGDbWOCgV^&@K^!A(VlOQ6+TahCq;lH75P8 zB{5Q2bz63;#+Q*@Jv+daQ8nGj9o6EDQ9_Rk#m34GjBjQm;wH?Ie#{pav|_kD^WxAEe|3i;9vsnxze& zr7Y@fa_Vg0o9$~+0l@B`r*oOrR#yGJ=l~7*1+aaDT1@SX&9@XD+Eh7Y4#%qBWz~NZ ztPKZNbtEWH58<<)&g1z6V{NuafAhe%Z+pP%TCFcllnB*%W}-|k!}PoN795RsvHL19 zwBZKy@A|3UHjFbPiQ5%~IniBi#9nPYgcnyiw7R>so_X+0=KR!h7J#TAIKpP^?h(*1Rm~M#iNYfetxt##T~t?QRv4|ev&bia=zGvqHV)=FHWpmu*3^#Gl zjv3cYeI~x*{4aQqH?R2g9S^1b>oV7l?wcB)@od5!y$9pf&f>Bw>6yZU?R6CY#;84+ zSA76dCri|s`xiDNT4?%Aqtuw^w_8LBoPWS(KFz?V!m^^Qd$P-Mx~R9cIN4j!TIc=A zCf|M5?cFw~b-vDgf4Z;IO@PtNq6daJ9PsbD8oo z&i*O8(y6qw(@!mP+b(MBey%img*Q6tQE5fn47aQIwKJY-@P?G!DJPfs_NT}J2d)(J+W-^-q;$;^ znLAPqNqkjYWU1N1IO-GaWwvd*5}#fG)InGglOlkcv(cbB5W_;WCT~05@WR?W8=JdP zq5M|DqN88P(PkGeZW29l@@8CLO)ctDU59UW0_Z28v~6}_EfQ`?x{L8;Yh>$WO}A(Z zhY1fdMu%6FlUt(e)gUQP597MG`$ey{StC`2d|~LoOL>$VyAWllBTB(h0l79ZsYA5{D_Dgjjn%|@*-!59%B&&bH3j?=?}1snMfceo%1E-WGg zgcvp`_91;KoSdx8^~K)-XsRH9C{;i2a>Fy$j~`8{5mX{jUQ)qf(C`j z4gAcWY7PnW$CA{iU|FZAj{l}lf9|B1($ z&duw7dgjW+%P)`pvhPz(21Bf@+*8T@GPkDh*smd4^~9d?9>4GEl~=DEKXU7>9G0^_ zttD|3Im+~WYXNrTe?BQ)3|+7EY{iQgZ3_e&LH$%l&~flQA;-w_iM45*WDYSyuo!oYSn128mq{+W=3)6{YAbD@O zHk@dJ&vV&MPnsK}Y%vUs?bXr%d`jnre#X56Q!((EFg1)(xJYL&!xzQpACH&i5&tX1 zd5HKKV`RBTgF9CDS>siTu4DR;VJp>kt|;KidT_D>+Sv!Z3%LAj+vAVC z`!ZbIH7nm}e)DU4dcw0@@$I1U+;h);ckbMk`QIUiKKFrJ&i}jVZ$J3xj^&?x@h6{{ z_u?P@d#H?93Edgh?R+^aS<0x;YcUoYi zwgTxa!Uzc6-V}w)hmQ#UA>nNg^C82}qkBYWRPhO%S!XGUrN~@jtnlw+GPHC2VILr& z(aNfOtdBMe;9t` zMdDowg8ymLWB-U0bE2Gs5*qH7*^&-*sD>1l5bR&ILbT~#7>7mZ&>1;X#L&#)L1P^n z@u>|l!!kUAg&?St0!~k;2F>pL;q^2W^3`}sV;;#$2Zq+?pJyG|W26tVw*cZ*ji17N zl(b}b+N7b?riZrhQChDE;4Vwx9S#7@0UZo-LGB>d_XiJ5aND?#dJ>g69r`S<)+qMc z`hy3nH3F0m`HG>OKxNJ*SU?T2AZIYXgTGiG#IP8pSgtM>;Az1GuEV1T1EG0t6955) zHD~z!(bWLs-{kunPbA%eCWWU z*=ko4Q;3o&RPj`7fRXK-GY^%SwE#kdcjUAvlccgGX)sG$0ke=po(FdmBO)Ox81+Qu z%K!FN9OF1Hs1Cjlh=MsSAt|w3vQRM|B{y#oG^;%Kvh{eRt2&BF)sA8{`->>mMHkfP z=$f^N@Br0sKna&%e6eqYU0y;#GbXa*JoCzLh6`vAKcBu-MBM~_vJTPI|7`r+^{_tt z9$BCL9Ez{ToQrqC<6CRp4Opcme>KpBK&qarpoo`da2u7ue-q>pHP$|*vqgNEIkR3oCUwvcm(@%#HO6dqd&JO z7brvmQAUYHnA3vXE6(E=Vo*>Iuxo6v_DMbQ$pX8*M5{MmLey1ylw|Cn1$O0pjVtS6 zoCkL!R!~$J0H5~~idE(~%84#D#*crXO+#k%1@W`E)A`w%R7_-vISwD4R5r+qoJ_=zhA{98a3gz|=*yu}&+x->C)5X(<1N|(AxUGWy zd`v-P#_^E6z=b>yqAFq;+0P{&E~p#yEUZLZ^92M_IEAYSE-mKRx;cTih|PVIn@E@S zV0qwC_zY09r(*hH%512nCwr3+P*gPNVN{>wspesnqXW`yBb+s>%5`eZ+VFF{QPlhk z8VdYoNqxEDDBn}<^JL7WGWNG4TtKTIID8|^M_P+wXg3XIXQ5Tlj47(0@EhtV(K`q6 zU>?>K3qFA|gJRt5xe`r4Z^H4rv0e@0I@(Hp&n$>g?AuO&i`5VLBQ~jnU>De1G_skMztig3a~R zwi%DD{{?~bNLRZl@Qr7uVhp_M^E`$5#0Q;?d&F*cdVbK-+JM=8-$2M4*C)_Bb zb;rhjN(hz1u53>!n!SB^jT#Ytve_s+?`-wOQRuJOb*+$stawrO{tD zd%|h~YH8VUHwttnZB$mAglT3JFE%=h;@_}K%NGh@D%bA6vklv?8&}i*d#3M5ED(*? zUFkg2xWbuA3s)EE*}Wy)hF#2`v!HuNu}IE>(QhnTwmI3jaQo!=jz)`@b=D-p*(9n_FAA(am>Fb&!Ru-}{j3 z-t|P{?z^Ub@7}g%cm9q~&YkMGYZJv!F6>?V_`XDD^)3D#k2!m^!xpfCActGn}_grnNXbF^P7;3r%v-jEeng6i!1#6jAi~^`IOh@lk0nR*So3S?p{jw`eKI17q<9QQ*Ei@+W7Z- zoprtUcs_O`J=K+VM#h&lyJc@YpLA54?|=VOx4KE^)s$;bPv#Tu0?)duC%;3}ina7? z@mH^&5x>5%X+^@HF%R1V7N>f5wk`K&)}6PY+v(mvnD={qxxhPP0kW`gL8;4o(rfpV zo6hyc`OUs~owl@1&My26o~M5oR_!<%l?RPFlIT>)#4|OwPHFX3T*LsN|NRB65@gN%{CP zIS`uMGU7W{*6JSGv`gV@41|?!-QP`@UsV>PWVe^Cod`!55RyO^8JBDaP+KimKR=%d zVn*z!7w-f*u_8~XvEVqPDZFhXFvfRdIqnCOokQ=Gp%cb5n@z{FBH7N( zhjrBtH4TFeVaQIxXE!>`O9y31<_g0>^w+i}$H>oH@_CWaI3A95&=iRsyhq4BJB$}) z9e|%9zs>}}1ReuMlrN&7>K$sH#Mmp1`FK`O;HX+cp2%m@hilkaxoF)N1zk;o=2Fkn zl`Co8mm8j-HBGdx>9T!5oYi4#_UpKQ!(KwOe*50E=Fc?k+0Eg4E~Ap;^HcDr!T zo-boZHhz$+f*j;`V%NESJ&_D#gi+RA^qb%O>Q}h$8s|1*t94yvH_#RnCtnG}@o;#i zV>mJIH|BWAF#aCMWwQUt#JrJPrDD-Fm#s84@!X!uxN$#1JJwuO>9MKO^R9Q^{U3a= z<{u({ZtTY?GPC}N|4L@hH99)vA0tfBV0F zgt90GslbX_T$XgvBJ;qwO-8h4%|$k?Tjv=2hXC0@97*M;kd$0EZQ9C}$Br3$6af0l zM5T}2`rMwY-;ec7>uFlrbF8lCYIa+vR_0k}T8hIAYV{}zh&6_Ns+yKz{zDY^$SaWH zJb@Eu7(nj1&wao0^uN;Xj*-Z-rgkEy)9T1DBIPYqcC403NPf+vOGA84q=ysaQ+6zn z_C){0VkD-CvBHUP)y#1?rbrKML3mE2^i6tb9}d5BLB4G%$%d*O28|aGJDg1VB!@r} zT3MJ5Cf6M9TxLOhDoh71RVqb>KR{O;6Y>+v33gS>g|~4Uu8-K7SAO&`jl7!H;OdH& zqx7D|c8Z1JkdH7Pu3$rvUbWI86W;ks$frcW{2rEv96!j|5lFK|SoN&cl4eUyq*@w# zN93eog}<45!88)3!_G|FsK*tXjSAf+z|zt@A4^09XSx<$4|P4gHm)0I0y(8VXs4%H(MFHLON;ghGTG=766OleU%C z0h<&F1 yD1^0x3W5k4$Lq}xP^L!!hEbblZzjeJ)PMMdJ>6(wpG=cnX2&huQna+^91-FQhkt&(k1#RB& z4$cfzsm;u#elpi{K1=HApR6KhtRF4~Z52=jb(z%RgBBeO_$1d1W)woT;!6{}&eIsY z=rLyH=c1{JGTi3X@SpZVb#&bb_n89ytU!tfh^9h}pOe>stO!~SR?uU}O%Zty3dTC2 zlnW?|imYf4eq=tKt~zgEFML5R@H48$kCXHnSl42;=ZwW@B#ZP;K{0l_maL&w7$?r# z`gKg(fLl^Be=DH%gM2T28rAFSI(rnuhkq{D&4&Nw0hGa}sR-L;X7=m<~Dcioq$~xVPM_P1qJL2lJ zbaVEqE)OSYK=M$|6z#NS&QN|xkvE|H*+Y)NHN=Cqr{`^N)7!QTZQnjfc0Q*`%E9c= zl9|i!C`7KbRj3ZB#ehl=m4s@?Czv6)XB?-~lRm6NIcK)O>nSheoCK)7!y}YH$_{Gx z1bHf~u(Xs!s#AagT^BF4_^d9S9-%mLFLFSKJbuGkNc9B5DXGSuH*2iM%|#kic+B&h zWgc2Jq{jfuC0E?Je}AUv+$DBqoCO0a?l_IRNnz*mJ(=b6+{WhBkXK5g2{sl-r(|YR zb83pXzA+wOMh`SDn0I&T{!}J4CqE_g{kulHT<`Au^kr?S)c)?&lc|im_!)2hlRNIe z_u0LBCsW3iFYfhmYV=)P#CFFh$;D+JZvj8cYvZ_A2`3fTxwNx!r<<^gceoZD z(0U99W z*DaCPo4F;Ure39Vs&|3A|JF8d>WVW|*CLVcT`_;)f7-o&HEsX0D^rVRWa7P7rF#8O zIbVD4jPK8%I?qYmaei;F>+}mh>59(w1qs@M?ohg@-ix~+_tqzdhgHKOuO5duCjSV%D>D;t3B(oKecN%3YYw3X0PTt`gDoPvnKTN} ztr*f%k~&JTNp9#$6ooU0J|nb7I6PI#ORE3}l#tsp1{nN?Vdv&xNb-~pW#n(V#)N=i z2Pvce9>wxM>Z!x9#)IWQMb(Zvt|dNQbVPH$5Qn23((J2Tbng+`Yc(6*0&=T`%}A|1 z2emp1nZ9lFWsb|GQA1lu%t;}N1$JjW#y2g{)p^ODF1t=%Apsn;i)EjpY@2zAaPsGX zzdQ(`WU^6x{PCKsGHQd1!dZn~A#$+7!Z?~)_e8V&)$G~qc2+XH zcN?-}F58)=F*P+MD(jBbaoBa0|ABMtjIbG>?pS%Qr|(m&|EAJ&u0e;pGp%kK zz)-^%Fa7p|XYYRYZVnq#t=uC&@(|-=IY-lCM`xtpeDa>BCVplwb~UK!!c{&t|AE+I zHQg{R$UvHOOk9}CVed3kgfki}6M2c`E=~Tb`>Pqx*C5{IeV@P2cqOuq{%87Is zb4+67!#-?;8_jJ-kf;pR;6a#&s_g_#KhlzZ^O0MhGQZ)%=n0;uu$Z$H8FTzE?7ik_ z22h$(r!^5{Ev~51%_3qx{_#CE>|UzQzD^Dw@v|O;bZVQQdc9 zpULsfN&E{>I%U&)&Aay;d1W_tIinAmyQrI+1wD=1lX_xqEQ*`kIh!GoY|b4Tnks#hlhusQ|+NHHV8~SNFTGKoA+kalQHGQnQD7|&+uuVzroxi8&Nh3Lb7%ayIS&!z*A(MEF<6}(!(xLij-2fL zd+fj}$xeVlL`)iz0zgpUj^`IA!8#_Kq>(4$X9?ZrbbP5{vXD;)t^zt*5Pr-D()f)= z;X{9RVt2%XB=h*U?N~tu$}{(%S$>~sI~bI(Kpr!Yn!}kX8B`M+ z+XFVS9{LBcvAsh`Hb6Xs-939^&$V&x#1665-xNXsBr4q~yO4&4`j z3`Tk}kI$3j;ss-@H&6$we6Axd%kFRj-W^Vd-$sqThSKM-Mps|}d)N~L6B^2;u>&2P zBjcr7|7RXxdL<4qPD&z~lA=1oX zD=Dg3(;N(PP8wSHeV+}=4q5?_3T5((hp3SrWN9sB1<>si@>)=EifF?vq#yeBT}g#^ z$Ay7PeHE;ww?tfnAudg6VJb%K1{cHr&TAO#byG=N}0gh-~}(l%T? zeL|=~lDOpA(o$?iHRhy+k2|=5q7c)=F-)>l+swcsF;bt+qKl1@Jv5B)mr~BAw>axL ztkQVH9C}U+F9HdZ=5(198w48&*oLYTXeeo^H9XYcfwv1@@V8h(hEFtXK~|4Xv^XHY zThfR$52KCwkK}7;21w_rGmJ3DNlPx0Gi)O2hy5euK8BaxQM)&($KdB=oUh^uz826P z1X1H`0 z6$>5=UL!Z2h(w7vm)vbSaY6yDW0Oo@QK^qeVb8U7V>RX|%^*@PnX9{A62{dz$Bi>hy zp4wuhR6U^aGNJ1nrTWs1B(T9MxUAzqZf4tq;Clm`$$Rn+^W9PJ9G9=v`HvF}tN(UF2 zk=?ZkK;KAO^>5QOOj=8Oo5Fz#a%O+$@Wzqh0hy3GJ2+r>^vj$ptYO)eMVuNrjjJh$ z-rqR9BQ6Gz5ASliYH%2mv2ja_Z3FH7N|W^UqE@B6VIh~PcGcW&qqU%zX)UiBZkCp$ zbXE#p2&bvv!^g9c5nS{>02l)JC?Nc)65RqdJR9&omOw~^_XtAp+OyDm6-TAKtQpTV zM3ybQRfnk~i+afoa?QaXRDI}d)U4o65S}mE_)*(4bQ|H6?|EB#Z$+zUT!xS4MQiOW z?3~1gT|MKOJq>?yd)liMZFeBbq~qUrnYYP>eTqAh>V z{R@+K-!bDl+;-z`y1AET`ByfseZsFBbgx^n#J!?%YVrEzx80SQ7hjUcZq&r>|8okC z6?UGe^G8Qhx`3C8cMCR_J{5GTD=5$*R|I9|4lawhXs#E?Ttqf#Se zcT0rvJft-D2ejJ6R3E$XGM+!vY>NDMR* zSXuY%QNPsN){^nOWzF5wdwuHiN7KXk%Ufq{Z&mY)-H9{uj_2NFH9>;XDGbc7YsymQ z&wI&hPGFkVe0Rnh>6YEJaK@}wxBXN3l+$ea=^1x*0VRTr+JO%5>;=ACa+6}}e&0DG zvuYJ?8^ag^IdXKF`S|<>QNa8x4Ar6@CC%4iic|E^PIy#;XdjsH3IcB)1@6UzBa+G9 zB1uNMdQosYgL_PXUz(Z#dq{d>fI!f&^+bNq*+DX+3rR@h9yKt51~<$;G@W}M2o>sh z%EGrRG{ycFPKW_Wtdc~Y{ehM_B{txM7Ok-x1&|?PV>WHMWlN7jT#$GspZEGrF0)j$ zwL`p(94eZ)B#_Al{3UXrx_l1ewZI*NlY|tc$)LbP3`c7Y@&BJRw-+X9R4BHxQCw3F zQZSj^@FPM6VDquyoDhAQLn-qwkSpMla7ZiUk^lM2o>O3b2%#*RhWq@fE640HAYVw zja@;e6jdLSaIT*Nhb%TCRLbzd;dty`YqlE4y-1>y(w&B<`rsu8L(HBKh|TR%b;o#|OD^9b~zgJLunRLk`K`&HS&TF0t+>2#bB z`Si*+8JS?GCd9Gyr^kMqqjgK!-ZOi5c};HJI`;2-4pN7&1U{RYc>gD)zJP>;boJFA z8vG-&-}K;kQ&Uf$-?Ox*X~&KSAAF;^(%0ucy1c2U$HdL?zY?qTF+ZE5aFMyGp&@3| z%FjgZmEX~^%I`XM_w}t@887{%3B$amKZk}Il{@3fzZ!4jS8J~|BOEZmljR=u@I|%*3uL|tJ1|FMuIlU;@ti(?2m^j5*7+RB$Q*?+cU&8H3~?vOKN?m948v4y zM*BF-F}g3NHq7bF8Z$BTt2RzHGllqK*tkpZ;~%@#{%YN8&0!43)hNty7sOT58Q7iE z9pJttu`j%u2jk9Zcb@*d$0$yZu2~xXG4^;Ib3|)@1@PXlSAqcajYs^yPoHR~Fy!nb zraiA)_uO;r-q-iu;(z&Da|#*Xl-uO6T1BkULDbI&Cb@AsUMU^DuE&l}Ba4F1?>2=Yc%I@K^V z+hB2>B0QHdhmq_=5(aPhX#1Mr!n`k#m^|T)0yoiZ;xq4M*h>OiMuj%awxy)4^-Rb` z`rwz0DPrptFG&oDYzYP(UVqjHnj7 zg9Vsnu;`1v9aHkLyy1XSiVQV+h#}?#>)8EwLVC;=E|xlLLY!#SY1k1;Av{zOH+pN; zj(o7)Q$k31)9}diX>Iryr^C0+tE_uOKdtJ<-@_S}R_M;5YH4xxH5R$4YCKxPMUV{B z<2k-&7BMJUwcie;X8$iO=JY8y2*r@D>L+OGB_|M!A;w-iH0ht#!_QJsLgIYe00s|5 z4yVv9x(x3tCWbU9c=4sHS*O>G6>WP+eL@80u#uVHfu<+lP$>=cqXMX1ORx*ZrG zAab!JPbEO1N(qZRO`2?Nlf)16IS*DQ8|{iD=6BLZ7sWE3xEQ(&FNd0W`iqbti$lSr zHL_@hlUMK~XbqVPKsd8>0k6f(NCQzg?g-Mckr;=!ZCMkHRIMd6yRm|xE8hOJs~^T1 zDNyE->sXjxy-!pIU;L4e1~hVu6eu(i#c4$pLr2iDfg}~U7@VZ9afb|j0?y9cHzwie zB*%c|72y>Ygh#97dVZvMF7PkFZk~?BB4<}h=m%Tu`f~{7I+vf>?Bj4$IFd-_2k|S0 zF-k+9M*17y7FC<&a5*u2kt`6k9~!8-n|3r|t{Qy315GU`xMd8M7atiwbpV4wbwX44 z8Pmnaa4N+tP54vx1T~DITmsKRTg0O%a1jT&ks2J?v4Ye%S%mT4Gq7S%Sd(KS!DYEW zh6UVUJD!1qQx*fufO5~4YF~vzrvxC68;vtMw-`e9bf8HnC-6T_5nDk8p`Kod?{~}$RY#>CCw+Zwjd_l zd;zvHNp4FxG_e(~9>3(62ZRdyD0%GUU6sIyn~qvewuK!zNr?)r4$SOiM)=SWsn$im zfV}AI0~gX~Nd<>8;Xui=!0NL{4pr3{g{kVCfZ`F3Mb1G(Ms1`lTvxYoV?*k^G0;1N z`2L6S&72EuNj;PXJqnd4mp*C;*K~4pAjLywSwKEVhZHXy&=_M=4WV*^n;X=Bd7UcKit9h^{x*-l5!#RZ~2eY}*+H{eVK zH9M*bDLF)$Uk^A7fz2mVfgOwzj>?5FofxIzjFojgMA_-QBrkX-B1H9HB92h_kb{J=O z)Nngl5~IAL9C!5&x6ASdAen7p!>+9y8+D`|cnU{s0h)e!nq{-LaGbWc5oOcESEWk4 z%Nkum4eM0e+Nt!GQr5Q9xWZvDFSAyb#7y5?B2l1r0P4!dl-rgaomof=0%L5=_7AtW z%1)sN)7Ybq?vl0g?OSaHaik7SdDKrcx|S3))a=>AMyMF6VJjx~o#SXsu3*jx6m4rg zFoIi=IKcfaRnfc`*HpBPColI2^5c3VE3~7%*fvBi`2WK zdli3%Xxr&DCL5EzfDzoJyU)4bNj4^)al{4ICvlG7g2uMR%?~H&A)2x|-bkD8q~2bC z^qw7!JLYX{yt~)Iol?c={@!KTwMoB#&EDS=&lE>@@@>pJx*q}%=bPkyTmG;PRcA*~*v5>Ci4yCY zokJ3e^?7s&_fA`2WM%Rt6hyiO_${wj9lKE10((0`4>AL)4TXH)F4=*z$z+F>ShbkU zFyw5=q`-_4CFPvRYVNCTm7T#V4Ks(q&$A7;ae@Q8!0b3?9xIQh%M3$|hfdH*#hSLdDby=hYa&P&3EO3)^r?~ZDjk&^q!dOH;wlmyy+%vDatiB?fCrX zcii+K|9$@RmFbnsUKZ2co~QRb{pByef9(BZAHH9?Z2WbOqi)Z6_a8j?$}1Z-;7aWe z_EdU~d;mF4K_B2izF${%?HU=$VZqba_w;vGHvP7;yNsMn;FoA``KSyZ)!iy~$88e0 zAMS7o_c-J1#7M?*v?>!>m4^(^X^dhD!-Z`+G#lKniZe2<$XH^8HJ(bZ#TskPQJlt& z>#WLBQ?U7rQ?=T!r!Z1@qmF1qVJpJ_h6}}IC-MX)Q{BgQAA4@_moyPJ2@{m&umY5TV@H&CNEcFn} z9hw0R_K9&zn%9jMVQ8rEfxkV9tAD{y<5NOx@x{*2KD%lMi}BZ38EBu!O<Z%51LX{#GWlS;W(9JGFLt7Gkb?+g zd<#=^xKG%hm!X}X0lSA-D9miHp;t}%C|_C|K2i1G!n!w1FG{?RJXIGUWyedzRG0&P z?=%0VL1-j8Eg{)pYbYP3fF_VW^&PlD z8am__!=S8co|GL9XIQ`mV^QouOpKj(*o0pdWxR}RF)L5*!Q>vhKv#a?*loj`)=VjNX} z_JIa^A$SK;jUx6HPro?n#aQem%>C++pMam2#j_n45A_Q=6sWUvY^@T~h$yPa$Sxe1 z@YDDUy9SOv$4m~Q9q>RQ$b>)xbjn=w&|_6GSNL;XI05UL6~v@DHYmWS;aR43z)u2W zAbTh!;U)5f*z|Cnr|XD@9KJpgq;OIe<|+y%o9&qTD}cIbjoEVDkfp@X5DjCtV%u4g zz?tlFjCjl)!3Crm*Ru*sVb+V7Ip5Wy)M?~iYQ^1%EMYuCrBGu&*{C&KkbO%dgus#Q z+#}Bk2EvGq93jes=3>4;grRHWMh+M&D2S!2@R4*}(~YocZY(7W%Sv>uRb1m7#0UmJ zA83UFau`J6J=*AE-Ih9vri~?wRYa&?boXz#7IIJ>V;5YK2jkG@~1kCjM&`F%>}kcayJKqfFINWA-1mW~SLwN};soycr&yqg^-ZWNdYW+~?270n<&Hrt)V_-z9v6z0J?ngKbiB6?Ha=2c(Su| z3EIWD4I!1caIhV<%bzR2Ig{(L<_(vp6r7<^_xDPH10gW9K^BOvB0!f}L65Xz$i6>D zWpWhOF`7SXF=kAbv#0BP?paThUo&Y&g9%A*R5lq^ap zl~U~}A=-+yYyg?*l*IT0zQfg!t}$1Qs5cz_SRg^u;}C(PxaVOE-An#n{S;0&q!DjA z&7C!fk!vk^MVw&hPrsZJYZD~mBX=c$L^?Z1rzE}RRPy^6>m2vGPdmx6y(X@zE)zuP(c48Q-I_w}h>t*Uq?Z)WQ8njnBBt8WT9GaiQNk z{kva{<8%(bSv$3M#{SXl69egjpI$NQyEm_1*0lro+IXv}YxSsd=#m*-e>LdYi;-HeC26Mg=AS=%hIF5E7BkphKgHtfn><@>^Z$aKL4-YGg$CTrm4lA zVq<&33J8oZaqLIP|F_=Y3XXIvqtBSflB=~qH2 zCx}aIEL!Sr#g9Rt+3L`Lgf%$_israDobfBd0J1SllW2UQjVy2?JU3GXrva@w975oG z7wo5?FeK(=3Z1zqb;u$^&1d~I0eKdOPIeOMs$HGre%w0B*{LO6ML6?eC4^pF32Z{& z_6N|1IWc9K%&O3b`gu<_6L^*4wpm~sPCvn(cSX&L+pC7R>L*N0Sbmbje~+)v1+%VJ zI#)isn|Pp3WAh^?wC=$$ogk}Io*!tx#u71^cX3EiP0b8P0H;V_Cc>8gDA z`j2v90-mUZk)G&wI$OBUno&lG8}^KkugQh`oR{vPkxCQfw>%c{GN(M+74KbO6HVhg zcJv(KAFDtsfa_I$#qpm8BAIYLRtKravsN}$-o(u|m1pn%_rHAh?v?biz`NT%*xP%s z=g5(s-~47AB4aoe5_HppJ3b%fr=jWn_ka4+_m3?-_S^T~{pUCSPd@+T_|s1xdyjFH zaqPfey5pA>azgARO0c%fkRt1OZ|S&y+zk!+G&F%P>YH(Y^39{%Sj`@1#`J(gzW&HP zkH7f{ut)w4!(Y4UMS9V`>Thp^`*HMKZdN>0zKU6+HZvKm0%u_Ei#HD(rft{_lxWi1{m2R{s~=TZU&0@7;Aj}$*Ug!=O?e)_r{Y~ z?WHhZ$94E?#LxLKRIb@W-g#@O>WZF+zE_RgxCN2Vah==OaT~Y$m3x5LqiX=U_ur3& zx5$Uc{nDjJu+{ma)SMIa+sc}Cx76iDLktinRm=T!b5B!bhbZvUb!$vY zq06gF!=07jp9re`Df4--(8-458v6W4% z@2>ED@V~17b^P7Bb!*m;Mx^H4g{CI{(l9HzYF z*xePSc{XE&5|at&(#sNKgo>~L`~TYe9w51jGrzCjydAw6-mKoP7I{ThxOX*>#aWcM zAjqOfRPQp#V_7+mWQoWw5f8+%g$p}_jxsW3kzQ#Liw`ctAG_enxW<^|Qq)}-`|y=H zoA?P4kbS<3Fiyo)PRc8ESLKu|XE{K*DwnhJ`})1NGqXGU2L#U5U4OgNf4=_u>#u*^ z{r-H@?+uQun15zJ;UByL25B4t*_Q};^@tby9DkVj8V`-e`y4Z7jj%c0eU0El6T7fn zM@ejfgZ@Ycg>S%@sqsY`pPaxqq=}pxL;mfV;zOoh=xW{{8R1L#-$tw?25p|WJvpC4 zPgf+xJ;K=$%5mm$vwDvobyO~d=X$~PjV(Q@u zU9N|G>}yQpVE*11_ugCs-RE}NDD6b7pXx?G|6VoAiveu%iG`6f?R{Vrj#yG8B=S_O zzanvl!7a#c#O?bzcboQF7-<$?KoK5Eju5oi&M8hya5g!aOmGqj+{$HMbs~87){uJr z?6FKV!ZOjOQ$5&b#VKqk+=P4e2GUFs#)8k3a~YKCxt}8+9Xs`Y0d^Ff)gyn(LT4kM z2Kwdf_XT{C%8A^O7mVHpb_YS>3BK`#swk3ZqxCQm#eHXKvdqxk# zo@(#CIMC^-Q|lf;k>)`{|9>|VWO+a4SHEUlGzVfEDL#XtX{P!kkbfPGClsZL#j%cA z8gP)JhVeBHoM-^?2-Bp8oc=1mpYfo25B6NK2)_{yhLZl7z&N^`?pi0-ft z$`}S<&}ook-UEq51t0I91%Q?2c&`}WjF#o5pJR=xDSRD(ScVdSL_6f*)HidET6ftN ziC*76E#PMX*hv3l6~QhH)z{G20O6OQHS5FYEZitPkYM8yYONgL!MqT_&6Ao>Elfzm zz>bg+B%sa3*9(nNw8sPIkmJ-ao5eJMUJo~IS{v?4$m$nmZR}!4?M~Tz5sznmd4D)G z#L_t&7A3O!Ys@_A{LrC808Umh;84A8C+R@t^(f%|_d%$*(OYom?ZAzHAh=A*97SJu zf-$AhC3S@!=h3g?sk$mV_tfaX??Y+XIWf>H$h}rFD5iDTp<^JxK~;YEOvi>91|z!^ z#cQWVYpb!uQ)sG+d%Ff;P*hdNK8dcKCS8~#%!`nE;%B8%2H=$HfAK!x_{wdjGA5g& z(O5D*@jRiixbZn$Z@{$s{v|}!1ZApqBqnH9WYmYH!uLG9&$9Z-4_D#VZm1llDBTc| zKXVL+o8cV633%%U3?o>LqJ-y4>A%8YZ-3VGp_UEG>TuSo9`tgBv387 z^fVGw*~1|^3bhXVbWo&^|NbpBww2x0jV4wAzCx{%&X0U=jE8@xcW3rR*wNc`ez&Iz zd!`(0Bg9);Uj2msm$$ICTb*NP1nv0C5 zoOGf)@QgVDn0acXsd!%T(bEvJ5|*0z<|)&oS>)tZUOgnHQlFC8B(q=P;!LVQ5nMar z)b2Vt<0B4Ll;f5s6_pfjhgF5a>vxnc?Nu5L1Y(!k?|Kt@vM2?frJoK072uK6qAngS zk(zl_Nj%gY^eaydg!K^&{(dFl_r$L&?RA_`?b$!Db7!&WJxW`yP^B&9kzl+ug@ ztK@3+a-mxBnn8i^%QTep25IV0Z#M9Ht6AEMSt(&G<|tlgVJ0~qG|w8>K66(fz(SG) zuXK>v0H{n?Mv;L^p`Y^mf+_6QmKAeDJDm3%Z`||yKepe03{ORmPvS?3E}`oJ9Hhqs z{$9%!0eyKwHnIIMkBgE^dE4P&J%qbBzYWhy*Dzq)`oisFbp6F}=EDOed`&@NYdAsI zS0sL_I`Bl0o4__SiI-7s8yy;jUq3hczF;hn_BFopo@F}SS100loTfbR7XC|v|_v;l*eD{cs`f{(_oFin*g^9 zfm_7588K5^ONgo=1wDebrG&FNw{L8KX^uiB)v5S|5=u17OvIoNc~0@i{7yC|{R*U4 zD4%bSIiaO_=CzT7WhZ#;Vkw)>nKA`7QI!xGdlhGW6Y@Sr$m4Vv*F*-j`T9-c zCI9%X6m1r@f}fj};KQB5cSn*G5{A$cQ;03a)X;1r$ONRciCqpI7ePu%oiw{#e8X~u zt1D|eFtt^XoMuBJKxPuLh0^p9EL1bQJWUG6Es5S^;knZ&10`~-!89Nxa~G3rV{@9M|b-j~|fikIH^$UZys9>tdMvMJ+NWZxe4GxCtf&pRhaFmQM; zV(Mo!vXE||M;mx@XxONe#q3fIPZ7Fyk_1aVt*ii7Os5o z>d9J2V^g<{Z}O<8Sq^;G&vvtXeGB5`#%ocVV1y^Saa%lodq67}r+7UvE#$BL(7v1b zb6)+|edpD8zq_!U^B=yN-_RJ-)obJDsnh)5oZr{?H|N)^`Sq{y&Ve7%&+|R!Ub#AR zDBf53s|@{$dd~mqS2xl}DfsE3yS{k-MY`tvx7XfC=bzbk*ZE&ywSM=ffBrw#7U<8g zS^4}kW{aay{ob|fuxlCbO@Q6+EnpKfyrug-!N0BjG`(>SUyUvxI5?LieI3D9r%MY3 zV&CH81F2_xka(C!x)~*ZaDvY;s_o>Uac8H&3$KfPk9)3)W2eAA!M0Z7dW6f;40CaL z>nMo}O_ zU&=U-(wO3U^ZOn94l*0RoXmXqFim|yuPJn>op!U{HhM+D_lQgT6RckpAD-lWki^e! zAI)hout`hYqm{cqKiHsc7BH@25W_PyU1-&_0cN)br0@J9}zw(~~C&cc971_tdG($;^wV(9R}} z-HorpE)=2Nh<@VGNj!~8B?+Ai2!=030pI!wy{LE?9HF}$6fyc44TZ*jxu_oI1}eHV zYU<9w0TYl^V1bRziKZACS4x+L8%iIgKns#HN?}ANHTN^nnuKy?fDZ$~mH7{sd@{ZY zc4tVFf-ndg&6^n&5*jxlT5^mR`#FI^0*d1{S++%0-AZ>&>3G?7EnC2J6w!YNZxElM z-Z_M$0zr#3lKORC%{<`NtfRU54wJ{PksL(_!!UB>DX?fPyHYGg#`5c z@6cz-f4y~Z*NotviLwT}Hw-+~BYJayj-j5O+QpBLYWn5(Se$Rdd#Qq*GRWM>=D^vc zxn)r^&OiP79msT$)E(Kyndrx6h<}y`tAj=m>b2+kFd#_9{pSQHac{@NR~aH6 zUHp*78fL^#MqJv1#PJg!mq+14`sCc;wS}LNdU?Dak9tH+a?#0#6F!WZc)^w|x!A)w zJ}7%4OraA&^Mh*y{piFL{R8y*z7q=Ca3x}93)TyKe2N=8!*h!BheOu4X0F}mh=LVp z+a%!`@NPm~nI|FXIioa<-adbVG`_FFp%$V&LRPdNU0#Oc8?T&tvrq!d6qvLk>9{WoGohA z$5$%GzrBXg8M84^8!WiT$V>MxaPU@MKkw6yg!jNPxK~~hHRS?r7CeTNIsY!bawynw zx-4G?gzKV%n>jx7>Kpw%m;jkMkBToiv_@9(c)KApH;-ejFdaXEUozoDWK`1eGcEg= zy$V8Wx~rI6%=NXPqrqw6>pN)dAMy+}vtUaqsoD1E1ZV$)h>SmDVq?5^!fU|Xv+GOn z0q*VB4z8Ls9>Ts4I{}~GbSI7{umaDY9zz_Qu8KN@FZ-dwE;UTH$%hqm(Nmck-H7G7 z{?x@;7sn6%y<52CMv-JcV4VZT9XM zatltr;D2TJM!W*=@eof!A1LhiE4eF=9D%+Z?SHi3WS@I>>unoEF8gVGMRFwfjqtNu zucd+Q!RJ2We&)9zL-*{;$!XeHI06UDow+*wJC~|?*n$mzJfSK*F5V4+gK@K>og)?4 zMOoAWRZU31Hl|tpwm%vUzMPj) zpMVx9gNjRdi{drV=Z0f=E}Rc?n4F>S;;=NnC2%K3L+{I;*czZ^7hl-mIdN7RuUnQf zBu&VtWl#!*F_~`mX%)A|y7*I1Vz_7xbwVr+PZPSxsjYJ8j37qq(&p@sRk*~+S_rB}PD@Ncs1v`voN+U1jp`NEB3(8oE5 zIC%F-a}#)LdPhrd@$l49_d+sl?JCy8S2hVLjwJ_{{f>T)?#qf1#_^7g`NQZ0>BEwwc97UQ(u zQt?Xasw|N95%EGj9raVW4(6&edan&6x? z4~Qa#&5z87D7G1M7G<1Qq=Je!$U7mCWh$bOw^nm)rA_@Hz-thZ=VHEfhLduZzd&OZ zrbWag=NA_1XA*EnzBMv7wyMMqDdQ9aGc}NZG@3D~;R_cguGGg)#(CFOp|X)*beL<> zVX)aQBPkP2-bQ|oo1f%nPbU7hhBc_^4Qdsl=jeH=&xS*IIC(=MDBPUCJ4g2vnzdli z3pQ3*IyZrzxi5s_#D;G{d9!fI%}x3a)%;*TTJiCo%95HgnmL?~l9O+l5j-u0*|u$2)WX%um3h)+y#J!LuNCtJM} zwi(bgec|!^v}{wQ&FY4aZ~?h-USLTv>3GVU^eO`MOebQZ)#`kI<1&YwL?)F@vhQBB zsGG|)WnRJAUT*d)g5l|yYo3j67l;>NJEy%p93*(qnPRWGNh%XU&Xj575}{Pqlr#Tb zqGX`kwYNsBotva}2M@K|`$#QqX=)$UUK)wbUk z3sx4#FZEy_S4H^L=;7a?TP8Mg^imI0b?fu(NdVSxBvGnt^c0&_kz3YI!cmSP7TVVrOQ{U&*jCX ztFKsM^y{U?OnJ0Ixmvn>afy)38;CgUMnW>zjX3N^LK^ODw=(7liGW-V-WCaIszoO3 z5(sISkqNs5LKO<$ zjZTs=Cac7SP3fvE z9Yu~gT}TpXO^}PpL!?}X^2+yU#;^lYrYt0FViJLg70WJ7dZ{93se*2NkLnfhl_$we zKF=c$R&gGNFK+>+C5A9=PEc&Lr{h^FmN^4fK1=l2Yc)rmCdyBRz17n~i`UTTDAChr z(QcLKazqpeon=rC5@Jx5sAW;GnOPv8`KG*P^3>oCd^L3D5bOU*y_(t3Uq_^$7=4hYnQ*^>P)qmt!F0Qs8rpyopWu7$tH7$lNgm`)NIuH#0P;{W+9$rISQ2 z|F1)yMC^a)+7XsH{w8-osOeugao^9;7@?27k<&|(lxRX|4xe-Q7)l0%nhv+=r4ve_ zKK_+^2E&7x#Fz$#F|si1kHH zEH1=r*{+D~MLb88iQaH92s$%&?I8L^qJest_1TEJQx$Dx_Uw#IUxo`B;;s;+A296^ zW6?uqrjpA&2@^FHCrTv1*nuLZ$8LyY44Q&1$gn=5(g!JrC^I@{2Fb}8z6A6ulIGRF zr_4?Fkjxuw2p5MK@IHe`qm8WUOg!|K#=D17* zSvu*cNelr!Omru=IY;!TTz~5D0&~P zpu-8DdA!~Yg+2itkD4BAGgD#?I&_|V7(}|~F<(JW=jT}Ms+JB9PY;iU2ZSZ)SZNVfG-W8bd z(g0i5QqFpN^J8vtcm#{lnRSM-g7P7if)A8%b|(JALNGK*u0}!ygmVN|p zstwli9^DfbuBY4+~jJ#Wfs0>i;63 zy?+P*a$Z2;l{udtqb4=OT0Pu|rngrb6h5}G9@Mw_gEc6F0QviUUVal~aH_yS?{@OP zNnWi%9+y1fhq(Z!nUB_Rc5%O%WB3}-iXu=Rtz(tcF=hN941+8EkX%)zYLjG8_yAqG z;WHSKYVTNuS2R75)I-B5flGU@f$D3=^8O^2L*5Idq=BOJ<($OjEFGBmF;xqD{d(S~ zV9XsqeE9b3*OS{*n5aq$ag?d+2L|d|(!@P-#>af%179#0>>YxdAp0zB0Ax+N6qC0; z;SX)7QPBSxaL$Jvh1sB}gM-+se|Sr=-_%SXrw7fYRLt) zTa}ESR2j}Yqb{xlwoJQxGIIym4oNSRM%=2o8fn#QOU1e+IIUD%%zA^kZD~3fv)jUV z+qd`sTYptWXJhqK?AEQUr?t#YwEW%I?@i#BxrvbT2=eeG78>uk&s+vV3xyfB&6X+ChQUj zX_%1-y97cSW@N%HL7!yiK*V7;64G!Z6LtxNG|b3^7Zk)Nd1-W%OAeJ(c?!PAsU1^b zWvlS>agplB1t=~k%FeB*5O8d2EF1JYtOL~!DHG976LY@AjQ*+PP=;1^?7^)F`xwiD z^Uh8+g94SED$fukci^U!NK?N$imW2%_#RWAWxJE=&R#8d#t15s9OJ$lvp26?cUZ