diff options
-rw-r--r-- | Makefile | 644 | ||||
-rw-r--r-- | dahdi_cfg.c | 1489 | ||||
-rw-r--r-- | dahdi_cfg.h | 27 | ||||
-rw-r--r-- | dahdi_diag.c | 31 | ||||
-rw-r--r-- | dahdi_monitor.c | 577 | ||||
-rw-r--r-- | dahdi_scan.c | 172 | ||||
-rw-r--r-- | dahdi_speed.c | 36 | ||||
-rw-r--r-- | dahdi_test.c | 135 | ||||
-rw-r--r-- | dahdi_tool.c | 589 | ||||
-rw-r--r-- | fxotune.c | 1110 | ||||
-rw-r--r-- | fxotune.h | 110 | ||||
-rw-r--r-- | fxstest.c | 144 | ||||
-rw-r--r-- | hdlcgen.c | 105 | ||||
-rw-r--r-- | hdlcstress.c | 186 | ||||
-rw-r--r-- | hdlctest.c | 261 | ||||
-rw-r--r-- | hdlcverify.c | 106 | ||||
-rw-r--r-- | makeopts.in | 47 | ||||
-rw-r--r-- | patgen.c | 98 | ||||
-rw-r--r-- | patlooptest.c | 124 | ||||
-rw-r--r-- | pattest.c | 104 | ||||
-rw-r--r-- | ppp/Makefile | 29 | ||||
-rw-r--r-- | ppp/zaptel.c | 284 | ||||
-rw-r--r-- | sethdlc.c | 693 | ||||
-rw-r--r-- | timertest.c | 54 | ||||
-rw-r--r-- | tonezone.c | 512 | ||||
-rw-r--r-- | tonezone.h | 86 | ||||
-rw-r--r-- | zonedata.c | 931 |
27 files changed, 8684 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..85e1c48 --- /dev/null +++ b/Makefile @@ -0,0 +1,644 @@ +# +# Makefile for Zaptel driver modules and utilities +# +# Copyright (C) 2001-2007 Digium, Inc. +# +# + +ifneq ($(KBUILD_EXTMOD),) +# We only get in here if we're from kernel 2.6 <= 2.6.9 and going through +# Kbuild. Later versions will include Kbuild instead of Makefile. +include $(src)/Kbuild + +else + +CFLAGS+=-DSTANDALONE_ZAPATA -DBUILDING_TONEZONE + +ifeq ($(MAKELEVEL),0) +PWD:=$(shell pwd) +endif + +ifeq ($(ARCH),) +ARCH:=$(shell uname -m | sed -e s/i.86/i386/) +endif + +ifeq ($(DEB_HOST_GNU_TYPE),) +UNAME_M:=$(shell uname -m) +else +UNAME_M:=$(DEB_HOST_GNU_TYPE) +endif + +# If you want to build for a kernel other than the current kernel, set KVERS +ifndef KVERS +KVERS:=$(shell uname -r) +endif +ifndef KSRC + ifneq (,$(wildcard /lib/modules/$(KVERS)/build)) + KSRC:=/lib/modules/$(KVERS)/build + else + KSRC_SEARCH_PATH:=/usr/src/linux-2.4 /usr/src/linux + KSRC:=$(shell for dir in $(KSRC_SEARCH_PATH); do if [ -d $$dir ]; then echo $$dir; break; fi; done) + endif +endif +KVERS_MAJ:=$(shell echo $(KVERS) | cut -d. -f1-2) +KINCLUDES:=$(KSRC)/include + +# We use the kernel's .config file as an indication that the KSRC +# directory is indeed a valid and configured kernel source (or partial +# source) directory. +# +# We also source it, as it has the format of Makefile variables list. +# Thus we will have many CONFIG_* variables from there. +KCONFIG:=$(KSRC)/.config +ifneq (,$(wildcard $(KCONFIG))) + HAS_KSRC=yes + include $(KCONFIG) +else + HAS_KSRC=no +endif + +ifeq ($(KVERS_MAJ),2.4) + BUILDVER:=linux24 +else + BUILDVER:=linux26 +endif + +# Set HOTPLUG_FIRMWARE=no to override automatic building with hotplug support +# if it is enabled in the kernel. +ifeq ($(BUILDVER),linux26) + ifneq (,$(wildcard $(DESTDIR)/etc/udev/rules.d)) + DYNFS=yes + UDEVRULES=yes + endif + ifeq (yes,$(HAS_KSRC)) + HOTPLUG_FIRMWARE:=$(shell if grep -q '^CONFIG_FW_LOADER=[ym]' $(KCONFIG); then echo "yes"; else echo "no"; fi) + endif +endif + +ifneq (,$(findstring $(CONFIG_DEVFS_FS),y m)) + DYNFS=yes + HAS_DEVFS=yes +endif + +# If the file .zaptel.makeopts is present in your home directory, you can +# include all of your favorite menuselect options so that every time you download +# a new version of Asterisk, you don't have to run menuselect to set them. +# The file /etc/zaptel.makeopts will also be included but can be overridden +# by the file in your home directory. + +GLOBAL_MAKEOPTS=$(wildcard /etc/zaptel.makeopts) +USER_MAKEOPTS=$(wildcard ~/.zaptel.makeopts) + +ifeq ($(strip $(foreach var,clean distclean dist-clean update,$(findstring $(var),$(MAKECMDGOALS)))),) + ifneq ($(wildcard menuselect.makeopts),) + include menuselect.makeopts + endif +endif + +ifeq ($(strip $(foreach var,clean distclean dist-clean update,$(findstring $(var),$(MAKECMDGOALS)))),) + ifneq ($(wildcard makeopts),) + include makeopts + endif +endif + +ifeq ($(BUILDVER),linux24) +MENUSELECT_MODULES+=xpp wctc4xxp wctdm24xxp zttranscode +endif + +ifeq ($(findstring xpp,$(MENUSELECT_MODULES)),) + BUILD_XPP:=yes +endif + +SUBDIRS_UTILS_ALL:= kernel/xpp/utils ppp +SUBDIRS_UTILS := +ifeq ($(BUILD_XPP),yes) + SUBDIRS_UTILS += kernel/xpp/utils +endif +#SUBDIRS_UTILS += ppp + +TOPDIR_MODULES:=pciradio tor2 torisa wcfxo wct1xxp wctdm wcte11xp wcusb zaptel ztd-eth ztd-loc ztdummy ztdynamic zttranscode +SUBDIR_MODULES:=wct4xxp wctc4xxp xpp wctdm24xxp wcte12xp +TOPDIR_MODULES+=$(MODULES_EXTRA) +SUBDIR_MODULES+=$(SUBDIRS_EXTRA) +BUILD_TOPDIR_MODULES:=$(filter-out $(MENUSELECT_MODULES),$(TOPDIR_MODULES)) +BUILD_SUBDIR_MODULES:=$(filter-out $(MENUSELECT_MODULES),$(SUBDIR_MODULES)) +BUILD_MODULES:=$(BUILD_TOPDIR_MODULES) $(BUILD_SUBDIR_MODULES) + +MOD_DESTDIR:=zaptel + +KERN_DIR:=kernel + +#NOTE NOTE NOTE +# +# all variables set before the include of Makefile.kernel26 are needed by the 2.6 kernel module build process + +ifneq ($(KBUILD_EXTMOD),) + +obj-m:=$(BUILD_TOPDIR_MODULES:%=%.o) +obj-m+=$(BUILD_SUBDIR_MODULES:%=%/) + +include $(src)/Makefile.kernel26 + +else +KBUILD_OBJ_M=$(BUILD_TOPDIR_MODULES:%=%.o) $(BUILD_SUBDIR_MODULES:%=%/) + +ifeq ($(BUILDVER),linux24) + INSTALL_MODULES:=$(BUILD_TOPDIR_MODULES:%=$(KERN_DIR)/%.o) + INSTALL_MODULES+=$(foreach mod,$(BUILD_SUBDIR_MODULES),$(KERN_DIR)/$(mod)/$(mod).o) + ALL_MODULES:=$(TOPDIR_MODULES:%=$(KERN_DIR)/%.o) + ALL_MODULES+=$(SUBDIR_MODULES:%=$(KERN_DIR)/%/%.o) +else + ALL_MODULES:=$(TOPDIR_MODULES:%=%.ko) + ALL_MODULES+=$(foreach mod,$(filter-out xpp,$(SUBDIR_MODULES)),$(mod)/$(mod).ko) + ALL_MODULES+=$(patsubst %,xpp/%.ko,xpp_usb xpd_fxo xpd_fxs xpp) +endif + +OPTFLAG=-O2 +CFLAGS+=-I. $(OPTFLAGS) -g -fPIC -Wall -DBUILDING_TONEZONE #-DTONEZONE_DRIVER +ifneq (,$(findstring ppc,$(UNAME_M))) +CFLAGS_PPC:=-fsigned-char +endif +ifneq (,$(findstring x86_64,$(UNAME_M))) +CFLAGS_x86_64:=-m64 +endif +CFLAGS+=$(CFLAGS_PPC) $(CFLAGS_x86_64) +KFLAGS=-I$(KINCLUDES) -O6 +KFLAGS+=-DMODULE -D__KERNEL__ -DEXPORT_SYMTAB -I$(KSRC)/drivers/net \ + -Wall -I. -Wstrict-prototypes -fomit-frame-pointer -I$(KSRC)/drivers/net/wan -I$(KINCLUDES)/net +ifneq (,$(wildcard $(KINCLUDES)/linux/modversions.h)) + KFLAGS+=-DMODVERSIONS -include $(KINCLUDES)/linux/modversions.h +endif +ifneq (,$(findstring ppc,$(UNAME_M))) +KFLAGS_PPC:=-msoft-float -fsigned-char +endif +KFLAGS+=$(KFLAGS_PPC) +ifeq ($(KVERS_MAJ),2.4) + ifneq (,$(findstring x86_64,$(UNAME_M))) + KFLAGS+=-mcmodel=kernel + endif +endif + +# +# Features are now configured in zconfig.h +# + +MODULE_ALIASES=wcfxs wctdm8xxp wct2xxp + +KFLAGS+=-DSTANDALONE_ZAPATA +CFLAGS+=-DSTANDALONE_ZAPATA +ifeq ($(BUILDVER),linux24) +KMAKE = $(MAKE) -C kernel HOTPLUG_FIRMWARE=no \ + HOSTCC=$(HOSTCC) ARCH=$(ARCH) KSRC=$(KSRC) LD=$(LD) CC=$(CC) \ + UNAME_M=$(UNAME_M) \ + BUILD_TOPDIR_MODULES="$(BUILD_TOPDIR_MODULES)" BUILD_SUBDIR_MODULES="$(BUILD_SUBDIR_MODULES)" +else +KMAKE = $(MAKE) -C $(KSRC) ARCH=$(ARCH) SUBDIRS=$(PWD)/kernel \ + HOTPLUG_FIRMWARE=$(HOTPLUG_FIRMWARE) KBUILD_OBJ_M="$(KBUILD_OBJ_M)" +endif +KMAKE_INST = $(KMAKE) \ + INSTALL_MOD_PATH=$(DESTDIR) INSTALL_MOD_DIR=misc modules_install + +ROOT_PREFIX= + +CONFIG_FILE=/etc/zaptel.conf +CFLAGS+=-DZAPTEL_CONFIG=\"$(CONFIG_FILE)\" + +# sample makefile "trace print" +#tracedummy=$(shell echo ====== GOT HERE ===== >&2; echo >&2) + +CHKCONFIG := $(wildcard /sbin/chkconfig) +UPDATE_RCD := $(wildcard /usr/sbin/update-rc.d) +ifeq (,$(DESTDIR)) + ifneq (,$(CHKCONFIG)) + ADD_INITD := $(CHKCONFIG) --add zaptel + else + ifndef (,$(UPDATE_RCD)) + ADD_INITD := $(UPDATE_RCD) zaptel defaults 15 30 + endif + endif +endif + +INITRD_DIR := $(firstword $(wildcard /etc/rc.d/init.d /etc/init.d)) +ifneq (,$(INITRD_DIR)) + INIT_TARGET := $(DESTDIR)$(INITRD_DIR)/zaptel + COPY_INITD := install -D zaptel.init $(INIT_TARGET) +endif +RCCONF_DIR := $(firstword $(wildcard /etc/sysconfig /etc/default)) + +NETSCR_DIR := $(firstword $(wildcard /etc/sysconfig/network-scripts )) +ifneq (,$(NETSCR_DIR)) + NETSCR_TARGET := $(DESTDIR)$(NETSCR_DIR)/ifup-hdlc + COPY_NETSCR := install -D ifup-hdlc $(NETSCR_TARGET) +endif + +ifneq ($(wildcard .version),) + ZAPTELVERSION:=$(shell cat .version) +else +ifneq ($(wildcard .svn),) + ZAPTELVERSION=SVN-$(shell build_tools/make_svn_branch_name) +endif +endif + +LTZ_A:=libtonezone.a +LTZ_A_OBJS:=zonedata.o tonezone.o +LTZ_SO:=libtonezone.so +LTZ_SO_OBJS:=zonedata.lo tonezone.lo +LTZ_SO_MAJOR_VER:=1 +LTZ_SO_MINOR_VER:=0 + +# libdir, includedir and mandir are defined in makeopts (from +# configure). +# we use /sbin, rather than configure's $(sbindir) because we use /sbin +# for historical reasons. +BIN_DIR:=/sbin +LIB_DIR:=$(libdir) +INC_DIR:=$(includedir)/zaptel +MAN_DIR:=$(mandir)/man8 +MOD_DIR:=$(DESTDIR)/lib/modules/$(KVERS)/misc + +# Utilities we build with a standard build procedure: +UTILS = zttool zttest ztmonitor ztspeed sethdlc-new ztcfg \ + ztcfg-dude usbfxstest fxstest fxotune ztdiag torisatool \ + ztscan + + +# Makefile mentions them. Source is not included (anynore?) +UTILS += fxsdump ztprovision + +# some tests: +UTILS += patgen pattest patlooptest hdlcstress hdlctest hdlcgen \ + hdlcverify timertest + +UTILSO = $(UTILS:%=%.o) + +BINS:=fxotune fxstest sethdlc-new ztcfg ztdiag ztmonitor ztspeed zttest ztscan +ifeq (1,$(PBX_LIBNEWT)) + BINS+=zttool +endif +BINS:=$(filter-out $(MENUSELECT_UTILS),$(BINS)) +MAN_PAGES:=$(wildcard $(BINS:%=doc/%.8)) + +# All the man pages. Not just installed ones: +GROFF_PAGES := $(wildcard doc/*.8 kernel/xpp/utils/*.8) +GROFF_HTML := $(GROFF_PAGES:%=%.html) + +all: menuselect.makeopts + @$(MAKE) _all + +_all: $(if $(BUILD_MODULES),modules) programs + +libs: $(LTZ_SO) $(LTZ_A) + +utils-subdirs: + @for dir in $(SUBDIRS_UTILS); do \ + $(MAKE) -C $$dir; \ + done + +programs: libs utils + +utils: $(BINS) utils-subdirs + +modules: prereq +ifeq (no,$(HAS_KSRC)) + echo "You do not appear to have the sources for the $(KVERS) kernel installed." + exit 1 +endif + $(KMAKE) modules + +version.h: + @ZAPTELVERSION="${ZAPTELVERSION}" build_tools/make_version_h > $@.tmp + @if cmp -s $@.tmp $@ ; then :; else \ + mv $@.tmp $@ ; \ + fi + @rm -f $@.tmp + +tests: patgen pattest patlooptest hdlcstress hdlctest hdlcgen hdlcverify timertest + +zonedata.o: tonezone.h + +zonedata.lo: zonedata.c tonezone.h + $(CC) -c $(CFLAGS) -o $@ $< + +tonezone.o: kernel/zaptel.h tonezone.h + +tonezone.lo: tonezone.c tonezone.h kernel/zaptel.h + $(CC) -c $(CFLAGS) -o $@ $< + +prereq: config.status version.h + +zttool.o: kernel/zaptel.h +zttool.o: CFLAGS+=$(NEWT_INCLUDE) +zttool: LDLIBS+=$(NEWT_LIB) + +ztscan.o: kernel/zaptel.h + +ztprovision.o: kernel/zaptel.h + +ztmonitor.o: kernel/zaptel.h + +ztspeed: CFLAGS= + +sethdlc-new: CFLAGS+=-I$(KINCLUDES) + +$(LTZ_A): $(LTZ_A_OBJS) + ar rcs $@ $^ + ranlib $@ + +$(LTZ_SO): $(LTZ_SO_OBJS) + $(CC) $(CFLAGS) -shared -Wl,-soname,$(LTZ_SO).$(LTZ_SO_MAJOR_VER).$(LTZ_SO_MINOR_VER) -o $@ $^ $(LDFLAGS) $(LDLIBS) -lm + +ztcfg.o: ztcfg.h kernel/zaptel.h +ztcfg: ztcfg.o $(LTZ_A) +ztcfg: LDLIBS+=-lm + +ztcfg-shared: ztcfg.o $(LTZ_SO) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LDLIBS) -lm + +ztcfg-dude: ztcfg-dude.o mknotch.o complex.o $(LTZ_SO) +ztcfg-dude: LDLIBS+=-lm -lstdc++ + +data: + $(MAKE) -C datamods datamods + +# FIXME: we assume CC can build the C++ modules: +complex.o mknotch.o: %.o: %.cc + $(CC) $(CFLAGS) -o $@ -c $< + +usbfxstest: LDLIBS+=-lzap +fxstest: $(LTZ_SO) +fxstest: LDLIBS+=-lm +fxotune: LDLIBS+=-lm +fxsdump: LDLIBS+=-lm + +stackcheck: checkstack modules + ./checkstack kernel/*.ko kernel/*/*.ko + + +tonezones.txt: zonedata.c + perl -ne 'next unless (/\.(country|description) = *"([^"]*)/); \ + print (($$1 eq "country")? "* $$2\t":"$$2\n");' $< \ + >$@ + +zaptel.conf.asciidoc: zaptel.conf.sample + perl -n -e \ + 'if (/^#($$|\s)(.*)/){ if (!$$in_doc){print "\n"}; $$in_doc=1; print "$$2\n" } else { if ($$in_doc){print "\n"}; $$in_doc=0; print " $$_" }' \ + $< >$@ + +README.html: README zaptel.conf.asciidoc tonezones.txt + $(ASCIIDOC) -n -a toc -a toclevels=3 $< + +kernel/xpp/README.Astribank.html: kernel/xpp/README.Astribank + cd $(@D); $(ASCIIDOC) -o $(@F) -n -a toc -a toclevels=4 $(<F) + +# on Debian: this requires the full groof, not just groff-base. +%.8.html: %.8 + man -Thtml $^ >$@ + +htmlman: $(GROFF_HTML) + + +MISDNVERSION=1_1_7_2 +MISDNUSERVERSION=1_1_7_2 +b410p: + @if test "$(DOWNLOAD)" = ":" ; then \ + echo "**************************************************"; \ + echo "*** ***"; \ + echo "*** You must have either wget or fetch to be ***"; \ + echo "*** able to automatically download and install ***"; \ + echo "*** b410p support. ***"; \ + echo "*** ***"; \ + echo "*** Please install one of these. ***"; \ + echo "*** ***"; \ + echo "**************************************************"; \ + exit 1; \ + fi + [ -f mISDN-$(MISDNVERSION).tar.gz ] || $(DOWNLOAD) http://downloads.digium.com/pub/zaptel/b410p/mISDN-$(MISDNVERSION).tar.gz + tar -zxf mISDN-$(MISDNVERSION).tar.gz + $(MAKE) -C mISDN-$(MISDNVERSION) install + [ -f mISDNuser-$(MISDNUSERVERSION).tar.gz ] || $(DOWNLOAD) http://downloads.digium.com/pub/zaptel/b410p/mISDNuser-$(MISDNUSERVERSION).tar.gz + tar -zxf mISDNuser-$(MISDNUSERVERSION).tar.gz + $(MAKE) -C mISDNuser-$(MISDNUSERVERSION) install + +$(UTILS): %: %.o + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LDLIBS) + +$(UTILSO): %.o: %.c + $(CC) $(CFLAGS) -o $@ -c $< + +install: all devices install-modules install-programs install-firmware + @echo "###################################################" + @echo "###" + @echo "### Zaptel installed successfully." + @echo "### If you have not done so before, install init scripts with:" + @echo "###" + @echo "### make config" + @echo "###" + @echo "###################################################" + +install-programs: install-utils install-libs install-include + +install-utils: utils install-utils-subdirs +ifneq (,$(BINS)) + install -d $(DESTDIR)$(BIN_DIR) + install $(BINS) $(DESTDIR)$(BIN_DIR)/ + install -d $(DESTDIR)$(MAN_DIR) + install -m 644 $(MAN_PAGES) $(DESTDIR)$(MAN_DIR)/ +endif +ifeq (,$(wildcard $(DESTDIR)$(CONFIG_FILE))) + $(INSTALL) -D -m 644 zaptel.conf.sample $(DESTDIR)$(CONFIG_FILE) +endif + +# Pushing those two to a separate target that is not used by default: +install-modconf: + build_tools/genmodconf $(BUILDVER) "$(ROOT_PREFIX)" "$(filter-out zaptel ztdummy xpp zttranscode ztdynamic,$(BUILD_MODULES)) $(MODULE_ALIASES)" + @if [ -d /etc/modutils ]; then \ + /sbin/update-modules ; \ + fi + +install-firmware: +ifeq ($(HOTPLUG_FIRMWARE),yes) + $(MAKE) -C firmware hotplug-install DESTDIR=$(DESTDIR) HOTPLUG_FIRMWARE=$(HOTPLUG_FIRMWARE) +endif + +install-libs: libs + $(INSTALL) -D -m 755 $(LTZ_A) $(DESTDIR)$(LIB_DIR)/$(LTZ_A) + $(INSTALL) -D -m 755 $(LTZ_SO) $(DESTDIR)$(LIB_DIR)/$(LTZ_SO).$(LTZ_SO_MAJOR_VER).$(LTZ_SO_MINOR_VER) +ifeq (,$(DESTDIR)) + if [ `id -u` = 0 ]; then \ + /sbin/ldconfig || : ;\ + fi +endif + rm -f $(DESTDIR)$(LIB_DIR)$(LTZ_SO) + $(LN) -sf $(LTZ_SO).$(LTZ_SO_MAJOR_VER).$(LTZ_SO_MINOR_VER) \ + $(DESTDIR)$(LIB_DIR)/$(LTZ_SO).$(LTZ_SO_MAJOR_VER) + $(LN) -sf $(LTZ_SO).$(LTZ_SO_MAJOR_VER).$(LTZ_SO_MINOR_VER) \ + $(DESTDIR)$(LIB_DIR)/$(LTZ_SO) +ifneq (no,$(USE_SELINUX)) + ifeq (,$(DESTDIR)) + /sbin/restorecon -v $(DESTDIR)$(LIB_DIR)/$(LTZ_SO) + endif +endif + $(INSTALL) -D -m 644 tonezone.h $(DESTDIR)$(INC_DIR)/tonezone.h + +install-utils-subdirs: + @for dir in $(SUBDIRS_UTILS); do \ + $(MAKE) -C $$dir install; \ + done + +install-include: + $(INSTALL) -D -m 644 kernel/zaptel.h $(DESTDIR)$(INC_DIR)/zaptel.h + +devices: +ifneq (yes,$(DYNFS)) + mkdir -p $(DESTDIR)/dev/zap + rm -f $(DESTDIR)/dev/zap/ctl + rm -f $(DESTDIR)/dev/zap/channel + rm -f $(DESTDIR)/dev/zap/pseudo + rm -f $(DESTDIR)/dev/zap/timer + rm -f $(DESTDIR)/dev/zap/transcode + rm -f $(DESTDIR)/dev/zap/253 + rm -f $(DESTDIR)/dev/zap/252 + rm -f $(DESTDIR)/dev/zap/251 + rm -f $(DESTDIR)/dev/zap/250 + mknod $(DESTDIR)/dev/zap/ctl c 196 0 + mknod $(DESTDIR)/dev/zap/transcode c 196 250 + mknod $(DESTDIR)/dev/zap/timer c 196 253 + mknod $(DESTDIR)/dev/zap/channel c 196 254 + mknod $(DESTDIR)/dev/zap/pseudo c 196 255 + N=1; \ + while [ $$N -lt 250 ]; do \ + rm -f $(DESTDIR)/dev/zap/$$N; \ + mknod $(DESTDIR)/dev/zap/$$N c 196 $$N; \ + N=$$[$$N+1]; \ + done +else # DYNFS + ifneq (yes,$(UDEVRULES)) #!UDEVRULES + @echo "**** Dynamic filesystem detected -- not creating device nodes" + else # UDEVRULES + install -d $(DESTDIR)/etc/udev/rules.d + build_tools/genudevrules > $(DESTDIR)/etc/udev/rules.d/zaptel.rules + endif +endif + +install-udev: devices + +uninstall-hotplug: + $(MAKE) -C firmware hotplug-uninstall DESTDIR=$(DESTDIR) + +uninstall-modules: +ifneq ($(BUILDVER),linux24) + @./build_tools/uninstall-modules $(DESTDIR)/lib/modules/$(KVERS) $(ALL_MODULES) +endif + +ifeq ($(BUILDVER),linux24) +install-modules: $(INSTALL_MODULES) uninstall-modules + $(INSTALL) -d $(DESTDIR)$(MOD_DIR) + $(INSTALL) -m 644 $(INSTALL_MODULES) $(DESTDIR)$(MOD_DIR) +else +install-modules: uninstall-modules + $(KMAKE_INST) +endif + [ `id -u` = 0 ] && /sbin/depmod -a $(KVERS) || : + +config: +ifneq (,$(COPY_INITD)) + $(COPY_INITD) +endif +ifneq (,$(RCCONF_DIR)) + ifeq (,$(wildcard $(DESTDIR)$(RCCONF_DIR)/zaptel)) + $(INSTALL) -D -m 644 zaptel.sysconfig $(DESTDIR)$(RCCONF_DIR)/zaptel + endif +endif +ifneq (,$(COPY_NETSCR)) + $(COPY_NETSCR) +endif +ifneq (,$(ADD_INITD)) + $(ADD_INITD) +endif + @echo "Zaptel has been configured." + @echo "" + @echo "If you have any zaptel hardware it is now recommended to " + @echo "edit /etc/default/zaptel or /etc/sysconfig/zaptel and set there an " + @echo "optimal value for the variable MODULES ." + @echo "" + @echo "I think that the zaptel hardware you have on your system is:" + @kernel/xpp/utils/zaptel_hardware || true + + +update: + @if [ -d .svn ]; then \ + echo "Updating from Subversion..." ; \ + svn update | tee update.out; \ + rm -f .version; \ + if [ `grep -c ^C update.out` -gt 0 ]; then \ + echo ; echo "The following files have conflicts:" ; \ + grep ^C update.out | cut -b4- ; \ + fi ; \ + rm -f update.out; \ + else \ + echo "Not under version control"; \ + fi + +clean: + -@$(MAKE) -C menuselect clean + rm -f torisatool + rm -f $(BINS) + rm -f *.o ztcfg tzdriver sethdlc sethdlc-new + rm -f $(LTZ_SO) $(LTZ_A) *.lo +ifeq (yes,$(HAS_KSRC)) + $(KMAKE) clean +else + rm -f kernel/*.o kernel/*.ko kernel/*/*.o kernel/*/*.ko +endif + @for dir in $(SUBDIRS_UTILS_ALL); do \ + $(MAKE) -C $$dir clean; \ + done + $(MAKE) -C firmware clean + rm -rf .tmp_versions + rm -f gendigits tones.h + rm -f libtonezone* + rm -f fxotune + rm -f core + rm -f ztcfg-shared fxstest + rm -rf misdn* + rm -rf mISDNuser* + rm -rf $(GROFF_HTML) + rm -rf README.html xpp/README.Astribank.html zaptel.conf.asciidoc + +distclean: dist-clean + +dist-clean: clean + @$(MAKE) -C menuselect dist-clean + @$(MAKE) -C firmware dist-clean + rm -f makeopts menuselect.makeopts menuselect-tree + rm -f config.log config.status + +config.status: configure + @CFLAGS="" ./configure + @echo "****" + @echo "**** The configure script was just executed, so 'make' needs to be" + @echo "**** restarted." + @echo "****" + @exit 1 + +menuselect.makeopts: menuselect/menuselect menuselect-tree + @menuselect/menuselect --check-deps ${GLOBAL_MAKEOPTS} ${USER_MAKEOPTS} $@ + +menuconfig: menuselect + +menuselect: menuselect/menuselect menuselect-tree + -@menuselect/menuselect $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS) menuselect.makeopts && echo "menuselect changes saved!" || echo "menuselect changes NOT saved!" + +menuselect/menuselect: menuselect/menuselect.c menuselect/menuselect_curses.c menuselect/menuselect_stub.c menuselect/menuselect.h menuselect/linkedlists.h config.status + @CFLAGS="" $(MAKE) -C menuselect CC=$(HOSTCC) + +menuselect-tree: zaptel.xml firmware/firmware.xml + @echo "Generating input for menuselect ..." + @build_tools/make_tree > $@ + +.PHONY: menuselect distclean dist-clean clean version.h all _all install b410p devices programs modules tests devel data stackcheck install-udev config update install-programs install-modules install-include install-libs install-utils-subdirs utils-subdirs uninstall-modules + +endif + +#end of: ifneq ($(KBUILD_EXTMOD),) +endif diff --git a/dahdi_cfg.c b/dahdi_cfg.c new file mode 100644 index 0000000..a880f3e --- /dev/null +++ b/dahdi_cfg.c @@ -0,0 +1,1489 @@ +/* + * Configuration program for Zapata Telephony Interface + * + * Written by Mark Spencer <markster@digium.com> + * Based on previous works, designs, and architectures conceived and + * written by Jim Dixon <jim@lambdatel.com>. + * + * Copyright (C) 2001 Jim Dixon / Zapata Telephony. + * Copyright (C) 2001 Linux Support Services, Inc. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under thet erms 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Primary Author: Mark Spencer <markster@digium.com> + * Radio Support by Jim Dixon <jim@lambdatel.com> + */ + +#include <stdio.h> +#include <getopt.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <errno.h> +#ifdef STANDALONE_ZAPATA +#include "kernel/zaptel.h" +#include "tonezone.h" +#else +#include <zaptel/zaptel.h> +#include <zaptel/tonezone.h> +#endif +#include "ztcfg.h" + +#define NUM_SPANS ZT_MAX_SPANS + +#define NUM_TONES 15 + +/* Assume no more than 1024 dynamics */ +#define NUM_DYNAMIC 1024 + +static int lineno=0; + +static FILE *cf; + +static char *filename=CONFIG_FILENAME; + +int rxtones[NUM_TONES + 1],rxtags[NUM_TONES + 1],txtones[NUM_TONES + 1]; +int bursttime = 0, debouncetime = 0, invertcor = 0, exttone = 0, corthresh = 0; +int txgain = 0, rxgain = 0, deemp = 0, preemp = 0; + +int corthreshes[] = {3125,6250,9375,12500,15625,18750,21875,25000,0} ; + +static int toneindex = 1; + +#define DEBUG_READER (1 << 0) +#define DEBUG_PARSER (1 << 1) +#define DEBUG_APPLY (1 << 2) +static int debug = 0; + +static int errcnt = 0; + +static int deftonezone = -1; + +static struct zt_lineconfig lc[ZT_MAX_SPANS]; + +static struct zt_chanconfig cc[ZT_MAX_CHANNELS]; + +static struct zt_dynamic_span zds[NUM_DYNAMIC]; + +static const char *sig[ZT_MAX_CHANNELS]; /* Signalling */ + +static int slineno[ZT_MAX_CHANNELS]; /* Line number where signalling specified */ + +static int spans=0; + +static int fo_real = 1; + +static int verbose = 0; + +static int force = 0; + +static int stopmode = 0; + +static int numdynamic = 0; + +static char zonestoload[ZT_TONE_ZONE_MAX][10]; + +static int numzones = 0; + +static int fd = -1; + +static const char *lbostr[] = { +"0 db (CSU)/0-133 feet (DSX-1)", +"133-266 feet (DSX-1)", +"266-399 feet (DSX-1)", +"399-533 feet (DSX-1)", +"533-655 feet (DSX-1)", +"-7.5db (CSU)", +"-15db (CSU)", +"-22.5db (CSU)" +}; + +static const char *laws[] = { + "Default", + "Mu-law", + "A-law" +}; + +static const char *sigtype_to_str(const int sig) +{ + switch (sig) { + case 0: + return "Unused"; + case ZT_SIG_EM: + return "E & M"; + case ZT_SIG_EM_E1: + return "E & M E1"; + case ZT_SIG_FXSLS: + return "FXS Loopstart"; + case ZT_SIG_FXSGS: + return "FXS Groundstart"; + case ZT_SIG_FXSKS: + return "FXS Kewlstart"; + case ZT_SIG_FXOLS: + return "FXO Loopstart"; + case ZT_SIG_FXOGS: + return "FXO Groundstart"; + case ZT_SIG_FXOKS: + return "FXO Kewlstart"; + case ZT_SIG_CAS: + return "CAS / User"; + case ZT_SIG_DACS: + return "DACS"; + case ZT_SIG_DACS_RBS: + return "DACS w/RBS"; + case ZT_SIG_CLEAR: + return "Clear channel"; + case ZT_SIG_SLAVE: + return "Slave channel"; + case ZT_SIG_HDLCRAW: + return "Raw HDLC"; + case ZT_SIG_HDLCNET: + return "Network HDLC"; + case ZT_SIG_HDLCFCS: + return "HDLC with FCS check"; + case ZT_SIG_HARDHDLC: + return "Hardware assisted D-channel"; + case ZT_SIG_MTP2: + return "MTP2"; + default: + return "Unknown"; + } +} + +int ind_ioctl(int channo, int fd, int op, void *data) +{ +ZT_INDIRECT_DATA ind; + + ind.chan = channo; + ind.op = op; + ind.data = data; + return ioctl(fd,ZT_INDIRECT,&ind); +} + +static void clear_fields() +{ + + memset(rxtones,0,sizeof(rxtones)); + memset(rxtags,0,sizeof(rxtags)); + memset(txtones,0,sizeof(txtones)); + bursttime = 0; + debouncetime = 0; + invertcor = 0; + exttone = 0; + txgain = 0; + rxgain = 0; + deemp = 0; + preemp = 0; +} + +static int error(char *fmt, ...) +{ + int res; + static int shown=0; + va_list ap; + if (!shown) { + fprintf(stderr, "Notice: Configuration file is %s\n", filename); + shown++; + } + res = fprintf(stderr, "line %d: ", lineno); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + errcnt++; + return res; +} + +static void trim(char *buf) +{ + /* Trim off trailing spaces, tabs, etc */ + while(strlen(buf) && (buf[strlen(buf) -1] < 33)) + buf[strlen(buf) -1] = '\0'; +} + +static int parseargs(char *input, char *output[], int maxargs, char sep) +{ + char *c; + int pos=0; + c = input; + output[pos++] = c; + while(*c) { + while(*c && (*c != sep)) c++; + if (*c) { + *c = '\0'; + c++; + while(*c && (*c < 33)) c++; + if (*c) { + if (pos >= maxargs) + return -1; + output[pos] = c; + trim(output[pos]); + pos++; + output[pos] = NULL; + /* Return error if we have too many */ + } else + return pos; + } + } + return pos; +} + +int dspanconfig(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int chans; + int timing; + argc = res = parseargs(args, realargs, 4, ','); + if (res != 4) { + error("Incorrect number of arguments to 'dynamic' (should be <driver>,<address>,<num channels>, <timing>)\n"); + } + res = sscanf(realargs[2], "%d", &chans); + if ((res == 1) && (chans < 1)) + res = -1; + if (res != 1) { + error("Invalid number of channels '%s', should be a number > 0.\n", realargs[2]); + } + + res = sscanf(realargs[3], "%d", &timing); + if ((res == 1) && (timing < 0)) + res = -1; + if (res != 1) { + error("Invalid timing '%s', should be a number > 0.\n", realargs[3]); + } + + + zap_copy_string(zds[numdynamic].driver, realargs[0], sizeof(zds[numdynamic].driver)); + zap_copy_string(zds[numdynamic].addr, realargs[1], sizeof(zds[numdynamic].addr)); + zds[numdynamic].numchans = chans; + zds[numdynamic].timing = timing; + + numdynamic++; + return 0; +} + +int spanconfig(char *keyword, char *args) +{ + static char *realargs[10]; + int res; + int argc; + int span; + int timing; + argc = res = parseargs(args, realargs, 7, ','); + if ((res < 5) || (res > 7)) { + error("Incorrect number of arguments to 'span' (should be <spanno>,<timing>,<lbo>,<framing>,<coding>[, crc4 | yellow [, yellow]])\n"); + } + res = sscanf(realargs[0], "%i", &span); + if (res != 1) { + error("Span number should be a valid span number, not '%s'\n", realargs[0]); + return -1; + } + res = sscanf(realargs[1], "%i", &timing); + if ((res != 1) || (timing < 0) || (timing > 15)) { + error("Timing should be a number from 0 to 15, not '%s'\n", realargs[1]); + return -1; + } + res = sscanf(realargs[2], "%i", &lc[spans].lbo); + if (res != 1) { + error("Line build-out (LBO) should be a number from 0 to 7 (usually 0) not '%s'\n", realargs[2]); + return -1; + } + if ((lc[spans].lbo < 0) || (lc[spans].lbo > 7)) { + error("Line build-out should be in the range 0 to 7, not %d\n", lc[spans].lbo); + return -1; + } + if (!strcasecmp(realargs[3], "d4")) { + lc[spans].lineconfig |= ZT_CONFIG_D4; + lc[spans].lineconfig &= ~ZT_CONFIG_ESF; + lc[spans].lineconfig &= ~ZT_CONFIG_CCS; + } else if (!strcasecmp(realargs[3], "esf")) { + lc[spans].lineconfig |= ZT_CONFIG_ESF; + lc[spans].lineconfig &= ~ZT_CONFIG_D4; + lc[spans].lineconfig &= ~ZT_CONFIG_CCS; + } else if (!strcasecmp(realargs[3], "ccs")) { + lc[spans].lineconfig |= ZT_CONFIG_CCS; + lc[spans].lineconfig &= ~(ZT_CONFIG_ESF | ZT_CONFIG_D4); + } else if (!strcasecmp(realargs[3], "cas")) { + lc[spans].lineconfig &= ~ZT_CONFIG_CCS; + lc[spans].lineconfig &= ~(ZT_CONFIG_ESF | ZT_CONFIG_D4); + } else { + error("Framing(T1)/Signalling(E1) must be one of 'd4', 'esf', 'cas' or 'ccs', not '%s'\n", realargs[3]); + return -1; + } + if (!strcasecmp(realargs[4], "ami")) { + lc[spans].lineconfig &= ~(ZT_CONFIG_B8ZS | ZT_CONFIG_HDB3); + lc[spans].lineconfig |= ZT_CONFIG_AMI; + } else if (!strcasecmp(realargs[4], "b8zs")) { + lc[spans].lineconfig |= ZT_CONFIG_B8ZS; + lc[spans].lineconfig &= ~(ZT_CONFIG_AMI | ZT_CONFIG_HDB3); + } else if (!strcasecmp(realargs[4], "hdb3")) { + lc[spans].lineconfig |= ZT_CONFIG_HDB3; + lc[spans].lineconfig &= ~(ZT_CONFIG_AMI | ZT_CONFIG_B8ZS); + } else { + error("Coding must be one of 'ami', 'b8zs' or 'hdb3', not '%s'\n", realargs[4]); + return -1; + } + if (argc > 5) { + if (!strcasecmp(realargs[5], "yellow")) + lc[spans].lineconfig |= ZT_CONFIG_NOTOPEN; + else if (!strcasecmp(realargs[5], "crc4")) { + lc[spans].lineconfig |= ZT_CONFIG_CRC4; + } else { + error("Only valid fifth arguments are 'yellow' or 'crc4', not '%s'\n", realargs[5]); + return -1; + } + } + if (argc > 6) { + if (!strcasecmp(realargs[6], "yellow")) + lc[spans].lineconfig |= ZT_CONFIG_NOTOPEN; + else { + error("Only valid sixth argument is 'yellow', not '%s'\n", realargs[6]); + return -1; + } + } + lc[spans].span = span; + lc[spans].sync = timing; + /* Valid span */ + spans++; + return 0; +} + +int apply_channels(int chans[], char *argstr) +{ + char *args[ZT_MAX_CHANNELS+1]; + char *range[3]; + int res,x, res2,y; + int chan; + int start, finish; + char argcopy[256]; + res = parseargs(argstr, args, ZT_MAX_CHANNELS, ','); + if (res < 0) + error("Too many arguments... Max is %d\n", ZT_MAX_CHANNELS); + for (x=0;x<res;x++) { + if (strchr(args[x], '-')) { + /* It's a range */ + zap_copy_string(argcopy, args[x], sizeof(argcopy)); + res2 = parseargs(argcopy, range, 2, '-'); + if (res2 != 2) { + error("Syntax error in range '%s'. Should be <val1>-<val2>.\n", args[x]); + return -1; + } + res2 =sscanf(range[0], "%i", &start); + if (res2 != 1) { + error("Syntax error. Start of range '%s' should be a number from 1 to %d\n", args[x], ZT_MAX_CHANNELS - 1); + return -1; + } else if ((start < 1) || (start >= ZT_MAX_CHANNELS)) { + error("Start of range '%s' must be between 1 and %d (not '%d')\n", args[x], ZT_MAX_CHANNELS - 1, start); + return -1; + } + res2 =sscanf(range[1], "%i", &finish); + if (res2 != 1) { + error("Syntax error. End of range '%s' should be a number from 1 to %d\n", args[x], ZT_MAX_CHANNELS - 1); + return -1; + } else if ((finish < 1) || (finish >= ZT_MAX_CHANNELS)) { + error("end of range '%s' must be between 1 and %d (not '%d')\n", args[x], ZT_MAX_CHANNELS - 1, finish); + return -1; + } + if (start > finish) { + error("Range '%s' should start before it ends\n", args[x]); + return -1; + } + for (y=start;y<=finish;y++) + chans[y]=1; + } else { + /* It's a single channel */ + res2 =sscanf(args[x], "%i", &chan); + if (res2 != 1) { + error("Syntax error. Channel should be a number from 1 to %d, not '%s'\n", ZT_MAX_CHANNELS - 1, args[x]); + return -1; + } else if ((chan < 1) || (chan >= ZT_MAX_CHANNELS)) { + error("Channel must be between 1 and %d (not '%d')\n", ZT_MAX_CHANNELS - 1, chan); + return -1; + } + chans[chan]=1; + } + } + return res; +} + +int parse_idle(int *i, char *s) +{ + char a,b,c,d; + if (s) { + if (sscanf(s, "%c%c%c%c", &a,&b,&c,&d) == 4) { + if (((a == '0') || (a == '1')) && ((b == '0') || (b == '1')) && ((c == '0') || (c == '1')) && ((d == '0') || (d == '1'))) { + *i = 0; + if (a == '1') + *i |= ZT_ABIT; + if (b == '1') + *i |= ZT_BBIT; + if (c == '1') + *i |= ZT_CBIT; + if (d == '1') + *i |= ZT_DBIT; + return 0; + } + } + } + error("CAS Signalling requires idle definition in the form ':xxxx' at the end of the channel definition, where xxxx represent the a, b, c, and d bits\n"); + return -1; +} + +static int parse_channel(char *channel, int *startchan) +{ + if (!channel || (sscanf(channel, "%i", startchan) != 1) || + (*startchan < 1)) { + error("DACS requires a starting channel in the form ':x' where x is the channel\n"); + return -1; + } + return 0; +} + +static int chanconfig(char *keyword, char *args) +{ + int chans[ZT_MAX_CHANNELS]; + int res = 0; + int x; + int master=0; + int dacschan = 0; + char *idle; + bzero(chans, sizeof(chans)); + strtok(args, ":"); + idle = strtok(NULL, ":"); + if (!strcasecmp(keyword, "dacs") || !strcasecmp(keyword, "dacsrbs")) { + res = parse_channel(idle, &dacschan); + } + if (!res) + res = apply_channels(chans, args); + if (res <= 0) + return -1; + for (x=1;x<ZT_MAX_CHANNELS;x++) + if (chans[x]) { + if (slineno[x]) { + error("Channel %d already configured as '%s' at line %d\n", x, sig[x], slineno[x]); + continue; + } + if ((!strcasecmp(keyword, "dacs") || !strcasecmp(keyword, "dacsrbs")) && slineno[dacschan]) { + error("DACS Destination channel %d already configured as '%s' at line %d\n", dacschan, sig[dacschan], slineno[dacschan]); + continue; + } else { + cc[dacschan].chan = dacschan; + cc[dacschan].master = dacschan; + slineno[dacschan] = lineno; + } + cc[x].chan = x; + cc[x].master = x; + slineno[x] = lineno; + if (!strcasecmp(keyword, "e&m")) { + cc[x].sigtype = ZT_SIG_EM; + sig[x] = sigtype_to_str(cc[x].sigtype); + } else if (!strcasecmp(keyword, "e&me1")) { + cc[x].sigtype = ZT_SIG_EM_E1; + sig[x] = sigtype_to_str(cc[x].sigtype); + } else if (!strcasecmp(keyword, "fxsls")) { + cc[x].sigtype = ZT_SIG_FXSLS; + sig[x] = sigtype_to_str(cc[x].sigtype); + } else if (!strcasecmp(keyword, "fxsgs")) { + cc[x].sigtype = ZT_SIG_FXSGS; + sig[x] = sigtype_to_str(cc[x].sigtype); + } else if (!strcasecmp(keyword, "fxsks")) { + cc[x].sigtype = ZT_SIG_FXSKS; + sig[x] = sigtype_to_str(cc[x].sigtype); + } else if (!strcasecmp(keyword, "fxols")) { + cc[x].sigtype = ZT_SIG_FXOLS; + sig[x] = sigtype_to_str(cc[x].sigtype); + } else if (!strcasecmp(keyword, "fxogs")) { + cc[x].sigtype = ZT_SIG_FXOGS; + sig[x] = sigtype_to_str(cc[x].sigtype); + } else if (!strcasecmp(keyword, "fxoks")) { + cc[x].sigtype = ZT_SIG_FXOKS; + sig[x] = sigtype_to_str(cc[x].sigtype); + } else if (!strcasecmp(keyword, "cas") || !strcasecmp(keyword, "user")) { + if (parse_idle(&cc[x].idlebits, idle)) + return -1; + cc[x].sigtype = ZT_SIG_CAS; + sig[x] = sigtype_to_str(cc[x].sigtype); + } else if (!strcasecmp(keyword, "dacs")) { + /* Setup channel for monitor */ + cc[x].idlebits = dacschan; + cc[x].sigtype = ZT_SIG_DACS; + sig[x] = sigtype_to_str(cc[x].sigtype); + /* Setup inverse */ + cc[dacschan].idlebits = x; + cc[dacschan].sigtype = ZT_SIG_DACS; + sig[x] = sigtype_to_str(cc[dacschan].sigtype); + dacschan++; + } else if (!strcasecmp(keyword, "dacsrbs")) { + /* Setup channel for monitor */ + cc[x].idlebits = dacschan; + cc[x].sigtype = ZT_SIG_DACS_RBS; + sig[x] = sigtype_to_str(cc[x].sigtype); + /* Setup inverse */ + cc[dacschan].idlebits = x; + cc[dacschan].sigtype = ZT_SIG_DACS_RBS; + sig[x] = sigtype_to_str(cc[dacschan].sigtype); + dacschan++; + } else if (!strcasecmp(keyword, "unused")) { + cc[x].sigtype = 0; + sig[x] = sigtype_to_str(cc[x].sigtype); + } else if (!strcasecmp(keyword, "indclear") || !strcasecmp(keyword, "bchan")) { + cc[x].sigtype = ZT_SIG_CLEAR; + sig[x] = sigtype_to_str(cc[x].sigtype); + } else if (!strcasecmp(keyword, "clear")) { + sig[x] = sigtype_to_str(ZT_SIG_CLEAR); + if (master) { + cc[x].sigtype = ZT_SIG_SLAVE; + cc[x].master = master; + } else { + cc[x].sigtype = ZT_SIG_CLEAR; + master = x; + } + } else if (!strcasecmp(keyword, "rawhdlc")) { + sig[x] = sigtype_to_str(ZT_SIG_HDLCRAW); + if (master) { + cc[x].sigtype = ZT_SIG_SLAVE; + cc[x].master = master; + } else { + cc[x].sigtype = ZT_SIG_HDLCRAW; + master = x; + } + } else if (!strcasecmp(keyword, "nethdlc")) { + sig[x] = sigtype_to_str(ZT_SIG_HDLCNET); + memset(cc[x].netdev_name, 0, sizeof(cc[x].netdev_name)); + if (master) { + cc[x].sigtype = ZT_SIG_SLAVE; + cc[x].master = master; + } else { + cc[x].sigtype = ZT_SIG_HDLCNET; + if (idle) { + zap_copy_string(cc[x].netdev_name, idle, sizeof(cc[x].netdev_name)); + } + master = x; + } + } else if (!strcasecmp(keyword, "fcshdlc")) { + sig[x] = sigtype_to_str(ZT_SIG_HDLCFCS); + if (master) { + cc[x].sigtype = ZT_SIG_SLAVE; + cc[x].master = master; + } else { + cc[x].sigtype = ZT_SIG_HDLCFCS; + master = x; + } + } else if (!strcasecmp(keyword, "dchan")) { + sig[x] = "D-channel"; + cc[x].sigtype = ZT_SIG_HDLCFCS; + } else if (!strcasecmp(keyword, "hardhdlc")) { + sig[x] = "Hardware assisted D-channel"; + cc[x].sigtype = ZT_SIG_HARDHDLC; + } else if (!strcasecmp(keyword, "mtp2")) { + sig[x] = "MTP2"; + cc[x].sigtype = ZT_SIG_MTP2; + } else { + fprintf(stderr, "Huh? (%s)\n", keyword); + } + } + return 0; +} + +static int setlaw(char *keyword, char *args) +{ + int res; + int law; + int x; + int chans[ZT_MAX_CHANNELS]; + + bzero(chans, sizeof(chans)); + res = apply_channels(chans, args); + if (res <= 0) + return -1; + if (!strcasecmp(keyword, "alaw")) { + law = ZT_LAW_ALAW; + } else if (!strcasecmp(keyword, "mulaw")) { + law = ZT_LAW_MULAW; + } else if (!strcasecmp(keyword, "deflaw")) { + law = ZT_LAW_DEFAULT; + } else { + fprintf(stderr, "Huh??? Don't know about '%s' law\n", keyword); + return -1; + } + for (x=0;x<ZT_MAX_CHANNELS;x++) { + if (chans[x]) + cc[x].deflaw = law; + } + return 0; +} + +static int registerzone(char *keyword, char *args) +{ + if (numzones >= ZT_TONE_ZONE_MAX) { + error("Too many tone zones specified\n"); + return 0; + } + zap_copy_string(zonestoload[numzones++], args, sizeof(zonestoload[0])); + return 0; +} + +static int defaultzone(char *keyword, char *args) +{ + struct tone_zone *z; + if (!(z = tone_zone_find(args))) { + error("No such tone zone known: %s\n", args); + return 0; + } + deftonezone = z->zone; + return 0; +} + +#if 0 +static int unimplemented(char *keyword, char *args) +{ + fprintf(stderr, "Warning: '%s' is not yet implemented\n", keyword); + return 0; +} +#endif + + +/* Radio functions */ + +int ctcss(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int rxtone; + int rxtag; + int txtone; + int isdcs = 0; + argc = res = parseargs(args, realargs, 3, ','); + if (res != 3) { + error("Incorrect number of arguments to 'ctcss' (should be <rxtone>,<rxtag>,<txtone>)\n"); + } + res = sscanf(realargs[0], "%d", &rxtone); + if ((res == 1) && (rxtone < 1)) + res = -1; + if (res != 1) { + error("Invalid rxtone '%s', should be a number > 0.\n", realargs[0]); + } + res = sscanf(realargs[1], "%i", &rxtag); + if ((res == 1) && (rxtag < 0)) + res = -1; + if (res != 1) { + error("Invalid rxtag '%s', should be a number > 0.\n", realargs[1]); + } + if ((*realargs[2] == 'D') || (*realargs[2] == 'd')) + { + realargs[2]++; + isdcs = 0x8000; + } + res = sscanf(realargs[2], "%d", &txtone); + if ((res == 1) && (rxtag < 0)) + res = -1; + if (res != 1) { + error("Invalid txtone '%s', should be a number > 0.\n", realargs[2]); + } + + if (toneindex >= NUM_TONES) + { + error("Cannot specify more then %d CTCSS tones\n",NUM_TONES); + } + rxtones[toneindex] = rxtone; + rxtags[toneindex] = rxtag; + txtones[toneindex] = txtone | isdcs; + toneindex++; + return 0; +} + +int dcsrx(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int rxtone; + argc = res = parseargs(args, realargs, 1, ','); + if (res != 1) { + error("Incorrect number of arguments to 'dcsrx' (should be <rxtone>)\n"); + } + res = sscanf(realargs[0], "%d", &rxtone); + if ((res == 1) && (rxtone < 1)) + res = -1; + if (res != 1) { + error("Invalid rxtone '%s', should be a number > 0.\n", realargs[0]); + } + + rxtones[0] = rxtone; + return 0; +} + +int tx(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int txtone; + int isdcs = 0; + argc = res = parseargs(args, realargs, 1, ','); + if (res != 1) { + error("Incorrect number of arguments to 'tx' (should be <txtone>)\n"); + } + if ((*realargs[0] == 'D') || (*realargs[0] == 'd')) + { + realargs[0]++; + isdcs = 0x8000; + } + res = sscanf(realargs[0], "%d", &txtone); + if ((res == 1) && (txtone < 1)) + res = -1; + if (res != 1) { + error("Invalid tx (tone) '%s', should be a number > 0.\n", realargs[0]); + } + + txtones[0] = txtone | isdcs; + return 0; +} + +int debounce_time(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int val; + argc = res = parseargs(args, realargs, 1, ','); + if (res != 1) { + error("Incorrect number of arguments to 'debouncetime' (should be <value>)\n"); + } + res = sscanf(realargs[0], "%d", &val); + if ((res == 1) && (val < 1)) + res = -1; + if (res != 1) { + error("Invalid value '%s', should be a number > 0.\n", realargs[0]); + } + + debouncetime = val; + return 0; +} + +int burst_time(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int val; + argc = res = parseargs(args, realargs, 1, ','); + if (res != 1) { + error("Incorrect number of arguments to 'bursttime' (should be <value>)\n"); + } + res = sscanf(realargs[0], "%d", &val); + if ((res == 1) && (val < 1)) + res = -1; + if (res != 1) { + error("Invalid value '%s', should be a number > 0.\n", realargs[0]); + } + + bursttime = val; + return 0; +} + +int tx_gain(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int val; + argc = res = parseargs(args, realargs, 1, ','); + if (res != 1) { + error("Incorrect number of arguments to 'txgain' (should be <value>)\n"); + } + res = sscanf(realargs[0], "%d", &val); + if (res != 1) { + error("Invalid value '%s', should be a number > 0.\n", realargs[0]); + } + + txgain = val; + return 0; +} + +int rx_gain(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int val; + argc = res = parseargs(args, realargs, 1, ','); + if (res != 1) { + error("Incorrect number of arguments to 'rxgain' (should be <value>)\n"); + } + res = sscanf(realargs[0], "%d", &val); + if (res != 1) { + error("Invalid value '%s', should be a number > 0.\n", realargs[0]); + } + + rxgain = val; + return 0; +} + +int de_emp(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int val; + argc = res = parseargs(args, realargs, 1, ','); + if (res != 1) { + error("Incorrect number of arguments to 'de-emp' (should be <value>)\n"); + } + res = sscanf(realargs[0], "%d", &val); + if ((res == 1) && (val < 1)) + res = -1; + if (res != 1) { + error("Invalid value '%s', should be a number > 0.\n", realargs[0]); + } + + deemp = val; + return 0; +} + +int pre_emp(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int val; + argc = res = parseargs(args, realargs, 1, ','); + if (res != 1) { + error("Incorrect number of arguments to 'pre_emp' (should be <value>)\n"); + } + res = sscanf(realargs[0], "%d", &val); + if ((res == 1) && (val < 1)) + res = -1; + if (res != 1) { + error("Invalid value '%s', should be a number > 0.\n", realargs[0]); + } + + preemp = val; + return 0; +} + +int invert_cor(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int val; + argc = res = parseargs(args, realargs, 1, ','); + if (res != 1) { + error("Incorrect number of arguments to 'invertcor' (should be <value>)\n"); + } + if ((*realargs[0] == 'y') || (*realargs[0] == 'Y')) val = 1; + else if ((*realargs[0] == 'n') || (*realargs[0] == 'N')) val = 0; + else + { + res = sscanf(realargs[0], "%d", &val); + if ((res == 1) && (val < 0)) + res = -1; + if (res != 1) { + error("Invalid value '%s', should be a number > 0.\n", realargs[0]); + } + } + invertcor = (val > 0); + return 0; +} + +int ext_tone(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int val; + argc = res = parseargs(args, realargs, 1, ','); + if (res != 1) { + error("Incorrect number of arguments to 'exttone' (should be <value>)\n"); + } + if ((*realargs[0] == 'y') || (*realargs[0] == 'Y')) val = 1; + else if ((*realargs[0] == 'n') || (*realargs[0] == 'N')) val = 0; + else if ((*realargs[0] == 'i') || (*realargs[0] == 'I')) val = 2; + else + { + res = sscanf(realargs[0], "%d", &val); + if ((res == 1) && (val < 0)) + res = -1; + if (val > 2) res = -1; + if (res != 1) { + error("Invalid value '%s', should be a number > 0.\n", realargs[0]); + } + } + exttone = val; + return 0; +} + +int cor_thresh(char *keyword, char *args) +{ + static char *realargs[10]; + int argc; + int res; + int val; + int x = 0; + argc = res = parseargs(args, realargs, 1, ','); + if (res != 1) { + error("Incorrect number of arguments to 'corthresh' (should be <value>)\n"); + } + res = sscanf(realargs[0], "%d", &val); + if ((res == 1) && (val < 1)) + res = -1; + for(x = 0; corthreshes[x]; x++) + { + if (corthreshes[x] == val) break; + } + if (!corthreshes[x]) res = -1; + if (res != 1) { + error("Invalid value '%s', should be a number > 0.\n", realargs[0]); + } + corthresh = x + 1; + return 0; +} + +int rad_apply_channels(int chans[], char *argstr) +{ + char *args[ZT_MAX_CHANNELS+1]; + char *range[3]; + int res,x, res2,y; + int chan; + int start, finish; + char argcopy[256]; + res = parseargs(argstr, args, ZT_MAX_CHANNELS, ','); + if (res < 0) + error("Too many arguments... Max is %d\n", ZT_MAX_CHANNELS); + for (x=0;x<res;x++) { + if (strchr(args[x], '-')) { + /* It's a range */ + zap_copy_string(argcopy, args[x], sizeof(argcopy)); + res2 = parseargs(argcopy, range, 2, '-'); + if (res2 != 2) { + error("Syntax error in range '%s'. Should be <val1>-<val2>.\n", args[x]); + return -1; + } + res2 =sscanf(range[0], "%i", &start); + if (res2 != 1) { + error("Syntax error. Start of range '%s' should be a number from 1 to %d\n", args[x], ZT_MAX_CHANNELS - 1); + return -1; + } else if ((start < 1) || (start >= ZT_MAX_CHANNELS)) { + error("Start of range '%s' must be between 1 and %d (not '%d')\n", args[x], ZT_MAX_CHANNELS - 1, start); + return -1; + } + res2 =sscanf(range[1], "%i", &finish); + if (res2 != 1) { + error("Syntax error. End of range '%s' should be a number from 1 to %d\n", args[x], ZT_MAX_CHANNELS - 1); + return -1; + } else if ((finish < 1) || (finish >= ZT_MAX_CHANNELS)) { + error("end of range '%s' must be between 1 and %d (not '%d')\n", args[x], ZT_MAX_CHANNELS - 1, finish); + return -1; + } + if (start > finish) { + error("Range '%s' should start before it ends\n", args[x]); + return -1; + } + for (y=start;y<=finish;y++) + chans[y]=1; + } else { + /* It's a single channel */ + res2 =sscanf(args[x], "%i", &chan); + if (res2 != 1) { + error("Syntax error. Channel should be a number from 1 to %d, not '%s'\n", ZT_MAX_CHANNELS - 1, args[x]); + return -1; + } else if ((chan < 1) || (chan >= ZT_MAX_CHANNELS)) { + error("Channel must be between 1 and %d (not '%d')\n", ZT_MAX_CHANNELS - 1, chan); + return -1; + } + chans[chan]=1; + } + } + return res; +} + +static int rad_chanconfig(char *keyword, char *args) +{ + int chans[ZT_MAX_CHANNELS]; + int res = 0; + int x,i,n; + struct zt_radio_param p; + + toneindex = 1; + bzero(chans, sizeof(chans)); + res = rad_apply_channels(chans, args); + if (res <= 0) + return -1; + for (x=1;x<ZT_MAX_CHANNELS;x++) { + if (chans[x]) { + p.radpar = ZT_RADPAR_NUMTONES; + if (ind_ioctl(x,fd,ZT_RADIO_GETPARAM,&p) == -1) + n = 0; else n = p.data; + if (n) + { + p.radpar = ZT_RADPAR_INITTONE; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot init tones for channel %d\n",x); + if (!rxtones[0]) for(i = 1; i <= n; i++) + { + if (rxtones[i]) + { + p.radpar = ZT_RADPAR_RXTONE; + p.index = i; + p.data = rxtones[i]; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot set rxtone on channel %d\n",x); + } + if (rxtags[i]) + { + p.radpar = ZT_RADPAR_RXTONECLASS; + p.index = i; + p.data = rxtags[i]; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot set rxtag on channel %d\n",x); + } + if (txtones[i]) + { + p.radpar = ZT_RADPAR_TXTONE; + p.index = i; + p.data = txtones[i]; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot set txtone on channel %d\n",x); + } + } else { /* if we may have DCS receive */ + if (rxtones[0]) + { + p.radpar = ZT_RADPAR_RXTONE; + p.index = 0; + p.data = rxtones[0]; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot set DCS rxtone on channel %d\n",x); + } + } + if (txtones[0]) + { + p.radpar = ZT_RADPAR_TXTONE; + p.index = 0; + p.data = txtones[0]; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot set default txtone on channel %d\n",x); + } + } + if (debouncetime) + { + p.radpar = ZT_RADPAR_DEBOUNCETIME; + p.data = debouncetime; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot set debouncetime on channel %d\n",x); + } + if (bursttime) + { + p.radpar = ZT_RADPAR_BURSTTIME; + p.data = bursttime; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot set bursttime on channel %d\n",x); + } + p.radpar = ZT_RADPAR_DEEMP; + p.data = deemp; + ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p); + p.radpar = ZT_RADPAR_PREEMP; + p.data = preemp; + ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p); + p.radpar = ZT_RADPAR_TXGAIN; + p.data = txgain; + ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p); + p.radpar = ZT_RADPAR_RXGAIN; + p.data = rxgain; + ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p); + p.radpar = ZT_RADPAR_INVERTCOR; + p.data = invertcor; + ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p); + p.radpar = ZT_RADPAR_EXTRXTONE; + p.data = exttone; + ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p); + if (corthresh) + { + p.radpar = ZT_RADPAR_CORTHRESH; + p.data = corthresh - 1; + if (ind_ioctl(x,fd,ZT_RADIO_SETPARAM,&p) == -1) + error("Cannot set corthresh on channel %d\n",x); + } + } + } + clear_fields(); + return 0; +} + +/* End Radio functions */ + +static void printconfig(int fd) +{ + int x,y; + int ps; + int configs=0; + struct zt_versioninfo vi; + + strcpy(vi.version, "Unknown"); + strcpy(vi.echo_canceller, "Unknown"); + + if (ioctl(fd, ZT_GETVERSION, &vi)) + error("Unable to read Zaptel version information.\n"); + + printf("\nZaptel Version: %s\n" + "Echo Canceller: %s\n" + "Configuration\n" + "======================\n\n", vi.version, vi.echo_canceller); + for (x=0;x<spans;x++) + printf("SPAN %d: %3s/%4s Build-out: %s\n", + x+1, ( lc[x].lineconfig & ZT_CONFIG_D4 ? "D4" : + lc[x].lineconfig & ZT_CONFIG_ESF ? "ESF" : + lc[x].lineconfig & ZT_CONFIG_CCS ? "CCS" : "CAS" ), + ( lc[x].lineconfig & ZT_CONFIG_AMI ? "AMI" : + lc[x].lineconfig & ZT_CONFIG_B8ZS ? "B8ZS" : + lc[x].lineconfig & ZT_CONFIG_HDB3 ? "HDB3" : "???" ), + lbostr[lc[x].lbo]); + for (x=0;x<numdynamic;x++) { + printf("Dynamic span %d: driver %s, addr %s, channels %d, timing %d\n", + x +1, zds[x].driver, zds[x].addr, zds[x].numchans, zds[x].timing); + } + if (verbose > 1) { + printf("\nChannel map:\n\n"); + for (x=1;x<ZT_MAX_CHANNELS;x++) { + if ((cc[x].sigtype != ZT_SIG_SLAVE) && (cc[x].sigtype)) { + configs++; + ps = 0; + if ((cc[x].sigtype & __ZT_SIG_DACS) == __ZT_SIG_DACS) + printf("Channel %02d %s to %02d", x, sig[x], cc[x].idlebits); + else { + printf("Channel %02d: %s (%s)", x, sig[x], laws[cc[x].deflaw]); + for (y=1;y<ZT_MAX_CHANNELS;y++) + if (cc[y].master == x) { + printf("%s%02d", ps++ ? " " : " (Slaves: ", y); + } + } + if (ps) printf(")\n"); else printf("\n"); + } else + if (cc[x].sigtype) configs++; + } + } else + for (x=1;x<ZT_MAX_CHANNELS;x++) + if (cc[x].sigtype) + configs++; + printf("\n%d channels to configure.\n\n", configs); +} + +static struct handler { + char *keyword; + int (*func)(char *keyword, char *args); +} handlers[] = { + { "span", spanconfig }, + { "dynamic", dspanconfig }, + { "loadzone", registerzone }, + { "defaultzone", defaultzone }, + { "e&m", chanconfig }, + { "e&me1", chanconfig }, + { "fxsls", chanconfig }, + { "fxsgs", chanconfig }, + { "fxsks", chanconfig }, + { "fxols", chanconfig }, + { "fxogs", chanconfig }, + { "fxoks", chanconfig }, + { "rawhdlc", chanconfig }, + { "nethdlc", chanconfig }, + { "fcshdlc", chanconfig }, + { "hardhdlc", chanconfig }, + { "mtp2", chanconfig }, + { "dchan", chanconfig }, + { "bchan", chanconfig }, + { "indclear", chanconfig }, + { "clear", chanconfig }, + { "unused", chanconfig }, + { "cas", chanconfig }, + { "dacs", chanconfig }, + { "dacsrbs", chanconfig }, + { "user", chanconfig }, + { "alaw", setlaw }, + { "mulaw", setlaw }, + { "deflaw", setlaw }, + { "ctcss", ctcss }, + { "dcsrx", dcsrx }, + { "rxdcs", dcsrx }, + { "tx", tx }, + { "debouncetime", debounce_time }, + { "bursttime", burst_time }, + { "exttone", ext_tone }, + { "invertcor", invert_cor }, + { "corthresh", cor_thresh }, + { "rxgain", rx_gain }, + { "txgain", tx_gain }, + { "deemp", de_emp }, + { "preemp", pre_emp }, + { "channel", rad_chanconfig }, + { "channels", rad_chanconfig }, +}; + +static char *readline() +{ + static char buf[256]; + char *c; + do { + if (!fgets(buf, sizeof(buf), cf)) + return NULL; + /* Strip comments */ + c = strchr(buf, '#'); + if (c) + *c = '\0'; + trim(buf); + lineno++; + } while (!strlen(buf)); + return buf; +} + +static void usage(char *argv0, int exitcode) +{ + char *c; + c = strrchr(argv0, '/'); + if (!c) + c = argv0; + else + c++; + fprintf(stderr, + "Usage: %s [options]\n" + " Valid options are:\n" + " -c <filename> -- Use <filename> instead of " CONFIG_FILENAME "\n" + " -d [level] -- Generate debugging output. (Default level is 1.)\n" + " -f -- Always reconfigure every channel\n" + " -h -- Generate this help statement\n" + " -s -- Shutdown spans only\n" + " -t -- Test mode only, do not apply\n" + " -v -- Verbose (more -v's means more verbose)\n" + ,c); + exit(exitcode); +} + +int main(int argc, char *argv[]) +{ + int c; + char *buf; + char *key, *value; + int x,found; + while((c = getopt(argc, argv, "fthc:vsd::")) != -1) { + switch(c) { + case 'c': + filename=optarg; + break; + case 'h': + usage(argv[0], 0); + break; + case '?': + usage(argv[0], 1); + break; + case 'v': + verbose++; + break; + case 'f': + force++; + break; + case 't': + fo_real = 0; + break; + case 's': + stopmode = 1; + break; + case 'd': + if (optarg) + debug = atoi(optarg); + else + debug = 1; + break; + } + } + if (fd == -1) fd = open(MASTER_DEVICE, O_RDWR); + if (fd < 0) + error("Unable to open master device '%s'\n", MASTER_DEVICE); + cf = fopen(filename, "r"); + if (cf) { + while((buf = readline())) { + if (debug & DEBUG_READER) + fprintf(stderr, "Line %d: %s\n", lineno, buf); + key = value = buf; + while(value && *value && (*value != '=')) value++; + if (value) + *value='\0'; + if (value) + value++; + while(value && *value && (*value < 33)) value++; + if (*value) { + trim(key); + if (debug & DEBUG_PARSER) + fprintf(stderr, "Keyword: [%s], Value: [%s]\n", key, value); + } else + error("Syntax error. Should be <keyword>=<value>\n"); + found=0; + for (x=0;x<sizeof(handlers) / sizeof(handlers[0]);x++) { + if (!strcasecmp(key, handlers[x].keyword)) { + found++; + handlers[x].func(key, value); + break; + } + } + if (!found) + error("Unknown keyword '%s'\n", key); + } + if (debug & DEBUG_READER) + fprintf(stderr, "<End of File>\n"); + fclose(cf); + } else { + error("Unable to open configuration file '%s'\n", filename); + } + + if (!errcnt) { + if (verbose) { + printconfig(fd); + } + if (fo_real) { + if (debug & DEBUG_APPLY) { + printf("About to open Master device\n"); + fflush(stdout); + } + for (x=0;x<numdynamic;x++) { + /* destroy them all */ + ioctl(fd, ZT_DYNAMIC_DESTROY, &zds[x]); + } + if (stopmode) { + for (x=0;x<spans;x++) { + if (ioctl(fd, ZT_SHUTDOWN, &lc[x].span)) { + fprintf(stderr, "Zaptel shutdown failed: %s\n", strerror(errno)); + close(fd); + exit(1); + } + } + } else { + for (x=0;x<spans;x++) { + if (ioctl(fd, ZT_SPANCONFIG, lc + x)) { + fprintf(stderr, "ZT_SPANCONFIG failed on span %d: %s (%d)\n", lc[x].span, strerror(errno), errno); + close(fd); + exit(1); + } + } + for (x=0;x<numdynamic;x++) { + if (ioctl(fd, ZT_DYNAMIC_CREATE, &zds[x])) { + fprintf(stderr, "Zaptel dynamic span creation failed: %s\n", strerror(errno)); + close(fd); + exit(1); + } + } + for (x=1;x<ZT_MAX_CHANNELS;x++) { + struct zt_params current_state; + int master; + int needupdate = force; + + if (debug & DEBUG_APPLY) { + printf("Configuring device %d\n", x); + fflush(stdout); + } + if (!cc[x].sigtype) + continue; + + if (!needupdate) { + memset(¤t_state, 0, sizeof(current_state)); + current_state.channo = cc[x].chan | ZT_GET_PARAMS_RETURN_MASTER; + if (ioctl(fd, ZT_GET_PARAMS, ¤t_state)) + needupdate = 1; + } + + if (!needupdate) { + master = current_state.channo >> 16; + + if (cc[x].sigtype != current_state.sigtype) { + needupdate++; + if (verbose > 1) + printf("Changing signalling on channel %d from %s to %s\n", + cc[x].chan, sigtype_to_str(current_state.sigtype), + sigtype_to_str(cc[x].sigtype)); + } + + if ((cc[x].deflaw != ZT_LAW_DEFAULT) && (cc[x].deflaw != current_state.curlaw)) { + needupdate++; + if (verbose > 1) + printf("Changing law on channel %d from %s to %s\n", + cc[x].chan, laws[current_state.curlaw], + laws[cc[x].deflaw]); + } + + if (cc[x].master != master) { + needupdate++; + if (verbose > 1) + printf("Changing master of channel %d from %d to %d\n", + cc[x].chan, master, + cc[x].master); + } + + if (cc[x].idlebits != current_state.idlebits) { + needupdate++; + if (verbose > 1) + printf("Changing idle bits of channel %d from %d to %d\n", + cc[x].chan, current_state.idlebits, + cc[x].idlebits); + } + } + + if (needupdate && ioctl(fd, ZT_CHANCONFIG, &cc[x])) { + fprintf(stderr, "ZT_CHANCONFIG failed on channel %d: %s (%d)\n", x, strerror(errno), errno); + if (errno == EINVAL) { + fprintf(stderr, "Did you forget that FXS interfaces are configured with FXO signalling\n" + "and that FXO interfaces use FXS signalling?\n"); + } + close(fd); + exit(1); + } + } + for (x=0;x<numzones;x++) { + if (debug & DEBUG_APPLY) { + printf("Loading tone zone for %s\n", zonestoload[x]); + fflush(stdout); + } + if (tone_zone_register(fd, zonestoload[x])) + if (errno != EBUSY) + error("Unable to register tone zone '%s'\n", zonestoload[x]); + } + if (debug & DEBUG_APPLY) { + printf("Doing startup\n"); + fflush(stdout); + } + if (deftonezone > -1) { + if (ioctl(fd, ZT_DEFAULTZONE, &deftonezone)) { + fprintf(stderr, "ZT_DEFAULTZONE failed: %s (%d)\n", strerror(errno), errno); + close(fd); + exit(1); + } + } + for (x=0;x<spans;x++) { + if (ioctl(fd, ZT_STARTUP, &lc[x].span)) { + fprintf(stderr, "Zaptel startup failed: %s\n", strerror(errno)); + close(fd); + exit(1); + } + } + } + } + } else { + fprintf(stderr, "\n%d error(s) detected\n\n", errcnt); + exit(1); + } + exit(0); +} diff --git a/dahdi_cfg.h b/dahdi_cfg.h new file mode 100644 index 0000000..8ad5d81 --- /dev/null +++ b/dahdi_cfg.h @@ -0,0 +1,27 @@ +/* + * BSD Telephony Of Mexico "Tormenta" card LINUX driver, version 1.6 1/9/01 + * + * Working with the "Tormenta ISA" Card + * + * + * Part of the "Zapata" Computer Telephony Technology. + * + * See http://www.bsdtelephony.com.mx + * + * + * The technologies, software, hardware, designs, drawings, scheumatics, board + * layouts and/or artwork, concepts, methodologies (including the use of all + * of these, and that which is derived from the use of all of these), all other + * intellectual properties contained herein, and all intellectual property + * rights have been and shall continue to be expressly for the benefit of all + * mankind, and are perpetually placed in the public domain, and may be used, + * copied, and/or modified by anyone, in any manner, for any legal purpose, + * without restriction. + */ + +#ifndef _ZTCFG_H +#define _ZTCFG_H + +#define CONFIG_FILENAME ZAPTEL_CONFIG +#define MASTER_DEVICE "/dev/zap/ctl" +#endif diff --git a/dahdi_diag.c b/dahdi_diag.c new file mode 100644 index 0000000..50900a8 --- /dev/null +++ b/dahdi_diag.c @@ -0,0 +1,31 @@ +#include <fcntl.h> +#include <sys/ioctl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#ifdef STANDALONE_ZAPATA +#include "kernel/zaptel.h" +#else +#include <zaptel/zaptel.h> +#endif + +int main(int argc, char *argv[]) +{ + int fd; + int chan; + if ((argc < 2) || (sscanf(argv[1], "%d", &chan) != 1)) { + fprintf(stderr, "Usage: ztdiag <channel>\n"); + exit(1); + } + fd = open("/dev/zap/ctl", O_RDWR); + if (fd < 0) { + perror("open(/dev/zap/ctl"); + exit(1); + } + if (ioctl(fd, ZT_CHANDIAG, &chan)) { + perror("ioctl(ZT_CHANDIAG)"); + exit(1); + } + exit(0); +} diff --git a/dahdi_monitor.c b/dahdi_monitor.c new file mode 100644 index 0000000..3c8b5ff --- /dev/null +++ b/dahdi_monitor.c @@ -0,0 +1,577 @@ +/* + * Monitor a Zaptel Channel + * + * Written by Mark Spencer <markster@digium.com> + * Based on previous works, designs, and architectures conceived and + * written by Jim Dixon <jim@lambdatel.com>. + * + * Copyright (C) 2001 Jim Dixon / Zapata Telephony. + * Copyright (C) 2001 Linux Support Services, Inc. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under thet erms 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Primary Author: Mark Spencer <markster@digium.com> + * + */ + +#include <stdio.h> +#include <getopt.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <fcntl.h> +#include <errno.h> +#include <ctype.h> +#ifdef STANDALONE_ZAPATA +#include "kernel/zaptel.h" +#include "tonezone.h" +#else +#include <zaptel/zaptel.h> +#include <zaptel/tonezone.h> +#endif +#include <linux/soundcard.h> + +/* +* defines for file handle numbers +*/ +#define MON_BRX 0 /*!< both channels if multichannel==1 or receive otherwise */ +#define MON_TX 1 /*!< transmit channel */ +#define MON_PRE_BRX 2 /*!< same as MON_BRX but before echo cancellation */ +#define MON_PRE_TX 3 /*!< same as MON_TX but before echo cancellation */ +#define MON_STEREO 4 /*!< stereo mix of rx/tx streams */ +#define MON_PRE_STEREO 5 /*!< stereo mix of rx/tx before echo can. This is exactly what is fed into the echo can */ + +#define BLOCK_SIZE 240 + +#define BUFFERS 4 + +#define FRAG_SIZE 8 + +/* Put the ofh (output file handles) outside + * the main loop in case we ever add a signal + * handler. + */ +static FILE* ofh[6] = {0, 0, 0, 0, 0, 0}; + +static int stereo = 0; +static int verbose = 0; + +int audio_open(void) +{ + int fd; + int speed = 8000; + int fmt = AFMT_S16_LE; + int fragsize = (BUFFERS << 16) | (FRAG_SIZE); + struct audio_buf_info ispace, ospace; + fd = open("/dev/dsp", O_WRONLY); + if (fd < 0) { + fprintf(stderr, "Unable to open /dev/dsp: %s\n", strerror(errno)); + return -1; + } + /* Step 1: Signed linear */ + if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt) < 0) { + fprintf(stderr, "ioctl(SETFMT) failed: %s\n", strerror(errno)); + close(fd); + return -1; + } + /* Step 2: Make non-stereo */ + if (ioctl(fd, SNDCTL_DSP_STEREO, &stereo) < 0) { + fprintf(stderr, "ioctl(STEREO) failed: %s\n", strerror(errno)); + close(fd); + return -1; + } + if (stereo != 0) { + fprintf(stderr, "Can't turn stereo off :(\n"); + } + /* Step 3: Make 8000 Hz */ + if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0) { + fprintf(stderr, "ioctl(SPEED) failed: %s\n", strerror(errno)); + close(fd); + return -1; + } + if (speed != 8000) + fprintf(stderr, "Warning: Requested 8000 Hz, got %d\n", speed); + if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &fragsize)) { + fprintf(stderr, "Sound card won't let me set fragment size to %u %u-byte buffers (%x)\n" + "so sound may be choppy: %s.\n", BUFFERS, (1 << FRAG_SIZE), fragsize, strerror(errno)); + } + bzero(&ispace, sizeof(ispace)); + bzero(&ospace, sizeof(ospace)); + + if (ioctl(fd, SNDCTL_DSP_GETISPACE, &ispace)) { + /* They don't support block size stuff, so just return but notify the user */ + fprintf(stderr, "Sound card won't let me know the input buffering...\n"); + } + if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &ospace)) { + /* They don't support block size stuff, so just return but notify the user */ + fprintf(stderr, "Sound card won't let me know the output buffering...\n"); + } + fprintf(stderr, "New input space: %d of %d %d byte fragments (%d bytes left)\n", + ispace.fragments, ispace.fragstotal, ispace.fragsize, ispace.bytes); + fprintf(stderr, "New output space: %d of %d %d byte fragments (%d bytes left)\n", + ospace.fragments, ospace.fragstotal, ospace.fragsize, ospace.bytes); + return fd; +} + +int pseudo_open(void) +{ + int fd; + int x = 1; + fd = open("/dev/zap/pseudo", O_RDWR); + if (fd < 0) { + fprintf(stderr, "Unable to open pseudo channel: %s\n", strerror(errno)); + return -1; + } + if (ioctl(fd, ZT_SETLINEAR, &x)) { + fprintf(stderr, "Unable to set linear mode: %s\n", strerror(errno)); + close(fd); + return -1; + } + x = BLOCK_SIZE; + if (ioctl(fd, ZT_SET_BLOCKSIZE, &x)) { + fprintf(stderr, "unable to set sane block size: %s\n", strerror(errno)); + close(fd); + return -1; + } + return fd; +} + +#define barlen 35 +#define baroptimal 3250 +//define barlevel 200 +#define barlevel ((baroptimal/barlen)*2) +#define maxlevel (barlen*barlevel) + +void draw_barheader() +{ + char bar[barlen+5]; + + memset(bar, '-', sizeof(bar)); + memset(bar, '<', 1); + memset(bar+barlen+2, '>', 1); + memset(bar+barlen+3, '\0', 1); + + zap_copy_string(bar+(barlen/2), "(RX)", 4); + printf("%s", bar); + + zap_copy_string(bar+(barlen/2), "(TX)", 4); + printf(" %s\n", bar); +} + +void draw_bar(int avg, int max) +{ + char bar[barlen+5]; + + memset(bar, ' ', sizeof(bar)); + + max /= barlevel; + avg /= barlevel; + if (avg > barlen) + avg = barlen; + if (max > barlen) + max = barlen; + + if (avg > 0) + memset(bar, '#', avg); + if (max > 0) + memset(bar + max, '*', 1); + + bar[barlen+1] = '\0'; + printf("%s", bar); + fflush(stdout); +} + +void visualize(short *tx, short *rx, int cnt) +{ + int x; + float txavg = 0; + float rxavg = 0; + static int txmax = 0; + static int rxmax = 0; + static int sametxmax = 0; + static int samerxmax = 0; + static int txbest = 0; + static int rxbest = 0; + float ms; + static struct timeval last; + struct timeval tv; + + gettimeofday(&tv, NULL); + ms = (tv.tv_sec - last.tv_sec) * 1000.0 + (tv.tv_usec - last.tv_usec) / 1000.0; + for (x=0;x<cnt;x++) { + txavg += abs(tx[x]); + rxavg += abs(rx[x]); + } + txavg = abs(txavg / cnt); + rxavg = abs(rxavg / cnt); + + if (txavg > txbest) + txbest = txavg; + if (rxavg > rxbest) + rxbest = rxavg; + + /* Update no more than 10 times a second */ + if (ms < 100) + return; + + /* Save as max levels, if greater */ + if (txbest > txmax) { + txmax = txbest; + sametxmax = 0; + } + if (rxbest > rxmax) { + rxmax = rxbest; + samerxmax = 0; + } + + memcpy(&last, &tv, sizeof(last)); + + /* Clear screen */ + printf("\r "); + draw_bar(rxbest, rxmax); + printf(" "); + draw_bar(txbest, txmax); + if (verbose) + printf(" Rx: %5d (%5d) Tx: %5d (%5d)", rxbest, rxmax, txbest, txmax); + txbest = 0; + rxbest = 0; + + /* If we have had the same max hits for x times, clear the values */ + sametxmax++; + samerxmax++; + if (sametxmax > 6) { + txmax = 0; + sametxmax = 0; + } + if (samerxmax > 6) { + rxmax = 0; + samerxmax = 0; + } +} + +int main(int argc, char *argv[]) +{ + int afd = -1; + int pfd[4] = {-1, -1, -1, -1}; + short buf_brx[BLOCK_SIZE * 2]; + short buf_tx[BLOCK_SIZE * 4]; + short stereobuf[BLOCK_SIZE * 4]; + int res_brx, res_tx; + int visual = 0; + int multichannel = 0; + int ossoutput = 0; + int preecho = 0; + int savefile = 0; + int stereo_output = 0; + int limit = 0; + int readcount = 0; + int x, i, chan; + struct zt_confinfo zc; + + if ((argc < 2) || (atoi(argv[1]) < 1)) { + fprintf(stderr, "Usage: ztmonitor <channel num> [-v[v]] [-m] [-o] [-p] [-l limit] [-f FILE | -s FILE | -r FILE1 -t FILE2] [-F FILE | -S FILE | -R FILE1 -T FILE2]\n"); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -v: Visual mode. Implies -m.\n"); + fprintf(stderr, " -vv: Visual/Verbose mode. Implies -m.\n"); + fprintf(stderr, " -l LIMIT: Stop after reading LIMIT bytes\n"); + fprintf(stderr, " -m: Separate rx/tx streams.\n"); + fprintf(stderr, " -o: Output audio via OSS. Note: Only 'normal' combined rx/tx streams are output via OSS.\n"); + fprintf(stderr, " -p: Get a pre-echocanceled stream.\n"); + fprintf(stderr, " -f FILE: Save combined rx/tx stream to FILE. Cannot be used with -m.\n"); + fprintf(stderr, " -r FILE: Save rx stream to FILE. Implies -m.\n"); + fprintf(stderr, " -t FILE: Save tx stream to FILE. Implies -m.\n"); + fprintf(stderr, " -s FILE: Save stereo rx/tx stream to FILE.\n"); + fprintf(stderr, " -F FILE: Save combined pre-echocanceled rx/tx stream to FILE. Cannot be used with -m. Implies -p.\n"); + fprintf(stderr, " -R FILE: Save pre-echocanceled rx stream to FILE. Implies -m and -p.\n"); + fprintf(stderr, " -T FILE: Save pre-echocanceled tx stream to FILE. Implies -m and -p.\n"); + fprintf(stderr, " -S FILE: Save pre-echocanceled stereo rx/tx stream to FILE. Implies -p.\n"); + fprintf(stderr, "Examples:\n"); + fprintf(stderr, "Save a stream to a file\n"); + fprintf(stderr, " ztmonitor 1 -f stream.raw\n"); + fprintf(stderr, "Visualize an rx/tx stream and save them to separate files.\n"); + fprintf(stderr, " ztmonitor 1 -v -r streamrx.raw -t streamtx.raw\n"); + fprintf(stderr, "Play a combined rx/tx stream via OSS and save it to a file\n"); + fprintf(stderr, " ztmonitor 1 -o -f stream.raw\n"); + fprintf(stderr, "Save a combined normal rx/tx stream and a combined 'preecho' rx/tx stream to files\n"); + fprintf(stderr, " ztmonitor 1 -p -f stream.raw -F streampreecho.raw\n"); + fprintf(stderr, "Save a normal rx/tx stream and a 'preecho' rx/tx stream to separate files\n"); + fprintf(stderr, " ztmonitor 1 -m -p -r streamrx.raw -t streamtx.raw -R streampreechorx.raw -T streampreechotx.raw\n"); + exit(1); + } + + chan = atoi(argv[1]); + + for (i = 2; i < argc; ++i) { + if (!strcmp(argv[i], "-v")) { + if (visual) + verbose = 1; + visual = 1; + multichannel = 1; + } else if (!strcmp(argv[i], "-vv")) { + visual = 1; + verbose = 1; + multichannel = 1; + } else if ((!strcmp(argv[i], "-f") || !strcmp(argv[i], "-r") || !strcmp(argv[i], "-t") + || !strcmp(argv[i], "-F") || !strcmp(argv[i], "-R") || !strcmp(argv[i], "-T") + || !strcmp(argv[i], "-s") || !strcmp(argv[i], "-S")) + && (i+1) < argc) { + char *output_file; + + /* Set which file descriptor to use */ + if (!strcmp(argv[i], "-f")) { + savefile = 1; + x = MON_BRX; + } else if (!strcmp(argv[i], "-r")) { + savefile = 1; + multichannel = 1; + x = MON_BRX; + } else if (!strcmp(argv[i], "-t")) { + savefile = 1; + multichannel = 1; + x = MON_TX; + } else if (!strcmp(argv[i], "-s")) { + savefile = 1; + stereo_output = 1; + multichannel = 1; + x = MON_STEREO; + } else if (!strcmp(argv[i], "-F")) { + savefile = 1; + preecho = 1; + x = MON_PRE_BRX; + } else if (!strcmp(argv[i], "-R")) { + savefile = 1; + multichannel = 1; + preecho = 1; + x = MON_PRE_BRX; + } else if (!strcmp(argv[i], "-T")) { + savefile = 1; + multichannel = 1; + preecho = 1; + x = MON_PRE_TX; + } else if (!strcmp(argv[i], "-S")) { + savefile = 1; + preecho = 1; + stereo_output = 1; + multichannel = 1; + x = MON_PRE_STEREO; + } else + x = MON_BRX; + + ++i; /* we care about the file name */ + output_file = argv[i]; + fprintf(stderr, "Output to %s\n", output_file); + if ((ofh[x] = fopen(output_file, "w"))<0) { + fprintf(stderr, "Could not open %s for writing: %s\n", + output_file, strerror(errno)); + exit(1); + } + fprintf(stderr, "Run e.g., 'sox -r 8000 -s -w -c 1 %s %s.wav' to convert.\n", + output_file, output_file); + } else if (!strcmp(argv[i], "-m")) { + multichannel = 1; + } else if (!strcmp(argv[i], "-o")) { + ossoutput = 1; + } else if (!strcmp(argv[i], "-p")) { + preecho = 1; + } else if (!strcmp(argv[i], "-l") && isdigit(argv[i+1][0])) { + limit = atoi(argv[i+1]); + i++; + } + } + + if (ossoutput) { + if (multichannel) { + printf("Multi-channel audio is enabled. OSS output will be disabled.\n"); + ossoutput = 0; + } else { + /* Open audio */ + if ((afd = audio_open()) < 0) { + printf("Cannot open audio ...\n"); + ossoutput = 0; + } + } + } + if (!ossoutput && !multichannel && !savefile) { + fprintf(stderr, "Nothing to do with the stream(s) ...\n"); + exit(1); + } + + /* Open Pseudo device */ + if ((pfd[MON_BRX] = pseudo_open()) < 0) + exit(1); + if (multichannel && ((pfd[MON_TX] = pseudo_open()) < 0)) + exit(1); + if (preecho) { + if ((pfd[MON_PRE_BRX] = pseudo_open()) < 0) + exit(1); + if (multichannel && ((pfd[MON_PRE_TX] = pseudo_open()) < 0)) + exit(1); + } + /* Conference them */ + if (multichannel) { + memset(&zc, 0, sizeof(zc)); + zc.chan = 0; + zc.confno = chan; + /* Two pseudo's, one for tx, one for rx */ + zc.confmode = ZT_CONF_MONITOR; + if (ioctl(pfd[MON_BRX], ZT_SETCONF, &zc) < 0) { + fprintf(stderr, "Unable to monitor: %s\n", strerror(errno)); + exit(1); + } + memset(&zc, 0, sizeof(zc)); + zc.chan = 0; + zc.confno = chan; + zc.confmode = ZT_CONF_MONITORTX; + if (ioctl(pfd[MON_TX], ZT_SETCONF, &zc) < 0) { + fprintf(stderr, "Unable to monitor: %s\n", strerror(errno)); + exit(1); + } + if (preecho) { + memset(&zc, 0, sizeof(zc)); + zc.chan = 0; + zc.confno = chan; + /* Two pseudo's, one for tx, one for rx */ + zc.confmode = ZT_CONF_MONITOR_RX_PREECHO; + if (ioctl(pfd[MON_PRE_BRX], ZT_SETCONF, &zc) < 0) { + fprintf(stderr, "Unable to monitor: %s\n", strerror(errno)); + exit(1); + } + memset(&zc, 0, sizeof(zc)); + zc.chan = 0; + zc.confno = chan; + zc.confmode = ZT_CONF_MONITOR_TX_PREECHO; + if (ioctl(pfd[MON_PRE_TX], ZT_SETCONF, &zc) < 0) { + fprintf(stderr, "Unable to monitor: %s\n", strerror(errno)); + exit(1); + } + } + } else { + memset(&zc, 0, sizeof(zc)); + zc.chan = 0; + zc.confno = chan; + zc.confmode = ZT_CONF_MONITORBOTH; + if (ioctl(pfd[MON_BRX], ZT_SETCONF, &zc) < 0) { + fprintf(stderr, "Unable to monitor: %s\n", strerror(errno)); + exit(1); + } + if (preecho) { + memset(&zc, 0, sizeof(zc)); + zc.chan = 0; + zc.confno = chan; + zc.confmode = ZT_CONF_MONITORBOTH_PREECHO; + if (ioctl(pfd[MON_PRE_BRX], ZT_SETCONF, &zc) < 0) { + fprintf(stderr, "Unable to monitor: %s\n", strerror(errno)); + exit(1); + } + } + } + if (visual) { + printf("\nVisual Audio Levels.\n"); + printf("--------------------\n"); + printf(" Use zapata.conf file to adjust the gains if needed.\n\n"); + printf("( # = Audio Level * = Max Audio Hit )\n"); + draw_barheader(); + } + /* Now, copy from pseudo to audio */ + for (;;) { + res_brx = read(pfd[MON_BRX], buf_brx, sizeof(buf_brx)); + if (res_brx < 1) + break; + readcount += res_brx; + if (ofh[MON_BRX]) + fwrite(buf_brx, 1, res_brx, ofh[MON_BRX]); + + if (multichannel) { + res_tx = read(pfd[MON_TX], buf_tx, res_brx); + if (res_tx < 1) + break; + if (ofh[MON_TX]) + fwrite(buf_tx, 1, res_tx, ofh[MON_TX]); + + if (stereo_output && ofh[MON_STEREO]) { + for (x=0;x<res_tx;x++) { + stereobuf[x*2] = buf_brx[x]; + stereobuf[x*2+1] = buf_tx[x]; + } + fwrite(stereobuf, 1, res_tx*2, ofh[MON_STEREO]); + } + + if (visual) { + if (res_brx == res_tx) + visualize((short *)buf_tx, (short *)buf_brx, res_brx/2); + else + printf("Huh? res_tx = %d, res_brx = %d?\n", res_tx, res_brx); + } + } + + if (preecho) { + res_brx = read(pfd[MON_PRE_BRX], buf_brx, sizeof(buf_brx)); + if (res_brx < 1) + break; + if (ofh[MON_PRE_BRX]) + fwrite(buf_brx, 1, res_brx, ofh[MON_PRE_BRX]); + + if (multichannel) { + res_tx = read(pfd[MON_PRE_TX], buf_tx, res_brx); + if (res_tx < 1) + break; + if (ofh[MON_PRE_TX]) + fwrite(buf_tx, 1, res_tx, ofh[MON_PRE_TX]); + + if (stereo_output && ofh[MON_PRE_STEREO]) { + for (x=0;x<res_brx;x++) { + stereobuf[x*2] = buf_brx[x]; + stereobuf[x*2+1] = buf_tx[x]; + } + fwrite(stereobuf, 1, res_brx*2, ofh[MON_PRE_STEREO]); + } + + /* XXX How are we going to visualize the preecho set of streams? + if (visual) { + if (res == res2) + visualize((short *)buf, (short *)buf2, res/2); + else + printf("Huh? res = %d, res2 = %d?\n", res, res2); + } */ + } + } + + if (ossoutput && afd) { + if (stereo) { + for (x=0;x<res_brx;x++) + buf_tx[x<<1] = buf_tx[(x<<1) + 1] = buf_brx[x]; + write(afd, buf_tx, res_brx << 1); + } else + write(afd, buf_brx, res_brx); + } + + if (limit && readcount >= limit) { + /* bail if we've read too much */ + break; + } + } + if (ofh[MON_BRX]) fclose(ofh[MON_BRX]); + if (ofh[MON_TX]) fclose(ofh[MON_TX]); + if (ofh[MON_PRE_BRX]) fclose(ofh[MON_PRE_BRX]); + if (ofh[MON_PRE_TX]) fclose(ofh[MON_PRE_TX]); + if (ofh[MON_STEREO]) fclose(ofh[MON_STEREO]); + if (ofh[MON_PRE_STEREO]) fclose(ofh[MON_PRE_STEREO]); + exit(0); +} diff --git a/dahdi_scan.c b/dahdi_scan.c new file mode 100644 index 0000000..64298ee --- /dev/null +++ b/dahdi_scan.c @@ -0,0 +1,172 @@ +/* + * Scan and output information about Zaptel spans and ports. + * + * Written by Brandon Kruse <bkruse@digium.com> + * and Kevin P. Fleming <kpfleming@digium.com> + * Copyright (C) 2007 Digium, Inc. + * + * Based on zttool written by Mark Spencer <markster@digium.com> + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under thet erms 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <errno.h> + +#ifdef STANDALONE_ZAPATA +#include "kernel/zaptel.h" +#else +#include <zaptel/zaptel.h> +#endif + +int main(int argc, char *argv[]) +{ + int ctl; + int x, y; + struct zt_params params; + unsigned int basechan = 1; + struct zt_spaninfo s; + char buf[100]; + char alarms[50]; + + if ((ctl = open("/dev/zap/ctl", O_RDWR)) < 0) { + fprintf(stderr, "Unable to open /dev/zap/ctl: %s\n", strerror(errno)); + exit(1); + } + + for (x = 1; x < ZT_MAX_SPANS; x++) { + memset(&s, 0, sizeof(s)); + s.spanno = x; + if (ioctl(ctl, ZT_SPANSTAT, &s)) + continue; + + alarms[0] = '\0'; + if (s.alarms) { + if (s.alarms & ZT_ALARM_BLUE) + strcat(alarms,"BLU/"); + if (s.alarms & ZT_ALARM_YELLOW) + strcat(alarms, "YEL/"); + if (s.alarms & ZT_ALARM_RED) + strcat(alarms, "RED/"); + if (s.alarms & ZT_ALARM_LOOPBACK) + strcat(alarms,"LB/"); + if (s.alarms & ZT_ALARM_RECOVER) + strcat(alarms,"REC/"); + if (s.alarms & ZT_ALARM_NOTOPEN) + strcat(alarms, "NOP/"); + if (!strlen(alarms)) + strcat(alarms, "UUU/"); + if (strlen(alarms)) { + /* Strip trailing / */ + alarms[strlen(alarms)-1]='\0'; + } + } else { + if (s.numchans) + strcpy(alarms, "OK"); + else + strcpy(alarms, "UNCONFIGURED"); + } + + fprintf(stdout, "[%d]\n", x); + fprintf(stdout, "active=yes\n"); + fprintf(stdout, "alarms=%s\n", alarms); + fprintf(stdout, "description=%s\n", s.desc); + fprintf(stdout, "name=%s\n", s.name); + fprintf(stdout, "manufacturer=%s\n", s.manufacturer); + fprintf(stdout, "devicetype=%s\n", s.devicetype); + fprintf(stdout, "location=%s\n", s.location); + fprintf(stdout, "basechan=%d\n", basechan); + fprintf(stdout, "totchans=%d\n", s.totalchans); + fprintf(stdout, "irq=%d\n", s.irq); + y = basechan; + memset(¶ms, 0, sizeof(params)); + params.channo = y; + if (ioctl(ctl, ZT_GET_PARAMS, ¶ms)) { + basechan += s.totalchans; + continue; + } + + if (params.sigcap & (__ZT_SIG_DACS | ZT_SIG_CAS)) { + /* this is a digital span */ + fprintf(stdout, "type=digital-%s\n", s.spantype); + fprintf(stdout, "syncsrc=%d\n", s.syncsrc); + fprintf(stdout, "lbo=%s\n", s.lboname); + fprintf(stdout, "coding_opts="); + buf[0] = '\0'; + if (s.linecompat & ZT_CONFIG_B8ZS) strcat(buf, "B8ZS,"); + if (s.linecompat & ZT_CONFIG_AMI) strcat(buf, "AMI,"); + if (s.linecompat & ZT_CONFIG_HDB3) strcat(buf, "HDB3,"); + buf[strlen(buf) - 1] = '\0'; + fprintf(stdout, "%s\n", buf); + fprintf(stdout, "framing_opts="); + buf[0] = '\0'; + if (s.linecompat & ZT_CONFIG_ESF) strcat(buf, "ESF,"); + if (s.linecompat & ZT_CONFIG_D4) strcat(buf, "D4,"); + if (s.linecompat & ZT_CONFIG_CCS) strcat(buf, "CCS,"); + if (s.linecompat & ZT_CONFIG_CRC4) strcat(buf, "CRC4,"); + buf[strlen(buf) - 1] = '\0'; + fprintf(stdout, "%s\n", buf); + fprintf(stdout, "coding="); + if (s.lineconfig & ZT_CONFIG_B8ZS) fprintf(stdout, "B8ZS"); + else if (s.lineconfig & ZT_CONFIG_AMI) fprintf(stdout, "AMI"); + else if (s.lineconfig & ZT_CONFIG_HDB3) fprintf(stdout, "HDB3"); + fprintf(stdout, "\n"); + fprintf(stdout, "framing="); + if (s.lineconfig & ZT_CONFIG_ESF) fprintf(stdout, "ESF"); + else if (s.lineconfig & ZT_CONFIG_D4) fprintf(stdout, "D4"); + else if (s.lineconfig & ZT_CONFIG_CCS) fprintf(stdout, "CCS"); + else if (s.lineconfig & ZT_CONFIG_CRC4) fprintf(stdout, "/CRC4"); + fprintf(stdout, "\n"); + } else { + /* this is an analog span */ + fprintf(stdout, "type=analog\n"); + for (y = basechan; y < (basechan + s.totalchans); y++) { + memset(¶ms, 0, sizeof(params)); + params.channo = y; + if (ioctl(ctl, ZT_GET_PARAMS, ¶ms)) { + fprintf(stdout, "port=%d,unknown\n", y); + continue; + }; + fprintf(stdout, "port=%d,", y); + switch (params.sigcap & (__ZT_SIG_FXO | __ZT_SIG_FXS)) { + case __ZT_SIG_FXO: + fprintf(stdout, "FXS"); + break; + case __ZT_SIG_FXS: + fprintf(stdout, "FXO"); + break; + default: + fprintf(stdout, "none"); + } + if (params.sigcap & ZT_SIG_BROKEN) + fprintf(stdout, " FAILED"); + fprintf(stdout, "\n"); + } + } + + basechan += s.totalchans; + } + + exit(0); +} diff --git a/dahdi_speed.c b/dahdi_speed.c new file mode 100644 index 0000000..7b41d78 --- /dev/null +++ b/dahdi_speed.c @@ -0,0 +1,36 @@ +/* + * + * Generic speed test -- Run an infinite loop and + * see how high we can count (in 5 seconds). You + * can use this to measure how much CPU zaptel REALLY + * is taking. + * + * MUST BE COMPILED WITHOUT OPTIMIZATION + * + */ + +#include <stdio.h> +#include <sys/signal.h> +#include <unistd.h> +#include <stdlib.h> + +static long count=0; + +static void alm(int sig) +{ + printf("Count: %ld\n", count); + exit(0); +} + + +int main(int argc, char *argv[]) +{ + int a=0,b=0,c; + signal(SIGALRM, alm); + alarm(5); + for (;;) { + for (c=0;c<1000;c++) + a = a * b; + count++; + } +} diff --git a/dahdi_test.c b/dahdi_test.c new file mode 100644 index 0000000..1ee1a2c --- /dev/null +++ b/dahdi_test.c @@ -0,0 +1,135 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <sys/time.h> +#include <sys/signal.h> +#include <math.h> +#include <getopt.h> + +#define SIZE 8000 + +static int pass = 0; +static float best = 0.0; +static float worst = 100.0; +static double total = 0.0; +static double delay_total = 0.0; + +void hup_handler(int sig) +{ + printf("\n--- Results after %d passes ---\n", pass); + printf("Best: %.3f -- Worst: %.3f -- Average: %f, Difference: %f\n", + best, worst, pass ? total/pass : 100.00, pass ? delay_total/pass : 100); + exit(0); +} + +static void usage(char *argv0) +{ + char *c; + c = strrchr(argv0, '/'); + if (!c) + c = argv0; + else + c++; + fprintf(stderr, + "Usage: %s [-c COUNT] [-v]\n" + " Valid options are:\n" + " -c COUNT Run just COUNT cycles (otherwise: forever).\n" + " -v More verbose output.\n" + " -h This help text.\n" + ,c); +} + +int main(int argc, char *argv[]) +{ + int fd; + int res; + int c; + int count=0; + int seconds = 0; + int curarg = 1; + int verbose=0; + char buf[8192]; + float score; + float ms; + struct timeval start, now; + fd = open("/dev/zap/pseudo", O_RDWR); + if (fd < 0) { + fprintf(stderr, "Unable to open zap interface: %s\n", strerror(errno)); + exit(1); + } + + while((c = getopt(argc, argv, "c:hv")) != -1) { + switch(c) { + case 'c': + seconds = atoi(optarg); + break; + case 'h': + usage(argv[0]); + exit(0); + break; + case '?': + usage(argv[0]); + exit(1); + break; + case 'v': + verbose++; + break; + } + } + while(curarg < argc) { + if (!strcasecmp(argv[curarg], "-v")) + verbose++; + if (!strcasecmp(argv[curarg], "-c") && argc > curarg) + seconds = atoi(argv[curarg + 1]); + curarg++; + } + printf("Opened pseudo zap interface, measuring accuracy...\n"); + signal(SIGHUP, hup_handler); + signal(SIGINT, hup_handler); + signal(SIGALRM, hup_handler); + /* Flush input buffer */ + for (count = 0;count < 4; count++) + res = read(fd, buf, sizeof(buf)); + count = 0; + ms = 0; /* Makes the compiler happy */ + if (seconds > 0) + alarm(seconds + 1); /* This will give 'seconds' cycles */ + for(;;) { + if (count == 0) + ms = 0; + gettimeofday(&start, NULL); + res = read(fd, buf, sizeof(buf)); + if (res < 0) { + fprintf(stderr, "Failed to read from pseudo interface: %s\n", strerror(errno)); + exit(1); + } + count += res; + gettimeofday(&now, NULL); + ms += (now.tv_sec - start.tv_sec) * 8000; + ms += (now.tv_usec - start.tv_usec) / 125.0; + if (count >= SIZE) { + double percent; + + percent = 100.0 * (count - ms) / count; + if (verbose) + printf("\n%d zaptel samples in %0.3f system clock sample intervals (%.3f%%)", + count, ms, 100 - percent); + else if ((pass % 8) == 7) printf("\n"); + score = 100.0 - fabs(percent); + if (score > best) + best = score; + if (score < worst) + worst = score; + if (!verbose) + printf("%f%% ", score); + total += score; + delay_total += 100 - percent; + fflush(stdout); + count = 0; + pass++; + } + } +} diff --git a/dahdi_tool.c b/dahdi_tool.c new file mode 100644 index 0000000..c1e97bc --- /dev/null +++ b/dahdi_tool.c @@ -0,0 +1,589 @@ +/* + * Configuration program for Zapata Telephony Interface + * + * Written by Mark Spencer <markster@digium.com> + * Based on previous works, designs, and architectures conceived and + * written by Jim Dixon <jim@lambdatel.com>. + * + * Copyright (C) 2001 Jim Dixon / Zapata Telephony. + * Copyright (C) 2001 Linux Support Services, Inc. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under thet erms 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Primary Author: Mark Spencer <markster@digium.com> + * + */ + +/*** MODULEINFO + <depend>newt</depend> + ***/ + +#include <stdio.h> +#include <getopt.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <errno.h> +#include <newt.h> +#ifdef STANDALONE_ZAPATA +#include "kernel/zaptel.h" +#include "tonezone.h" +#else +#include <zaptel/zaptel.h> +#include <zaptel/tonezone.h> +#endif + +static int ctl = -1; +static int span_max_chan_pos; + +static ZT_SPANINFO s[ZT_MAX_SPANS]; + +static char *zt_txlevelnames[] = { +"0 db (CSU)/0-133 feet (DSX-1)", +"133-266 feet (DSX-1)", +"266-399 feet (DSX-1)", +"399-533 feet (DSX-1)", +"533-655 feet (DSX-1)", +"-7.5db (CSU)", +"-15db (CSU)", +"-22.5db (CSU)" +} ; + +static char *alarmstr(int span) +{ + static char alarms[80]; + strcpy(alarms, ""); + if (s[span].alarms > 0) { + if (s[span].alarms & ZT_ALARM_BLUE) + strcat(alarms,"Blue Alarm/"); + if (s[span].alarms & ZT_ALARM_YELLOW) + strcat(alarms, "Yellow Alarm/"); + if (s[span].alarms & ZT_ALARM_RED) + strcat(alarms, "Red Alarm/"); + if (s[span].alarms & ZT_ALARM_LOOPBACK) + strcat(alarms,"Loopback/"); + if (s[span].alarms & ZT_ALARM_RECOVER) + strcat(alarms,"Recovering/"); + if (s[span].alarms & ZT_ALARM_NOTOPEN) + strcat(alarms, "Not Open/"); + if (!strlen(alarms)) + strcat(alarms, "<unknown>/"); + if (strlen(alarms)) { + /* Strip trailing / */ + alarms[strlen(alarms)-1]='\0'; + } + } else + strcpy(alarms, "No alarms."); + return alarms; +} + +static char *getalarms(int span, int err) +{ + int res; + static char tmp[256]; + char alarms[50]; + s[span].spanno = span; + res = ioctl(ctl, ZT_SPANSTAT, &s[span]); + if (res) { + if (err) + fprintf(stderr, "Unable to get span info on span %d: %s\n", span, strerror(errno)); + return NULL; + } + strcpy(alarms, ""); + if (s[span].alarms > 0) { + if (s[span].alarms & ZT_ALARM_BLUE) + strcat(alarms,"BLU/"); + if (s[span].alarms & ZT_ALARM_YELLOW) + strcat(alarms, "YEL/"); + if (s[span].alarms & ZT_ALARM_RED) + strcat(alarms, "RED/"); + if (s[span].alarms & ZT_ALARM_LOOPBACK) + strcat(alarms,"LB/"); + if (s[span].alarms & ZT_ALARM_RECOVER) + strcat(alarms,"REC/"); + if (s[span].alarms & ZT_ALARM_NOTOPEN) + strcat(alarms, "NOP/"); + if (!strlen(alarms)) + strcat(alarms, "UUU/"); + if (strlen(alarms)) { + /* Strip trailing / */ + alarms[strlen(alarms)-1]='\0'; + } + } else { + if (s[span].numchans) + strcpy(alarms, "OK"); + else + strcpy(alarms, "UNCONFIGURED"); + } + + snprintf(tmp, sizeof(tmp), "%-15s %s", alarms, s[span].desc); + return tmp; +} + +static void add_cards(newtComponent spans) +{ + int x; + char *s; + void *prev=NULL; + + if (spans) + prev = newtListboxGetCurrent(spans); + newtListboxClear(spans); + for (x=0;x<ZT_MAX_SPANS;x++) { + s = getalarms(x, 0); + if (s && spans) { + /* Found one! */ + newtListboxAppendEntry(spans, s, (void *)(long)x); + } + } + if (spans) + newtListboxSetCurrentByKey(spans, prev); + +} + +static void sel_callback(newtComponent c, void *cbdata) +{ + int span; + char info[256]; + char info2[256]; + cbdata = newtListboxGetCurrent(c); + if (cbdata) { + span = (long)(cbdata); + snprintf(info, sizeof (info), "Span %d: %d total channels, %d configured", span, s[span].totalchans, s[span].numchans); + snprintf(info2, sizeof(info2), "%-59s F1=Details F10=Quit", info); + } else { + span = -1; + strcpy(info, "There are no zaptel spans on this system."); + snprintf(info2, sizeof(info2), "%-59s F10=Quit", info); + } + newtPopHelpLine(); + newtPushHelpLine(info2); +} + +static void show_bits(int span, newtComponent bitbox, newtComponent inuse, newtComponent levels, newtComponent bpvcount, + newtComponent alarms, newtComponent syncsrc, newtComponent irqmisses) +{ + ZT_PARAMS zp; + int x; + int res; + char c; + char tabits[80]; + char tbbits[80]; + char tcbits[80]; + char tdbits[80]; + char rabits[80]; + char rbbits[80]; + char rcbits[80]; + char rdbits[80]; + char tmp[1024]; + + int use = 0; + + memset(tabits,0, sizeof(tabits)); + memset(tbbits,0, sizeof(tbbits)); + memset(rabits,0, sizeof(rabits)); + memset(rbbits,0, sizeof(rbbits)); + memset(tcbits,0, sizeof(tcbits)); + memset(tdbits,0, sizeof(tdbits)); + memset(rcbits,0, sizeof(rcbits)); + memset(rdbits,0, sizeof(rdbits)); + memset(tabits,32, span_max_chan_pos); + memset(tbbits,32, span_max_chan_pos); + memset(rabits,32, span_max_chan_pos); + memset(rbbits,32, span_max_chan_pos); + memset(tcbits,32, span_max_chan_pos); + memset(tdbits,32, span_max_chan_pos); + memset(rcbits,32, span_max_chan_pos); + memset(rdbits,32, span_max_chan_pos); + + for (x=0;x<ZT_MAX_CHANNELS;x++) { + memset(&zp, 0, sizeof(zp)); + zp.channo = x; + res = ioctl(ctl, ZT_GET_PARAMS, &zp); + if (!res) { + if (zp.spanno == span) { + if (zp.sigtype && (zp.rxbits > -1)) { + if (zp.rxbits & ZT_ABIT) + rabits[zp.chanpos - 1] = '1'; + else + rabits[zp.chanpos - 1] = '0'; + if (zp.rxbits & ZT_BBIT) + rbbits[zp.chanpos - 1] = '1'; + else + rbbits[zp.chanpos - 1] = '0'; + + if (zp.rxbits & ZT_CBIT) + rcbits[zp.chanpos - 1] = '1'; + else + rcbits[zp.chanpos - 1] = '0'; + if (zp.rxbits & ZT_DBIT) + rdbits[zp.chanpos - 1] = '1'; + else + rdbits[zp.chanpos - 1] = '0'; + + if (zp.txbits & ZT_ABIT) + tabits[zp.chanpos - 1] = '1'; + else + tabits[zp.chanpos - 1] = '0'; + if (zp.txbits & ZT_BBIT) + tbbits[zp.chanpos - 1] = '1'; + else + tbbits[zp.chanpos - 1] = '0'; + if (zp.txbits & ZT_CBIT) + tcbits[zp.chanpos - 1] = '1'; + else + tcbits[zp.chanpos - 1] = '0'; + if (zp.txbits & ZT_DBIT) + tdbits[zp.chanpos - 1] = '1'; + else + tdbits[zp.chanpos - 1] = '0'; + } else { + c = '-'; + if (!zp.sigtype) + c = ' '; + tabits[zp.chanpos - 1] = c; + tbbits[zp.chanpos - 1] = c; + tcbits[zp.chanpos - 1] = c; + tdbits[zp.chanpos - 1] = c; + rabits[zp.chanpos - 1] = c; + rbbits[zp.chanpos - 1] = c; + rcbits[zp.chanpos - 1] = c; + rdbits[zp.chanpos - 1] = c; + } + if (zp.rxisoffhook) + use++; + } + } + } + snprintf(tmp, sizeof(tmp), "%s\n%s\n%s\n%s\n\n%s\n%s\n%s\n%s", tabits, tbbits,tcbits,tdbits,rabits,rbbits,rcbits,rdbits); + newtTextboxSetText(bitbox, tmp); + sprintf(tmp, "%3d/%3d/%3d", s[span].totalchans, s[span].numchans, use); + newtTextboxSetText(inuse, tmp); + sprintf(tmp, "%s/", zt_txlevelnames[s[span].txlevel]); + strcat(tmp, zt_txlevelnames[s[span].rxlevel]); + sprintf(tmp, "%3d/%3d", s[span].txlevel, s[span].rxlevel); + newtTextboxSetText(levels, tmp); + sprintf(tmp, "%7d", s[span].bpvcount); + newtTextboxSetText(bpvcount, tmp); + sprintf(tmp, "%7d", s[span].irqmisses); + newtTextboxSetText(irqmisses, tmp); + newtTextboxSetText(alarms, alarmstr(span)); + if (s[span].syncsrc > 0) + strcpy(tmp, s[s[span].syncsrc].desc); + else + strcpy(tmp, "Internally clocked"); + newtTextboxSetText(syncsrc, tmp); + + +} + +static void do_loop(int span, int looped) +{ + newtComponent form; + newtComponent label; + char s1[256]; + struct zt_maintinfo m; + int res; + struct newtExitStruct es; + + newtOpenWindow(20,12,40,4, s[span].desc); + + form = newtForm(NULL, NULL, 0); + m.spanno = span; + if (looped) { + snprintf(s1, sizeof(s1), "Looping UP span %d...\n", span); + m.command = ZT_MAINT_LOOPUP; + } else { + snprintf(s1, sizeof(s1), "Looping DOWN span %d...\n", span); + m.command = ZT_MAINT_LOOPDOWN; + } + + label = newtLabel(3,1,s1); + newtFormAddComponent(form, label); + newtPushHelpLine("Please wait..."); + + newtFormSetTimer(form, 200); + newtFormRun(form, &es); + res = ioctl(ctl, ZT_MAINT, &m); + newtFormDestroy(form); + newtPopWindow(); + newtPopHelpLine(); +} + +static newtComponent spans; +static void show_span(int span) +{ + newtComponent form; + newtComponent back; + newtComponent loop; + newtComponent label; + newtComponent bitbox; + newtComponent inuse; + newtComponent levels; + newtComponent bpvcount; + newtComponent alarms; + newtComponent syncsrc; + newtComponent irqmisses; + + char s1[] = " 1111111111222222222233"; + char s2[] = "1234567890123456789012345678901"; + int x; + int looped = 0; + struct newtExitStruct es; + + void *ss; + char info2[256]; + + if (span < 0) { + /* Display info on a span */ + ss = newtListboxGetCurrent(spans); + if (ss) { + span = (long)(ss); + } + } + + snprintf(info2, sizeof(info2), "%-59s F10=Back", s[span].desc); + newtOpenWindow(10,2,60,20, s[span].desc); + newtPushHelpLine(info2); + + back = newtButton(48,8,"Back"); + loop = newtButton(48,14,"Loop"); + form = newtForm(NULL, NULL, 0); + + newtFormAddComponents(form, back, loop, NULL); + + span_max_chan_pos = s[span].totalchans; + for (x=0;x<ZT_MAX_CHANNELS;x++) { + ZT_PARAMS zp; + int res; + memset(&zp, 0, sizeof(zp)); + zp.channo = x; + res = ioctl(ctl, ZT_GET_PARAMS, &zp); + if (!res && zp.spanno == span && zp.chanpos > span_max_chan_pos ) + span_max_chan_pos = zp.chanpos; + } + + if (span_max_chan_pos > 32) + span_max_chan_pos = 32; + + s1[span_max_chan_pos] = '\0'; + s2[span_max_chan_pos] = '\0'; + + bitbox = newtTextbox(8,10,span_max_chan_pos,9,0); + newtFormAddComponent(form, bitbox); + + label = newtLabel(8,8,s1); + newtFormAddComponent(form, label); + + label = newtLabel(8,9,s2); + newtFormAddComponent(form, label); + + newtFormAddHotKey(form, NEWT_KEY_F10); + newtFormSetTimer(form, 200); + + label = newtLabel(4,10,"TxA"); + newtFormAddComponent(form, label); + + label = newtLabel(4,11,"TxB"); + newtFormAddComponent(form, label); + + label = newtLabel(4,12,"TxC"); + newtFormAddComponent(form, label); + + label = newtLabel(4,13,"TxD"); + newtFormAddComponent(form, label); + + label = newtLabel(4,15,"RxA"); + newtFormAddComponent(form, label); + + label = newtLabel(4,16,"RxB"); + newtFormAddComponent(form, label); + + label = newtLabel(4,17,"RxC"); + newtFormAddComponent(form, label); + + label = newtLabel(4,18,"RxD"); + newtFormAddComponent(form, label); + + + label = newtLabel(4,7,"Total/Conf/Act: "); + newtFormAddComponent(form, label); + + inuse = newtTextbox(24,7,12,1,0); + newtFormAddComponent(form, inuse); + + label = newtLabel(4,6,"Tx/Rx Levels: "); + newtFormAddComponent(form, label); + + levels = newtTextbox(24,6,30,1,0); + newtFormAddComponent(form, levels); + + label = newtLabel(4,5,"Bipolar Viol: "); + newtFormAddComponent(form, label); + + bpvcount = newtTextbox(24,5,30,1,0); + newtFormAddComponent(form, bpvcount); + + label = newtLabel(4,4,"IRQ Misses: "); + newtFormAddComponent(form, label); + + irqmisses = newtTextbox(24,4,30,1,0); + newtFormAddComponent(form, irqmisses); + + label = newtLabel(4,3,"Sync Source: "); + newtFormAddComponent(form, label); + + syncsrc = newtTextbox(24,3,30,1,0); + newtFormAddComponent(form, syncsrc); + + label = newtLabel(4,2,"Current Alarms: "); + newtFormAddComponent(form, label); + + alarms = newtTextbox(24,2,30,1,0); + newtFormAddComponent(form, alarms); + + for(;;) { + /* Wait for user to select something */ + do { + add_cards(NULL); + show_bits(span, bitbox, inuse, levels, bpvcount, alarms, syncsrc, irqmisses); + newtFormRun(form, &es); + } while(es.reason == NEWT_EXIT_TIMER); + switch(es.reason) { + case NEWT_EXIT_COMPONENT: + if (es.u.co == loop) { + looped = !looped; + do_loop(span, looped); + newtFormSetTimer(form, 200); + } + if (es.u.co == back) { + goto out; + } + break; + case NEWT_EXIT_HOTKEY: + switch(es.u.key) { +#if 0 + case NEWT_KEY_F1: + show_span(-1); + break; +#endif + case NEWT_KEY_F10: + goto out; + } + break; + default: + break; + } + } + +out: + newtFormDestroy(form); + newtPopWindow(); + newtPopHelpLine(); + span_max_chan_pos = 0; +} + +static void show_spans(void) +{ + newtComponent form; + newtComponent quit; + newtComponent label; + newtComponent sel; + + + struct newtExitStruct es; + + + quit = newtButton(50,14,"Quit"); + sel = newtButton(10,14,"Select"); + + spans = newtListbox(5, 2, 10, NEWT_FLAG_SCROLL); + newtListboxSetWidth(spans, 65); + + label = newtLabel(5,1,"Alarms Span"); + + newtCenteredWindow(72,18, "Zapata Telephony Interfaces"); + form = newtForm(NULL, NULL, 0); + + newtFormSetTimer(form, 200); + + newtFormAddComponents(form, spans, sel, quit, label, NULL); + + newtComponentAddCallback(spans, sel_callback, NULL); + + newtFormAddHotKey(form, NEWT_KEY_F1); + newtFormAddHotKey(form, NEWT_KEY_F10); + + for(;;) { + /* Wait for user to select something */ + do { + add_cards(spans); + newtFormRun(form, &es); + } while(es.reason == NEWT_EXIT_TIMER); + + switch(es.reason) { + case NEWT_EXIT_COMPONENT: + if (es.u.co == quit) { + /* Quit if appropriate */ + newtFormDestroy(form); + return; + } else if (es.u.co == sel) { + show_span(-1); + } + break; + case NEWT_EXIT_HOTKEY: + switch(es.u.key) { + case NEWT_KEY_F1: + show_span(-1); + break; + case NEWT_KEY_F10: + newtFormDestroy(form); + return; + } + break; + default: + break; + } + } +} + +static void cleanup(void) +{ + newtPopWindow(); +} + +int main(int argc, char *argv[]) +{ + + ctl = open("/dev/zap/ctl", O_RDWR); + if (ctl < 0) { + fprintf(stderr, "Unable to open /dev/zap/ctl: %s\n", strerror(errno)); + exit(1); + } + newtInit(); + newtCls(); + + newtDrawRootText(0,0,"Zaptel Tool (C)2002 Linux Support Services, Inc."); + newtPushHelpLine("Welcome to the Zaptel Tool!"); + show_spans(); + cleanup(); + newtFinished(); + return 0; +} diff --git a/fxotune.c b/fxotune.c new file mode 100644 index 0000000..d7ee9ca --- /dev/null +++ b/fxotune.c @@ -0,0 +1,1110 @@ +/* + * This file and contents thereof are licensed under the terms and + * conditions of the GNU Public License version 2. For more information + * (including terms and conditions) see http://www.gnu.org/ + * + * fxotune.c -- A utility for tuning the various settings on the fxo + * modules for the TDM400 cards. + * + * by Matthew Fredrickson <creslin@digium.com> + * + * (C) 2004-2005 Digium, Inc. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <math.h> +#include <sys/time.h> + +#ifdef STANDALONE_ZAPATA +#include "kernel/zaptel.h" +#else +#include <zaptel/zaptel.h> +#endif +#include "kernel/wctdm.h" +#include "fxotune.h" + +#define TEST_DURATION 2000 +#define BUFFER_LENGTH (2 * TEST_DURATION) +#define SKIP_SAMPLES 800 + +static const float amplitude = 16384.0; + +static char *zappath = "/dev/zap"; +static char *configfile = "/etc/fxotune.conf"; + +static int audio_dump_fd = -1; + +static char *usage = +"Usage: fxotune [-v[vv] (-s | -i <options> | -d <options>)\n" +"\n" +" -s : set previously calibrated echo settings\n" +" -i : calibrate echo settings\n" +" options : [<dialstring>] [-t <calibtype>]\n" +" [-b <startdev>][-e <stopdev>]\n" +" [-n <dialstring>][-l <delaytosilence>][-m <silencegoodfor>]\n" +" -d : dump input and output waveforms to ./fxotune_dump.vals\n" +" options : [-b <device>][-w <waveform>]\n" +" [-n <dialstring>][-l <delaytosilence>][-m <silencegoodfor>]\n" +" -v : more output (-vv, -vvv also)\n" +" -o <path> : Write the received raw 16-bit signed linear audio that is\n" +" used in processing to the file specified by <path>\n" +" -c <config_file>\n" +"\n" +" <calibtype> - type of calibration\n" +" (default 2, old method 1)\n" +" <startdev>\n" +" <stopdev> - defines a range of devices to test\n" +" (default: 1-252)\n" +" <dialstring> - string to dial to clear the line\n" +" (default 5)\n" +" <delaytosilence> - seconds to wait for line to clear (default 0)\n" +" <silencegoodfor> - seconds before line will no longer be clear\n" +" (default 18)\n" +" <device> - the device to perform waveform dump on\n" +" (default 1)\n" +" <waveform> - -1 for multitone waveform, or frequency of\n" +" single tone (default -1)\n" +" <config_file> - Alternative file to set from / calibrate to.\n" +" (Default: /etc/fxotune.conf)\n" +; + + +#define OUT_OF_BOUNDS(x) ((x) < 0 || (x) > 255) + +struct silence_info{ + char *dialstr; + /** fd of device we are working with */ + int device; + /** seconds we should wait after dialing the dialstring before we know for sure we'll have silence */ + int initial_delay; + /** seconds after which a reset should occur */ + int reset_after; + /** time of last reset */ + struct timeval last_reset; +}; + +static short outbuf[TEST_DURATION]; +static int debug = 0; + +static FILE *debugoutfile = NULL; + +static int fxotune_read(int fd, void *buffer, int len) +{ + int res; + + res = read(fd, buffer, len); + + if ((res > 0) && (audio_dump_fd != -1)) { + write(audio_dump_fd, buffer, len); + } + + return res; +} + +/** + * Makes sure that the line is clear. + * Right now, we do this by relying on the user to specify how long after dialing the + * dialstring we can rely on the line being silent (before the telco complains about + * the user not hitting the next digit). + * + * A more robust way to do this would be to actually measure the sound levels on the line, + * but that's a lot more complicated, and this should work. + * + * @return 0 if succesful (no errors), 1 if unsuccesful + */ +static int ensure_silence(struct silence_info *info) +{ + struct timeval tv; + long int elapsedms; + + gettimeofday(&tv, NULL); + + if (info->last_reset.tv_sec == 0) { + /* this is the first request, we will force it to run */ + elapsedms = -1; + } else { + /* this is not the first request, we will compute elapsed time */ + elapsedms = ((tv.tv_sec - info->last_reset.tv_sec) * 1000L + (tv.tv_usec - info->last_reset.tv_usec) / 1000L); + } + if (debug > 4) { + fprintf(stdout, "Reset line request received - elapsed ms = %li / reset after = %ld\n", elapsedms, info->reset_after * 1000L); + } + + if (elapsedms > 0 && elapsedms < info->reset_after * 1000L) + return 0; + + if (debug > 1){ + fprintf(stdout, "Resetting line\n"); + } + + /* do a line reset */ + /* prepare line for silence */ + /* Do line hookstate reset */ + int x = ZT_ONHOOK; + + if (ioctl(info->device, ZT_HOOK, &x)) { + fprintf(stderr, "Unable to hang up fd %d\n", info->device); + return -1; + } + + sleep(2); + x = ZT_OFFHOOK; + if (ioctl(info->device, ZT_HOOK, &x)) { + fprintf(stderr, "Cannot bring fd %d off hook\n", info->device); + return -1; + } + sleep(2); /* Added to ensure that dial can actually takes place */ + + struct zt_dialoperation dop; + memset(&dop, 0, sizeof(dop)); + dop.op = ZT_DIAL_OP_REPLACE; + dop.dialstr[0] = 'T'; + zap_copy_string(dop.dialstr + 1, info->dialstr, sizeof(dop.dialstr)); + + + if (ioctl(info->device, ZT_DIAL, &dop)) { + fprintf(stderr, "Unable to dial!\n"); + return -1; + } + sleep(1); + sleep(info->initial_delay); + + + gettimeofday(&info->last_reset, NULL); + + + return 0; +} + +/** + * Generates a tone of specified frequency. + * + * @param hz the frequency of the tone to be generated + * @param index the current sample + * to begenerated. For a normal waveform you need to increment + * this every time you execute the function. + * + * @return 16bit slinear sample for the specified index + */ +static short inline gentone(int hz, int index) +{ + return amplitude * sin((index * 2.0 * M_PI * hz)/8000); +} + +/* Using DTMF tones for now since they provide good mid band testing + * while not being harmonics of each other */ +static int freqs[] = {697, 770, 941, 1209, 1336, 1633}; +static int freqcount = 6; + +/** + * Generates a waveform of several frequencies. + * + * @param index the current sample + * to begenerated. For a normal waveform you need to increment + * this every time you execute the function. + * + * @return 16bit slinear sample for the specified index + */ +static short inline genwaveform(int index) +{ + int i = 0; + float response = (float)0; + for (i = 0; i < freqcount; i++){ + response += sin((index * 2.0 * M_PI * freqs[i])/8000); + } + + + return amplitude * response / freqcount; +} + + +/** + * Calculates the RMS of the waveform buffer of samples in 16bit slinear format. + * prebuf the buffer of either shorts or floats + * bufsize the number of elements in the prebuf buffer (not the number of bytes!) + * short_format 1 if prebuf points to an array of shorts, 0 if it points to an array of floats + * + * Formula for RMS (http://en.wikipedia.org/wiki/Root_mean_square): + * + * Xrms = sqrt(1/N Sum(x1^2, x2^2, ..., xn^2)) + * + * Note: this isn't really a power calculation - but it gives a good measure of the level of the response + * + * @param prebuf the buffer containing the values to compute + * @param bufsize the size of the buffer + * @param short_format 1 if prebuf contains short values, 0 if it contains float values + */ +static float power_of(void *prebuf, int bufsize, int short_format) +{ + float sum_of_squares = 0; + int numsamples = 0; + float finalanswer = 0; + short *sbuf = (short*)prebuf; + float *fbuf = (float*)prebuf; + int i = 0; + + if (short_format) { + /* idiot proof checks */ + if (bufsize <= 0) + return -1; + + numsamples = bufsize; /* Got rid of divide by 2 - the bufsize parameter should give the number of samples (that's what it does for the float computation, and it should do it here as well) */ + + for (i = 0; i < numsamples; i++) { + sum_of_squares += ((float)sbuf[i] * (float)sbuf[i]); + } + } else { + /* Version for float inputs */ + for (i = 0; i < bufsize; i++) { + sum_of_squares += (fbuf[i] * fbuf[i]); + } + } + + finalanswer = sum_of_squares/(float)bufsize; /* need to divide by the number of elements in the sample for RMS calc */ + + if (finalanswer < 0) { + printf("Error: Final answer negative number %f\n", finalanswer); + return -3; + } + + return sqrtf(finalanswer); +} + +/* + * In an effort to eliminate as much as possible the effect of outside noise, we use principles + * from the Fourier Transform to attempt to calculate the return loss of our signal for each setting. + * + * To begin, we send our output signal out on the line. We then receive back the reflected + * response. In the Fourier Transform, each evenly distributed frequency within the window + * is correlated (multiplied against, then the resulting samples are added together) with + * the real (cos) and imaginary (sin) portions of that frequency base to detect that frequency. + * + * Instead of doing a complete Fourier Transform, we solve the transform for only our signal + * by multiplying the received signal by the real and imaginary portions of our reference + * signal. This then gives us the real and imaginary values that we can use to calculate + * the return loss of the sinusoids that we sent out on the line. This is done by finding + * the magnitude (think polar form) of the vector resulting from the real and imaginary + * portions calculated above. + * + * This essentially filters out any other noise which maybe present on the line which is outside + * the frequencies used in our test multi-tone. + */ +static float db_loss(float measured, float reference) +{ + return 20 * (logf(measured/reference)/logf(10)); +} + +static void one_point_dft(const short *inbuf, int len, int frequency, float *real, float *imaginary) +{ + float myreal = 0, myimag = 0; + int i; + + for (i = 0; i < len; i++) { + myreal += (float) inbuf[i] * cos((i * 2.0 * M_PI * frequency)/8000); + myimag += (float) inbuf[i] * sin((i * 2.0 * M_PI * frequency)/8000); + } + + myimag *= -1; + + *real = myreal / (float) len; + *imaginary = myimag / (float) len; +} + +static float calc_magnitude(short *inbuf, int insamps) +{ + float real, imaginary, magnitude; + float totalmagnitude = 0; + int i; + + for (i = 0; i < freqcount; i++) { + one_point_dft(inbuf, insamps, freqs[i], &real, &imaginary); + magnitude = sqrtf((real * real) + (imaginary * imaginary)); + totalmagnitude += magnitude; + } + + return totalmagnitude; +} + +/** + * dumps input and output buffer contents for the echo test - used to see exactly what's going on + */ +static int maptone(int whichzap, int freq, char *dialstr, int delayuntilsilence) +{ + int i = 0; + int res = 0, x = 0; + struct zt_bufferinfo bi; + short inbuf[TEST_DURATION]; /* changed from BUFFER_LENGTH - this buffer is for short values, so it should be allocated using the length of the test */ + FILE *outfile = NULL; + + outfile = fopen("fxotune_dump.vals", "w"); + if (!outfile) { + fprintf(stdout, "Cannot create fxotune_dump.vals\n"); + return -1; + } + + x = 1; + if (ioctl(whichzap, ZT_SETLINEAR, &x)) { + fprintf(stderr, "Unable to set channel to signed linear mode.\n"); + return -1; + } + + memset(&bi, 0, sizeof(bi)); + if (ioctl(whichzap, ZT_GET_BUFINFO, &bi)) { + fprintf(stderr, "Unable to get buffer information!\n"); + return -1; + } + bi.numbufs = 2; + bi.bufsize = TEST_DURATION; /* KD - changed from BUFFER_LENGTH; */ + bi.txbufpolicy = ZT_POLICY_IMMEDIATE; + bi.rxbufpolicy = ZT_POLICY_IMMEDIATE; + if (ioctl(whichzap, ZT_SET_BUFINFO, &bi)) { + fprintf(stderr, "Unable to set buffer information!\n"); + return -1; + } + + /* Fill the output buffers */ + int leadin = 50; + int trailout = 100; + for (i = 0; i < leadin; i++) + outbuf[i] = 0; + for (; i < TEST_DURATION - trailout; i++){ + outbuf[i] = freq > 0 ? gentone(freq, i) : genwaveform(i); /* if frequency is negative, use a multi-part waveform instead of a single frequency */ + } + for (; i < TEST_DURATION; i++) + outbuf[i] = 0; + + /* Make sure the line is clear */ + struct silence_info sinfo; + memset(&sinfo, 0, sizeof(sinfo)); + sinfo.device = whichzap; + sinfo.dialstr = dialstr; + sinfo.initial_delay = delayuntilsilence; + sinfo.reset_after = 4; /* doesn't matter - we are only running one test */ + + if (ensure_silence(&sinfo)){ + fprintf(stderr, "Unable to get a clear outside line\n"); + return -1; + } + + /* Flush buffers */ + x = ZT_FLUSH_READ | ZT_FLUSH_WRITE | ZT_FLUSH_EVENT; + if (ioctl(whichzap, ZT_FLUSH, &x)) { + fprintf(stderr, "Unable to flush I/O: %s\n", strerror(errno)); + return -1; + } + + /* send data out on line */ + res = write(whichzap, outbuf, BUFFER_LENGTH); /* we are sending a TEST_DURATION length array of shorts (which are 2 bytes each) */ + if (res != BUFFER_LENGTH) { + fprintf(stderr, "Could not write all data to line\n"); + return -1; + } + +retry: + /* read return response */ + res = fxotune_read(whichzap, inbuf, BUFFER_LENGTH); + if (res != BUFFER_LENGTH) { + int x; + + ioctl(whichzap, ZT_GETEVENT, &x); + goto retry; + } + + /* write content of output buffer to debug file */ + float power_result = power_of(inbuf, TEST_DURATION, 1); + float power_waveform = power_of(outbuf, TEST_DURATION, 1); + float echo = power_result/power_waveform; + + fprintf(outfile, "Buffers, freq=%d, outpower=%0.0f, echo=%0.4f\n", freq, power_result, echo); + fprintf(outfile, "Sample, Input (received from the line), Output (sent to the line)\n"); + for (i = 0; i < TEST_DURATION; i++){ + fprintf(outfile, "%d, %d, %d\n", + i, + inbuf[i], + outbuf[i] + ); + } + + fclose(outfile); + + fprintf(stdout, "echo ratio = %0.4f (%0.1f / %0.1f)\n", echo, power_result, power_waveform); + + return 0; +} + + + +/** + * Perform calibration type 2 on the specified device + * + * Determine optimum echo coefficients for the specified device + * + * New tuning strategy. If we have a number that we can dial that will result in silence from the + * switch, the tune will be *much* faster (we don't have to keep hanging up and dialing a digit, etc...) + * The downside is that the user needs to actually find a 'no tone' phone number at their CO's switch - but for + * really fixing echo problems, this is what it takes. + * + * Also, for the purposes of optimizing settings, if we pick a single frequency and test with that, + * we can try a whole bunch of impedence/echo coefficients. This should give better results than trying + * a bunch of frequencies, and we can always do a a frequency sweep to pick between the best 3 or 4 + * impedence/coefficients configurations. + * + * Note: It may be possible to take this even further and do some pertubation analysis on the echo coefficients + * themselves (maybe use the 72 entry sweep to find some settings that are close to working well, then + * deviate the coefficients a bit to see if we can improve things). A better way to do this would be to + * use the optimization strategy from silabs. For reference, here is an application note that describes + * the echo coefficients (and acim values): + * + * http://www.silabs.com/public/documents/tpub_doc/anote/Wireline/Silicon_DAA/en/an84.pdf + * + */ +static int acim_tune2(int whichzap, int freq, char *dialstr, int delayuntilsilence, int silencegoodfor, struct wctdm_echo_coefs *coefs_out) +{ + int i = 0; + int res = 0, x = 0; + int lowesttry = -1; + float lowesttryresult = 999999999999.0; + float lowestecho = 999999999999.0;; + struct zt_bufferinfo bi; + + short inbuf[TEST_DURATION * 2]; + + if (debug && !debugoutfile) { + if (!(debugoutfile = fopen("fxotune.vals", "w"))) { + fprintf(stdout, "Cannot create fxotune.vals\n"); + return -1; + } + } + + /* Set echo settings */ + if (ioctl(whichzap, WCTDM_SET_ECHOTUNE, &echo_trys[0])) { + fprintf(stderr, "Unable to set impedance on fd %d\n", whichzap); + return -1; + } + + x = 1; + if (ioctl(whichzap, ZT_SETLINEAR, &x)) { + fprintf(stderr, "Unable to set channel to signed linear mode.\n"); + return -1; + } + + memset(&bi, 0, sizeof(bi)); + if (ioctl(whichzap, ZT_GET_BUFINFO, &bi)) { + fprintf(stderr, "Unable to get buffer information!\n"); + return -1; + } + bi.numbufs = 2; + bi.bufsize = BUFFER_LENGTH; + bi.txbufpolicy = ZT_POLICY_IMMEDIATE; + bi.rxbufpolicy = ZT_POLICY_IMMEDIATE; + if (ioctl(whichzap, ZT_SET_BUFINFO, &bi)) { + fprintf(stderr, "Unable to set buffer information!\n"); + return -1; + } + x = ZT_OFFHOOK; + if (ioctl(whichzap, ZT_HOOK, &x)) { + fprintf(stderr, "Cannot bring fd %d off hook", whichzap); + return -1; + } + + + /* Set up silence settings */ + struct silence_info sinfo; + memset(&sinfo, 0, sizeof(sinfo)); + sinfo.device = whichzap; + sinfo.dialstr = dialstr; + sinfo.initial_delay = delayuntilsilence; + sinfo.reset_after = silencegoodfor; + + /* Fill the output buffers */ + for (i = 0; i < TEST_DURATION; i++) + outbuf[i] = freq > 0 ? gentone(freq, i) : genwaveform(i); /* if freq is negative, use a multi-frequency waveform */ + + /* compute power of input (so we can later compute echo levels relative to input) */ + float waveform_power = calc_magnitude(outbuf, TEST_DURATION); + + + /* sweep through the various coefficient settings and see how our responses look */ + + int echo_trys_size = 72; + int trys = 0; + for (trys = 0; trys < echo_trys_size; trys++){ + + /* ensure silence on the line */ + if (ensure_silence(&sinfo)){ + fprintf(stderr, "Unable to get a clear outside line\n"); + return -1; + } + + if (ioctl(whichzap, WCTDM_SET_ECHOTUNE, &echo_trys[trys])) { + fprintf(stderr, "Unable to set echo coefficients on fd %d\n", whichzap); + return -1; + } + + /* Flush buffers */ + x = ZT_FLUSH_READ | ZT_FLUSH_WRITE | ZT_FLUSH_EVENT; + if (ioctl(whichzap, ZT_FLUSH, &x)) { + fprintf(stderr, "Unable to flush I/O: %s\n", strerror(errno)); + return -1; + } + + /* send data out on line */ + res = write(whichzap, outbuf, BUFFER_LENGTH); + if (res != BUFFER_LENGTH) { + fprintf(stderr, "Could not write all data to line\n"); + return -1; + } + +retry: + /* read return response */ + res = fxotune_read(whichzap, inbuf, BUFFER_LENGTH * 2); + if (res != BUFFER_LENGTH * 2) { + int x; + + ioctl(whichzap, ZT_GETEVENT, &x); + goto retry; + } + + float freq_result = calc_magnitude(inbuf, TEST_DURATION * 2); + float echo = db_loss(freq_result, waveform_power); + +#if 0 + if (debug > 0) + fprintf(stdout, "%3d,%d,%d,%d,%d,%d,%d,%d,%d: magnitude = %0.0f, echo = %0.4f dB\n", + echo_trys[trys].acim, echo_trys[trys].coef1, echo_trys[trys].coef2, + echo_trys[trys].coef3, echo_trys[trys].coef4, echo_trys[trys].coef5, + echo_trys[trys].coef6, echo_trys[trys].coef7, echo_trys[trys].coef8, + freq_result, echo); +#endif + + if (freq_result < lowesttryresult){ + lowesttry = trys; + lowesttryresult = freq_result; + lowestecho = echo; + } + if (debug) { + char result[256]; + snprintf(result, sizeof(result), "%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%f,%f", + echo_trys[trys].acim, + echo_trys[trys].coef1, + echo_trys[trys].coef2, + echo_trys[trys].coef3, + echo_trys[trys].coef4, + echo_trys[trys].coef5, + echo_trys[trys].coef6, + echo_trys[trys].coef7, + echo_trys[trys].coef8, + freq_result, + echo + ); + + fprintf(debugoutfile, "%s\n", result); + fprintf(stdout, "%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d: magnitude = %0.0f, echo = %0.4f dB\n", + echo_trys[trys].acim, echo_trys[trys].coef1, echo_trys[trys].coef2, + echo_trys[trys].coef3, echo_trys[trys].coef4, echo_trys[trys].coef5, + echo_trys[trys].coef6, echo_trys[trys].coef7, echo_trys[trys].coef8, + freq_result, echo); + } + } + + if (debug > 0) + fprintf(stdout, "Config with lowest response = %d, magnitude = %0.0f, echo = %0.4f dB\n", lowesttry, lowesttryresult, lowestecho); + + memcpy(coefs_out, &echo_trys[lowesttry], sizeof(struct wctdm_echo_coefs)); + + return 0; +} + +/** + * Perform calibration type 1 on the specified device. Only tunes the line impedance. Look for best response range + */ +static int acim_tune(int whichzap, char *dialstr, int delayuntilsilence, int silencegoodfor, struct wctdm_echo_coefs *coefs_out) +{ + int i = 0, freq = 0, acim = 0; + int res = 0, x = 0; + struct zt_bufferinfo bi; + struct wctdm_echo_coefs coefs; + short inbuf[TEST_DURATION]; /* changed from BUFFER_LENGTH - this buffer is for short values, so it should be allocated using the length of the test */ + int lowest = 0; + FILE *outfile = NULL; + float acim_results[16]; + + + if (debug) { + outfile = fopen("fxotune.vals", "w"); + if (!outfile) { + fprintf(stdout, "Cannot create fxotune.vals\n"); + return -1; + } + } + + /* Set up silence settings */ + struct silence_info sinfo; + memset(&sinfo, 0, sizeof(sinfo)); + sinfo.device = whichzap; + sinfo.dialstr = dialstr; + sinfo.initial_delay = delayuntilsilence; + sinfo.reset_after = silencegoodfor; + + /* Set echo settings */ + memset(&coefs, 0, sizeof(coefs)); + if (ioctl(whichzap, WCTDM_SET_ECHOTUNE, &coefs)) { + fprintf(stdout, "Skipping non-TDM / non-FXO\n"); + return -1; + } + + x = 1; + if (ioctl(whichzap, ZT_SETLINEAR, &x)) { + fprintf(stderr, "Unable to set channel to signed linear mode.\n"); + return -1; + } + + memset(&bi, 0, sizeof(bi)); + if (ioctl(whichzap, ZT_GET_BUFINFO, &bi)) { + fprintf(stderr, "Unable to get buffer information!\n"); + return -1; + } + bi.numbufs = 2; + bi.bufsize = BUFFER_LENGTH; + bi.txbufpolicy = ZT_POLICY_IMMEDIATE; + bi.rxbufpolicy = ZT_POLICY_IMMEDIATE; + if (ioctl(whichzap, ZT_SET_BUFINFO, &bi)) { + fprintf(stderr, "Unable to set buffer information!\n"); + return -1; + } + + for (acim = 0; acim < 16; acim++) { + float freq_results[15]; + + coefs.acim = acim; + if (ioctl(whichzap, WCTDM_SET_ECHOTUNE, &coefs)) { + fprintf(stderr, "Unable to set impedance on fd %d\n", whichzap); + return -1; + } + + for (freq = 200; freq <=3000; freq+=200) { + /* Fill the output buffers */ + for (i = 0; i < TEST_DURATION; i++) + outbuf[i] = gentone(freq, i); + + /* Make sure line is ready for next test iteration */ + if (ensure_silence(&sinfo)){ + fprintf(stderr, "Unable to get a clear line\n"); + return -1; + } + + + /* Flush buffers */ + x = ZT_FLUSH_READ | ZT_FLUSH_WRITE | ZT_FLUSH_EVENT; + if (ioctl(whichzap, ZT_FLUSH, &x)) { + fprintf(stderr, "Unable to flush I/O: %s\n", strerror(errno)); + return -1; + } + + /* send data out on line */ + res = write(whichzap, outbuf, BUFFER_LENGTH); + if (res != BUFFER_LENGTH) { + fprintf(stderr, "Could not write all data to line\n"); + return -1; + } + + /* read return response */ +retry: + /* read return response */ + res = fxotune_read(whichzap, inbuf, BUFFER_LENGTH); + if (res != BUFFER_LENGTH) { + int x; + + ioctl(whichzap, ZT_GETEVENT, &x); + goto retry; + } + + /* calculate power of response */ + + freq_results[(freq/200)-1] = power_of(inbuf+SKIP_SAMPLES, TEST_DURATION-SKIP_SAMPLES, 1); /* changed from inbuf+SKIP_BYTES, BUFFER_LENGTH-SKIP_BYTES, 1 */ + if (debug) fprintf(outfile, "%d,%d,%f\n", acim, freq, freq_results[(freq/200)-1]); + } + acim_results[acim] = power_of(freq_results, 15, 0); + } + + if (debug) { + for (i = 0; i < 16; i++) + fprintf(outfile, "acim_results[%d] = %f\n", i, acim_results[i]); + } + /* Find out what the "best" impedance is for the line */ + lowest = 0; + for (i = 0; i < 16; i++) { + if (acim_results[i] < acim_results[lowest]) { + lowest = i; + } + } + + coefs_out->acim = lowest; + coefs_out->coef1 = 0; + coefs_out->coef2 = 0; + coefs_out->coef3 = 0; + coefs_out->coef4 = 0; + coefs_out->coef5 = 0; + coefs_out->coef6 = 0; + coefs_out->coef7 = 0; + coefs_out->coef8 = 0; + + return 0; +} + +/** + * Reads echo register settings from the configuration file and pushes them into + * the appropriate devices + * + * @param configfilename the path of the file that the calibration results should be written to + * + * @return 0 if successful, !0 otherwise + */ +static int do_set(char *configfilename) +{ + FILE *fp = NULL; + int res = 0; + int fd = 0; + + fp = fopen(configfile, "r"); + + if (!fp) { + fprintf(stdout, "Cannot open %s!\n",configfile); + return -1; + } + + + while (res != EOF) { + struct wctdm_echo_coefs mycoefs; + char completezappath[56] = ""; + int myzap,myacim,mycoef1,mycoef2,mycoef3,mycoef4,mycoef5,mycoef6,mycoef7,mycoef8; + + + res = fscanf(fp, "%d=%d,%d,%d,%d,%d,%d,%d,%d,%d",&myzap,&myacim,&mycoef1, + &mycoef2,&mycoef3,&mycoef4,&mycoef5,&mycoef6,&mycoef7, + &mycoef8); + + if (res == EOF) { + break; + } + + /* Check to be sure conversion is done correctly */ + if (OUT_OF_BOUNDS(myacim) || OUT_OF_BOUNDS(mycoef1)|| + OUT_OF_BOUNDS(mycoef2)|| OUT_OF_BOUNDS(mycoef3)|| + OUT_OF_BOUNDS(mycoef4)|| OUT_OF_BOUNDS(mycoef5)|| + OUT_OF_BOUNDS(mycoef6)|| OUT_OF_BOUNDS(mycoef7)|| OUT_OF_BOUNDS(mycoef8)) { + + fprintf(stdout, "Bounds check error on inputs from %s:%d\n", configfile, myzap); + return -1; + } + + mycoefs.acim = myacim; + mycoefs.coef1 = mycoef1; + mycoefs.coef2 = mycoef2; + mycoefs.coef3 = mycoef3; + mycoefs.coef4 = mycoef4; + mycoefs.coef5 = mycoef5; + mycoefs.coef6 = mycoef6; + mycoefs.coef7 = mycoef7; + mycoefs.coef8 = mycoef8; + + snprintf(completezappath, sizeof(completezappath), "%s/%d", zappath, myzap); + fd = open(completezappath, O_RDWR); + + if (fd < 0) { + fprintf(stdout, "open error on %s: %s\n", completezappath, strerror(errno)); + return -1; + } + + if (ioctl(fd, WCTDM_SET_ECHOTUNE, &mycoefs)) { + fprintf(stdout, "%s: %s\n", completezappath, strerror(errno)); + return -1; + } + + close(fd); + } + + fclose(fp); + + if (debug) + fprintf(stdout, "fxotune: successfully set echo coeffecients on FXO modules\n"); + return 0; +} + +/** + * Output waveform information from a single test + * + * Clears the line, then sends a single waveform (multi-tone, or single tone), and listens + * for the response on the line. Output is written to fxotune_dump.vals + * + * @param startdev the device to test + * @param dialstr the string that should be dialed to clear the dialtone from the line + * @param delayuntilsilence the number of seconds to wait after dialing dialstr before starting the test + * @param silencegoodfor the number of seconds that the test can run before having to reset the line again + * (this is basically the amount of time it takes before the 'if you'd like to make a call...' message + * kicks in after you dial dialstr. This test is so short that the value is pretty much ignored. + * @param waveformtype the type of waveform to use - -1 = multi-tone waveform, otherwise the specified value + * is used as the frequency of a single tone. A value of 0 will output silence. + */ +static int do_dump(int startdev, char* dialstr, int delayuntilsilence, int silencegoodfor, int waveformtype) +{ + int res = 0; + int fd; + char zapdev[80] = ""; + + int zapmodule = startdev; + snprintf(zapdev, sizeof(zapdev), "%s/%d", zappath, zapmodule); + + fd = open(zapdev, O_RDWR); + if (fd < 0) { + fprintf(stdout, "%s absent: %s\n", zapdev, strerror(errno)); + return -1; + } + + fprintf(stdout, "Dumping module %s\n", zapdev); + res = maptone(fd, waveformtype, dialstr, delayuntilsilence); + + close(fd); + + if (res) { + fprintf(stdout, "Failure!\n"); + return res; + } else { + fprintf(stdout, "Done!\n"); + return 0; + } + +} + +/** + * Performs calibration on all specified devices + * + * @param startdev the first device to check + * @param enddev the last device to check + * @param calibtype the type of calibration to perform. 1=old style (loops through individual frequencies + * doesn't optimize echo coefficients. 2=new style (uses multi-tone and optimizes echo coefficients + * and acim setting) + * @param configfilename the path of the file that the calibration results should be written to + * @param dialstr the string that should be dialed to clear the dialtone from the line + * @param delayuntilsilence the number of seconds to wait after dialing dialstr before starting the test + * @param silencegoodfor the number of seconds that the test can run before having to reset the line again + * (this is basically the amount of time it takes before the 'if you'd like to make a call...' message + * kicks in after you dial dialstr + * + * @return 0 if successful, !0 otherwise + */ +static int do_calibrate(int startdev, int enddev, int calibtype, char* configfilename, char* dialstr, int delayuntilsilence, int silencegoodfor) +{ + int problems = 0; + int res = 0; + int configfd, fd; + int devno = 0; + char zapdev[80] = ""; + struct wctdm_echo_coefs coefs; + + configfd = open(configfile, O_CREAT|O_TRUNC|O_WRONLY, 0666); + + if (configfd < 0) { + fprintf(stderr, "Cannot generate config file %s: open: %s\n", configfile, strerror(errno)); + return -1; + } + + for (devno = startdev; devno <= enddev; devno++) { + snprintf(zapdev, sizeof(zapdev), "%s/%d", zappath, devno); + + fd = open(zapdev, O_RDWR); + if (fd < 0) { + fprintf(stdout, "%s absent: %s\n", zapdev, strerror(errno)); + continue; + } + + fprintf(stdout, "Tuning module %s\n", zapdev); + + if (1 == calibtype) + res = acim_tune(fd, dialstr, delayuntilsilence, silencegoodfor, &coefs); + else + res = acim_tune2(fd, -1, dialstr, delayuntilsilence, silencegoodfor, &coefs); + + close(fd); + + if (res) { + fprintf(stdout, "Failure!\n"); + problems++; + } else { + fprintf(stdout, "Done!\n"); + } + + if (res == 0) { + + /* Do output to file */ + int len = 0; + static char output[255] = ""; + + snprintf(output, sizeof(output), "%d=%d,%d,%d,%d,%d,%d,%d,%d,%d\n", + devno, + coefs.acim, + coefs.coef1, + coefs.coef2, + coefs.coef3, + coefs.coef4, + coefs.coef5, + coefs.coef6, + coefs.coef7, + coefs.coef8 + ); + + if (debug) + fprintf(stdout, "Found best echo coefficients: %s\n", output); + + len = strlen(output); + res = write(configfd, output, strlen(output)); + if (res != len) { + fprintf(stdout, "Unable to write line \"%s\" to file.\n", output); + return -1; + } + } + } + + close(configfd); + + if (problems) + fprintf(stdout, "Unable to tune %d devices, even though those devices are present\n", problems); + + return problems; +} + +int main(int argc , char **argv) +{ + int startdev = 1; /* -b */ + int stopdev = 252; /* -e */ + int calibtype = 2; /* -t */ + int waveformtype = -1; /* -w multi-tone by default. If > 0, single tone of specified frequency */ + int delaytosilence = 0; /* -l */ + int silencegoodfor = 18; /* -m */ + + char* dialstr = "5"; /* -n */ + + int res = 0; + + int doset = 0; /* -s */ + int docalibrate = 0; /* -i <dialstr> */ + int dodump = 0; /* -d */ + + int i = 0; + + for (i = 1; i < argc; i++){ + if (!(argv[i][0] == '-' || argv[i][0] == '/') || (strlen(argv[i]) <= 1)){ + fprintf(stdout, "Unknown option : %s\n", argv[i]); + /* Show usage */ + fputs(usage, stdout); + return -1; + } + int moreargs = (i < argc - 1); + + switch(argv[i][1]){ + case 's': + doset=1; + continue; + case 'i': + docalibrate = 1; + if (moreargs){ /* we need to check for a value after 'i' for backwards compatability with command line options of old fxotune */ + if (argv[i+1][0] != '-' && argv[i+1][0] != '/') + dialstr = argv[++i]; + } + continue; + case 'c': + configfile = moreargs ? argv[++i] : configfile; + continue; + case 'd': + dodump = 1; + continue; + case 'b': + startdev = moreargs ? atoi(argv[++i]) : startdev; + break; + case 'e': + stopdev = moreargs ? atoi(argv[++i]) : stopdev; + break; + case 't': + calibtype = moreargs ? atoi(argv[++i]) : calibtype; + break; + case 'w': + waveformtype = moreargs ? atoi(argv[++i]) : waveformtype; + break; + case 'l': + delaytosilence = moreargs ? atoi(argv[++i]) : delaytosilence; + break; + case 'm': + silencegoodfor = moreargs ? atoi(argv[++i]) : silencegoodfor; + break; + case 'n': + dialstr = moreargs ? argv[++i] : dialstr; + break; + case 'v': + debug = strlen(argv[i])-1; + break; + case 'o': + if (moreargs) { + audio_dump_fd = open(argv[++i], O_WRONLY|O_CREAT|O_TRUNC, 0666); + if (audio_dump_fd == -1) { + fprintf(stdout, "Unable to open file %s: %s\n", argv[i], strerror(errno)); + return -1; + } + break; + } else { + fprintf(stdout, "No path supplied to -o option!\n"); + return -1; + } + default: + fprintf(stdout, "Unknown option : %s\n", argv[i]); + /* Show usage */ + fputs(usage, stdout); + return -1; + + } + } + + if (debug > 3){ + fprintf(stdout, "Running with parameters:\n"); + fprintf(stdout, "\tdoset=%d\n", doset); + fprintf(stdout, "\tdocalibrate=%d\n", docalibrate); + fprintf(stdout, "\tdodump=%d\n", dodump); + fprintf(stdout, "\tstartdev=%d\n", startdev); + fprintf(stdout, "\tstopdev=%d\n", stopdev); + fprintf(stdout, "\tcalibtype=%d\n", calibtype); + fprintf(stdout, "\twaveformtype=%d\n", waveformtype); + fprintf(stdout, "\tdelaytosilence=%d\n", delaytosilence); + fprintf(stdout, "\tsilencegoodfor=%d\n", silencegoodfor); + fprintf(stdout, "\tdialstr=%s\n", dialstr); + fprintf(stdout, "\tdebug=%d\n", debug); + } + + if (docalibrate){ + res = do_calibrate(startdev, stopdev, calibtype, configfile, dialstr, delaytosilence, silencegoodfor); + if (!res) + return do_set(configfile); + else + return -1; + } + + if (doset) + return do_set(configfile); + + if (dodump){ + res = do_dump(startdev, dialstr, delaytosilence, silencegoodfor, waveformtype); + if (!res) + return 0; + else + return -1; + } + + fputs(usage, stdout); + return -1; +} diff --git a/fxotune.h b/fxotune.h new file mode 100644 index 0000000..093f03e --- /dev/null +++ b/fxotune.h @@ -0,0 +1,110 @@ +/* + * This file and contents thereof are distributed under the terms of the + * GNU Public License version 2. For terms and conditions of this license + * see http://www.gnu.org + * + * fxotune.h -- data structures and associated definitions for fxotune.c + * + * By Matthew Fredrickson <creslin@digium.com> + * + * Echo coefficients and acim register values taken from AN84 from Silicon + * Laboratories app note AN84 for setting echo cancellation coefficients + * + * (C) 2005 Digium, Inc. + */ + +struct wctdm_echo_coefs echo_trys [] = +{ + /* 600 ohm echo settings */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 10, 0, 6, 1, 254, 2, 255, 0, 0}, + { 3, 255, 255, 0, 1, 0, 0, 0, 0}, + { 3, 1, 253, 253, 2, 255, 0, 0, 0}, + { 9, 254, 251, 255, 2, 0, 1, 0, 0}, + { 5, 3, 251, 250, 2, 254, 0, 0, 255}, + { 8, 253, 2, 244, 255, 10, 244, 3, 253}, + { 10, 249, 244, 8, 12, 245, 252, 0, 1}, + + /* 900 ohm echo settings */ + { 1, 0, 0, 0, 0, 0, 0, 0, 0}, + { 10, 252, 255, 1, 255, 0, 0, 0, 0}, + { 7, 255, 251, 251, 2, 255, 255, 1, 255}, + { 3, 1, 251, 250, 1, 254, 255, 0, 255}, + { 5, 252, 250, 0, 0, 255, 1, 0, 0}, + { 5, 3, 251, 250, 1, 253, 0, 0, 255}, + { 8, 253, 2, 244, 255, 10, 244, 3, 253}, + { 10, 249, 244, 8, 12, 245, 252, 0, 1}, + + /* 270 ohm + (750 ohm || 150 nF) (CTR21) */ + { 2, 0, 0, 0, 0, 0, 0, 0, 0}, + { 7, 0, 0, 255, 254, 0, 0, 0, 0}, + { 9, 0, 253, 254, 2, 255, 0, 0, 0}, + { 5, 1, 249, 254, 4, 253, 1, 0, 0}, + { 5, 252, 250, 1, 1, 254, 0, 255, 0}, + { 5, 3, 251, 250, 2, 253, 255, 255, 255}, + { 8, 253, 2, 244, 255, 10, 244, 3, 253}, + { 10, 249, 244, 8, 12, 245, 252, 0, 1}, + + /* 220 ohm + (820 ohm || 120 nF) (Australia/NewZealand) and 220 ohm + (820 ohm + * || 115nF) (Slovakia/SAfrica/Germany/Austria/Bulgaria) + */ + { 3, 0, 0, 0, 0, 0, 0, 0, 0}, + { 7, 0, 255, 254, 255, 0, 255, 0, 0}, + { 9, 0, 253, 253, 1, 255, 0, 0, 0}, + { 5, 1, 249, 254, 3, 253, 1, 0, 0}, + { 5, 252, 250, 1, 1, 254, 0, 255, 0}, + { 5, 3, 251, 251, 2, 253, 255, 255, 255}, + { 8, 253, 2, 244, 255, 10, 244, 3, 253}, + { 10, 249, 244, 8, 12, 245, 252, 0, 1}, + + /* 370 ohm + (620ohm || 310nF) (New Zealand #2/India) CO Termination */ + { 4, 0, 0, 0, 0, 0, 0, 0, 0}, + { 9, 255, 1, 4, 0, 0, 1, 255, 0}, + { 9, 0, 253, 0, 3, 254, 0, 0, 255}, + { 9, 2, 250, 253, 5, 253, 1, 0 ,255}, + { 5, 252, 250, 1, 2, 255, 0 ,255, 0}, + { 5, 3, 251, 250, 3, 254, 255, 255, 255}, + { 8, 253, 2, 244, 255, 10, 244, 3, 253}, + { 10, 249, 244, 8, 12, 245, 252, 0, 1}, + + /* 320 ohm + (1050ohm || 230 nF) (England) CO Termination */ + { 5, 0, 0, 0, 0, 0, 0, 0, 0}, + { 9, 0 ,255, 1, 255, 255, 0, 255, 0}, + { 5, 255, 252, 0, 2, 254, 0, 255, 255}, + { 9, 2, 250, 253, 4, 252, 0, 255, 255}, + { 5, 252, 250, 1, 1, 254, 0 ,255, 255}, + { 5, 3, 251, 250, 2, 253, 255, 255, 254}, + { 3, 1, 1, 242, 2, 9, 245, 3, 253}, + { 10, 249, 244, 8, 12, 245, 252, 0, 1}, + + /* 370 ohm + (820 ohm || 110 nF) CO Termination */ + { 6, 0, 0, 0, 0, 0, 0, 0, 0}, + { 6, 1, 254, 253, 0, 255, 0, 0, 0}, + { 9, 0, 251, 252, 2, 255, 0, 0, 0}, + { 5, 1, 248, 252, 4, 253, 1, 0, 0}, + { 5, 252, 250, 0, 0, 254, 0 , 255, 0}, + { 5, 3, 251, 250, 2, 253, 255, 255, 254}, + { 3, 1, 1, 242, 2, 9, 245, 3, 253}, + { 10, 249, 244, 8, 12, 245, 252, 0, 1}, + + /* 275 ohm + (780 ohm || 115 nF) CO Termination */ + { 7, 0, 0, 0, 0, 0, 0, 0, 0}, + { 7, 255, 255, 255, 255, 0, 0, 0, 0}, + { 9, 0, 253, 254, 2, 255, 0, 0, 0}, + { 5, 1, 249, 254, 4, 253, 1, 0, 0}, + { 5, 252, 250, 1, 1, 254, 0, 255, 0}, + { 5, 3, 251, 250, 2, 253, 255, 255, 255}, + { 8, 253, 2, 244, 255, 10, 244, 3, 253}, + { 10, 249, 244, 8, 12, 245, 252, 0, 1}, + + /* Make sure we include the rest of the impedances */ + { 8, 0, 0, 0, 0, 0, 0, 0, 0}, + { 9, 0, 0, 0, 0, 0, 0, 0, 0}, + { 10, 0, 0, 0, 0, 0, 0, 0, 0}, + { 11, 0, 0, 0, 0, 0, 0, 0, 0}, + { 12, 0, 0, 0, 0, 0, 0, 0, 0}, + { 13, 0, 0, 0, 0, 0, 0, 0, 0}, + { 14, 0, 0, 0, 0, 0, 0, 0, 0}, + { 15, 0, 0, 0, 0, 0, 0, 0, 0}, +}; + diff --git a/fxstest.c b/fxstest.c new file mode 100644 index 0000000..ad1e4e6 --- /dev/null +++ b/fxstest.c @@ -0,0 +1,144 @@ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include "kernel/zaptel.h" +#include "tonezone.h" +#include "kernel/wctdm.h" + +static int tones[] = { + ZT_TONE_DIALTONE, + ZT_TONE_BUSY, + ZT_TONE_RINGTONE, + ZT_TONE_CONGESTION, + ZT_TONE_DIALRECALL, +}; + +int main(int argc, char *argv[]) +{ + int fd; + int res; + int x; + if (argc < 3) { + fprintf(stderr, "Usage: fxstest <zap device> <cmd>\n" + " where cmd is one of:\n" + " stats - reports voltages\n" + " regdump - dumps ProSLIC registers\n" + " tones - plays a series of tones\n" + " polarity - tests polarity reversal\n" + " ring - rings phone\n"); + exit(1); + } + fd = open(argv[1], O_RDWR); + if (fd < 0) { + fprintf(stderr, "Unable to open %s: %s\n", argv[1], strerror(errno)); + exit(1); + } + if (!strcasecmp(argv[2], "ring")) { + fprintf(stderr, "Ringing phone...\n"); + x = ZT_RING; + res = ioctl(fd, ZT_HOOK, &x); + if (res) { + fprintf(stderr, "Unable to ring phone...\n"); + } else { + fprintf(stderr, "Phone is ringing...\n"); + sleep(2); + } + } else if (!strcasecmp(argv[2], "polarity")) { + fprintf(stderr, "Twiddling polarity...\n"); + x = 0; + res = ioctl(fd, ZT_SETPOLARITY, &x); + if (res) { + fprintf(stderr, "Unable to polarity...\n"); + } else { + fprintf(stderr, "Polarity is forward...\n"); + sleep(2); + x = 1; + ioctl(fd, ZT_SETPOLARITY, &x); + fprintf(stderr, "Polarity is reversed...\n"); + sleep(5); + x = 0; + ioctl(fd, ZT_SETPOLARITY, &x); + fprintf(stderr, "Polarity is forward...\n"); + sleep(2); + } + } else if (!strcasecmp(argv[2], "tones")) { + int x = 0; + for (;;) { + res = tone_zone_play_tone(fd, tones[x]); + if (res) + fprintf(stderr, "Unable to play tone %d\n", tones[x]); + sleep(3); + x=(x+1) % (sizeof(tones) / sizeof(tones[0])); + } + } else if (!strcasecmp(argv[2], "stats")) { + struct wctdm_stats stats; + res = ioctl(fd, WCTDM_GET_STATS, &stats); + if (res) { + fprintf(stderr, "Unable to get stats on channel %s\n", argv[1]); + } else { + printf("TIP: %7.4f Volts\n", (float)stats.tipvolt / 1000.0); + printf("RING: %7.4f Volts\n", (float)stats.ringvolt / 1000.0); + printf("VBAT: %7.4f Volts\n", (float)stats.batvolt / 1000.0); + } + } else if (!strcasecmp(argv[2], "regdump")) { + struct wctdm_regs regs; + int numregs = NUM_REGS; + memset(®s, 0, sizeof(regs)); + res = ioctl(fd, WCTDM_GET_REGS, ®s); + if (res) { + fprintf(stderr, "Unable to get registers on channel %s\n", argv[1]); + } else { + for (x=60;x<NUM_REGS;x++) { + if (regs.direct[x]) + break; + } + if (x == NUM_REGS) + numregs = 60; + printf("Direct registers: \n"); + for (x=0;x<numregs;x++) { + printf("%3d. %02x ", x, regs.direct[x]); + if ((x % 8) == 7) + printf("\n"); + } + if (numregs == NUM_REGS) { + printf("\n\nIndirect registers: \n"); + for (x=0;x<NUM_INDIRECT_REGS;x++) { + printf("%3d. %04x ", x, regs.indirect[x]); + if ((x % 6) == 5) + printf("\n"); + } + } + printf("\n\n"); + } + } else if (!strcasecmp(argv[2], "setdirect") || + !strcasecmp(argv[2], "setindirect")) { + struct wctdm_regop regop; + int val; + int reg; + if ((argc < 5) || (sscanf(argv[3], "%i", ®) != 1) || + (sscanf(argv[4], "%i", &val) != 1)) { + fprintf(stderr, "Need a register and value...\n"); + } else { + regop.reg = reg; + regop.val = val; + if (!strcasecmp(argv[2], "setindirect")) { + regop.indirect = 1; + regop.val &= 0xff; + } else { + regop.indirect = 0; + } + res = ioctl(fd, WCTDM_SET_REG, ®op); + if (res) + fprintf(stderr, "Unable to get registers on channel %s\n", argv[1]); + else + printf("Success.\n"); + } + } else + fprintf(stderr, "Invalid command\n"); + close(fd); + return 0; +} diff --git a/hdlcgen.c b/hdlcgen.c new file mode 100644 index 0000000..417004b --- /dev/null +++ b/hdlcgen.c @@ -0,0 +1,105 @@ +#define FAST_HDLC_NEED_TABLES +#include "kernel/fasthdlc.h" +#include <stdio.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> + +#define RANDOM "/dev/urandom" /* Not genuinely random */ +/* #define RANDOM "/dev/random" */ /* Quite genuinely random */ + +int myread(int fd, char *buf, int len) +{ + int sofar; + int res; + sofar = 0; + while(sofar < len) { + res = read(fd, buf + sofar, len - sofar); + if (res < 0) + return res; + sofar += res; + } + return sofar; +} + +int main(int argc, char *argv[]) +{ + unsigned char buf[1024]; + unsigned char outbuf[2048]; + int res; + int randin; + int randout; + int hdlcout; + int cnt; + int hdlccnt; + int x; + int flags; + struct fasthdlc_state transmitter; + + fasthdlc_precalc(); + + fasthdlc_init(&transmitter); + + randin = open(RANDOM, O_RDONLY); + if (randin < 0) { + fprintf(stderr, "Unable to open %s: %s\n", RANDOM, strerror(errno)); + exit(1); + } + randout = open("random.raw", O_WRONLY|O_TRUNC|O_CREAT, 0666); + if (randout < 0) { + fprintf(stderr, "Unable to open random.raw: %s\n", strerror(errno)); + exit(1); + } + hdlcout = open("random.hdlc", O_WRONLY|O_TRUNC|O_CREAT, 0666); + if (hdlcout < 0) { + fprintf(stderr, "Unable to open random.hdlc: %s\n", strerror(errno)); + exit(1); + } + for (;;) { + cnt = (rand() % 256) + 4; /* Read a pseudo-random amount of stuff */ + res = myread(randin, buf, cnt); + if (res != cnt) { + fprintf(stderr, "Tried to read %d bytes, but read %d instead\n", cnt, res); + exit(1); + } + res = write(randout, buf, cnt); + if (res != cnt) { + fprintf(stderr, "Tried to write %d bytes, but wrote %d instead\n", cnt, res); + exit(1); + } + /* HDLC encode */ + hdlccnt = 0; + /* Start with a flag */ + fasthdlc_tx_frame(&transmitter); + if (transmitter.bits >= 8) + outbuf[hdlccnt++] = fasthdlc_tx_run(&transmitter); + for (x=0;x<cnt;x++) { + res = fasthdlc_tx_load(&transmitter, buf[x]); + if (res < 0) { + fprintf(stderr, "Unable to load byte :(\n"); + exit(1); + } + while(transmitter.bits >= 8) { + outbuf[hdlccnt++] = fasthdlc_tx_run(&transmitter); + } + } + flags = (rand() % 4); + for (x=0;x<flags;x++) { + if (transmitter.bits < 8) + fasthdlc_tx_frame(&transmitter); + else + fprintf(stderr, "Huh? Don't need a frame?\n"); + outbuf[hdlccnt++] = fasthdlc_tx_run(&transmitter); + } + if (argc > 1) + printf("Encoded %d byte message with %d bytes of HDLC and %d extra flags\n", cnt, hdlccnt, flags); + res = write(hdlcout, outbuf, hdlccnt); + if (res != hdlccnt) { + fprintf(stderr, "Tried to write %d HDLC bytes, but wrote %d instead\n", cnt, res); + exit(1); + } + + } +} diff --git a/hdlcstress.c b/hdlcstress.c new file mode 100644 index 0000000..91af39d --- /dev/null +++ b/hdlcstress.c @@ -0,0 +1,186 @@ +#include <stdio.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include "kernel/zaptel.h" +#include <stdio.h> +#include <linux/types.h> +#include <linux/ppp_defs.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <stdlib.h> +#include "bittest.h" +#define FAST_HDLC_NEED_TABLES +#include "kernel/fasthdlc.h" + +/* #define BLOCK_SIZE 2048 */ +#define BLOCK_SIZE 2041 + +static int hdlcmode = 0; + + +static unsigned short fcstab[256] = +{ + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +#define PPP_INITFCS 0xffff /* Initial FCS value */ +#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */ +#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) + +void print_packet(unsigned char *buf, int len) +{ + int x; + printf("{ "); + for (x=0;x<len;x++) + printf("%02x ",buf[x]); + printf("}\n"); +} + +static int fd; +static struct fasthdlc_state fs; + +void send_packet(unsigned char *buf, int len) +{ + int res; + int x; + unsigned char outbuf[BLOCK_SIZE]; + int pos=0; + unsigned int fcs = PPP_INITFCS; + if (hdlcmode) + res = write(fd, buf, len + 2); + else { + for (x=0;x<len;x++) { + if (fasthdlc_tx_load(&fs, buf[x])) + printf("Load error\n"); + fcs = PPP_FCS(fcs, buf[x]); + outbuf[pos++] = fasthdlc_tx_run(&fs); + if (fs.bits > 7) + outbuf[pos++] = fasthdlc_tx_run(&fs); + } + fcs ^= 0xffff; + if (fasthdlc_tx_load(&fs, (fcs & 0xff))) + fprintf(stderr, "Load error (fcs1)\n"); + outbuf[pos++] = fasthdlc_tx_run(&fs); + if (fs.bits > 7) + outbuf[pos++] = fasthdlc_tx_run(&fs); + if (fasthdlc_tx_load(&fs, ((fcs >> 8) & 0xff))) + fprintf(stderr, "Load error (fcs2)\n"); + outbuf[pos++] = fasthdlc_tx_run(&fs); + if (fs.bits > 7) + outbuf[pos++] = fasthdlc_tx_run(&fs); + if (fasthdlc_tx_frame(&fs)) + fprintf(stderr, "Frame error\n"); + if (fs.bits > 7) + outbuf[pos++] = fasthdlc_tx_run(&fs); + if (fs.bits > 7) + outbuf[pos++] = fasthdlc_tx_run(&fs); + write(fd, outbuf, pos); + } +} + +int main(int argc, char *argv[]) +{ + int res, x; + ZT_PARAMS tp; + ZT_BUFFERINFO bi; + int bs = BLOCK_SIZE; + unsigned char c=0; + unsigned char outbuf[BLOCK_SIZE]; + if (argc < 2) { + fprintf(stderr, "Usage: markhdlctest <tor device>\n"); + exit(1); + } + fd = open(argv[1], O_RDWR, 0600); + if (fd < 0) { + fprintf(stderr, "Unable to open %s: %s\n", argv[1], strerror(errno)); + exit(1); + } + if (ioctl(fd, ZT_SET_BLOCKSIZE, &bs)) { + fprintf(stderr, "Unable to set block size to %d: %s\n", bs, strerror(errno)); + exit(1); + } + if (ioctl(fd, ZT_GET_PARAMS, &tp)) { + fprintf(stderr, "Unable to get channel parameters\n"); + exit(1); + } + if ((tp.sigtype & ZT_SIG_HDLCRAW) == ZT_SIG_HDLCRAW) { + printf("In HDLC mode\n"); + hdlcmode = 1; + } else if ((tp.sigtype & ZT_SIG_CLEAR) == ZT_SIG_CLEAR) { + printf("In CLEAR mode\n"); + hdlcmode = 0; + } else { + fprintf(stderr, "Not in a reasonable mode\n"); + exit(1); + } + res = ioctl(fd, ZT_GET_BUFINFO, &bi); + if (!res) { + bi.txbufpolicy = ZT_POLICY_IMMEDIATE; + bi.rxbufpolicy = ZT_POLICY_IMMEDIATE; + bi.numbufs = 4; + res = ioctl(fd, ZT_SET_BUFINFO, &bi); + if (res < 0) { + fprintf(stderr, "Unable to set buf info: %s\n", strerror(errno)); + exit(1); + } + } else { + fprintf(stderr, "Unable to get buf info: %s\n", strerror(errno)); + exit(1); + } + ioctl(fd, ZT_GETEVENT); + fasthdlc_precalc(); + fasthdlc_init(&fs); +#if 0 + print_packet(outbuf, res); + printf("FCS is %x, PPP_GOODFCS is %x\n", + fcs,PPP_GOODFCS); +#endif + for(;;) { + if (c < 1) + c = 1; + for (x=0;x<50;x++) { + outbuf[x] = c; + } + send_packet(outbuf, 50); +#if 0 + printf("Wrote %d of %d bytes\n", res, c); +#endif + c = bit_next(c); +#if 0 + printf("(%d) Wrote %d bytes\n", packets++, res); +#endif + } + +} diff --git a/hdlctest.c b/hdlctest.c new file mode 100644 index 0000000..4cd4d91 --- /dev/null +++ b/hdlctest.c @@ -0,0 +1,261 @@ +#include <stdio.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include "kernel/zaptel.h" +#include <stdio.h> +#include <linux/types.h> +#include <linux/ppp_defs.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <stdlib.h> +#include "bittest.h" +#define FAST_HDLC_NEED_TABLES +#include "kernel/fasthdlc.h" + +#define BLOCK_SIZE 2039 + +static unsigned short fcstab[256] = +{ + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +#define PPP_INITFCS 0xffff /* Initial FCS value */ +#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */ +#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) + +void print_packet(unsigned char *buf, int len) +{ + int x; + printf("{ "); + for (x=0;x<len;x++) + printf("%02x ",buf[x]); + printf("}\n"); +} + +static int bytes = 0; +static int errors=0; +static int c; + +void dump_bits(unsigned char *outbuf, int len) +{ + int x,i; + for (x=0;x<len;x++) { + for (i=0;i<8;i++) { + if (outbuf[x] & (1 << (7-i))) + printf("1"); + else + printf("0"); + } + } + printf("\n"); +} + +void dump_bitslong(unsigned int outbuf, int bits) +{ + int i; + printf("Dumping %d bits from %04x\n", bits, outbuf); + for (i=0;i<bits;i++) { + if (outbuf & (1 << (31 - i))) + printf("1"); + else + printf("0"); + } + printf("\n"); +} + +int check_frame(unsigned char *outbuf, int res) +{ + static int setup = 0; + int x; + unsigned short fcs = PPP_INITFCS; + if (c < 1) + c = 1; + if (!setup) { + c = outbuf[0]; + setup++; + } + for (x=0;x<res;x++) { + if (outbuf[x] != c && (x < res - 2)) { + printf("(Error %d): Unexpected result, %d != %d, position %d %d bytes since last error.\n", ++errors, outbuf[x], c, x, bytes); + if (!x) + c = outbuf[0]; + bytes=0; + } else + bytes++; + fcs = PPP_FCS(fcs, outbuf[x]); + } + if (fcs != PPP_GOODFCS) + printf("FCS Check failed :( (%04x != %04x)\n", fcs, PPP_GOODFCS); +#if 0 + if (res != c) { + printf("Res is %d, expected %d\n", res, c+2); + } +#endif + c = bit_next(c); + return 0; +} + +int main(int argc, char *argv[]) +{ + int fd; + int res, x; + ZT_PARAMS tp; + ZT_BUFFERINFO bi; + int bs = BLOCK_SIZE; + int pos = 0; + unsigned char inbuf[BLOCK_SIZE]; + unsigned char outbuf[BLOCK_SIZE]; + int bytes=0; + int out; + unsigned int olddata1; + int oldones1; + int oldbits1; + unsigned int olddata; + int oldones; + int oldbits; + int hdlcmode = 0; + struct fasthdlc_state fs; + if (argc < 2) { + fprintf(stderr, "Usage: markhdlctest <tor device>\n"); + exit(1); + } + fd = open(argv[1], O_RDWR, 0600); + if (fd < 0) { + fprintf(stderr, "Unable to open %s: %s\n", argv[1], strerror(errno)); + exit(1); + } + if (ioctl(fd, ZT_SET_BLOCKSIZE, &bs)) { + fprintf(stderr, "Unable to set block size to %d: %s\n", bs, strerror(errno)); + exit(1); + } + if (ioctl(fd, ZT_GET_PARAMS, &tp)) { + fprintf(stderr, "Unable to get channel parameters\n"); + exit(1); + } + if ((tp.sigtype & ZT_SIG_HDLCRAW) == ZT_SIG_HDLCRAW) { + printf("In HDLC mode\n"); + hdlcmode = 1; + } else if ((tp.sigtype & ZT_SIG_CLEAR) == ZT_SIG_CLEAR) { + printf("In CLEAR mode\n"); + hdlcmode = 0; + } else { + fprintf(stderr, "Not in a reasonable mode\n"); + exit(1); + } + res = ioctl(fd, ZT_GET_BUFINFO, &bi); + if (!res) { + bi.txbufpolicy = ZT_POLICY_IMMEDIATE; + bi.rxbufpolicy = ZT_POLICY_IMMEDIATE; + bi.numbufs = 4; + res = ioctl(fd, ZT_SET_BUFINFO, &bi); + if (res < 0) { + fprintf(stderr, "Unable to set buf info: %s\n", strerror(errno)); + exit(1); + } + } else { + fprintf(stderr, "Unable to get buf info: %s\n", strerror(errno)); + exit(1); + } + ioctl(fd, ZT_GETEVENT); + fasthdlc_precalc(); + fasthdlc_init(&fs); + for(;;) { + res = read(fd, outbuf, sizeof(outbuf)); + if (hdlcmode) { + if (res < 0) { + if (errno == ELAST) { + if (ioctl(fd, ZT_GETEVENT, &x) < 0) { + fprintf(stderr, "Unaable to get event: %s\n", strerror(errno)); + exit(1); + } + fprintf(stderr, "Event: %d (%d bytes since last error)\n", x, bytes); + bytes=0; + continue; + } else { + fprintf(stderr, "Error: %s\n", strerror(errno)); + exit(1); + } + } +#if 0 + printf("Res is %d, buf0 is %d, buf1 is %d\n", res, outbuf[0], outbuf[1]); +#endif + if (res < 2) { + fprintf(stderr, "Too small? Only got %d bytes\n", res); + } + check_frame(outbuf, res); + } else { + for (x=0;x<res;x++) { + oldones1 = oldones; + oldbits1 = oldbits; + olddata1 = olddata; + oldones = fs.ones; + oldbits = fs.bits; + olddata = fs.data; + fasthdlc_rx_load(&fs, outbuf[x]); + out = fasthdlc_rx_run(&fs); + if (out & RETURN_EMPTY_FLAG) { + /* Empty */ + } else if (out & RETURN_COMPLETE_FLAG) { + if (pos && (pos < 2)) { + printf("Too short? (%d)\n", pos); + } else if (pos) { + check_frame(inbuf, pos); + } + pos = 0; + } else if (out & RETURN_DISCARD_FLAG) { + printf("Discard (search = %d, len = %d, buf = %d, x=%d, res=%d, oldones: %d, oldbits: %d)\n", c, pos, inbuf[0], x, res, oldones, oldbits); + dump_bitslong(olddata, oldbits); + printf("Discard oldones: %d, oldbits: %d)\n", oldones1, oldbits1); + dump_bitslong(olddata1, oldbits1); + if (x > 64) { + dump_bits(outbuf + x - 64, 64); + dump_bits(outbuf + x, 64); + } + pos = 0; + } else { + if ((out != c) && (pos < c) && !pos) { + printf("Warning: Expecting %d at pos %d, got %d (x =%d)\n", c, pos, out, x); + if (x > 64) { + dump_bits(outbuf + x - 64, 64); + dump_bits(outbuf + x, 64); + } + } + inbuf[pos++] = out; + } + } + } + } + +} diff --git a/hdlcverify.c b/hdlcverify.c new file mode 100644 index 0000000..8212b2a --- /dev/null +++ b/hdlcverify.c @@ -0,0 +1,106 @@ +#define FAST_HDLC_NEED_TABLES +#include "kernel/fasthdlc.h" +#include <stdio.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> + +int myread(int fd, char *buf, int len) +{ + int sofar; + int res; + sofar = 0; + while(sofar < len) { + res = read(fd, buf + sofar, len - sofar); + if (res < 0) + return res; + sofar += res; + } + return sofar; +} + +static inline unsigned char nextchar(int fd) +{ + static unsigned char inbuf[2048]; + static int bytes = 0; + static int pos = 0; + if (pos >= bytes) { + pos = 0; + bytes = read(fd, inbuf, sizeof(inbuf)); + if (bytes < 0) { + fprintf(stderr, "Unable to read more data: %s\n", strerror(errno)); + exit(1); + } + if (bytes == 0) { + fprintf(stderr, "-- END OF DATA --\n"); + exit(0); + } + } + return inbuf[pos++]; +} + +int main(int argc, char *argv[]) +{ + unsigned char decbuf[1024]; + unsigned char actual[1024]; + int res; + int datain; + int hdlcin; + int hdlccnt; + int x; + struct fasthdlc_state receiver; + + fasthdlc_precalc(); + + fasthdlc_init(&receiver); + + hdlcin = open("random.hdlc", O_RDONLY); + if (hdlcin < 0) { + fprintf(stderr, "Unable to open %s: %s\n", "random.hdlc", strerror(errno)); + exit(1); + } + datain = open("random.raw", O_RDONLY); + if (datain < 0) { + fprintf(stderr, "Unable to open random.raw: %s\n", strerror(errno)); + exit(1); + } + hdlccnt = 0; + for (;;) { + /* Feed in some input */ + if (fasthdlc_rx_load(&receiver, nextchar(hdlcin))) { + fprintf(stderr, "Unable to feed receiver :(\n"); + exit(1); + } + res = fasthdlc_rx_run(&receiver); + if (res & RETURN_EMPTY_FLAG) + continue; + if (res & RETURN_COMPLETE_FLAG) { + if (hdlccnt) { + if (argc > 1) + printf("Got message of length %d\n", hdlccnt); + res = myread(datain, actual, hdlccnt); + if (res != hdlccnt) { + fprintf(stderr, "Tried to read %d bytes, but read %d instead\n", hdlccnt, res); + exit(1); + } + for (x=0;x<hdlccnt;x++) { + if (actual[x] != decbuf[x]) { + fprintf(stderr, "Found discrepancy at offset %d\n", x); + exit(1); + } + } + /* Reset message receiver */ + hdlccnt = 0; + } + } else if (res & RETURN_DISCARD_FLAG) { + if (1 || hdlccnt) { + fprintf(stderr, "Detected abort :(\n"); + exit(1); + } + } else { + decbuf[hdlccnt++] = res; + } + } +} diff --git a/makeopts.in b/makeopts.in new file mode 100644 index 0000000..c2bf075 --- /dev/null +++ b/makeopts.in @@ -0,0 +1,47 @@ +CC=@CC@ +HOSTCC=@HOSTCC@ +CFLAGS=@CFLAGS@ +LDFLAGS=@LDFLAGS@ + +INSTALL=@INSTALL@ +GREP=@GREP@ +SHELL=@SHELL@ +LN=@LN@ + +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +datarootdir = @datarootdir@ +datadir = @datadir@ +includedir = @includedir@ +infodir = @infodir@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ + +DOWNLOAD=@DOWNLOAD@ + +PBX_LIBNEWT=@PBX_LIBNEWT@ +NEWT_LIB=@NEWT_LIB@ +NEWT_INCLUDE=@NEWT_INCLUDE@ + +CURSES_LIB=@CURSES_LIB@ +CURSES_INCLUDE=@CURSES_INCLUDE@ + +NCURSES_LIB=@NCURSES_LIB@ +NCURSES_INCLUDE=@NCURSES_INCLUDE@ + +PBX_LIBUSB=@PBX_LIBUSB@ +USB_LIB=@USB_LIB@ +USB_INCLUDE=@USB_INCLUDE@ + +USE_SELINUX=@USE_SELINUX@ + +PPPD_VERSION=@PPPD_VERSION@ + +ASCIIDOC=@ASCIIDOC@ diff --git a/patgen.c b/patgen.c new file mode 100644 index 0000000..34f455a --- /dev/null +++ b/patgen.c @@ -0,0 +1,98 @@ +#include <stdio.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include <linux/types.h> +#include <linux/ppp_defs.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <stdlib.h> +#include "bittest.h" + +#ifdef STANDALONE_ZAPATA +#include "kernel/zaptel.h" +#else +#include <zaptel/zaptel.h> +#endif + +/* #define BLOCK_SIZE 2048 */ +#define BLOCK_SIZE 2041 + +void print_packet(unsigned char *buf, int len) +{ + int x; + printf("{ "); + for (x=0;x<len;x++) + printf("%02x ",buf[x]); + printf("}\n"); +} + +int main(int argc, char *argv[]) +{ + int fd; + int res, res1, x; + ZT_PARAMS tp; + int bs = BLOCK_SIZE; + unsigned char c=0; + unsigned char outbuf[BLOCK_SIZE]; + if (argc < 2) { + fprintf(stderr, "Usage: %s <tor device>\n", argv[0]); + exit(1); + } + fd = open(argv[1], O_RDWR, 0600); + if (fd < 0) { + fprintf(stderr, "Unable to open %s: %s\n", argv[1], strerror(errno)); + exit(1); + } + if (ioctl(fd, ZT_SET_BLOCKSIZE, &bs)) { + fprintf(stderr, "Unable to set block size to %d: %s\n", bs, strerror(errno)); + exit(1); + } + if (ioctl(fd, ZT_GET_PARAMS, &tp)) { + fprintf(stderr, "Unable to get channel parameters\n"); + exit(1); + } + ioctl(fd, ZT_GETEVENT); +#if 0 + print_packet(outbuf, res); + printf("FCS is %x, PPP_GOODFCS is %x\n", + fcs,PPP_GOODFCS); +#endif + for(;;) { + res = bs; + for (x=0;x<bs;x++) { + outbuf[x] = c; + c = bit_next(c); + } + res1 = write(fd, outbuf, res); + if (res1 < res) { + int e; + ZT_SPANINFO zi; + res = ioctl(fd,ZT_GETEVENT,&e); + if (res == -1) + { + perror("ZT_GETEVENT"); + exit(1); + } + if (e == ZT_EVENT_NOALARM) + printf("ALARMS CLEARED\n"); + if (e == ZT_EVENT_ALARM) + { + zi.spanno = 0; + res = ioctl(fd,ZT_SPANSTAT,&zi); + if (res == -1) + { + perror("ZT_SPANSTAT"); + exit(1); + } + printf("Alarm mask %x hex\n",zi.alarms); + } + continue; + } +#if 0 + printf("(%d) Wrote %d bytes\n", packets++, res); +#endif + } + +} diff --git a/patlooptest.c b/patlooptest.c new file mode 100644 index 0000000..738da63 --- /dev/null +++ b/patlooptest.c @@ -0,0 +1,124 @@ +#include <stdio.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <stdlib.h> +#include <time.h> + +#ifdef STANDALONE_ZAPATA +#include "kernel/zaptel.h" +#else +#include <zaptel/zaptel.h> +#endif + +#define BLOCK_SIZE 2039 + +void print_packet(unsigned char *buf, int len) +{ + int x; + printf("{ "); + for (x=0;x<len;x++) + printf("%02x ",buf[x]); + printf("}\n"); +} + +int main(int argc, char *argv[]) +{ + int fd; + int res, x; + int i; + ZT_PARAMS tp; + int bs = BLOCK_SIZE; + int skipcount = 10; + unsigned char c=0,c1=0; + unsigned char inbuf[BLOCK_SIZE]; + unsigned char outbuf[BLOCK_SIZE]; + int setup=0; + int errors=0; + int bytes=0; + int timeout=0; + time_t start_time=0; + if (argc < 2 || argc > 3 ) { + fprintf(stderr, "Usage: %s <zaptel device> [timeout]\n",argv[0]); + exit(1); + } + fd = open(argv[1], O_RDWR, 0600); + if (fd < 0) { + fprintf(stderr, "Unable to open %s: %s\n", argv[1], strerror(errno)); + exit(1); + } + if (ioctl(fd, ZT_SET_BLOCKSIZE, &bs)) { + fprintf(stderr, "Unable to set block size to %d: %s\n", bs, strerror(errno)); + exit(1); + } + if (ioctl(fd, ZT_GET_PARAMS, &tp)) { + fprintf(stderr, "Unable to get channel parameters\n"); + exit(1); + } + ioctl(fd, ZT_GETEVENT); + + i = ZT_FLUSH_ALL; + if (ioctl(fd,ZT_FLUSH,&i) == -1) + { + perror("tor_flush"); + exit(255); + } + if(argc==3){ + timeout=atoi(argv[2]); + start_time=time(NULL); + printf("Using Timeout of %d Seconds\n",timeout); + } + + for(;;) { + res = bs; + for (x=0;x<bs;x++) + outbuf[x] = c1++; + + res = write(fd,outbuf,bs); + if (res != bs) + { + printf("Res is %d: %s\n", res, strerror(errno)); + ioctl(fd, ZT_GETEVENT, &x); + printf("Event: %d\n", x); + exit(1); + } + + if (skipcount) + { + if (skipcount > 1) read(fd,inbuf,bs); + skipcount--; + if (!skipcount) puts("Going for it..."); + continue; + } + + res = read(fd, inbuf, bs); + if (res < bs) { + printf("Res is %d\n", res); + exit(1); + } + if (!setup) { + c = inbuf[0]; + setup++; + } + for (x=0;x<bs;x++) { + if (inbuf[x] != c) { + printf("(Error %d): Unexpected result, %d != %d, %d bytes since last error.\n", ++errors, inbuf[x],c, bytes); + c = inbuf[x]; + bytes=0; + } + c++; + bytes++; + } +#if 0 + printf("(%d) Wrote %d bytes\n", packets++, res); +#endif + if(timeout && (time(NULL)-start_time)>timeout){ + printf("Timeout achieved Ending Program\n"); + return errors; + } + } + +} diff --git a/pattest.c b/pattest.c new file mode 100644 index 0000000..69c3b6e --- /dev/null +++ b/pattest.c @@ -0,0 +1,104 @@ +#include <stdio.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include <linux/types.h> +#include <linux/ppp_defs.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <stdlib.h> +#include "bittest.h" + +#ifdef STANDALONE_ZAPATA +#include "kernel/zaptel.h" +#else +#include <zaptel/zaptel.h> +#endif + +#define BLOCK_SIZE 2039 + +void print_packet(unsigned char *buf, int len) +{ + int x; + printf("{ "); + for (x=0;x<len;x++) + printf("%02x ",buf[x]); + printf("}\n"); +} + +int main(int argc, char *argv[]) +{ + int fd; + int res, x; + ZT_PARAMS tp; + int bs = BLOCK_SIZE; + unsigned char c=0; + unsigned char outbuf[BLOCK_SIZE]; + int setup=0; + int errors=0; + int bytes=0; + if (argc < 2) { + fprintf(stderr, "Usage: markhdlctest <tor device>\n"); + exit(1); + } + fd = open(argv[1], O_RDWR, 0600); + if (fd < 0) { + fprintf(stderr, "Unable to open %s: %s\n", argv[1], strerror(errno)); + exit(1); + } + if (ioctl(fd, ZT_SET_BLOCKSIZE, &bs)) { + fprintf(stderr, "Unable to set block size to %d: %s\n", bs, strerror(errno)); + exit(1); + } + if (ioctl(fd, ZT_GET_PARAMS, &tp)) { + fprintf(stderr, "Unable to get channel parameters\n"); + exit(1); + } + ioctl(fd, ZT_GETEVENT); + for(;;) { + res = bs; + res = read(fd, outbuf, res); + if (res < bs) { + int e; + ZT_SPANINFO zi; + res = ioctl(fd,ZT_GETEVENT,&e); + if (res == -1) + { + perror("ZT_GETEVENT"); + exit(1); + } + if (e == ZT_EVENT_NOALARM) + printf("ALARMS CLEARED\n"); + if (e == ZT_EVENT_ALARM) + { + zi.spanno = 0; + res = ioctl(fd,ZT_SPANSTAT,&zi); + if (res == -1) + { + perror("ZT_SPANSTAT"); + exit(1); + } + printf("Alarm mask %x hex\n",zi.alarms); + } + continue; + } + if (!setup) { + c = outbuf[0]; + setup++; + } + for (x=0;x<bs;x++) { + if (outbuf[x] != c) { + printf("(Error %d): Unexpected result, %d != %d, %d bytes since last error.\n", ++errors, outbuf[x], c, bytes); + c = outbuf[x]; + bytes=0; + } + c = bit_next(c); + bytes++; + } +#if 0 + printf("(%d) Wrote %d bytes\n", packets++, res); +#endif + } + +} diff --git a/ppp/Makefile b/ppp/Makefile new file mode 100644 index 0000000..86b65a6 --- /dev/null +++ b/ppp/Makefile @@ -0,0 +1,29 @@ +#COPTS = -O2 -g + +-include ../makeopts + +CFLAGS += $(COPTS) -I.. -fPIC +LDFLAGS += -shared + +INCLUDE_DIR = $(includedir)/pppd + +LIBDIR = $(libdir)/pppd/$(PPPD_VERSION) + +PLUGINS := zaptel.so + +all: $(PLUGINS) + +%.so: %.c +ifeq (,$(PPPD_VERSION)) + @echo "pppd version not found (in patchlevel.h)." + @echo "Install ppp source/headers and/or ./configure --with-ppp=PATH." + exit 1 +endif + $(CC) -o $@ $(CFLAGS) $^ $(LDFLAGS) + +install: $(PLUGINS) + $(INSTALL) -d $(DESTDIR)$(LIBDIR) + $(INSTALL) -m 0644 $? $(DESTDIR)$(LIBDIR) + +clean: + rm -f *.o *.so *.a diff --git a/ppp/zaptel.c b/ppp/zaptel.c new file mode 100644 index 0000000..121bfec --- /dev/null +++ b/ppp/zaptel.c @@ -0,0 +1,284 @@ +/* zaptel.c - pppd plugin to implement PPP over Zaptel HDLC channel. + * + * Copyright 2002 Digium, Inc. + * Mark Spencer <markster@digium.inc> + * + * Borrows from PPPoE by Michal Ostrowski <mostrows@styx.uwaterloo.ca>, + * Jamal Hadi Salim <hadi@cyberus.ca> + * + * which in turn... + * + * Borrows heavily from the PPPoATM plugin by Mitchell Blank Jr., + * which is based in part on work from Jens Axboe and Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> +#include <errno.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <pppd/pppd.h> +#include <pppd/fsm.h> +#include <pppd/lcp.h> +#include <pppd/ipcp.h> +#include <pppd/ccp.h> +#include <pppd/pathnames.h> + +#include "zaptel.h" + +extern int new_style_driver; + +const char pppd_version[] = VERSION; + +#define _PATH_ZAPOPT _ROOT_PATH "/etc/ppp/options." + +#define ZAP_MTU (ZT_DEFAULT_MTU_MRU - 16) +extern int kill_link; +int retries = 0; + +int setdevname_zaptel(const char *cp); + +static option_t zaptel_options[] = { + { "device name", o_wild, (void *) &setdevname_zaptel, + "Serial port device name", + OPT_DEVNAM | OPT_PRIVFIX | OPT_NOARG | OPT_A2STRVAL | OPT_STATIC, + devnam}, + { NULL } +}; + +static int zapfd = -1; +static int zapchan = 0; + +static int connect_zaptel(void) +{ + + ZT_PARAMS ztp; + int res; + int x; + + info("Zaptel device is '%s'\n", devnam); + + strlcpy(ppp_devnam, devnam, sizeof(ppp_devnam)); + + if (strlen(devnam) && strcmp(devnam, "stdin")) { + /* Get the channel number */ + zapchan = atoi(devnam); + if (zapchan < 1) { + fatal("'%s' is not a valid device name\n", devnam); + return -1; + } + + /* Open /dev/zap/channel interface */ + zapfd = open("/dev/zap/channel", O_RDWR); + if (zapfd < 0) { + fatal("Unable to open zaptel channel interface: '%s'\n", strerror(errno)); + return zapfd; + } + + /* Specify which channel we really want */ + x = zapchan; + res = ioctl(zapfd, ZT_SPECIFY, &x); + if (res) { + fatal("Unable to specify channel %d: %s\n", zapchan, strerror(errno)); + close(zapfd); + zapfd = -1; + return -1; + } + } else + zapfd = STDIN_FILENO; + + + /* Get channel parameters */ + memset(&ztp, 0, sizeof(ztp)); + ztp.channo = -1; + + res = ioctl(zapfd, ZT_GET_PARAMS, &ztp); + + if (res) { + fatal("Device '%s' does not appear to be a zaptel device\n", devnam ? devnam : "<stdin>"); + } + + x = 1; + + /* Throw into HDLC/PPP mode */ + res = ioctl(zapfd, ZT_HDLCPPP, &x); + + if (res) { + fatal("Unable to put device '%s' into HDLC mode\n", devnam); + close(zapfd); + zapfd = -1; + return -1; + } + + /* Once the logging is fixed, print a message here indicating + connection parameters */ + zapchan = ztp.channo; + info("Connected to zaptel device '%s' (%d)\n", ztp.name, ztp.channo); + + return zapfd; +} + +static void disconnect_zaptel(void) +{ + int res; + int x = 0; + /* Throw out of HDLC mode */ + res = ioctl(zapfd, ZT_HDLCPPP, &x); + + if (res) { + warn("Unable to take device '%s' out of HDLC mode\n", devnam); + } + + /* Close if it's not stdin */ + if (strlen(devnam)) + close(zapfd); + warn("Disconnect from zaptel"); + +} + + +static int setspeed_zaptel(const char *cp) +{ + return 0; +} + +static void zaptel_extra_options() +{ + int ret; + char buf[256]; + snprintf(buf, 256, _PATH_ZAPOPT "%s",devnam); + if(!options_from_file(buf, 0, 0, 1)) + exit(EXIT_OPTION_ERROR); + +} + + + +static void send_config_zaptel(int mtu, + u_int32_t asyncmap, + int pcomp, + int accomp) +{ + int sock; + + if (mtu > ZAP_MTU) { + warn("Couldn't increase MTU to %d.", mtu); + mtu = ZAP_MTU; + } +} + + +static void recv_config_zaptel(int mru, + u_int32_t asyncmap, + int pcomp, + int accomp) +{ + if (mru > ZAP_MTU) + error("Couldn't increase MRU to %d", mru); +} + +static void set_xaccm_pppoe(int unit, ext_accm accm) +{ + /* NOTHING */ +} + + + +struct channel zaptel_channel; + +/* Check is cp is a valid zaptel device + * return either 1 if "cp" is a reasonable thing to name a device + * or die. + * Note that we don't actually open the device at this point + * We do need to fill in: + * devnam: a string representation of the device + */ + +int (*old_setdevname_hook)(const char* cp) = NULL; +int setdevname_zaptel(const char *cp) +{ + int ret; + int chan; + + /* If already set, forgoe */ + if (strlen(devnam)) + return 1; + + + if (strcmp(cp, "stdin")) { + ret = sscanf(cp, "%d", &chan); + if (ret != 1) { + fatal("Zaptel: Invalid channel: '%s'\n", cp); + return -1; + } + } + + zap_copy_string(devnam, cp, sizeof(devnam)); + + info("Using zaptel device '%s'\n", devnam); + + ret = 1; + + if( ret == 1 && the_channel != &zaptel_channel ){ + + the_channel = &zaptel_channel; + + modem = 0; + + lcp_allowoptions[0].neg_accompression = 0; + lcp_wantoptions[0].neg_accompression = 0; + + lcp_allowoptions[0].neg_pcompression = 0; + lcp_wantoptions[0].neg_pcompression = 0; + + ccp_allowoptions[0].deflate = 0 ; + ccp_wantoptions[0].deflate = 0 ; + + ipcp_allowoptions[0].neg_vj=0; + ipcp_wantoptions[0].neg_vj=0; + + ccp_allowoptions[0].bsd_compress = 0; + ccp_wantoptions[0].bsd_compress = 0; + + lcp_allowoptions[0].neg_asyncmap = 0; + lcp_wantoptions[0].neg_asyncmap = 0; + + } + return ret; +} + + + +void plugin_init(void) +{ + if (!ppp_available() && !new_style_driver) + fatal("Kernel doesn't support ppp_generic needed for Zaptel PPP"); + add_options(zaptel_options); + + info("Zaptel Plugin Initialized"); +} + +struct channel zaptel_channel = { + options: zaptel_options, + process_extra_options: &zaptel_extra_options, + check_options: NULL, + connect: &connect_zaptel, + disconnect: &disconnect_zaptel, + establish_ppp: &generic_establish_ppp, + disestablish_ppp: &generic_disestablish_ppp, + send_config: &send_config_zaptel, + recv_config: &recv_config_zaptel, + close: NULL, + cleanup: NULL +}; + diff --git a/sethdlc.c b/sethdlc.c new file mode 100644 index 0000000..09f8348 --- /dev/null +++ b/sethdlc.c @@ -0,0 +1,693 @@ +/* + * sethdlc.c + * + * Copyright (C) 1999 - 2002 Krzysztof Halasa <khc@pm.waw.pl> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <errno.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <asm/types.h> +#include <linux/hdlc.h> +#include <netinet/in.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <linux/if.h> +#include <linux/sockios.h> +#include "kernel/zaptel.h" + +#if GENERIC_HDLC_VERSION != 4 +#error Generic HDLC layer version mismatch, please get correct sethdlc.c +#endif + +#if !defined(IF_PROTO_HDLC_ETH) || !defined(IF_PROTO_FR_ETH_PVC) +#warning "No kernel support for Ethernet over Frame Relay / HDLC, skipping it" +#endif + + +static struct ifreq req; /* for ioctl */ +static int argc; +static char **argv; +int sock; + + +static void error(const char *format, ...) __attribute__ ((noreturn)); + +static void error(const char *format, ...) +{ + va_list args; + + va_start(args, format); + fprintf(stderr, "%s: ", req.ifr_name); + vfprintf(stderr, format, args); + va_end(args); + exit(1); +} + + + +typedef struct { + const char *name; + const unsigned int value; +} parsertab; + + + +static int checkkey(const char* name) +{ + if (argc < 1) + return -1; /* no enough parameters */ + + if (strcmp(name, argv[0])) + return -1; + argc--; + argv++; + return 0; +} + + + +static int checktab(parsertab *tab, unsigned int *value) +{ + int i; + + if (argc < 1) + return -1; /* no enough parameters */ + + for (i = 0; tab[i].name; i++) + if (!strcmp(tab[i].name, argv[0])) { + argc--; + argv++; + *value = tab[i].value; + return 0; + } + + return -1; /* Not found */ +} + + + +static const char* tabstr(unsigned int value, parsertab *tab, + const char* unknown) +{ + int i; + for (i = 0; tab[i].name; i++) + if (tab[i].value == value) + return tab[i].name; + + return unknown; /* Not found */ +} + + + +static unsigned int match(const char* name, unsigned int *value, + unsigned int minimum, unsigned int maximum) +{ + char test; + + if (argc < 1) + return -1; /* no enough parameters */ + + if (name) { + if (strcmp(name, argv[0])) + return -1; + argc--; + argv++; + } + + if (argc < 1) + error("Missing parameter\n"); + + if (sscanf(argv[0], "%u%c", value, &test) != 1) + error("Invalid parameter: %s\n", argv[0]); + + if ((*value > maximum) || (*value < minimum)) + error("Parameter out of range [%u - %u]: %u\n", + minimum, maximum, *value); + + argc--; + argv++; + return 0; +} + + +static parsertab ifaces[] = {{ "v35", IF_IFACE_V35 }, + { "v24", IF_IFACE_V24 }, + { "x21", IF_IFACE_X21 }, + { "e1", IF_IFACE_E1 }, + { "t1", IF_IFACE_T1 }, + { NULL, 0 }}; + +static parsertab clocks[] = {{ "int", CLOCK_INT }, + { "ext", CLOCK_EXT }, + { "txint", CLOCK_TXINT }, + { "txfromrx", CLOCK_TXFROMRX }, + { NULL, 0 }}; + + +static parsertab protos[] = {{ "hdlc", IF_PROTO_HDLC}, + { "cisco", IF_PROTO_CISCO}, + { "fr", IF_PROTO_FR}, + { "ppp", IF_PROTO_PPP}, + { "x25", IF_PROTO_X25}, +#ifdef IF_PROTO_HDLC_ETH + { "hdlc-eth", IF_PROTO_HDLC_ETH}, +#endif + { NULL, 0 }}; + + +static parsertab hdlc_enc[] = {{ "nrz", ENCODING_NRZ }, + { "nrzi", ENCODING_NRZI }, + { "fm-mark", ENCODING_FM_MARK }, + { "fm-space", ENCODING_FM_SPACE }, + { "manchester", ENCODING_MANCHESTER }, + { NULL, 0 }}; + +static parsertab hdlc_par[] = {{ "no-parity", PARITY_NONE }, + { "crc16", PARITY_CRC16_PR1 }, + { "crc16-pr0", PARITY_CRC16_PR0 }, + { "crc16-itu", PARITY_CRC16_PR1_CCITT }, + { "crc16-itu-pr0", PARITY_CRC16_PR0_CCITT }, + { "crc32-itu", PARITY_CRC32_PR1_CCITT }, + { NULL, 0 }}; + +static parsertab lmi[] = {{ "none", LMI_NONE }, + { "ansi", LMI_ANSI }, + { "ccitt", LMI_CCITT }, + { NULL, 0 }}; + + +static void set_iface(void) +{ + int orig_argc = argc; + te1_settings te1; + + memset(&te1, 0, sizeof(te1)); + req.ifr_settings.type = IF_IFACE_SYNC_SERIAL; + + while (argc > 0) { + if (req.ifr_settings.type == IF_IFACE_SYNC_SERIAL) + if (!checktab(ifaces, &req.ifr_settings.type)) + continue; + + if (!te1.clock_type) + if (!checkkey("clock")) { + if (!checktab(clocks, &te1.clock_type)) + continue; + error("Invalid clock type\n"); + } + + if (!te1.clock_rate && + (te1.clock_type == CLOCK_INT || + te1.clock_type == CLOCK_TXINT)) + if (!match("rate", &te1.clock_rate, 1, 0xFFFFFFFF)) + continue; + if (!te1.loopback) { + if (!checkkey("loopback") || + !checkkey("lb")) { + te1.loopback = 1; + continue; + } + } + /* slotmap goes here */ + + if (orig_argc == argc) + return; /* not an iface definition */ + error("Invalid parameter: %s\n", argv[0]); + } + + if (!te1.clock_rate && + (te1.clock_type == CLOCK_INT || + te1.clock_type == CLOCK_TXINT)) + te1.clock_rate = 64000; + + /* FIXME stupid hack, will remove it later */ + req.ifr_settings.ifs_ifsu.te1 = &te1; + if (req.ifr_settings.type == IF_IFACE_E1 || + req.ifr_settings.type == IF_IFACE_T1) + req.ifr_settings.size = sizeof(te1_settings); + else + req.ifr_settings.size = sizeof(sync_serial_settings); + + if (ioctl(sock, SIOCWANDEV, &req)) + error("Unable to set interface information: %s\n", + strerror(errno)); + + exit(0); +} + + + +static void set_proto_fr(void) +{ + unsigned int lmi_type = 0; + fr_proto fr; + + memset(&fr, 0, sizeof(fr)); + + while (argc > 0) { + if (!lmi_type) + if (!checkkey("lmi")) { + if (!checktab(lmi, &lmi_type)) + continue; + error("Invalid LMI type: %s\n", + argv[0]); + } + + if (lmi_type && lmi_type != LMI_NONE) { + if (!fr.dce) + if (!checkkey("dce")) { + fr.dce = 1; + continue; + } + + if (!fr.t391) + if (!match("t391", &fr.t391, + 1, 1000)) + continue; + if (!fr.t392) + if (!match("t392", &fr.t392, + 1, 1000)) + continue; + if (!fr.n391) + if (!match("n391", &fr.n391, + 1, 1000)) + continue; + if (!fr.n392) + if (!match("n392", &fr.n392, + 1, 1000)) + continue; + if (!fr.n393) + if (!match("n393", &fr.n393, + 1, 1000)) + continue; + } + error("Invalid parameter: %s\n", argv[0]); + } + + /* polling verification timer*/ + if (!fr.t391) fr.t391 = 10; + /* link integrity verification polling timer */ + if (!fr.t392) fr.t392 = 15; + /* full status polling counter*/ + if (!fr.n391) fr.n391 = 6; + /* error threshold */ + if (!fr.n392) fr.n392 = 3; + /* monitored events count */ + if (!fr.n393) fr.n393 = 4; + + if (!lmi_type) + fr.lmi = LMI_DEFAULT; + else + fr.lmi = lmi_type; + + req.ifr_settings.ifs_ifsu.fr = &fr; + req.ifr_settings.size = sizeof(fr); + + if (ioctl(sock, SIOCWANDEV, &req)) + error("Unable to set FR protocol information: %s\n", + strerror(errno)); +} + + + +static void set_proto_hdlc(int eth) +{ + unsigned int enc = 0, par = 0; + raw_hdlc_proto raw; + + memset(&raw, 0, sizeof(raw)); + + while (argc > 0) { + if (!enc) + if (!checktab(hdlc_enc, &enc)) + continue; + if (!par) + if (!checktab(hdlc_par, &par)) + continue; + + error("Invalid parameter: %s\n", argv[0]); + } + + if (!enc) + raw.encoding = ENCODING_DEFAULT; + else + raw.encoding = enc; + + if (!par) + raw.parity = ENCODING_DEFAULT; + else + raw.parity = par; + + req.ifr_settings.ifs_ifsu.raw_hdlc = &raw; + req.ifr_settings.size = sizeof(raw); + + if (ioctl(sock, SIOCWANDEV, &req)) + error("Unable to set HDLC%s protocol information: %s\n", + eth ? "-ETH" : "", strerror(errno)); +} + + + +static void set_proto_cisco(void) +{ + cisco_proto cisco; + memset(&cisco, 0, sizeof(cisco)); + + while (argc > 0) { + if (!cisco.interval) + if (!match("interval", &cisco.interval, + 1, 100)) + continue; + if (!cisco.timeout) + if (!match("timeout", &cisco.timeout, + 1, 100)) + continue; + + error("Invalid parameter: %s\n", + argv[0]); + } + + if (!cisco.interval) + cisco.interval = 10; + if (!cisco.timeout) + cisco.timeout = 25; + + req.ifr_settings.ifs_ifsu.cisco = &cisco; + req.ifr_settings.size = sizeof(cisco); + + if (ioctl(sock, SIOCWANDEV, &req)) + error("Unable to set Cisco HDLC protocol information: %s\n", + strerror(errno)); +} + + + +static void set_proto(void) +{ + if (checktab(protos, &req.ifr_settings.type)) + return; + + switch(req.ifr_settings.type) { + case IF_PROTO_HDLC: set_proto_hdlc(0); break; +#ifdef IF_PROTO_HDLC_ETH + case IF_PROTO_HDLC_ETH: set_proto_hdlc(1); break; +#endif + case IF_PROTO_CISCO: set_proto_cisco(); break; + case IF_PROTO_FR: set_proto_fr(); break; + + case IF_PROTO_PPP: + case IF_PROTO_X25: + req.ifr_settings.ifs_ifsu.sync = NULL; /* FIXME */ + req.ifr_settings.size = 0; + + if (!ioctl(sock, SIOCWANDEV, &req)) + break; + + error("Unable to set %s protocol information: %s\n", + req.ifr_settings.type == IF_PROTO_PPP + ? "PPP" : "X.25", strerror(errno)); + + default: error("Unknown protocol %u\n", req.ifr_settings.type); + } + + if (argc > 0) + error("Unexpected parameter: %s\n", argv[0]); + + close(sock); + exit(0); +} + + + +static void set_pvc(void) +{ + char *op = argv[0]; + parsertab ops[] = {{ "create", IF_PROTO_FR_ADD_PVC }, + { "delete", IF_PROTO_FR_DEL_PVC }, + { NULL, 0 }}; + fr_proto_pvc pvc; + + memset(&pvc, 0, sizeof(pvc)); + + if (checktab(ops, &req.ifr_settings.type)) + return; + +#ifdef IF_PROTO_FR_ETH_PVC + if (!match("ether", &pvc.dlci, 0, 1023)) { + if (req.ifr_settings.type == IF_PROTO_FR_ADD_PVC) + req.ifr_settings.type = IF_PROTO_FR_ADD_ETH_PVC; + else + req.ifr_settings.type = IF_PROTO_FR_DEL_ETH_PVC; + + } else +#endif + if (match(NULL, &pvc.dlci, 0, 1023)) + return; + + if (argc != 0) + return; + + req.ifr_settings.ifs_ifsu.fr_pvc = &pvc; + req.ifr_settings.size = sizeof(pvc); + + if (ioctl(sock, SIOCWANDEV, &req)) + error("Unable to %s PVC: %s\n", op, strerror(errno)); + exit(0); +} + + + +static void private(void) +{ + if (argc < 1) + return; + + if (!strcmp(argv[0], "private")) { + if (argc != 1) + return; + if (ioctl(sock, SIOCDEVPRIVATE, &req)) + error("SIOCDEVPRIVATE: %s\n", strerror(errno)); + exit(0); + } +} + + + +static void show_port(void) +{ + const char *s; + char buffer[128]; + const te1_settings *te1 = (void*)buffer; + const raw_hdlc_proto *raw = (void*)buffer; + const cisco_proto *cisco = (void*)buffer; + const fr_proto *fr = (void*)buffer; +#ifdef IF_PROTO_FR_PVC + const fr_proto_pvc_info *pvc = (void*)buffer; +#endif + req.ifr_settings.ifs_ifsu.sync = (void*)buffer; /* FIXME */ + + printf("%s: ", req.ifr_name); + + req.ifr_settings.size = sizeof(buffer); + req.ifr_settings.type = IF_GET_IFACE; + + if (ioctl(sock, SIOCWANDEV, &req)) + if (errno != EINVAL) { + printf("unable to get interface information: %s\n", + strerror(errno)); + close(sock); + exit(1); + } + + /* Get and print physical interface settings */ + if (req.ifr_settings.type == IF_IFACE_SYNC_SERIAL) + s = ""; /* Unspecified serial interface */ + else + s = tabstr(req.ifr_settings.type, ifaces, NULL); + + if (!s) + printf("unknown interface 0x%x\n", req.ifr_settings.type); + else { + if (*s) + printf("interface %s ", s); + + printf("clock %s", tabstr(te1->clock_type, clocks, + "type unknown")); + if (te1->clock_type == CLOCK_INT || + te1->clock_type == CLOCK_TXINT) + printf(" rate %u", te1->clock_rate); + + if (te1->loopback) + printf(" loopback"); + + if (req.ifr_settings.type == IF_IFACE_E1 || + req.ifr_settings.type == IF_IFACE_T1) { + unsigned int u; + printf(" slotmap "); + for (u = te1->slot_map; u != 0; u /= 2) + printf("%u", u % 2); + } + printf("\n"); + } + + /* Get and print protocol settings */ + do { + printf("\t"); + req.ifr_settings.size = sizeof(buffer); + req.ifr_settings.type = IF_GET_PROTO; + + if (ioctl(sock, SIOCWANDEV, &req)) { + if (errno == EINVAL) + printf("no protocol set\n"); + else + printf("unable to get protocol information: " + "%s\n", strerror(errno)); + break; + } + + switch(req.ifr_settings.type) { + case IF_PROTO_FR: + printf("protocol fr lmi %s", + tabstr(fr->lmi, lmi, "unknown")); + if (fr->lmi == LMI_ANSI || + fr->lmi == LMI_CCITT) + printf("%s t391 %u t392 %u n391 %u n392 %u " + "n393 %u\n", + fr->dce ? " dce" : "", + fr->t391, + fr->t392, + fr->n391, + fr->n392, + fr->n393); + else + putchar('\n'); + break; + +#ifdef IF_PROTO_FR_PVC + case IF_PROTO_FR_PVC: + printf("Frame-Relay PVC: DLCI %u, master device %s\n", + pvc->dlci, pvc->master); + break; +#endif + +#ifdef IF_PROTO_FR_ETH_PVC + case IF_PROTO_FR_ETH_PVC: + printf("Frame-Relay PVC (Ethernet emulation): DLCI %u," + " master device %s\n", pvc->dlci, pvc->master); + break; +#endif + + case IF_PROTO_HDLC: + printf("protocol hdlc %s %s\n", + tabstr(raw->encoding, hdlc_enc, "unknown"), + tabstr(raw->parity, hdlc_par, "unknown")); + break; + +#ifdef IF_PROTO_HDLC_ETH + case IF_PROTO_HDLC_ETH: + printf("protocol hdlc-eth %s %s\n", + tabstr(raw->encoding, hdlc_enc, "unknown"), + tabstr(raw->parity, hdlc_par, "unknown")); + break; +#endif + + case IF_PROTO_CISCO: + printf("protocol cisco interval %u timeout %u\n", + cisco->interval, + cisco->timeout); + break; + + case IF_PROTO_PPP: + printf("protocol ppp\n"); + break; + + case IF_PROTO_X25: + printf("protocol x25\n"); + break; + + default: + printf("unknown protocol %u\n", req.ifr_settings.type); + } + }while(0); + + close(sock); + exit(0); +} + + + +static void usage(void) +{ + fprintf(stderr, "sethdlc version 1.15\n" + "Copyright (C) 2000 - 2003 Krzysztof Halasa <khc@pm.waw.pl>\n" + "\n" + "Usage: sethdlc INTERFACE [PHYSICAL] [clock CLOCK] [LOOPBACK] " + "[slotmap SLOTMAP]\n" + " sethdlc INTERFACE [PROTOCOL]\n" + " sethdlc INTERFACE create | delete" +#ifdef IF_PROTO_FR_ETH_PVC + " [ether]" +#endif + " DLCI\n" + " sethdlc INTERFACE private...\n" + "\n" + "PHYSICAL := v24 | v35 | x21 | e1 | t1\n" + "CLOCK := int [rate RATE] | ext | txint [rate RATE] | txfromrx\n" + "LOOPBACK := loopback | lb\n" + "\n" + "PROTOCOL := hdlc [ENCODING] [PARITY] |\n" +#ifdef IF_PROTO_HDLC_ETH + " hdlc-eth [ENCODING] [PARITY] |\n" +#endif + " cisco [interval val] [timeout val] |\n" + " fr [lmi LMI] |\n" + " ppp |\n" + " x25\n" + "\n" + "ENCODING := nrz | nrzi | fm-mark | fm-space | manchester\n" + "PARITY := no-parity | crc16 | crc16-pr0 | crc16-itu | crc16-itu-pr0 | crc32-itu\n" + "LMI := none | ansi [LMI_SPEC] | ccitt [LMI_SPEC]\n" + "LMI_SPEC := [dce] [t391 val] [t392 val] [n391 val] [n392 val] [n393 val]\n"); + exit(0); +} + + + +int main(int arg_c, char *arg_v[]) +{ + argc = arg_c; + argv = arg_v; + + if (argc <= 1) + usage(); + + sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sock < 0) + error("Unable to create socket: %s\n", strerror(errno)); + + zap_copy_string(req.ifr_name, argv[1], sizeof(req.ifr_name)); /* Device name */ + + if (argc == 2) + show_port(); + + argc -= 2; + argv += 2; + + set_iface(); + set_proto(); + set_pvc(); + private(); + + close(sock); + usage(); + exit(0); +} diff --git a/timertest.c b/timertest.c new file mode 100644 index 0000000..3a0c2e4 --- /dev/null +++ b/timertest.c @@ -0,0 +1,54 @@ +#include <string.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/select.h> +#include <sys/time.h> +#include <errno.h> + +#ifdef STANDALONE_ZAPATA +#include "kernel/zaptel.h" +#else +#include <zaptel/zaptel.h> +#endif + +int main(int argc, char *argv[]) +{ + int fd; + int x = 8000; + int res; + fd_set fds; + struct timeval orig, now; + fd = open("/dev/zap/timer", O_RDWR); + if (fd < 0) { + fprintf(stderr, "Unable to open timer: %s\n", strerror(errno)); + exit(1); + } + printf("Opened timer...\n"); + if (ioctl(fd, ZT_TIMERCONFIG, &x)) { + fprintf(stderr, "Unable to set timer: %s\n", strerror(errno)); + exit(1); + } + printf("Set timer duration to %d samples (%d ms)\n", x, x/8); + printf("Waiting...\n"); + gettimeofday(&orig, NULL); + for(;;) { + FD_ZERO(&fds); + FD_SET(fd, &fds); + res = select(fd + 1, NULL, NULL, &fds, NULL); + if (res != 1) { + fprintf(stderr, "Unexpected result %d: %s\n", res, strerror(errno)); + exit(1); + } + x = -1; + if (ioctl(fd, ZT_TIMERACK, &x)) { + fprintf(stderr, "Unable to ack timer: %s\n", strerror(errno)); + exit(1); + } + gettimeofday(&now, NULL); + printf("Timer Expired (%ld ms)!\n", (now.tv_sec - orig.tv_sec) * 1000 + (now.tv_usec - orig.tv_usec) / 1000); + } + exit(0); +} diff --git a/tonezone.c b/tonezone.c new file mode 100644 index 0000000..f48cf88 --- /dev/null +++ b/tonezone.c @@ -0,0 +1,512 @@ +/* + * BSD Telephony Of Mexico "Tormenta" Tone Zone Support 2/22/01 + * + * Working with the "Tormenta ISA" Card + * + * This program is free software; you can redistribute it and/or modify + * it under thet erms of the GNU Lesser 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 Lesser 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Primary Author: Mark Spencer <markster@digium.com> + * + */ + +#include <stdio.h> +#include <string.h> +#include <math.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> + +#include "kernel/zaptel.h" +#include "tonezone.h" + +#define DEFAULT_ZT_DEV "/dev/zap/ctl" + +#define MAX_SIZE 16384 +#define CLIP 32635 +#define BIAS 0x84 + +struct tone_zone *tone_zone_find(char *country) +{ + struct tone_zone *z; + z = builtin_zones; + while(z->zone > -1) { + if (!strcasecmp(country, z->country)) + return z; + z++; + } + return NULL; +} + +struct tone_zone *tone_zone_find_by_num(int id) +{ + struct tone_zone *z; + z = builtin_zones; + while(z->zone > -1) { + if (z->zone == id) + return z; + z++; + } + return NULL; +} + +#define LEVEL -10 + +static int build_tone(void *data, int size, struct tone_zone_sound *t, int *count) +{ + char *dup, *s; + struct zt_tone_def *td=NULL; + int firstnobang = -1; + int freq1, freq2, time; + int modulate = 0; + float gain; + int used = 0; + dup = strdup(t->data); + s = strtok(dup, ","); + while(s && strlen(s)) { + /* Handle optional ! which signifies don't start here*/ + if (s[0] == '!') + s++; + else if (firstnobang < 0) { +#if 0 + printf("First no bang: %s\n", s); +#endif + firstnobang = *count; + } + if (sscanf(s, "%d+%d/%d", &freq1, &freq2, &time) == 3) { + /* f1+f2/time format */ +#if 0 + printf("f1+f2/time format: %d, %d, %d\n", freq1, freq2, time); +#endif + } else if (sscanf(s, "%d*%d/%d", &freq1, &freq2, &time) == 3) { + /* f1*f2/time format */ + modulate = 1; +#if 0 + printf("f1+f2/time format: %d, %d, %d\n", freq1, freq2, time); +#endif + } else if (sscanf(s, "%d+%d", &freq1, &freq2) == 2) { +#if 0 + printf("f1+f2 format: %d, %d\n", freq1, freq2); +#endif + time = 0; + } else if (sscanf(s, "%d*%d", &freq1, &freq2) == 2) { + modulate = 1; +#if 0 + printf("f1+f2 format: %d, %d\n", freq1, freq2); +#endif + time = 0; + } else if (sscanf(s, "%d/%d", &freq1, &time) == 2) { +#if 0 + printf("f1/time format: %d, %d\n", freq1, time); +#endif + freq2 = 0; + } else if (sscanf(s, "%d", &freq1) == 1) { +#if 0 + printf("f1 format: %d\n", freq1); +#endif + firstnobang = *count; + freq2 = 0; + time = 0; + } else { + fprintf(stderr, "tone component '%s' of '%s' is a syntax error\n", s,t->data); + return -1; + } +#if 0 + printf("Using %d samples for %d and %d\n", time * 8, freq1, freq2); +#endif + if (size < sizeof(*td)) { + fprintf(stderr, "Not enough space for tones\n"); + return -1; + } + td = data; + + /* Bring it down -8 dbm */ + gain = pow(10.0, (LEVEL - 3.14) / 20.0) * 65536.0 / 2.0; + + td->fac1 = 2.0 * cos(2.0 * M_PI * (freq1 / 8000.0)) * 32768.0; + td->init_v2_1 = sin(-4.0 * M_PI * (freq1 / 8000.0)) * gain; + td->init_v3_1 = sin(-2.0 * M_PI * (freq1 / 8000.0)) * gain; + + td->fac2 = 2.0 * cos(2.0 * M_PI * (freq2 / 8000.0)) * 32768.0; + td->init_v2_2 = sin(-4.0 * M_PI * (freq2 / 8000.0)) * gain; + td->init_v3_2 = sin(-2.0 * M_PI * (freq2 / 8000.0)) * gain; + + td->modulate = modulate; + + data += sizeof(*td); + used += sizeof(*td); + size -= sizeof(*td); + td->tone = t->toneid; + if (time) { + /* We should move to the next tone */ + td->next = *count + 1; + td->samples = time * 8; + } else { + /* Stay with us */ + td->next = *count; + td->samples = 8000; + } + *count += 1; + s = strtok(NULL, ","); + } + if (td && time) { + /* If we don't end on a solid tone, return */ + td->next = firstnobang; + } + if (firstnobang < 0) + fprintf(stderr, "tone '%s' does not end with a solid tone or silence (all tone components have an exclamation mark)\n", t->data); + + return used; +} + +char *tone_zone_tone_name(int id) +{ + static char tmp[80]; + switch(id) { + case ZT_TONE_DIALTONE: + return "Dialtone"; + case ZT_TONE_BUSY: + return "Busy"; + case ZT_TONE_RINGTONE: + return "Ringtone"; + case ZT_TONE_CONGESTION: + return "Congestion"; + case ZT_TONE_CALLWAIT: + return "Call Waiting"; + case ZT_TONE_DIALRECALL: + return "Dial Recall"; + case ZT_TONE_RECORDTONE: + return "Record Tone"; + case ZT_TONE_CUST1: + return "Custom 1"; + case ZT_TONE_CUST2: + return "Custom 2"; + case ZT_TONE_INFO: + return "Special Information"; + case ZT_TONE_STUTTER: + return "Stutter Dialtone"; + default: + snprintf(tmp, sizeof(tmp), "Unknown tone %d", id); + return tmp; + } +} + +#ifdef TONEZONE_DRIVER +static void dump_tone_zone(void *data, int size) +{ + struct zt_tone_def_header *z; + struct zt_tone_def *td; + int x; + int len = sizeof(*z); + + z = data; + data += sizeof(*z); + printf("Header: %d tones, %d bytes of data, zone %d (%s)\n", + z->count, size, z->zone, z->name); + for (x = 0; x < z->count; x++) { + td = data; + printf("Tone Fragment %d: tone is %d, next is %d, %d samples\n", + x, td->tone, td->next, td->samples); + data += sizeof(*td); + len += sizeof(*td); + } + printf("Total measured bytes of data: %d\n", len); +} +#endif + +/* Tone frequency tables */ +struct mf_tone { + int tone; + float f1; /* first freq */ + float f2; /* second freq */ +}; + +static struct mf_tone dtmf_tones[] = { + { ZT_TONE_DTMF_0, 941.0, 1336.0 }, + { ZT_TONE_DTMF_1, 697.0, 1209.0 }, + { ZT_TONE_DTMF_2, 697.0, 1336.0 }, + { ZT_TONE_DTMF_3, 697.0, 1477.0 }, + { ZT_TONE_DTMF_4, 770.0, 1209.0 }, + { ZT_TONE_DTMF_5, 770.0, 1336.0 }, + { ZT_TONE_DTMF_6, 770.0, 1477.0 }, + { ZT_TONE_DTMF_7, 852.0, 1209.0 }, + { ZT_TONE_DTMF_8, 852.0, 1336.0 }, + { ZT_TONE_DTMF_9, 852.0, 1477.0 }, + { ZT_TONE_DTMF_s, 941.0, 1209.0 }, + { ZT_TONE_DTMF_p, 941.0, 1477.0 }, + { ZT_TONE_DTMF_A, 697.0, 1633.0 }, + { ZT_TONE_DTMF_B, 770.0, 1633.0 }, + { ZT_TONE_DTMF_C, 852.0, 1633.0 }, + { ZT_TONE_DTMF_D, 941.0, 1633.0 }, + { 0, 0, 0 } +}; + +static struct mf_tone mfr1_tones[] = { + { ZT_TONE_MFR1_0, 1300.0, 1500.0 }, + { ZT_TONE_MFR1_1, 700.0, 900.0 }, + { ZT_TONE_MFR1_2, 700.0, 1100.0 }, + { ZT_TONE_MFR1_3, 900.0, 1100.0 }, + { ZT_TONE_MFR1_4, 700.0, 1300.0 }, + { ZT_TONE_MFR1_5, 900.0, 1300.0 }, + { ZT_TONE_MFR1_6, 1100.0, 1300.0 }, + { ZT_TONE_MFR1_7, 700.0, 1500.0 }, + { ZT_TONE_MFR1_8, 900.0, 1500.0 }, + { ZT_TONE_MFR1_9, 1100.0, 1500.0 }, + { ZT_TONE_MFR1_KP, 1100.0, 1700.0 }, /* KP */ + { ZT_TONE_MFR1_ST, 1500.0, 1700.0 }, /* ST */ + { ZT_TONE_MFR1_STP, 900.0, 1700.0 }, /* KP' or ST' */ + { ZT_TONE_MFR1_ST2P, 1300.0, 1700.0 }, /* KP'' or ST'' */ + { ZT_TONE_MFR1_ST3P, 700.0, 1700.0 }, /* KP''' or ST''' */ + { 0, 0, 0 } +}; + +static struct mf_tone mfr2_fwd_tones[] = { + { ZT_TONE_MFR2_FWD_1, 1380.0, 1500.0 }, + { ZT_TONE_MFR2_FWD_2, 1380.0, 1620.0 }, + { ZT_TONE_MFR2_FWD_3, 1500.0, 1620.0 }, + { ZT_TONE_MFR2_FWD_4, 1380.0, 1740.0 }, + { ZT_TONE_MFR2_FWD_5, 1500.0, 1740.0 }, + { ZT_TONE_MFR2_FWD_6, 1620.0, 1740.0 }, + { ZT_TONE_MFR2_FWD_7, 1380.0, 1860.0 }, + { ZT_TONE_MFR2_FWD_8, 1500.0, 1860.0 }, + { ZT_TONE_MFR2_FWD_9, 1620.0, 1860.0 }, + { ZT_TONE_MFR2_FWD_10, 1740.0, 1860.0 }, + { ZT_TONE_MFR2_FWD_11, 1380.0, 1980.0 }, + { ZT_TONE_MFR2_FWD_12, 1500.0, 1980.0 }, + { ZT_TONE_MFR2_FWD_13, 1620.0, 1980.0 }, + { ZT_TONE_MFR2_FWD_14, 1740.0, 1980.0 }, + { ZT_TONE_MFR2_FWD_15, 1860.0, 1980.0 }, + { 0, 0, 0 } +}; + +static struct mf_tone mfr2_rev_tones[] = { + { ZT_TONE_MFR2_REV_1, 1020.0, 1140.0 }, + { ZT_TONE_MFR2_REV_2, 900.0, 1140.0 }, + { ZT_TONE_MFR2_REV_3, 900.0, 1020.0 }, + { ZT_TONE_MFR2_REV_4, 780.0, 1140.0 }, + { ZT_TONE_MFR2_REV_5, 780.0, 1020.0 }, + { ZT_TONE_MFR2_REV_6, 780.0, 900.0 }, + { ZT_TONE_MFR2_REV_7, 660.0, 1140.0 }, + { ZT_TONE_MFR2_REV_8, 660.0, 1020.0 }, + { ZT_TONE_MFR2_REV_9, 660.0, 900.0 }, + { ZT_TONE_MFR2_REV_10, 660.0, 780.0 }, + { ZT_TONE_MFR2_REV_11, 540.0, 1140.0 }, + { ZT_TONE_MFR2_REV_12, 540.0, 1020.0 }, + { ZT_TONE_MFR2_REV_13, 540.0, 900.0 }, + { ZT_TONE_MFR2_REV_14, 540.0, 780.0 }, + { ZT_TONE_MFR2_REV_15, 540.0, 660.0 }, + { 0, 0, 0 } +}; + +static int build_mf_tones(void *data, int size, int *count, struct mf_tone *tone, int low_tone_level, int high_tone_level) +{ + struct zt_tone_def *td; + float gain; + int used = 0; + + while (tone->tone) { + if (size < sizeof(*td)) { + fprintf(stderr, "Not enough space for samples\n"); + return -1; + } + td = data; + data += sizeof(*td); + used += sizeof(*td); + size -= sizeof(*td); + td->tone = tone->tone; + *count += 1; + + /* Bring it down 6 dBm */ + gain = pow(10.0, (low_tone_level - 3.14) / 20.0) * 65536.0 / 2.0; + td->fac1 = 2.0 * cos(2.0 * M_PI * (tone->f1 / 8000.0)) * 32768.0; + td->init_v2_1 = sin(-4.0 * M_PI * (tone->f1 / 8000.0)) * gain; + td->init_v3_1 = sin(-2.0 * M_PI * (tone->f1 / 8000.0)) * gain; + + gain = pow(10.0, (high_tone_level - 3.14) / 20.0) * 65536.0 / 2.0; + td->fac2 = 2.0 * cos(2.0 * M_PI * (tone->f2 / 8000.0)) * 32768.0; + td->init_v2_2 = sin(-4.0 * M_PI * (tone->f2 / 8000.0)) * gain; + td->init_v3_2 = sin(-2.0 * M_PI * (tone->f2 / 8000.0)) * gain; + + tone++; + } + + return used; +} + +int tone_zone_register_zone(int fd, struct tone_zone *z) +{ + char buf[MAX_SIZE]; + int res; + int count = 0; + int x; + int space = MAX_SIZE; + void *ptr = buf; + int iopenedit = 1; + struct zt_tone_def_header *h; + + memset(buf, 0, sizeof(buf)); + + h = ptr; + ptr += sizeof(*h); + space -= sizeof(*h); + h->zone = z->zone; + + zap_copy_string(h->name, z->description, sizeof(h->name)); + + for (x = 0; x < ZT_MAX_CADENCE; x++) + h->ringcadence[x] = z->ringcadence[x]; + + for (x = 0; x < ZT_TONE_MAX; x++) { + if (!strlen(z->tones[x].data)) + continue; + +#if 0 + printf("Tone: %d, string: %s\n", z->tones[x].toneid, z->tones[x].data); +#endif + if ((res = build_tone(ptr, space, &z->tones[x], &count)) < 0) { + fprintf(stderr, "Tone %d not built.\n", x); + return -1; + } + ptr += res; + space -= res; + } + + if ((res = build_mf_tones(ptr, space, &count, dtmf_tones, z->dtmf_low_level, z->dtmf_high_level)) < 0) { + fprintf(stderr, "Could not build DTMF tones.\n"); + return -1; + } + ptr += res; + space -= res; + + if ((res = build_mf_tones(ptr, space, &count, mfr1_tones, z->mfr1_level, z->mfr1_level)) < 0) { + fprintf(stderr, "Could not build MFR1 tones.\n"); + return -1; + } + ptr += res; + space -= res; + + if ((res = build_mf_tones(ptr, space, &count, mfr2_fwd_tones, z->mfr2_level, z->mfr2_level)) < 0) { + fprintf(stderr, "Could not build MFR2 FWD tones.\n"); + return -1; + } + ptr += res; + space -= res; + + if ((res = build_mf_tones(ptr, space, &count, mfr2_rev_tones, z->mfr2_level, z->mfr2_level)) < 0) { + fprintf(stderr, "Could not build MFR2 REV tones.\n"); + return -1; + } + ptr += res; + space -= res; + + h->count = count; + + if (fd < 0) { + if ((fd = open(DEFAULT_ZT_DEV, O_RDWR)) < 0) { + fprintf(stderr, "Unable to open %s and fd not provided\n", DEFAULT_ZT_DEV); + return -1; + } + iopenedit = 1; + } + + x = z->zone; + if ((res = ioctl(fd, ZT_FREEZONE, &x))) { + if (errno != EBUSY) + fprintf(stderr, "ioctl(ZT_FREEZONE) failed: %s\n", strerror(errno)); + return res; + } + +#if defined(TONEZONE_DRIVER) + dump_tone_zone(h, MAX_SIZE - space); +#endif + + if ((res = ioctl(fd, ZT_LOADZONE, h))) { + fprintf(stderr, "ioctl(ZT_LOADZONE) failed: %s\n", strerror(errno)); + return res; + } + + if (iopenedit) + close(fd); + + return res; +} + +int tone_zone_register(int fd, char *country) +{ + struct tone_zone *z; + z = tone_zone_find(country); + if (z) { + return tone_zone_register_zone(-1, z); + } else { + return -1; + } +} + +int tone_zone_set_zone(int fd, char *country) +{ + int res=-1; + struct tone_zone *z; + if (fd > -1) { + z = tone_zone_find(country); + if (z) + res = ioctl(fd, ZT_SETTONEZONE, &z->zone); + if ((res < 0) && (errno == ENODATA)) { + tone_zone_register_zone(fd, z); + res = ioctl(fd, ZT_SETTONEZONE, &z->zone); + } + } + return res; +} + +int tone_zone_get_zone(int fd) +{ + int x=-1; + if (fd > -1) { + ioctl(fd, ZT_GETTONEZONE, &x); + return x; + } + return -1; +} + +int tone_zone_play_tone(int fd, int tone) +{ + struct tone_zone *z; + int res = -1; + int zone; + +#if 0 + fprintf(stderr, "Playing tone %d (%s) on %d\n", tone, tone_zone_tone_name(tone), fd); +#endif + if (fd > -1) { + res = ioctl(fd, ZT_SENDTONE, &tone); + if ((res < 0) && (errno == ENODATA)) { + ioctl(fd, ZT_GETTONEZONE, &zone); + z = tone_zone_find_by_num(zone); + if (z) { + res = tone_zone_register_zone(fd, z); + /* Recall the zone */ + ioctl(fd, ZT_SETTONEZONE, &zone); + if (res < 0) { + fprintf(stderr, "Failed to register zone '%s': %s\n", z->description, strerror(errno)); + } else { + res = ioctl(fd, ZT_SENDTONE, &tone); + } + } else + fprintf(stderr, "Don't know anything about zone %d\n", zone); + } + } + return res; +} diff --git a/tonezone.h b/tonezone.h new file mode 100644 index 0000000..ca795f6 --- /dev/null +++ b/tonezone.h @@ -0,0 +1,86 @@ +/* + * BSD Telephony Of Mexico "Tormenta" Tone Zone Support 2/22/01 + * + * Working with the "Tormenta ISA" Card + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser 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 Lesser 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Primary Author: Mark Spencer <markster@linux-support.net> + * + */ + +#ifndef _TONEZONE_H +#define _TONEZONE_H + +#ifdef STANDALONE_ZAPATA +#include "kernel/zaptel.h" +#else +#include <zaptel/zaptel.h> +#endif + +struct tone_zone_sound { + int toneid; + char data[256]; /* Actual zone description */ + /* Description is a series of tones of the format: + [!]freq1[+freq2][/time] separated by commas. There + are no spaces. The sequence is repeated back to the + first tone description not preceeded by !. time is + specified in milliseconds */ +}; + +struct tone_zone { + int zone; /* Zone number */ + char country[10]; /* Country code */ + char description[40]; /* Description */ + int ringcadence[ZT_MAX_CADENCE]; /* Ring cadence */ + struct tone_zone_sound tones[ZT_TONE_MAX]; + int dtmf_high_level; /* Power level of high frequency component + of DTMF, expressed in dBm0. */ + int dtmf_low_level; /* Power level of low frequency component + of DTMF, expressed in dBm0. */ + int mfr1_level; /* Power level of MFR1, expressed in dBm0. */ + int mfr2_level; /* Power level of MFR2, expressed in dBm0. */ +}; + +extern struct tone_zone builtin_zones[]; + +/* Register a given two-letter tone zone if we can */ +int tone_zone_register(int fd, char *country); + +/* Register a given two-letter tone zone if we can */ +int tone_zone_register_zone(int fd, struct tone_zone *z); + +/* Retrieve a raw tone zone structure */ +struct tone_zone *tone_zone_find(char *country); + +/* Retrieve a raw tone zone structure by id instead of country*/ +struct tone_zone *tone_zone_find_by_num(int id); + +/* Retrieve a string name for a given tone id */ +char *tone_zone_tone_name(int id); + +/* Set a given file descriptor into a given country -- USE THIS + INTERFACE INSTEAD OF THE IOCTL ITSELF. Auto-loads tone + zone if necessary */ +int tone_zone_set_zone(int fd, char *country); + +/* Get the current tone zone */ +int tone_zone_get_zone(int fd); + +/* Play a given tone, loading tone zone automatically + if necessary */ +int tone_zone_play_tone(int fd, int toneid); + +#endif diff --git a/zonedata.c b/zonedata.c new file mode 100644 index 0000000..7cb2938 --- /dev/null +++ b/zonedata.c @@ -0,0 +1,931 @@ +/* + * BSD Telephony Of Mexico "Tormenta" Tone Zone Support 2/22/01 + * + * Working with the "Tormenta ISA" Card + * + * This program is free software; you can redistribute it and/or modify + * it under thet erms of the GNU Lesser 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 Lesser 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Primary Author: Mark Spencer <markster@digium.com> + * + * This information from ITU E.180 Supplement 2. + * UK information from BT SIN 350 Issue 1.1 + * Helpful reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf + */ +#include "tonezone.h" + +struct tone_zone builtin_zones[] = +{ + { .zone = 0, + .country = "us", + .description = "United States / North America", + .ringcadence = { 2000, 4000 }, + .tones = { + { ZT_TONE_DIALTONE, "350+440" }, + { ZT_TONE_BUSY, "480+620/500,0/500" }, + { ZT_TONE_RINGTONE, "440+480/2000,0/4000" }, + { ZT_TONE_CONGESTION, "480+620/250,0/250" }, + { ZT_TONE_CALLWAIT, "440/300,0/10000" }, + { ZT_TONE_DIALRECALL, "!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440" }, + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + { ZT_TONE_INFO, "!950/330,!1400/330,!1800/330,0" }, + { ZT_TONE_STUTTER, "!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 1, + .country = "au", + .description = "Australia", + .ringcadence = { 400, 200, 400, 2000 }, + .tones = { + { ZT_TONE_DIALTONE, "413+438" }, + { ZT_TONE_BUSY, "425/375,0/375" }, + { ZT_TONE_RINGTONE, "413+438/400,0/200,413+438/400,0/2000" }, + /* XXX Congestion: Should reduce by 10 db every other cadence XXX */ + { ZT_TONE_CONGESTION, "425/375,0/375,420/375,0/375" }, + { ZT_TONE_CALLWAIT, "425/100,0/200,425/200,0/4400" }, + { ZT_TONE_DIALRECALL, "413+428" }, + { ZT_TONE_RECORDTONE, "!425/1000,!0/15000,425/360,0/15000" }, + { ZT_TONE_INFO, "425/2500,0/500" }, + { ZT_TONE_STUTTER, "413+438/100,0/40" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 2, + .country = "fr", + .description = "France", + .ringcadence = { 1500, 3500 }, + .tones = { + /* Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf */ + /* Dialtone can also be 440+330 */ + { ZT_TONE_DIALTONE, "440" }, + { ZT_TONE_BUSY, "440/500,0/500" }, + { ZT_TONE_RINGTONE, "440/1500,0/3500" }, + /* CONGESTION - not specified */ + { ZT_TONE_CONGESTION, "440/250,0/250" }, + { ZT_TONE_CALLWAIT, "440/300,0/10000" }, + /* DIALRECALL - not specified */ + { ZT_TONE_DIALRECALL, "!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440" }, + /* RECORDTONE - not specified */ + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + { ZT_TONE_INFO, "!950/330,!1400/330,!1800/330,0" }, + { ZT_TONE_STUTTER, "!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,440" }, + }, + .dtmf_high_level = -11, + .dtmf_low_level = -9, + .mfr1_level = -7, + .mfr2_level = -8, + }, + { .zone = 3, + .country = "nl", + .description = "Netherlands", + .ringcadence = { 1000, 4000 }, + .tones = { + /* Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf */ + /* Most of these 425's can also be 450's */ + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "425/500,0/500" }, + { ZT_TONE_RINGTONE, "425/1000,0/4000" }, + { ZT_TONE_CONGESTION, "425/250,0/250" }, + { ZT_TONE_CALLWAIT, "425/500,0/9500" }, + /* DIALRECALL - not specified */ + { ZT_TONE_DIALRECALL, "!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425" }, + /* RECORDTONE - not specified */ + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + { ZT_TONE_INFO, "950/330,1400/330,1800/330,0/1000" }, + { ZT_TONE_STUTTER, "425/500,0/50" }, + }, + .dtmf_high_level = -11, + .dtmf_low_level = -9, + .mfr1_level = -7, + .mfr2_level = -8, + }, + { .zone = 4, + .country = "uk", + .description = "United Kingdom", + .ringcadence = { 400, 200, 400, 2000 }, + .tones = { + /* From British Telecom SIN350 v1.2 */ + { ZT_TONE_DIALTONE, "350+440" }, + { ZT_TONE_BUSY, "400/375,0/375" }, + { ZT_TONE_RINGTONE, "400+450/400,0/200,400+450/400,0/2000" }, + { ZT_TONE_CONGESTION, "400/400,0/350,400/225,0/525" }, + { ZT_TONE_CALLWAIT, "400/100,0/4000" }, + { ZT_TONE_DIALRECALL, "350+440" }, + { ZT_TONE_RECORDTONE, "1400/500,0/60000" }, + { ZT_TONE_INFO, "950/330,0/15,1400/330,0/15,1800/330,0/1000" }, + { ZT_TONE_STUTTER, "350+440/750,440/750" }, + }, + .dtmf_high_level = -11, + .dtmf_low_level = -13, + .mfr1_level = -7, + .mfr2_level = -8, + }, + { .zone = 5, + .country = "fi", + .description = "Finland", + .ringcadence = { 1000, 4000 }, + .tones = { + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "425/300,0/300" }, + { ZT_TONE_RINGTONE, "425/1000,0/4000" }, + { ZT_TONE_CONGESTION, "425/200,0/200" }, + { ZT_TONE_CALLWAIT, "425/150,0/150,425/150,0/8000" }, + { ZT_TONE_DIALRECALL, "425/650,0/25" }, + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + { ZT_TONE_INFO, "950/650,0/325,950/325,0/30,1400/1300,0/2600" }, + { ZT_TONE_STUTTER, "425/650,0/25" }, + }, + .dtmf_high_level = -11, + .dtmf_low_level = -9, + .mfr1_level = -7, + .mfr2_level = -8, + }, + { .zone = 6, + .country = "es", + .description = "Spain", + .ringcadence = { 1500, 3000}, + .tones = { + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "425/200,0/200" }, + { ZT_TONE_RINGTONE, "425/1500,0/3000" }, + { ZT_TONE_CONGESTION, "425/200,0/200,425/200,0/200,425/200,0/600" }, + { ZT_TONE_CALLWAIT, "425/175,0/175,425/175,0/3500" }, + { ZT_TONE_DIALRECALL, "!425/200,!0/200,!425/200,!0/200,!425/200,!0/200,425" }, + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + { ZT_TONE_INFO, "950/330,0/1000" }, + { ZT_TONE_STUTTER, "425/500,0/50" }, + }, + .dtmf_high_level = -11, + .dtmf_low_level = -13, + .mfr1_level = -7, + .mfr2_level = -8, + }, + { .zone = 7, + .country = "jp", + .description = "Japan", + .ringcadence = { 1000, 2000 }, + .tones = { + { ZT_TONE_DIALTONE, "400" }, + { ZT_TONE_BUSY, "400/500,0/500" }, + { ZT_TONE_RINGTONE, "400+15/1000,0/2000" }, + { ZT_TONE_CONGESTION, "400/500,0/500" }, + { ZT_TONE_CALLWAIT, "400+16/500,0/8000" }, + { ZT_TONE_DIALRECALL, "!400/200,!0/200,!400/200,!0/200,!400/200,!0/200,400" }, + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + { ZT_TONE_INFO, "!950/330,!1400/330,!1800/330,0" }, + { ZT_TONE_STUTTER, "!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400" }, + }, + .dtmf_high_level = -7, + .dtmf_low_level = -7, + .mfr1_level = -7, + .mfr2_level = -8, + }, + { .zone = 8, + .country = "no", + .description = "Norway", + .ringcadence = { 1000, 4000 }, + .tones = { + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "425/500,0/500" }, + { ZT_TONE_RINGTONE, "425/1000,0/4000" }, + { ZT_TONE_CONGESTION, "425/200,0/200" }, + { ZT_TONE_CALLWAIT, "425/200,0/600,425/200,0/10000" }, + { ZT_TONE_DIALRECALL, "470/400,425/400" }, + { ZT_TONE_RECORDTONE, "1400/400,0/15000" }, + { ZT_TONE_INFO, "!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0" }, + { ZT_TONE_STUTTER, "470/400,425/400" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 9, + .country = "at", + .description = "Austria", + .ringcadence = { 1000, 5000 }, + .tones = { + /* Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf */ + { ZT_TONE_DIALTONE, "420" }, + { ZT_TONE_BUSY, "420/400,0/400" }, + { ZT_TONE_RINGTONE, "420/1000,0/5000" }, + { ZT_TONE_CONGESTION, "420/200,0/200" }, + { ZT_TONE_CALLWAIT, "420/40,0/1960" }, + { ZT_TONE_DIALRECALL, "420" }, + /* RECORDTONE - not specified */ + { ZT_TONE_RECORDTONE, "1400/80,0/14920" }, + { ZT_TONE_INFO, "950/330,1450/330,1850/330,0/1000" }, + { ZT_TONE_STUTTER, "380+420" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 10, + .country = "nz", + .description = "New Zealand", + .ringcadence = { 400, 200, 400, 2000 }, + .tones = { + { ZT_TONE_DIALTONE, "400" }, + { ZT_TONE_BUSY, "400/500,0/500" }, + { ZT_TONE_RINGTONE, "400+450/400,0/200,400+450/400,0/2000" }, + { ZT_TONE_CONGESTION, "400/250,0/250" }, + { ZT_TONE_CALLWAIT, "400/250,0/250,400/250,0/3250" }, + { ZT_TONE_DIALRECALL, "!400/100!0/100,!400/100,!0/100,!400/100,!0/100,400" }, + { ZT_TONE_RECORDTONE, "1400/425,0/15000" }, + { ZT_TONE_INFO, "400/750,0/100,400/750,0/100,400/750,0/100,400/750,0/400" }, + { ZT_TONE_STUTTER, "!400/100!0/100,!400/100,!0/100,!400/100,!0/100,!400/100!0/100,!400/100,!0/100,!400/100,!0/100,400" }, + }, + .dtmf_high_level = -11, + .dtmf_low_level = -9, + .mfr1_level = -7, + .mfr2_level = -8, + }, + { .zone = 11, + .country = "it", + .description = "Italy", + .ringcadence = { 1000, 4000 }, + .tones = { + /* Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf */ + { ZT_TONE_DIALTONE, "425/200,0/200,425/600,0/1000" }, + { ZT_TONE_BUSY, "425/500,0/500" }, + { ZT_TONE_RINGTONE, "425/1000,0/4000" }, + { ZT_TONE_CONGESTION, "425/200,0/200" }, + { ZT_TONE_CALLWAIT, "425/400,0/100,425/250,0/100,425/150,0/14000" }, + { ZT_TONE_DIALRECALL, "470/400,425/400" }, + { ZT_TONE_RECORDTONE, "1400/400,0/15000" }, + { ZT_TONE_INFO, "!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0" }, + { ZT_TONE_STUTTER, "470/400,425/400" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 12, + .country = "us-old", + .description = "United States Circa 1950 / North America", + .ringcadence = { 2000, 4000 }, + .tones = { + { ZT_TONE_DIALTONE, "600*120" }, + { ZT_TONE_BUSY, "500*100/500,0/500" }, + { ZT_TONE_RINGTONE, "420*40/2000,0/4000" }, + { ZT_TONE_CONGESTION, "500*100/250,0/250" }, + { ZT_TONE_CALLWAIT, "440/300,0/10000" }, + { ZT_TONE_DIALRECALL, "!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,600*120" }, + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + { ZT_TONE_INFO, "!950/330,!1400/330,!1800/330,0" }, + { ZT_TONE_STUTTER, "!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,600*120" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 13, + .country = "gr", + .description = "Greece", + .ringcadence = { 1000, 4000 }, + .tones = { + { ZT_TONE_DIALTONE, "425/200,0/300,425/700,0/800" }, + { ZT_TONE_BUSY, "425/300,0/300" }, + { ZT_TONE_RINGTONE, "425/1000,0/4000" }, + { ZT_TONE_CONGESTION, "425/200,0/200" }, + { ZT_TONE_CALLWAIT, "425/150,0/150,425/150,0/8000" }, + { ZT_TONE_DIALRECALL, "425/650,0/25" }, + { ZT_TONE_RECORDTONE, "1400/400,0/15000" }, + { ZT_TONE_INFO, "!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0" }, + { ZT_TONE_STUTTER, "425/650,0/25" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 14, + .country = "tw", + .description = "Taiwan", + .ringcadence = { 1000, 4000 }, + .tones = { + { ZT_TONE_DIALTONE, "350+440" }, + { ZT_TONE_BUSY, "480+620/500,0/500" }, + { ZT_TONE_RINGTONE, "440+480/1000,0/2000" }, + { ZT_TONE_CONGESTION, "480+620/250,0/250" }, + { ZT_TONE_CALLWAIT, "350+440/250,0/250,350+440/250,0/3250" }, + { ZT_TONE_DIALRECALL, "300/1500,0/500" }, + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + { ZT_TONE_INFO, "!950/330,!1400/330,!1800/330,0" }, + { ZT_TONE_STUTTER, "!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440" }, + }, + .dtmf_high_level = -11, + .dtmf_low_level = -9, + .mfr1_level = -7, + .mfr2_level = -8, + }, + { .zone = 15, + .country = "cl", + .description = "Chile", + .ringcadence = { 1000, 3000 }, + .tones = { + { ZT_TONE_DIALTONE, "400" }, + { ZT_TONE_BUSY, "400/500,0/500" }, + { ZT_TONE_RINGTONE, "400/1000,0/3000" }, + { ZT_TONE_CONGESTION, "400/200,0/200" }, + { ZT_TONE_CALLWAIT, "400/250,0/8750" }, + { ZT_TONE_DIALRECALL, "!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400" }, + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + { ZT_TONE_INFO, "!950/333,!1400/333,!1800/333,0" }, + { ZT_TONE_STUTTER, "!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 16, + .country = "se", + .description = "Sweden", + .ringcadence = { 1000, 5000 }, + .tones = { + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "425/250,0/250" }, + { ZT_TONE_RINGTONE, "425/1000,0/5000" }, + { ZT_TONE_CONGESTION, "425/250,0/750" }, + { ZT_TONE_CALLWAIT, "425/200,0/500,425/200,0/9100" }, + { ZT_TONE_DIALRECALL, "!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425" }, + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + { ZT_TONE_INFO, "!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024," + "!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024," + "!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024," + "!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024," + "!950/332,!0/24,!1400/332,!0/24,!1800/332,0" }, + /*{ ZT_TONE_STUTTER, "425/320,0/20" }, Real swedish standard, not used for now */ + { ZT_TONE_STUTTER, "!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425" }, + }, + .dtmf_high_level = -9, + .dtmf_low_level = -10, + .mfr1_level = -7, + .mfr2_level = -8, + }, + { .zone = 17, + .country = "be", + .description = "Belgium", + .ringcadence = { 1000, 3000 }, + .tones = { + /* Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf */ + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "425/500,0/500" }, + { ZT_TONE_RINGTONE, "425/1000,0/3000" }, + { ZT_TONE_CONGESTION, "425/167,0/167" }, + { ZT_TONE_CALLWAIT, "1400/175,0/175,1400/175,0/3500" }, + /* DIALRECALL - not specified */ + { ZT_TONE_DIALRECALL, "!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440" }, + /* RECORDTONE - not specified */ + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + { ZT_TONE_INFO, "900/330,1400/330,1800/330,0/1000" }, + { ZT_TONE_STUTTER, "425/1000,0/250" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 18, + .country = "sg", + .description = "Singapore", + .ringcadence = { 400, 200, 400, 2000 }, + .tones = { + /* Reference: http://www.ida.gov.sg/idaweb/doc/download/I397/ida_ts_pstn1_i4r2.pdf */ + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "425/750,0/750" }, + { ZT_TONE_RINGTONE, "425*24/400,0/200,425*24/400,0/2000" }, + { ZT_TONE_CONGESTION, "425/250,0/250" }, + { ZT_TONE_CALLWAIT, "425*24/300,0/200,425*24/300,0/3200" }, + /* DIALRECALL - not specified - use repeating Holding Tone A,B*/ + { ZT_TONE_DIALRECALL, "425*24/500,0/500,425/500,0/2500" }, + /* RECORDTONE - not specified */ + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + { ZT_TONE_INFO, "950/330,1400/330,1800/330,0/1000" }, + { ZT_TONE_STUTTER, "!425/200,!0/200,!425/600,!0/200,!425/200,!0/200,!425/600,!0/200,!425/200,!0/200,!425/600,!0/200,!425/200,!0/200,!425/600,!0/200,425" }, + }, + .dtmf_high_level = -11, + .dtmf_low_level = -9, + .mfr1_level = -7, + .mfr2_level = -8, + }, + { .zone = 19, + .country = "il", + .description = "Israel", + .ringcadence = { 1000, 3000 }, + .tones = { + { ZT_TONE_DIALTONE, "414" }, + { ZT_TONE_BUSY, "414/500,0/500" }, + { ZT_TONE_RINGTONE, "414/1000,0/3000" }, + { ZT_TONE_CONGESTION, "414/250,0/250" }, + { ZT_TONE_CALLWAIT, "414/100,0/100,414/100,0/100,414/600,0/3000" }, + { ZT_TONE_DIALRECALL, "!414/100,!0/100,!414/100,!0/100,!414/100,!0/100,414" }, + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + { ZT_TONE_INFO, "1000/330,1400/330,1800/330,0/1000" }, + { ZT_TONE_STUTTER, "!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,414" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 20, + .country = "br", + .description = "Brazil", + .ringcadence = { 1000, 4000 }, + .tones = { + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "425/250,0/250" }, + { ZT_TONE_RINGTONE, "425/1000,0/4000" }, + { ZT_TONE_CONGESTION, "425/250,0/250,425/750,0/250" }, + { ZT_TONE_CALLWAIT, "425/50,0/1000" }, + { ZT_TONE_DIALRECALL, "350+440" }, + { ZT_TONE_RECORDTONE, "425/250,0/250" }, + { ZT_TONE_INFO, "950/330,1400/330,1800/330" }, + { ZT_TONE_STUTTER, "350+440" } }, + .dtmf_high_level = -10, + .dtmf_low_level = -12, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 21, + .country = "hu", + .description = "Hungary", + .ringcadence = { 1250, 3750 }, + .tones = { + /* Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf */ + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "425/300,0/300" }, + { ZT_TONE_RINGTONE, "425/1250,0/3750" }, + { ZT_TONE_CONGESTION, "425/300,0/300" }, + { ZT_TONE_CALLWAIT, "425/40,0/1960" }, + { ZT_TONE_DIALRECALL, "425+450" }, + /* RECORDTONE - not specified */ + { ZT_TONE_RECORDTONE, "1400/400,0/15000" }, + { ZT_TONE_INFO, "!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0" }, + { ZT_TONE_STUTTER, "350+375+400" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 22, + .country = "lt", + .description = "Lithuania", + .ringcadence = { 1000, 4000 }, + .tones = { + /* Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf */ + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "425/350,0/350" }, + { ZT_TONE_RINGTONE, "425/1000,0/4000" }, + { ZT_TONE_CONGESTION, "425/200,0/200" }, + { ZT_TONE_CALLWAIT, "425/150,0/150,425/150,0/4000" }, + /* DIALRECALL - not specified */ + { ZT_TONE_DIALRECALL, "!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440" }, + /* RECORDTONE - not specified */ + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + { ZT_TONE_INFO, "!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0" }, + /* STUTTER not specified */ + { ZT_TONE_STUTTER, "!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 23, + .country = "pl", + .description = "Poland", + .ringcadence = { 1000, 4000 }, + .tones = { + /* Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf */ + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "425/500,0/500" }, + { ZT_TONE_RINGTONE, "425/1000,0/4000" }, + { ZT_TONE_CONGESTION, "425/500,0/500" }, + { ZT_TONE_CALLWAIT, "425/150,0/150,425/150,0/4000" }, + /* DIALRECALL - not specified */ + { ZT_TONE_DIALRECALL, "!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440" }, + /* RECORDTONE - not specified */ + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + { ZT_TONE_INFO, "!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,0" }, + /* STUTTER not specified */ + { ZT_TONE_STUTTER, "!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 24, + .country = "za", + .description = "South Africa", + .ringcadence = { 400, 200, 400, 2000 }, + .tones = { + { ZT_TONE_DIALTONE, "400*33" }, + { ZT_TONE_BUSY, "400/500,0/500" }, + { ZT_TONE_RINGTONE, "400*33/400,0/200,400*33/400,0/2000" }, + { ZT_TONE_CONGESTION, "400/250,0/250" }, + { ZT_TONE_CALLWAIT, "400*33/250,0/250,400*33/250,0/250,400*33/250,0/250,400*33/250,0/250" }, + /* DIALRECALL - not specified */ + { ZT_TONE_DIALRECALL, "350+440" }, + /* RECORDTONE - not specified */ + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + { ZT_TONE_INFO, "!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0" }, + /* STUTTER not specified */ + { ZT_TONE_STUTTER, "!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,400*33" }, + }, + .dtmf_high_level = -11, + .dtmf_low_level = -13, + .mfr1_level = -7, + .mfr2_level = -8, + }, + { .zone = 25, + .country = "pt", + .description = "Portugal", + .ringcadence = { 1000, 5000 }, + .tones = { + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "425/500,0/500" }, + { ZT_TONE_RINGTONE, "425/1000,0/5000" }, + { ZT_TONE_CONGESTION, "425/200,0/200" }, + { ZT_TONE_CALLWAIT, "425/200,425/200,425/200,0/5000" }, + /* DIALRECALL - not specified */ + { ZT_TONE_DIALRECALL, "425/1000,0/200" }, + /* RECORDTONE - not specified */ + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + { ZT_TONE_INFO, "950/330,1400/330,1800/330,0/1000" }, + /* STUTTER not specified */ + { ZT_TONE_STUTTER, "!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 26, + .country = "ee", + .description = "Estonia", + .ringcadence = { 1000, 4000 }, + .tones = { + /* Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf */ + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "425/300,0/300" }, + { ZT_TONE_RINGTONE, "425/1000,0/4000" }, + { ZT_TONE_CONGESTION, "425/200,0/200" }, + { ZT_TONE_CALLWAIT, "950/650,0/325,950/325,0/30,1400/1300,0/2600" }, + /* DIALRECALL - not specified */ + { ZT_TONE_DIALRECALL, "425/650,0/25" }, + /* RECORDTONE - not specified */ + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + { ZT_TONE_INFO, "950/0,0/325,950/325,0/30,1400/1300,0/2600" }, + /* STUTTER not specified */ + { ZT_TONE_STUTTER, "!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 27, + .country = "mx", + .description = "Mexico", + .ringcadence = { 2000, 4000 }, + .tones = { + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "425/250,0/250" }, + { ZT_TONE_RINGTONE, "425/1000,0/4000" }, + { ZT_TONE_CONGESTION, "425/250,0/250" }, + { ZT_TONE_CALLWAIT, "425/200,0/600,425/200,0/10000" }, + { ZT_TONE_DIALRECALL, "!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440" }, + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + { ZT_TONE_INFO, "950/330,0/30,1400/330,0/30,1800/330,0/1000" }, + { ZT_TONE_STUTTER, "!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440" }, + }, + .dtmf_high_level = -8, + .dtmf_low_level = -6, + .mfr1_level = -7, + .mfr2_level = -8, + }, + { .zone = 28, + .country = "in", + .description = "India", + .ringcadence = { 400, 200, 400, 2000 }, + .tones = { + /* Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf */ + { ZT_TONE_DIALTONE, "400*25" }, + { ZT_TONE_BUSY, "400/750,0/750" }, + { ZT_TONE_RINGTONE, "400*25/400,0/200,400*25/400,0/2000" }, + { ZT_TONE_CONGESTION, "400/250,0/250" }, + { ZT_TONE_CALLWAIT, "400/200,0/100,400/200,0/7500" }, + /* DIALRECALL - not specified */ + { ZT_TONE_DIALRECALL, "!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440" }, + /* RECORDTONE - not specified */ + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + /* INFO - not specified */ + { ZT_TONE_INFO, "!950/330,!1400/330,!1800/330,0/1000" }, + /* STUTTER - not specified */ + { ZT_TONE_STUTTER, "!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 29, + .country = "de", + .description = "Germany", + .ringcadence = { 1000, 4000 }, + .tones = { + /* Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf */ + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "425/480,0/480" }, + { ZT_TONE_RINGTONE, "425/1000,0/4000" }, + { ZT_TONE_CONGESTION, "425/240,0/240" }, + { ZT_TONE_CALLWAIT, "!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,0" }, + /* DIALRECALL - not specified */ + { ZT_TONE_DIALRECALL, "!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425" }, + /* RECORDTONE - not specified */ + { ZT_TONE_RECORDTONE, "1400/80,0/15000" }, + { ZT_TONE_INFO, "950/330,1400/330,1800/330,0/1000" }, + { ZT_TONE_STUTTER, "425+400" }, + }, + .dtmf_high_level = -11, + .dtmf_low_level = -9, + .mfr1_level = -7, + .mfr2_level = -8, + }, + { .zone = 30, + .country = "ch", + .description = "Switzerland", + .ringcadence = { 1000, 4000 }, + .tones = { + /* Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf */ + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "425/500,0/500" }, + { ZT_TONE_RINGTONE, "425/1000,0/4000" }, + { ZT_TONE_CONGESTION, "425/200,0/200" }, + { ZT_TONE_CALLWAIT, "425/200,0/200,425/200,0/4000" }, + /* DIALRECALL - not specified */ + { ZT_TONE_DIALRECALL, "!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425" }, + /* RECORDTONE - not specified */ + { ZT_TONE_RECORDTONE, "1400/80,0/15000" }, + { ZT_TONE_INFO, "950/330,1400/330,1800/330,0/1000" }, + { ZT_TONE_STUTTER, "425+340/1100,0/1100" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 31, + .country = "dk", + .description = "Denmark", + .ringcadence = { 1000, 4000 }, + .tones = { + /* Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf */ + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "425/500,0/500" }, + { ZT_TONE_RINGTONE, "425/1000,0/4000" }, + { ZT_TONE_CONGESTION, "425/200,0/200" }, + { ZT_TONE_CALLWAIT, "!425/200,!0/600,!425/200,!0/3000,!425/200,!0/200,!425/200,0" }, + /* DIALRECALL - not specified */ + { ZT_TONE_DIALRECALL, "!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425" }, + /* RECORDTONE - not specified */ + { ZT_TONE_RECORDTONE, "1400/80,0/15000" }, + { ZT_TONE_INFO, "950/330,1400/330,1800/330,0/1000" }, + /* STUTTER - not specified */ + { ZT_TONE_STUTTER, "425/450,0/50" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 32, + .country = "cz", + .description = "Czech Republic", + .ringcadence = { 1000, 4000 }, + .tones = { + /* Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf */ + { ZT_TONE_DIALTONE, "425/330,0/330,425/660,0/660" }, + { ZT_TONE_BUSY, "425/330,0/330" }, + { ZT_TONE_RINGTONE, "425/1000,0/4000" }, + { ZT_TONE_CONGESTION, "425/165,0/165" }, + { ZT_TONE_CALLWAIT, "425/330,0/9000" }, + /* DIALRECALL - not specified */ + { ZT_TONE_DIALRECALL, "!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425/330,0/330,425/660,0/660" }, + /* RECORDTONE - not specified */ + { ZT_TONE_RECORDTONE, "1400/500,0/14000" }, + { ZT_TONE_INFO, "950/330,0/30,1400/330,0/30,1800/330,0/1000" }, + /* STUTTER - not specified */ + { ZT_TONE_STUTTER, "425/450,0/50" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 33, + .country = "cn", + .description = "China", + .ringcadence = { 1000, 4000 }, + .tones = { + /* Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf */ + { ZT_TONE_DIALTONE, "450" }, + { ZT_TONE_BUSY, "450/350,0/350" }, + { ZT_TONE_RINGTONE, "450/1000,0/4000" }, + { ZT_TONE_CONGESTION, "450/700,0/700" }, + { ZT_TONE_CALLWAIT, "450/400,0/4000" }, + { ZT_TONE_DIALRECALL, "450" }, + { ZT_TONE_RECORDTONE, "950/400,0/10000" }, + { ZT_TONE_INFO, "450/100,0/100,450/100,0/100,450/100,0/100,450/400,0/400" }, + /* STUTTER - not specified */ + { ZT_TONE_STUTTER, "450+425" }, + }, + .dtmf_high_level = -11, + .dtmf_low_level = -9, + .mfr1_level = -7, + .mfr2_level = -8, + }, + { .zone = 34, + .country = "ar", + .description = "Argentina", + .ringcadence = { 1000, 4500 }, + .tones = { + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "425/300,0/300" }, + { ZT_TONE_RINGTONE, "425/1000,0/4500" }, + { ZT_TONE_CONGESTION, "425/200,0/300" }, + { ZT_TONE_CALLWAIT, "425/200,0/9000" }, + { ZT_TONE_DIALRECALL, "!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425/330,0/330,425/660,0/660" }, + { ZT_TONE_RECORDTONE, "1400/500,0/14000" }, + { ZT_TONE_INFO, "425/100,0/100" }, + { ZT_TONE_STUTTER, "425/450,0/50" }, + }, + .dtmf_high_level = -11, + .dtmf_low_level = -9, + .mfr1_level = -7, + .mfr2_level = -8, + }, + { .zone = 35, + .country = "my", + .description = "Malaysia", + .ringcadence = { 400, 200, 400, 2000 }, + .tones = { + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "425/500,0/500" }, + { ZT_TONE_RINGTONE, "425/400,0/200,425/400,0/2000" }, + { ZT_TONE_CONGESTION, "425/500,0/500" }, + { ZT_TONE_CALLWAIT, "425/100,0/4000" }, + { ZT_TONE_DIALRECALL, "350+440" }, + { ZT_TONE_RECORDTONE, "1400/500,0/60000" }, + { ZT_TONE_INFO, "950/330,0/15,1400/330,0/15,1800/330,0/1000" }, + { ZT_TONE_STUTTER, "450+425" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 36, + .country = "th", + .description = "Thailand", + .ringcadence = { 1000, 4000 }, + .tones = { + /* Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf */ + { ZT_TONE_DIALTONE, "400*50" }, + { ZT_TONE_BUSY, "400/500,0/500" }, + { ZT_TONE_RINGTONE, "400/1000,0/4000" }, + { ZT_TONE_CONGESTION, "400/300,0/300" }, + { ZT_TONE_CALLWAIT, "1000/400,10000/400,1000/400" }, + /* DIALRECALL - not specified - use special dial tone instead. */ + { ZT_TONE_DIALRECALL, "400*50/400,0/100,400*50/400,0/100" }, + /* RECORDTONE - not specified */ + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + /* INFO - specified as an announcement - use tones instead. */ + { ZT_TONE_INFO, "950/330,1400/330,1800/330" }, + /* STUTTER - not specified */ + { ZT_TONE_STUTTER, "!400/200,!0/200,!400/600,!0/200,!400/200,!0/200,!400/600,!0/200,!400/200,!0/200,!400/600,!0/200,!400/200,!0/200,!400/600,!0/200,400" }, + }, + .dtmf_high_level = -11, + .dtmf_low_level = -9, + .mfr1_level = -7, + .mfr2_level = -8, + }, + { .zone = 37, + .country = "bg", + .description = "Bulgaria", + .ringcadence = { 1000, 4000 }, + .tones = { + /* Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf */ + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "425/500,0/500" }, + { ZT_TONE_RINGTONE, "425/1000,0/4000" }, + { ZT_TONE_CONGESTION, "425/250,0/250" }, + { ZT_TONE_CALLWAIT, "425/150,0/150,425/150,0/4000" }, + { ZT_TONE_DIALRECALL, "!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425" }, + { ZT_TONE_RECORDTONE, "1400/425,0/15000" }, + { ZT_TONE_INFO, "950/330,1400/330,1800/330,0/1000" }, + { ZT_TONE_STUTTER, "425/1500,0/100" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 38, + .country = "ve", + .description = "Venezuela", + .ringcadence = { 1000, 4000 }, + .tones = { + /* Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf */ + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "425/500,0/500" }, + { ZT_TONE_RINGTONE, "425/1000,0/4000" }, + { ZT_TONE_CONGESTION, "425/250,0/250" }, + { ZT_TONE_CALLWAIT, "400+450/300,0/6000" }, + { ZT_TONE_DIALRECALL, "425" }, + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + { ZT_TONE_INFO, "!950/330,!1440/330,!1800/330,0/1000" }, + /* STUTTER - not specified */ + { ZT_TONE_STUTTER, "!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425" }, + }, + .dtmf_high_level = -7, + .dtmf_low_level = -9, + .mfr1_level = -7, + .mfr2_level = -8, + }, + { .zone = 39, + .country = "ph", + .description = "Philippines", + .ringcadence = { 1000, 4000 }, + .tones = { + /* Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf */ + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "480+620/500,0/500" }, + { ZT_TONE_RINGTONE, "425+480/1000,0/4000" }, + { ZT_TONE_CONGESTION, "480+620/250,0/250" }, + { ZT_TONE_CALLWAIT, "440/300,0/10000" }, + /* DIAL RECALL - not specified */ + { ZT_TONE_DIALRECALL, "!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440" }, + /* RECORD TONE - not specified */ + { ZT_TONE_RECORDTONE, "1400/500,0/15000" }, + /* INFO TONE - not specified */ + { ZT_TONE_INFO, "!950/330,!1400/330,!1800/330,0" }, + /* STUTTER TONE - not specified */ + { ZT_TONE_STUTTER, "!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = 40, + .country = "ru", + .description = "Russian Federation", + .ringcadence = { 1000, 4000 }, + .tones = { + /* References: + http://www.minsvyaz.ru/site.shtml?id=1806 + http://www.aboutphone.info/lib/gost/45-223-2001.html */ + { ZT_TONE_DIALTONE, "425" }, + { ZT_TONE_BUSY, "425/350,0/350" }, + { ZT_TONE_RINGTONE, "425/1000,0/4000" }, + { ZT_TONE_CONGESTION, "425/175,0/175" }, + { ZT_TONE_CALLWAIT, "425/200,0/5000" }, + { ZT_TONE_RECORDTONE, "1400/400,0/15000" }, + { ZT_TONE_INFO, "950/330,1440/330,1800/330,0/1000" }, + { ZT_TONE_STUTTER, "!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425" }, + }, + .dtmf_high_level = -10, + .dtmf_low_level = -10, + .mfr1_level = -10, + .mfr2_level = -8, + }, + { .zone = -1 } +}; |