--- zaptel-1.4.11~dfsg.orig/debian/NEWS.Debian +++ zaptel-1.4.11~dfsg/debian/NEWS.Debian @@ -0,0 +1,13 @@ +zaptel (1:1.4.10~dfsg-1) unstable; urgency=low + + Certain versions of Zaptel introduce incompatibilities between older kernel + modules and newer userspace programs. If programs such as ztcfg or asterisk + fail talking with Zaptel kernel with error 25 (ENOTTY: "Inappropriate ioctl + for this device") then you should upgrade the Zaptel modules by e.g: + 'm-a a-i zaptel' and reload drivers. + . + The version of the currently running Zaptel is in + /sys/module/zaptel/version . + + -- Tzafrir Cohen Thu, 20 Mar 2008 16:31:25 +0200 + --- zaptel-1.4.11~dfsg.orig/debian/libtonezone1.docs +++ zaptel-1.4.11~dfsg/debian/libtonezone1.docs @@ -0,0 +1 @@ +tonezones.txt --- zaptel-1.4.11~dfsg.orig/debian/zaptel.install +++ zaptel-1.4.11~dfsg/debian/zaptel.install @@ -0,0 +1,7 @@ +sbin/* +usr/sbin/* +usr/share/perl5/Zaptel* +usr/share/zaptel/* +usr/share/man/man8/* +etc/hotplug/usb/* +etc/udev/rules.d/* --- zaptel-1.4.11~dfsg.orig/debian/zaptel.postrm +++ zaptel-1.4.11~dfsg/debian/zaptel.postrm @@ -0,0 +1,11 @@ +#! /bin/sh -e + +if [ "$1" = 'purge' ]; then + # leftovers from the Sarge: + rm -f /etc/modutils/zaptel /etc/udev/rules.d/zaptel.perms + + # leftovers from Etch: + rm -f /etc/zaptel.conf +fi + +#DEBHELPER# --- zaptel-1.4.11~dfsg.orig/debian/zaptel.doc-base.zaptel.conf +++ zaptel-1.4.11~dfsg/debian/zaptel.doc-base.zaptel.conf @@ -0,0 +1,10 @@ +Document: zaptel.conf +Title: Reference Zaptel Configuration File +Author: Zapata Telephony Project +Abstract: A sample annotated zaptel.conf . This is the closest + thing to a reference for zaptel.conf (used by ztcfg(1) ). +Section: Comm + +Format: text +Files: /usr/share/doc/zaptel/examples/zaptel.conf.sample.gz + --- zaptel-1.4.11~dfsg.orig/debian/zaptel-source.dirs +++ zaptel-1.4.11~dfsg/debian/zaptel-source.dirs @@ -0,0 +1,2 @@ +usr/src +usr/include/linux --- zaptel-1.4.11~dfsg.orig/debian/control.modules.in +++ zaptel-1.4.11~dfsg/debian/control.modules.in @@ -0,0 +1,17 @@ +Source: zaptel +Section: comm +Priority: extra +Maintainer: Debian VoIP Team +Uploaders: Kilian Krause , Jose Carlos Garcia Sogo , Mark Purcell , Santiago Garcia Mantinan , Santiago Ruano Rincon +Build-Depends: debhelper (>> 3.0.0), bzip2 +Standards-Version: 3.6.1.1 + +Package: zaptel-modules-_KVERS_ +Architecture: any +Provides: zaptel-modules +Description: zaptel modules for Linux (kernel _KVERS_). + This package contains the set of loadable kernel modules for the + zapata telephony API. + This package contains the compiled kernel modules for _KVERS_ + . + In order to compile these modules use the module-assistant utility. --- zaptel-1.4.11~dfsg.orig/debian/zaptel.lintian +++ zaptel-1.4.11~dfsg/debian/zaptel.lintian @@ -0,0 +1 @@ +zaptel: mknod-in-maintainer-script postinst:16 --- zaptel-1.4.11~dfsg.orig/debian/zaptel-source.links +++ zaptel-1.4.11~dfsg/debian/zaptel-source.links @@ -0,0 +1 @@ +usr/include/zaptel/zaptel.h usr/include/linux/zaptel.h --- zaptel-1.4.11~dfsg.orig/debian/watch +++ zaptel-1.4.11~dfsg/debian/watch @@ -0,0 +1,5 @@ +version=3 +opts=dversionmangle=s/\~dfsg//,downloadurlmangle=s/.*ref=// \ + http://downloads.digium.com/pub/telephony/zaptel/releases/ \ + http://www\.digium\.com/elqNow/elqRedir\.htm\?ref=http://downloads\.digium\.com/pub/telephony/zaptel/releases/zaptel-([0-9.]*)\.tar\.gz\ + debian svn-upgrade --- zaptel-1.4.11~dfsg.orig/debian/backports/dapper +++ zaptel-1.4.11~dfsg/debian/backports/dapper @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Hook for automatic backports at buildserver.net +# +# Target dist: Ubuntu Dapper + +# Downgrade debhelper to 4.0.4 compat level as in zaptel 1:1.0.7-5 (DH_COMPAT=4 is set in rules anyway?!) +sed -i -e 's#^\(Build-Depends:.*\)debhelper[^,$]*[\ ,$]\+\(.*\)$#\1debhelper (>= 4.0.4), \2#' debian/control + +# Undo binary:Version and source:Version +sed -i -e 's#binary:Version#Source-Version#g' \ + -e 's#binary:Version#Source-Version#g' \ + -e 's#^\(Build-Depends:.*\),\ *dpkg-dev ([^)]*)\(.*\)$#\1\2#' \ + debian/control + +exit 0 --- zaptel-1.4.11~dfsg.orig/debian/backports/sarge +++ zaptel-1.4.11~dfsg/debian/backports/sarge @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Hook for automatic backports at Buildserver.NET +# +# Target dist: Debian Sarge + +# Downgrade debhelper to 4.0.4 compat level as in zaptel 1:1.0.7-5 (DH_COMPAT=4 is set in rules anyway?!) +sed -i -e 's#^\(Build-Depends:.*\)debhelper[^,$]*[\ ,$]\+\(.*\)$#\1debhelper (>= 4.0.4), \2#' debian/control + +# Undo binary:Version and source:Version +sed -i -e 's#binary:Version#Source-Version#g' \ + -e 's#binary:Version#Source-Version#g' \ + -e 's#^\(Build-Depends:.*\),\ *dpkg-dev ([^)]*)\(.*\)$#\1\2#' \ + debian/control + +exit 0 --- zaptel-1.4.11~dfsg.orig/debian/libtonezone-dev.install +++ zaptel-1.4.11~dfsg/debian/libtonezone-dev.install @@ -0,0 +1,3 @@ +usr/include/zaptel/tonezone.h +usr/lib/lib*.a +usr/lib/lib*.so --- zaptel-1.4.11~dfsg.orig/debian/compat +++ zaptel-1.4.11~dfsg/debian/compat @@ -0,0 +1 @@ +4 --- zaptel-1.4.11~dfsg.orig/debian/zaptel.dirs +++ zaptel-1.4.11~dfsg/debian/zaptel.dirs @@ -0,0 +1,3 @@ +etc/modprobe.d +etc/udev/rules.d +usr/share/lintian/overrides --- zaptel-1.4.11~dfsg.orig/debian/docs +++ zaptel-1.4.11~dfsg/debian/docs @@ -0,0 +1,8 @@ +README +README.html +README.fxotune +README.fxsusb +kernel/xpp/README.Astribank +kernel/xpp/README.Astribank.html +kernel/xpp/Changelog_xpp +tonezones.txt --- zaptel-1.4.11~dfsg.orig/debian/rules +++ zaptel-1.4.11~dfsg/debian/rules @@ -0,0 +1,276 @@ +#!/usr/bin/make -f +# Sample debian/rules that uses debhelper. +# GNU copyright 1997 to 1999 by Joey Hess. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +# This is the debhelper compatibility version to use. +USE_BRISTUFF=1 +export HOTPLUG_FIRMWARE=1 + +-include /usr/share/quilt/quilt.make + +ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS))) + CFLAGS += -g +endif + +## MODULE-ASSISTANT STUFF +# prefix of the target package name +PREFIX:=zaptel +SKPG:=$(PREFIX)-source +PACKAGE:=$(PREFIX)-modules +# modifieable for experiments or debugging m-a +MA_DIR ?= /usr/share/modass +# load generic variable handling +-include $(MA_DIR)/include/generic.make +# load default rules +-include $(MA_DIR)/include/common-rules.make + +DEBVERSION:=$(shell head -n 1 debian/changelog \ + | sed -e 's/^[^(]*(\([^)]*\)).*/\1/') +UPVERSION:=$(shell echo $(DEBVERSION) | sed -e 's/^.*://' -e 's/-[0-9.]*$$//' -e 's/~dfsg$$//') + +UPFILENAME := zaptel_$(UPVERSION).orig.tar.gz +FILENAME := zaptel_$(UPVERSION)~dfsg.orig.tar.gz +URL := http://ftp2.digium.com/pub/zaptel/releases/zaptel-$(UPVERSION).tar.gz + +# If the makefile was properly-writen, there was a good separation +# between kernel and userspace. As things stand now I'd like to work +# around problems with bilding stuff with HOSTCC by simply providing +# the generated headers as part of the source: +GENERATED_SOURCES := version.h + +EXTRA_MODS=ds1x1f opvxa1200 + +ifneq (,$(filter-out powerpc m68k armeb mips,$(shell dpkg-architecture -qDEB_HOST_ARCH))) +EXTRA_MODS += wcopenpci +endif + +BRISTUFF_MODULES=cwain qozap zaphfc ztgsm +MOD_EXAMPLES_DIR:=modexamples +ifeq (1,$(USE_BRISTUFF)) + BRISTUFF_EXAMPLES:=$(MOD_EXAMPLES_DIR)/* + # bristuff modules that are copied from a subdirectory to the current + # directory. + EXTRA_MODS+=$(BRISTUFF_MODULES) + EXTRA_SUBDIRS += vzaphfc +endif + +ifndef ECHO_CAN_NAME +ECHO_CAN_NAME=OSLEC +endif + +ifeq ($(ECHO_CAN_NAME),OSLEC) + EXTRA_SUBDIRS += oslec +endif + +MOD_ARGS=MODULES_EXTRA="$(EXTRA_MODS)" SUBDIRS_EXTRA="$(EXTRA_SUBDIRS)" + +kdist_clean: clean-unpatched + +kdist_config: prep-deb-files + +binary-modules: prep-deb-files + dh_testdir + dh_testroot + dh_clean -k + cp -a $(CURDIR)/debian/generated/* . + ./configure + make $(MOD_ARGS) modules KERNEL_SOURCES=$(KSRC) MODVERSIONS=detect KERNEL=linux-$(KVERS) + make $(MOD_ARGS) install-modules KERNELRELEASE=$(KVERS) DESTDIR=$(CURDIR)/debian/$(PKGNAME) +ifeq (2.6,$(shell echo $(KVERS) | cut -d. -f1-2)) + # The 2.6 modules are way too big. This is only in kernel 2.6 + find debian/$(PKGNAME)/lib/modules -name '*.ko' |xargs strip -g + $(RM) -f debian/$(PKGNAME)/lib/modules/$(KVERS)/modules.* +endif + dh_installmodules + dh_installdebconf + dh_installchangelogs + dh_compress + dh_fixperms + dh_installdeb + dh_gencontrol -- -v$(VERSION) + dh_md5sums + dh_builddeb --destdir=$(DEB_DESTDIR) + +## END OF M-A SECTION + +tonezones.txt: zonedata.c + echo >$@ "# A list of tonezones supported by this copy of zaptel and libtonezones:" + perl -ne 'next unless (/\.(country|description) = *"([^"]*)/); \ + print (($$1 eq "country")? "$$2\t":"$$2\n");' $< \ + >>$@ + +# A workaround for version incompatibility of asciidoc to fix Sarge build. +# This is fixed properly upstream svn, but adding an autoconf patch is +# just as much work. Remove this on Zaptel 1.4.6. +debian/fake/asciidoc: + mkdir -p $(@D) + echo '#!/bin/sh' >$@ + echo 'exec /usr/bin/asciidoc -b xhtml "$$@"' >>$@ + chmod a+x $@ + +ASCIIDOC_6_TEST = asciidoc --version 2>&1 | grep -q 'asciidoc 6' +ASCIIDOC_SET_PATH = if $(ASCIIDOC_6_TEST); then \ + PATH=$(CURDIR)/debian/fake:$$PATH; export PATH; \ + fi + +build: patch build-stamp +build-stamp: tonezones.txt + dh_testdir + + for mod in $(BRISTUFF_MODULES); do for i in c h; do \ + ln -s $$mod/$$mod.$$i kernel/$$mod.$$i; \ + done; done + ./configure + $(MAKE) $(GENERATED_SOURCES) + $(ASCIIDOC_SET_PATH) ; \ + $(MAKE) programs README.html kernel/xpp/README.Astribank.html + touch $@ + +clean: clean-unpatched unpatch +clean-unpatched: + dh_testdir + dh_testroot + rm -f *-stamp + + # Delete the generated bristuff symlinks: + $(RM) -f $(BRISTUFF_MODULES:%=%.[ch]) + # Add here commands to clean up after the build process. + rm -rf $(MOD_EXAMPLES_DIR) + rm -f tonezones.txt + rm -f $(GENERATED_SOURCES) + rm -rf debian/fake + # * Makefile does not exist when running svn-buildpackage + # as the source tree is not there. + # FIXME: This will fail with an ugly warning on the clean of the + # modules build. However only fter the actuual clean. + [ ! -f Makefile ] || $(MAKE) dist-clean || true + #rm -f debian/manpage.links debian/manpage.refs debian/*.8 + dh_clean + +install: install-arch install-indep + +install-arch: build-stamp + dh_testdir + dh_testroot + dh_clean -k -a + dh_installdirs -a + + # Add here commands to install the package into debian/tmp + + $(MAKE) install-programs DESTDIR=$(CURDIR)/debian/tmp + $(MAKE) install-libs DESTDIR=$(CURDIR)/debian/tmp + /sbin/ldconfig -n $(CURDIR)/debian/tmp/usr/lib + install -D -m 644 tonezone.h $(CURDIR)/debian/tmp/usr/include/zaptel/tonezone.h + + dh_install -a --sourcedir=$(CURDIR)/debian/tmp + + cp debian/$(PREFIX).lintian \ + $(CURDIR)/debian/$(PREFIX)/usr/share/lintian/overrides/$(PREFIX) + +TARPARDIR=$(CURDIR)/debian/tmp +TARDIR=$(TARPARDIR)/modules/$(PREFIX) +install-indep: build-stamp + dh_testdir + dh_testroot + dh_clean -k -i + dh_installdirs -i + + # driver source code + mkdir -p $(TARDIR)/debian/generated + cp Makefile configure install-sh makeopts.in .version *.c *.h $(TARDIR)/ + for dir in build_tools firmware include kernel; do \ + if [ -d $$dir ]; then cp -r $$dir $(TARDIR); fi; \ + done + cp -a $(GENERATED_SOURCES) $(TARDIR)/debian/generated/ + dh_install -i kernel/zaptel.h usr/include/zaptel/ + dh_install -pzaptel-firmware kernel/xpp/firmwares/*.hex usr/share/zaptel/ + + # Packaging infrastructure + cp -r debian/rules debian/changelog debian/copyright\ + debian/control debian/compat \ + debian/control.modules.in \ + $(TARDIR)/debian/ + + tar cjf debian/$(PREFIX)-source/usr/src/$(PREFIX).tar.bz2 \ + -C $(TARPARDIR) modules +ifeq (1,$(USE_BRISTUFF)) + set -e; for module in cwain qozap zaphfc ztgsm; do \ + mkdir -p $(MOD_EXAMPLES_DIR)/$$module; \ + cp -a kernel/$$module/*.conf* $(MOD_EXAMPLES_DIR)/$$module; \ + done +else + mkdir -p $(MOD_EXAMPLES_DIR)/bristuff_stub +endif + +# Build architecture-independent files here. +binary-indep: build install-indep + dh_testdir + dh_testroot + + dh_installdocs -i + dh_installchangelogs -i ChangeLog + dh_installexamples -i $(BRISTUFF_EXAMPLES) + dh_link -i + dh_compress -i + dh_fixperms -i + dh_installdeb -i + dh_gencontrol -i + dh_md5sums -i + dh_builddeb -i + +# Build architecture-dependent files here. +binary-arch: build install-arch + dh_testdir + dh_testroot + + #install -m644 debian/$(PREFIX).modprobe.d debian/$(PREFIX)/etc/modprobe.d/$(PREFIX) + + dh_installdocs -a + + # should be removed, eventually. Still left for compatibility + dh_installinit --update-rcd-params="defaults 15 30" + dh_installexamples -a zaptel.conf.sample \ + kernel/oslec/oslec-ctrl-panel.sh + + + #dh_installmodules -a + dh_installchangelogs -a ChangeLog + dh_link -a + dh_strip -a + dh_compress -a + dh_fixperms -a + dh_makeshlibs -a -V + dh_installdeb -a + dh_shlibdeps -a -ldebian/libtonezone1/usr/lib + dh_gencontrol -a + dh_md5sums -a + dh_builddeb -a + +print-version: + @@echo "Debian version: $(DEBVERSION)" + @@echo "Upstream version: $(UPVERSION)" + +TARBALL_DIR=../tarballs/zaptel-$(UPVERSION).tmp +get-orig-source: + @@dh_testdir + @@[ -d ../tarballs/. ]||mkdir -p ../tarballs + @@echo Downloading $(UPFILENAME) from $(URL) ... + @@wget -nv -T10 -t3 --verbose -O ../tarballs/$(UPFILENAME) $(URL) + @@echo Repacking as DFSG-free... + @@mkdir -p $(TARBALL_DIR)/ + @@cd $(TARBALL_DIR) ; \ + tar xfz ../$(UPFILENAME) + @@rm -rf $(TARBALL_DIR)/zaptel-$(UPVERSION)/kernel/wct4xxp/OCT*.ima + @@rm -rf $(TARBALL_DIR)/zaptel-$(UPVERSION)/kernel/wctc4xxp/*.bin + @@rm -rf $(TARBALL_DIR)/zaptel-$(UPVERSION)/kernel/xpp/firmwares/*.hex + @@cd $(TARBALL_DIR) ; \ + tar cfz ../$(FILENAME) * + @@echo Cleaning up... + @@$(RM) -rf $(TARBALL_DIR)/ + @@$(RM) -f ../tarballs/$(UPFILENAME) + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install configure patch unpatch --- zaptel-1.4.11~dfsg.orig/debian/libtonezone-dev.links +++ zaptel-1.4.11~dfsg/debian/libtonezone-dev.links @@ -0,0 +1,3 @@ +usr/lib/libtonezone.so.1.0 usr/lib/libtonezone.so +usr/include/zaptel/tonezone.h usr/include/tonezone.h + --- zaptel-1.4.11~dfsg.orig/debian/patches/zaphfc-florz +++ zaptel-1.4.11~dfsg/debian/patches/zaphfc-florz @@ -0,0 +1,1264 @@ +## zaphfc-florz by Faidon Liambotis +## +## florz' patch to zaphfc +## Author: Florian Zumbiehl + +diff -urNad trunk~/zaphfc/Makefile trunk/zaphfc/Makefile +--- trunk~/kernel/zaphfc/Makefile 2007-07-22 05:01:06.000000000 +0300 ++++ trunk/kernel/zaphfc/Makefile 2007-08-05 14:46:58.000000000 +0300 +@@ -2,12 +2,11 @@ + BRISTUFFBASE = $(shell dirname `pwd`) + + ZAP = $(shell [ -f $(BRISTUFFBASE)/zaptel/zaptel.h ] && echo "-I$(BRISTUFFBASE)/zaptel") +-RTAI = $(shell [ -f /usr/realtime/include/rtai.h ] && echo "-DRTAITIMING -I/usr/realtime/include") + +-CFLAGS+=-I. $(ZAP) $(RTAI) -O2 -g -Wall -DBUILDING_TONEZONE ++CFLAGS+=-I. $(ZAP) -O2 -g -Wall -DBUILDING_TONEZONE + CFLAGS+=$(shell if uname -m | grep -q ppc; then echo "-fsigned-char"; fi) + +-KFLAGS=-D__KERNEL__ -DMODULE -DEXPORT_SYMTAB -fomit-frame-pointer -O2 -Wall -I$(KINCLUDES) $(ZAP) $(RTAI) -Wall ++KFLAGS=-D__KERNEL__ -DMODULE -DEXPORT_SYMTAB -fomit-frame-pointer -O2 -Wall -I$(KINCLUDES) $(ZAP) -Wall + KFLAGS+=$(shell [ -f $(KINCLUDES)/linux/modversions.h ] && echo "-DMODVERSIONS -include $(KINCLUDES)/linux/modversions.h") + KFLAGS+=$(shell if uname -m | grep -q ppc; then echo "-msoft-float -fsigned-char"; fi) + +diff -urN bristuff-0.4.0-test1/kernel/zaphfc/zaphfc.c zaphfc_0.4.0-test1_florz-13/zaphfc.c +--- bristuff-0.4.0-test1/kernel/zaphfc/zaphfc.c 2007-04-03 11:38:45.000000000 +0200 ++++ zaphfc_0.4.0-test1_florz-13/kernel/zaphfc/zaphfc.c 2007-04-17 21:14:39.000000000 +0200 +@@ -7,19 +7,21 @@ + * + * Klaus-Peter Junghanns + * ++ * Copyright (C) 2004, 2005, 2006 Florian Zumbiehl ++ * - support for slave mode of the HFC-S chip which allows it to ++ * sync its sample clock to an external source/another HFC chip ++ * - support for "interrupt bundling" (let only one card generate ++ * 8 kHz timing interrupt no matter how many cards there are ++ * in the system) ++ * - interrupt loss tolerant b channel handling ++ * + * This program is free software and may be modified and +- * distributed under the terms of the GNU Public License. ++ * distributed under the terms of the GNU General Public License. + * + */ + + #include + #include +-#ifdef RTAITIMING +-#include +-#include +-#include +-#include +-#endif + #include + #include + #include +@@ -31,6 +33,8 @@ + #include + #endif + ++#define log2(n) ffz(~(n)) ++ + #if CONFIG_PCI + + #define CLKDEL_TE 0x0f /* CLKDEL in TE mode */ +@@ -73,41 +77,30 @@ + static int hfc_dev_count = 0; + static int modes = 0; // all TE + static int debug = 0; ++static int sync_slave = 0; // all master ++static int timer_card = 0; ++static int jitterbuffer = 1; + static struct pci_dev *multi_hfc = NULL; + static spinlock_t registerlock = SPIN_LOCK_UNLOCKED; + +-void hfc_shutdownCard(struct hfc_card *hfctmp) { +- unsigned long flags; +- +- if (hfctmp == NULL) { +- return; +- } +- +- if (hfctmp->pci_io == NULL) { +- return; +- } +- +- spin_lock_irqsave(&hfctmp->lock,flags); +- ++void hfc_shutdownCard1(struct hfc_card *hfctmp) { + printk(KERN_INFO "zaphfc: shutting down card at %p.\n",hfctmp->pci_io); + + /* Clear interrupt mask */ + hfctmp->regs.int_m2 = 0; + hfc_outb(hfctmp, hfc_INT_M2, hfctmp->regs.int_m2); + +- /* Reset pending interrupts */ +- hfc_inb(hfctmp, hfc_INT_S1); ++ /* Remove interrupt handler */ ++ free_irq(hfctmp->irq,hfctmp); ++} ++ ++void hfc_shutdownCard2(struct hfc_card *hfctmp) { ++ unsigned long flags; + +- /* Wait for interrupts that might still be pending */ +- spin_unlock_irqrestore(&hfctmp->lock, flags); +- set_current_state(TASK_UNINTERRUPTIBLE); +- schedule_timeout((30 * HZ) / 1000); // wait 30 ms + spin_lock_irqsave(&hfctmp->lock,flags); + +- /* Remove interrupt handler */ +- if (hfctmp->irq) { +- free_irq(hfctmp->irq, hfctmp); +- } ++ /* Reset pending interrupts */ ++ hfc_inb(hfctmp, hfc_INT_S1); + + /* Soft-reset the card */ + hfc_outb(hfctmp, hfc_CIRM, hfc_CIRM_RESET); // softreset on +@@ -121,8 +114,8 @@ + + pci_write_config_word(hfctmp->pcidev, PCI_COMMAND, 0); // disable memio and bustmaster + +- if (hfctmp->fifomem != NULL) { +- kfree(hfctmp->fifomem); ++ if (hfctmp->fifos != NULL) { ++ free_pages((unsigned long)hfctmp->fifos,log2(hfc_FIFO_MEM_SIZE_PAGES)); + } + iounmap((void *) hfctmp->pci_io); + hfctmp->pci_io = NULL; +@@ -132,11 +125,24 @@ + spin_unlock_irqrestore(&hfctmp->lock,flags); + if (hfctmp->ztdev != NULL) { + zt_unregister(&hfctmp->ztdev->span); +- kfree(hfctmp->ztdev); ++ vfree(hfctmp->ztdev); + printk(KERN_INFO "unregistered from zaptel.\n"); + } + } + ++void hfc_shutdownCard(struct hfc_card *hfctmp) { ++ if (hfctmp == NULL) { ++ return; ++ } ++ ++ if (hfctmp->pci_io == NULL) { ++ return; ++ } ++ ++ hfc_shutdownCard1(hfctmp); ++ hfc_shutdownCard2(hfctmp); ++} ++ + void hfc_resetCard(struct hfc_card *hfctmp) { + unsigned long flags; + +@@ -180,14 +186,14 @@ + hfctmp->regs.ctmt = hfc_CTMT_TRANSB1 | hfc_CTMT_TRANSB2; // all bchans are transparent , no freaking hdlc + hfc_outb(hfctmp, hfc_CTMT, hfctmp->regs.ctmt); + +- hfctmp->regs.int_m1 = 0; ++ hfctmp->regs.int_m1=hfc_INTS_L1STATE; ++ if(hfctmp->cardno==timer_card){ ++ hfctmp->regs.int_m2=hfc_M2_PROC_TRANS; ++ }else{ ++ hfctmp->regs.int_m1|=hfc_INTS_DREC; ++ hfctmp->regs.int_m2=0; ++ } + hfc_outb(hfctmp, hfc_INT_M1, hfctmp->regs.int_m1); +- +-#ifdef RTAITIMING +- hfctmp->regs.int_m2 = 0; +-#else +- hfctmp->regs.int_m2 = hfc_M2_PROC_TRANS; +-#endif + hfc_outb(hfctmp, hfc_INT_M2, hfctmp->regs.int_m2); + + /* Clear already pending ints */ +@@ -199,8 +205,8 @@ + hfctmp->regs.sctrl = 3 | hfc_SCTRL_NONE_CAP | hfc_SCTRL_MODE_TE; /* set tx_lo mode, error in datasheet ! */ + } + +- hfctmp->regs.mst_mode = hfc_MST_MODE_MASTER; /* HFC Master Mode */ + hfc_outb(hfctmp, hfc_MST_MODE, hfctmp->regs.mst_mode); ++ hfc_outb(hfctmp, hfc_MST_EMOD, hfctmp->regs.mst_emod); + + hfc_outb(hfctmp, hfc_SCTRL, hfctmp->regs.sctrl); + hfctmp->regs.sctrl_r = 3; +@@ -212,10 +218,8 @@ + hfc_outb(hfctmp, hfc_CIRM, 0x80 | 0x40); // bit order + + /* Finally enable IRQ output */ +-#ifndef RTAITIMING + hfctmp->regs.int_m2 |= hfc_M2_IRQ_ENABLE; + hfc_outb(hfctmp, hfc_INT_M2, hfctmp->regs.int_m2); +-#endif + + /* clear pending ints */ + hfc_inb(hfctmp, hfc_INT_S1); +@@ -232,370 +236,215 @@ + spin_unlock(®isterlock); + } + +-static void hfc_btrans(struct hfc_card *hfctmp, char whichB) { +- // we are called with irqs disabled from the irq handler +- int count, maxlen, total; +- unsigned char *f1, *f2; +- unsigned short *z1, *z2, newz1; +- int freebytes; +- +- if (whichB == 1) { +- f1 = (char *)(hfctmp->fifos + hfc_FIFO_B1TX_F1); +- f2 = (char *)(hfctmp->fifos + hfc_FIFO_B1TX_F2); +- z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B1TX_Z1 + (*f1 * 4)); +- z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B1TX_Z2 + (*f1 * 4)); +- } else { +- f1 = (char *)(hfctmp->fifos + hfc_FIFO_B2TX_F1); +- f2 = (char *)(hfctmp->fifos + hfc_FIFO_B2TX_F2); +- z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B2TX_Z1 + (*f1 * 4)); +- z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B2TX_Z2 + (*f1 * 4)); +- } +- +- freebytes = *z2 - *z1; +- if (freebytes <= 0) { +- freebytes += hfc_B_FIFO_SIZE; +- } +- count = ZT_CHUNKSIZE; +- +- total = count; +- if (freebytes < count) { +- hfctmp->clicks++; +- /* only spit out this warning once per second to not make things worse! */ +- if (hfctmp->clicks > 100) { +- printk(KERN_CRIT "zaphfc: bchan tx fifo full, dropping audio! (z1=%d, z2=%d)\n",*z1,*z2); +- hfctmp->clicks = 0; +- } +- return; +- } +- +- maxlen = (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL) - *z1; +- if (maxlen > count) { +- maxlen = count; +- } +- newz1 = *z1 + total; +- if (newz1 >= (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL)) { newz1 -= hfc_B_FIFO_SIZE; } ++/*===========================================================================*/ + +- if (whichB == 1) { +- memcpy((char *)(hfctmp->fifos + hfc_FIFO_B1TX_ZOFF + *z1),hfctmp->ztdev->chans[0].writechunk, maxlen); +- } else { +- memcpy((char *)(hfctmp->fifos + hfc_FIFO_B2TX_ZOFF + *z1),hfctmp->ztdev->chans[1].writechunk, maxlen); +- } +- +- count -= maxlen; +- if (count > 0) { +- // Buffer wrap +- if (whichB == 1) { +- memcpy((char *)(hfctmp->fifos + hfc_FIFO_B1TX_ZOFF + hfc_B_SUB_VAL),hfctmp->ztdev->chans[0].writechunk+maxlen, count); +- } else { +- memcpy((char *)(hfctmp->fifos + hfc_FIFO_B2TX_ZOFF + hfc_B_SUB_VAL),hfctmp->ztdev->chans[1].writechunk+maxlen, count); +- } +- } ++#if hfc_B_FIFO_SIZE%ZT_CHUNKSIZE ++#error hfc_B_FIFO_SIZE is not a multiple of ZT_CHUNKSIZE even though the code assumes this ++#endif ++ ++static void hfc_dch_init(struct hfc_card *hfctmp){ ++ struct dch *chtmp=&hfctmp->dch; + +- *z1 = newz1; /* send it now */ ++ chtmp->rx.f1.p=(u8 *)(hfctmp->fifos+hfc_FIFO_DRX_F1); ++ chtmp->rx.f2.v=0x1f; ++ chtmp->rx.f2.z2.v=0x1ff; + +-// if (count > 0) printk(KERN_CRIT "zaphfc: bchan tx fifo (f1=%d, f2=%d, z1=%d, z2=%d)\n",(*f1) & hfc_FMASK,(*f2) & hfc_FMASK, *z1, *z2); +- return; ++ chtmp->tx.f1.p=(u8 *)(hfctmp->fifos+hfc_FIFO_DTX_F1); ++ chtmp->tx.f1.v=0x1f; ++ chtmp->tx.f1.z1.v=0x1ff; ++ chtmp->tx.f2.p=(u8 *)(hfctmp->fifos+hfc_FIFO_DTX_F2); + } + +-static void hfc_brec(struct hfc_card *hfctmp, char whichB) { +- // we are called with irqs disabled from the irq handler +- int count, maxlen, drop; +- volatile unsigned char *f1, *f2; +- volatile unsigned short *z1, *z2, newz2; +- int bytes = 0; +- +- if (whichB == 1) { +- f1 = (char *)(hfctmp->fifos + hfc_FIFO_B1RX_F1); +- f2 = (char *)(hfctmp->fifos + hfc_FIFO_B1RX_F2); +- z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B1RX_Z1 + (*f1 * 4)); +- z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B1RX_Z2 + (*f1 * 4)); +- } else { +- f1 = (char *)(hfctmp->fifos + hfc_FIFO_B2RX_F1); +- f2 = (char *)(hfctmp->fifos + hfc_FIFO_B2RX_F2); +- z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B2RX_Z1 + (*f1 * 4)); +- z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B2RX_Z2 + (*f1 * 4)); +- } ++static void hfc_bch_init(struct hfc_card *hfctmp){ ++ struct bch *chtmp=&hfctmp->bch; + +- bytes = *z1 - *z2; +- if (bytes < 0) { +- bytes += hfc_B_FIFO_SIZE; +- } +- count = ZT_CHUNKSIZE; +- +- if (bytes < ZT_CHUNKSIZE) { +-#ifndef RTAITIMING +- printk(KERN_CRIT "zaphfc: bchan rx fifo not enough bytes to receive! (z1=%d, z2=%d, wanted %d got %d), probably a buffer overrun.\n",*z1,*z2,ZT_CHUNKSIZE,bytes); +-#endif +- return; +- } ++ chtmp->checkcnt=0; ++ chtmp->fill_fifo=0; + +- /* allowing the buffering of hfc_BCHAN_BUFFER bytes of audio data works around irq jitter */ +- if (bytes > hfc_BCHAN_BUFFER + ZT_CHUNKSIZE) { +- /* if the system is too slow to handle it, we will have to drop it all (except 1 zaptel chunk) */ +- drop = bytes - ZT_CHUNKSIZE; +- hfctmp->clicks++; +- /* only spit out this warning once per second to not make things worse! */ +- if (hfctmp->clicks > 100) { +- printk(KERN_CRIT "zaphfc: dropped audio (z1=%d, z2=%d, wanted %d got %d, dropped %d).\n",*z1,*z2,count,bytes,drop); +- hfctmp->clicks = 0; +- } +- /* hm, we are processing the b chan data tooooo slowly... let's drop the lost audio */ +- newz2 = *z2 + drop; +- if (newz2 >= (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL)) { +- newz2 -= hfc_B_FIFO_SIZE; +- } +- *z2 = newz2; +- } ++ chtmp->rx.c[0].z1p=(unsigned short *)(hfctmp->fifos+hfc_FIFO_B1RX_Z1+0x1f*4); ++ chtmp->rx.c[0].fifo_base=(char *)(hfctmp->fifos+hfc_FIFO_B1RX_ZOFF); ++ chtmp->rx.c[1].z1p=(unsigned short *)(hfctmp->fifos+hfc_FIFO_B2RX_Z1+0x1f*4); ++ chtmp->rx.c[1].fifo_base=(char *)(hfctmp->fifos+hfc_FIFO_B2RX_ZOFF); ++ chtmp->rx.z2=hfc_B_SUB_VAL; ++ chtmp->rx.diff=0; + +- +- maxlen = (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL) - *z2; +- if (maxlen > count) { +- maxlen = count; +- } +- if (whichB == 1) { +- memcpy(hfctmp->ztdev->chans[0].readchunk,(char *)(hfctmp->fifos + hfc_FIFO_B1RX_ZOFF + *z2), maxlen); +- } else { +- memcpy(hfctmp->ztdev->chans[1].readchunk,(char *)(hfctmp->fifos + hfc_FIFO_B2RX_ZOFF + *z2), maxlen); +- } +- newz2 = *z2 + count; +- if (newz2 >= (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL)) { +- newz2 -= hfc_B_FIFO_SIZE; ++ chtmp->tx.c[0].z1p=(unsigned short *)(hfctmp->fifos+hfc_FIFO_B1TX_Z1+0x1f*4); ++ chtmp->tx.c[0].z2p=(unsigned short *)(hfctmp->fifos+hfc_FIFO_B1TX_Z2+0x1f*4); ++ chtmp->tx.c[0].fifo_base=(char *)(hfctmp->fifos+hfc_FIFO_B1TX_ZOFF); ++ chtmp->tx.c[0].filled=0; ++ chtmp->tx.c[1].z1p=(unsigned short *)(hfctmp->fifos+hfc_FIFO_B2TX_Z1+0x1f*4); ++ chtmp->tx.c[1].z2p=(unsigned short *)(hfctmp->fifos+hfc_FIFO_B2TX_Z2+0x1f*4); ++ chtmp->tx.c[1].fifo_base=(char *)(hfctmp->fifos+hfc_FIFO_B2TX_ZOFF); ++ chtmp->tx.c[1].filled=0; ++ chtmp->tx.z1=hfc_B_SUB_VAL; ++ chtmp->tx.diff=0; ++ ++ hfc_dch_init(hfctmp); ++ ++ chtmp->initialized=0; ++} ++ ++static int hfc_bch_check(struct hfc_card *hfctmp){ ++ struct bch *chtmp=&hfctmp->bch; ++ int x,r; ++ ++ for(x=0;x<2;x++){ ++ chtmp->tx.c[x].filled=(chtmp->tx.z1-*chtmp->tx.c[x].z2p+hfc_B_FIFO_SIZE)%hfc_B_FIFO_SIZE; ++ chtmp->rx.c[x].filled=(*chtmp->rx.c[x].z1p-chtmp->rx.z2+hfc_B_FIFO_SIZE)%hfc_B_FIFO_SIZE; + } +- *z2 = newz2; +- +- count -= maxlen; +- if (count > 0) { +- // Buffer wrap +- if (whichB == 1) { +- z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B1RX_Z2 + (*f1 * 4)); +- memcpy(hfctmp->ztdev->chans[0].readchunk + maxlen,(char *)(hfctmp->fifos + hfc_FIFO_B1RX_ZOFF + hfc_B_SUB_VAL), count); +- } else { +- z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B2RX_Z2 + (*f1 * 4)); +- memcpy(hfctmp->ztdev->chans[1].readchunk + maxlen,(char *)(hfctmp->fifos + hfc_FIFO_B2RX_ZOFF + hfc_B_SUB_VAL), count); +- } +- newz2 = *z2 + count; +- if (newz2 >= (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL)) { +- newz2 -= hfc_B_FIFO_SIZE; ++ if(chtmp->fill_fifo){ ++ chtmp->checkcnt++; ++ chtmp->checkcnt%=ZT_CHUNKSIZE; ++ r=!chtmp->checkcnt; ++ }else{ ++ x=chtmp->tx.c[0].filled-chtmp->tx.c[1].filled; ++ if(abs(x-chtmp->tx.diff)>1){ ++ printk(KERN_CRIT "zaphfc[%d]: tx sync changed: %d, %d\n",hfctmp->cardno,chtmp->tx.c[0].filled,chtmp->tx.c[1].filled); ++ chtmp->tx.diff=x; + } ++ r=chtmp->tx.c[0].filled<=ZT_CHUNKSIZE*jitterbuffer&&chtmp->tx.c[1].filled<=ZT_CHUNKSIZE*jitterbuffer; + } ++ return(r); ++} + ++#define hfc_bch_inc_z(a,b) (a)=((a)-hfc_B_SUB_VAL+(b))%hfc_B_FIFO_SIZE+hfc_B_SUB_VAL + +- if (whichB == 1) { +- zt_ec_chunk(&hfctmp->ztdev->chans[0], hfctmp->ztdev->chans[0].readchunk, hfctmp->ztdev->chans[0].writechunk); +- } else { +- zt_ec_chunk(&hfctmp->ztdev->chans[1], hfctmp->ztdev->chans[1].readchunk, hfctmp->ztdev->chans[1].writechunk); ++static void hfc_bch_tx(struct hfc_card *hfctmp){ ++ struct bch *chtmp=&hfctmp->bch; ++ int x; ++ ++ for(x=0;x<2;x++) ++ memcpy((void *)(chtmp->tx.c[x].fifo_base+chtmp->tx.z1),hfctmp->ztdev->chans[x].writechunk,ZT_CHUNKSIZE); ++ hfc_bch_inc_z(chtmp->tx.z1,ZT_CHUNKSIZE); ++ if(chtmp->fill_fifo){ ++ chtmp->fill_fifo--; ++ }else if(chtmp->tx.c[0].filled<=1||chtmp->tx.c[1].filled<=1){ ++ chtmp->fill_fifo=jitterbuffer; ++ if(chtmp->initialized) ++ printk(KERN_CRIT "zaphfc[%d]: b channel buffer underrun: %d, %d\n",hfctmp->cardno,chtmp->tx.c[0].filled,chtmp->tx.c[1].filled); + } +- return; ++ if(!chtmp->fill_fifo) ++ for(x=0;x<2;x++)*chtmp->tx.c[x].z1p=chtmp->tx.z1; + } + +- +-static void hfc_dtrans(struct hfc_card *hfctmp) { +- // we are called with irqs disabled from the irq handler ++static void hfc_bch_rx(struct hfc_card *hfctmp){ ++ struct bch *chtmp=&hfctmp->bch; + int x; +- int count, maxlen, total; +- unsigned char *f1, *f2, newf1; +- unsigned short *z1, *z2, newz1; +- int frames, freebytes; + +- if (hfctmp->ztdev->chans[2].bytes2transmit == 0) { +- return; ++ x=chtmp->rx.c[0].filled-chtmp->rx.c[1].filled; ++ if(abs(x-chtmp->rx.diff)>1){ ++ printk(KERN_CRIT "zaphfc[%d]: rx sync changed: %d, %d\n",hfctmp->cardno,chtmp->rx.c[0].filled,chtmp->rx.c[1].filled); ++ chtmp->rx.diff=x; + } +- +- f1 = (char *)(hfctmp->fifos + hfc_FIFO_DTX_F1); +- f2 = (char *)(hfctmp->fifos + hfc_FIFO_DTX_F2); +- z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DTX_Z1 + (*f1 * 4)); +- z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DTX_Z2 + (*f1 * 4)); +- +- frames = (*f1 - *f2) & hfc_FMASK; +- if (frames < 0) { +- frames += hfc_MAX_DFRAMES + 1; ++ if(chtmp->rx.c[0].filled>=ZT_CHUNKSIZE&&chtmp->rx.c[1].filled>=ZT_CHUNKSIZE){ ++ if((chtmp->rx.c[0].filled>=ZT_CHUNKSIZE*(jitterbuffer+2)&&chtmp->rx.c[1].filled>=ZT_CHUNKSIZE*(jitterbuffer+2))||!chtmp->initialized){ ++ if(chtmp->initialized) ++ printk(KERN_CRIT "zaphfc[%d]: b channel buffer overflow: %d, %d\n",hfctmp->cardno,chtmp->rx.c[0].filled,chtmp->rx.c[1].filled); ++ hfc_bch_inc_z(chtmp->rx.z2,chtmp->rx.c[0].filled-chtmp->rx.c[0].filled%ZT_CHUNKSIZE-ZT_CHUNKSIZE); ++ chtmp->initialized=1; ++ } ++ for(x=0;x<2;x++){ ++ memcpy(hfctmp->ztdev->chans[x].readchunk,(void *)(chtmp->rx.c[x].fifo_base+chtmp->rx.z2),ZT_CHUNKSIZE); ++ zt_ec_chunk(&hfctmp->ztdev->chans[x],hfctmp->ztdev->chans[x].readchunk,hfctmp->ztdev->chans[x].writechunk); ++ } ++ hfc_bch_inc_z(chtmp->rx.z2,ZT_CHUNKSIZE); + } ++} + +- if (frames >= hfc_MAX_DFRAMES) { +- printk(KERN_CRIT "zaphfc: dchan tx fifo total number of frames exceeded!\n"); +- return; +- } ++/*===========================================================================*/ + +- freebytes = *z2 - *z1; +- if (freebytes <= 0) { +- freebytes += hfc_D_FIFO_SIZE; +- } +- count = hfctmp->ztdev->chans[2].bytes2transmit; +- +- total = count; +- if (freebytes < count) { +- printk(KERN_CRIT "zaphfc: dchan tx fifo not enough free bytes! (z1=%d, z2=%d)\n",*z1,*z2); +- return; +- } +- +- newz1 = (*z1 + count) & hfc_ZMASK; +- newf1 = ((*f1 + 1) & hfc_MAX_DFRAMES) | (hfc_MAX_DFRAMES + 1); // next frame +- +- if (count > 0) { +- if (debug) { +- printk(KERN_CRIT "zaphfc: card %d TX [ ", hfctmp->cardno); +- for (x=0; xdch; ++ u8 tx_f2_v; ++ u16 x; ++ ++ if(hfctmp->ztdev->chans[2].bytes2transmit){ ++ if(debug){ ++ printk(KERN_CRIT "zaphfc[%d]: card TX [ ",hfctmp->cardno); ++ for(x=0;xztdev->chans[2].bytes2transmit;x++){ + printk("%#2x ",hfctmp->dtransbuf[x]); + } +- if (hfctmp->ztdev->chans[2].eoftx == 1) { +- printk("] %d bytes\n", count); +- } else { +- printk("..] %d bytes\n", count); +- } +- } +- maxlen = hfc_D_FIFO_SIZE - *z1; +- if (maxlen > count) { +- maxlen = count; ++ printk("] %d bytes\n",hfctmp->ztdev->chans[2].bytes2transmit); + } +- memcpy((char *)(hfctmp->fifos + hfc_FIFO_DTX_ZOFF + *z1),hfctmp->ztdev->chans[2].writechunk, maxlen); +- count -= maxlen; +- if (count > 0) { +- memcpy((char *)(hfctmp->fifos + hfc_FIFO_DTX_ZOFF),(char *)(hfctmp->ztdev->chans[2].writechunk + maxlen), count); ++ tx_f2_v=*chtmp->tx.f2.p; ++ if(!(tx_f2_v-chtmp->tx.f1.v+hfc_MAX_DFRAMES+1-1)&(hfc_MAX_DFRAMES+1-1)){ ++ printk(KERN_CRIT "zaphfc[%d]: dchan tx fifo total number of frames exceeded!\n",hfctmp->cardno); ++ }else{ ++ if(((*(volatile u16 *)(hfctmp->fifos+hfc_FIFO_DTX_Z2+tx_f2_v*4)-chtmp->tx.f1.z1.v+hfc_D_FIFO_SIZE-1)&(hfc_D_FIFO_SIZE-1))ztdev->chans[2].bytes2transmit){ ++ printk(KERN_CRIT "zaphfc[%d]: dchan tx fifo not enough space for frame!\n",hfctmp->cardno); ++ }else{ ++ chtmp->tx.f1.v=((chtmp->tx.f1.v+1)&hfc_MAX_DFRAMES)|(hfc_MAX_DFRAMES+1); ++ x=min(hfctmp->ztdev->chans[2].bytes2transmit,hfc_D_FIFO_SIZE-chtmp->tx.f1.z1.v); ++ memcpy(hfctmp->fifos+hfc_FIFO_DTX_ZOFF+chtmp->tx.f1.z1.v,hfctmp->ztdev->chans[2].writechunk,x); ++ memcpy(hfctmp->fifos+hfc_FIFO_DTX_ZOFF,hfctmp->ztdev->chans[2].writechunk+x,hfctmp->ztdev->chans[2].bytes2transmit-x); ++ *(volatile u16 *)(hfctmp->fifos+hfc_FIFO_DTX_Z2+chtmp->tx.f1.v*4)=chtmp->tx.f1.z1.v; ++ chtmp->tx.f1.z1.v=(chtmp->tx.f1.z1.v+hfctmp->ztdev->chans[2].bytes2transmit+hfc_D_FIFO_SIZE)&(hfc_D_FIFO_SIZE-1); ++ *(volatile u16 *)(hfctmp->fifos+hfc_FIFO_DTX_Z1+chtmp->tx.f1.v*4)=chtmp->tx.f1.z1.v; ++ *chtmp->tx.f1.p=chtmp->tx.f1.v; ++ } + } + } +- +- *z1 = newz1; +- +- if (hfctmp->ztdev->chans[2].eoftx == 1) { +- *f1 = newf1; +- z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DTX_Z1 + (*f1 * 4)); +- *z1 = newz1; +- hfctmp->ztdev->chans[2].eoftx = 0; +- } +-// printk(KERN_CRIT "zaphfc: dchan tx fifo (f1=%d, f2=%d, z1=%d, z2=%d)\n",(*f1) & hfc_FMASK,(*f2) & hfc_FMASK, *z1, *z2); +- return; + } + +-/* receive a complete hdlc frame, skip broken or short frames */ +-static void hfc_drec(struct hfc_card *hfctmp) { +- int count=0, maxlen=0, framelen=0; +- unsigned char *f1, *f2, *crcstat; +- unsigned short *z1, *z2, oldz2, newz2; ++static void hfc_dch_rx(struct hfc_card *hfctmp){ ++ struct dch *chtmp=&hfctmp->dch; ++ u16 size; + + hfctmp->ztdev->chans[2].bytes2receive=0; +- hfctmp->ztdev->chans[2].eofrx = 0; +- +- /* put the received data into the zaptel buffer +- we'll call zt_receive() later when the timer fires. */ +- f1 = (char *)(hfctmp->fifos + hfc_FIFO_DRX_F1); +- f2 = (char *)(hfctmp->fifos + hfc_FIFO_DRX_F2); +- +- if (*f1 == *f2) return; /* nothing received, strange eh? */ +- +- z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DRX_Z1 + (*f2 * 4)); +- z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DRX_Z2 + (*f2 * 4)); +- +- /* calculate length of frame, including 2 bytes CRC and 1 byte STAT */ +- count = *z1 - *z2; +- +- if (count < 0) { +- count += hfc_D_FIFO_SIZE; /* ring buffer wrapped */ +- } +- count++; +- framelen = count; +- +- crcstat = (char *)(hfctmp->fifos + hfc_FIFO_DRX_ZOFF + *z1); +- +- if ((framelen < 4) || (*crcstat != 0x0)) { +- /* the frame is too short for a valid HDLC frame or the CRC is borked */ +- printk(KERN_CRIT "zaphfc: empty HDLC frame or bad CRC received (framelen = %d, stat = %#x, card = %d).\n", framelen, *crcstat, hfctmp->cardno); +- oldz2 = *z2; +- *f2 = ((*f2 + 1) & hfc_MAX_DFRAMES) | (hfc_MAX_DFRAMES + 1); /* NEXT!!! */ +- // recalculate z2, because Z2 is a function of F2 Z2(F2) and we INCed F2!!! +- z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DRX_Z2 + (*f2 * 4)); +- *z2 = (oldz2 + framelen) & hfc_ZMASK; +- hfctmp->drecinframe = 0; +- hfctmp->regs.int_drec--; +- /* skip short or broken frames */ +- hfctmp->ztdev->chans[2].bytes2receive = 0; +- return; +- } +- +- count -= 1; /* strip STAT */ +- hfctmp->ztdev->chans[2].eofrx = 1; +- +- if (count + *z2 <= hfc_D_FIFO_SIZE) { +- maxlen = count; +- } else { +- maxlen = hfc_D_FIFO_SIZE - *z2; ++ hfctmp->ztdev->chans[2].eofrx=0; ++ if(*chtmp->rx.f1.p==chtmp->rx.f2.v){ ++ hfctmp->regs.int_drec=0; ++ }else{ ++ size=((*(volatile u16 *)(hfctmp->fifos+hfc_FIFO_DRX_Z1+chtmp->rx.f2.v*4)-chtmp->rx.f2.z2.v+hfc_D_FIFO_SIZE)&(hfc_D_FIFO_SIZE-1))+1; ++ if(size<4){ ++ printk(KERN_CRIT "zaphfc[%d]: empty HDLC frame received.\n",hfctmp->cardno); ++ }else{ ++ u16 x=min(size,(u16)(hfc_D_FIFO_SIZE-chtmp->rx.f2.z2.v)); ++ memcpy(hfctmp->drecbuf,hfctmp->fifos+hfc_FIFO_DRX_ZOFF+chtmp->rx.f2.z2.v,x); ++ memcpy(hfctmp->drecbuf+x,hfctmp->fifos+hfc_FIFO_DRX_ZOFF,size-x); ++ if(hfctmp->drecbuf[size-1]){ ++ printk(KERN_CRIT "zaphfc[%d]: received d channel frame with bad CRC.\n",hfctmp->cardno); ++ }else{ ++ hfctmp->ztdev->chans[2].bytes2receive=size-1; ++ hfctmp->ztdev->chans[2].eofrx=1; ++ } ++ } ++ chtmp->rx.f2.z2.v=(chtmp->rx.f2.z2.v+size)&(hfc_D_FIFO_SIZE-1); ++ chtmp->rx.f2.v=((chtmp->rx.f2.v+1)&hfc_MAX_DFRAMES)|(hfc_MAX_DFRAMES+1); + } +- +- /* copy first part */ +- memcpy(hfctmp->drecbuf, (char *)(hfctmp->fifos + hfc_FIFO_DRX_ZOFF + *z2), maxlen); +- hfctmp->ztdev->chans[2].bytes2receive += maxlen; +- +- count -= maxlen; +- if (count > 0) { +- /* ring buffer wrapped, copy rest from start of d fifo */ +- memcpy(hfctmp->drecbuf + maxlen, (char *)(hfctmp->fifos + hfc_FIFO_DRX_ZOFF), count); +- hfctmp->ztdev->chans[2].bytes2receive += count; +- } +- +- /* frame read */ +- oldz2 = *z2; +- newz2 = (oldz2 + framelen) & hfc_ZMASK; +- *f2 = ((*f2 + 1) & hfc_MAX_DFRAMES) | (hfc_MAX_DFRAMES + 1); /* NEXT!!! */ +- /* recalculate z2, because Z2 is a function of F2 Z2(F2) and we INCed F2!!! */ +- z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DRX_Z2 + (*f2 * 4)); +- *z2 = newz2; +- hfctmp->drecinframe = 0; +- hfctmp->regs.int_drec--; + } + +-#ifndef RTAITIMING +-ZAP_IRQ_HANDLER(hfc_interrupt) { +- struct hfc_card *hfctmp = dev_id; +- unsigned long flags = 0; +- unsigned char stat; ++/*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ ++ ++ZAP_IRQ_HANDLER(hfc_interrupt) { +-#else +-static void hfc_service(struct hfc_card *hfctmp) { +-#endif ++ struct hfc_card *hfctmp = dev_id; ++ struct hfc_card *hfctmp2; + struct zt_hfc *zthfc; +- unsigned char s1, s2, l1state; ++ unsigned char stat, s1, s2, l1state; ++ unsigned long flags; ++ unsigned long flags2=0; + int x; + + if (!hfctmp) { +-#ifndef RTAITIMING + #ifdef LINUX26 + return IRQ_NONE; + #else + return; + #endif +-#else +- /* rtai */ +- return; +-#endif + } + + if (!hfctmp->pci_io) { + printk(KERN_WARNING "%s: IO-mem disabled, cannot handle interrupt\n", + __FUNCTION__); +-#ifndef RTAITIMING + #ifdef LINUX26 + return IRQ_NONE; + #else + return; + #endif +-#else +- /* rtai */ +- return; +-#endif + } +- +- /* we assume a few things in this irq handler: +- - the hfc-pci will only generate "timer" irqs (proc/non-proc) +- - we need to use every 8th IRQ (to generate 1khz timing) +- OR +- - if we use rtai for timing the hfc-pci will not generate ANY irq, +- instead rtai will call this "fake" irq with a 1khz realtime timer. :) +- - rtai will directly service the card, not like it used to by triggering +- the linux irq +- */ + +-#ifndef RTAITIMING + spin_lock_irqsave(&hfctmp->lock, flags); + stat = hfc_inb(hfctmp, hfc_STATUS); +- + if ((stat & hfc_STATUS_ANYINT) == 0) { + // maybe we are sharing the irq + spin_unlock_irqrestore(&hfctmp->lock,flags); +@@ -605,8 +458,6 @@ + return; + #endif + } +-#endif +- + s1 = hfc_inb(hfctmp, hfc_INT_S1); + s2 = hfc_inb(hfctmp, hfc_INT_S2); + if (s1 != 0) { +@@ -625,18 +476,10 @@ + } + switch (l1state) { + case 3: +-#ifdef RTAITIMING +- sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT] layer 1 ACTIVATED (G%d) [realtime]", hfctmp->cardno, l1state); +-#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT] layer 1 ACTIVATED (G%d)", hfctmp->cardno, l1state); +-#endif + break; + default: +-#ifdef RTAITIMING +- sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT] layer 1 DEACTIVATED (G%d) [realtime]", hfctmp->cardno, l1state); +-#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT] layer 1 DEACTIVATED (G%d)", hfctmp->cardno, l1state); +-#endif + } + if (l1state == 2) { + hfc_outb(hfctmp, hfc_STATES, hfc_STATES_ACTIVATE | hfc_STATES_DO_ACTION | hfc_STATES_NT_G2_G3); +@@ -650,18 +493,10 @@ + } + switch (l1state) { + case 7: +-#ifdef RTAITIMING +- sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE] layer 1 ACTIVATED (F%d) [realtime]", hfctmp->cardno, l1state); +-#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE] layer 1 ACTIVATED (F%d)", hfctmp->cardno, l1state); +-#endif + break; + default: +-#ifdef RTAITIMING +- sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE] layer 1 DEACTIVATED (F%d) [realtime]", hfctmp->cardno, l1state); +-#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE] layer 1 DEACTIVATED (F%d)", hfctmp->cardno, l1state); +-#endif + } + if (l1state == 3) { + hfc_outb(hfctmp, hfc_STATES, hfc_STATES_DO_ACTION | hfc_STATES_ACTIVATE); +@@ -671,7 +506,7 @@ + } + if (s1 & hfc_INTS_DREC) { + // D chan RX (bit 5) +- hfctmp->regs.int_drec++; ++ hfctmp->regs.int_drec = 1; + // mr. zapata there is something for you! + // printk(KERN_CRIT "d chan rx\n"); + } +@@ -692,14 +527,10 @@ + // B1 chan TX (bit 0) + } + } +-#ifdef RTAITIMING +- /* fake an irq */ +- s2 |= hfc_M2_PROC_TRANS; +-#endif + if (s2 != 0) { + if (s2 & hfc_M2_PMESEL) { + // kaboom irq (bit 7) +- printk(KERN_CRIT "zaphfc: sync lost, pci performance too low. you might have some cpu throtteling enabled.\n"); ++ // printk(KERN_CRIT "zaphfc: sync lost, pci performance too low. you might have some cpu throtteling enabled.\n"); + } + if (s2 & hfc_M2_GCI_MON_REC) { + // RxR monitor channel (bit 2) +@@ -707,32 +538,30 @@ + if (s2 & hfc_M2_GCI_I_CHG) { + // GCI I-change (bit 1) + } +- if (s2 & hfc_M2_PROC_TRANS) { ++ if((s2&hfc_M2_PROC_TRANS)&&(hfctmp->cardno==timer_card)){ + // processing/non-processing transition (bit 0) +- hfctmp->ticks++; +-#ifndef RTAITIMING +- if (hfctmp->ticks > 7) { +- // welcome to zaptel timing :) +-#endif +- hfctmp->ticks = 0; +- ++ hfctmp2=hfctmp; ++ hfctmp=hfc_dev_list; ++ while(hfctmp){ ++ if(hfctmp->active){ ++ if(hfctmp!=hfctmp2)spin_lock_irqsave(&hfctmp->lock, flags2); ++ if(hfc_bch_check(hfctmp)){ + if (hfctmp->ztdev->span.flags & ZT_FLAG_RUNNING) { + // clear dchan buffer ++ // memset(hfctmp->drecbuf, 0x0, sizeof(hfctmp->drecbuf)); ++ + hfctmp->ztdev->chans[2].bytes2transmit = 0; + hfctmp->ztdev->chans[2].maxbytes2transmit = hfc_D_FIFO_SIZE; + + zt_transmit(&(hfctmp->ztdev->span)); + +- hfc_btrans(hfctmp,1); +- hfc_btrans(hfctmp,2); +- hfc_dtrans(hfctmp); ++ hfc_bch_tx(hfctmp); ++ hfc_dch_tx(hfctmp); + } +- +- hfc_brec(hfctmp,1); +- hfc_brec(hfctmp,2); +- if (hfctmp->regs.int_drec > 0) { ++ hfc_bch_rx(hfctmp); ++ if (hfctmp->regs.int_drec) { + // dchan data to read +- hfc_drec(hfctmp); ++ hfc_dch_rx(hfctmp); + if (hfctmp->ztdev->chans[2].bytes2receive > 0) { + if (debug) { + printk(KERN_CRIT "zaphfc: card %d RX [ ", hfctmp->cardno); +@@ -757,19 +586,18 @@ + if (hfctmp->ztdev->span.flags & ZT_FLAG_RUNNING) { + zt_receive(&(hfctmp->ztdev->span)); + } +- +-#ifndef RTAITIMING + } +-#endif ++ if(hfctmp!=hfctmp2)spin_unlock_irqrestore(&hfctmp->lock,flags2); ++ } ++ hfctmp=hfctmp->next; ++ } ++ hfctmp=hfctmp2; + } +- + } +-#ifndef RTAITIMING + spin_unlock_irqrestore(&hfctmp->lock,flags); + #ifdef LINUX26 + return IRQ_RETVAL(1); + #endif +-#endif + } + + +@@ -826,22 +654,21 @@ + } + alreadyrunning = span->flags & ZT_FLAG_RUNNING; + +- if (!alreadyrunning) { +- span->chans[2].flags &= ~ZT_FLAG_HDLC; +- span->chans[2].flags |= ZT_FLAG_BRIDCHAN; +- +- span->flags |= ZT_FLAG_RUNNING; ++ if (alreadyrunning) return 0; + +- hfctmp->ticks = -2; +- hfctmp->clicks = 0; +- hfctmp->regs.fifo_en = hfc_FIFOEN_D | hfc_FIFOEN_B1 | hfc_FIFOEN_B2; +- hfc_outb(hfctmp, hfc_FIFO_EN, hfctmp->regs.fifo_en); +- } else { +- return 0; +- } ++ span->chans[2].flags &= ~ZT_FLAG_HDLC; ++ span->chans[2].flags |= ZT_FLAG_BRIDCHAN; ++ ++ span->flags |= ZT_FLAG_RUNNING; ++ ++ hfctmp->ticks = -2; ++ hfctmp->regs.fifo_en = hfc_FIFOEN_D | hfc_FIFOEN_B1 | hfc_FIFOEN_B2; ++ hfc_outb(hfctmp, hfc_FIFO_EN, hfctmp->regs.fifo_en); + ++ hfc_bch_init(hfctmp); + // drivers, start engines! + hfc_outb(hfctmp, hfc_STATES, hfc_STATES_DO_ACTION | hfc_STATES_ACTIVATE); ++ hfctmp->active=1; + return 0; + } + +@@ -871,17 +698,9 @@ + + sprintf(zthfc->span.name, "ZTHFC%d", hfc_dev_count + 1); + if (hfctmp->regs.nt_mode == 1) { +-#ifdef RTAITIMING +- sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT] [realtime]", hfc_dev_count + 1); +-#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT]", hfc_dev_count + 1); +-#endif + } else { +-#ifdef RTAITIMING +- sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE] [realtime]", hfc_dev_count + 1); +-#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE]", hfc_dev_count + 1); +-#endif + } + + zthfc->span.spanconfig = zthfc_spanconfig; +@@ -918,32 +737,6 @@ + return 0; + } + +-#ifdef RTAITIMING +-#define TICK_PERIOD 1000000 +-#define TICK_PERIOD2 1000000000 +-#define TASK_PRIORITY 1 +-#define STACK_SIZE 10000 +- +-static RT_TASK rt_task; +-static struct hfc_card *rtai_hfc_list[hfc_MAX_CARDS]; +-static unsigned char rtai_hfc_counter = 0; +- +-static void rtai_register_hfc(struct hfc_card *hfctmp) { +- rtai_hfc_list[rtai_hfc_counter++] = hfctmp; +-} +- +-static void rtai_loop(int t) { +- int i=0; +- for (;;) { +- for (i=0; i < rtai_hfc_counter; i++) { +- if (rtai_hfc_list[i] != NULL) +- hfc_service(rtai_hfc_list[i]); +- } +- rt_task_wait_period(); +- } +-} +-#endif +- + int hfc_findCards(int pcivendor, int pcidevice, char *vendor_name, char *card_name) { + struct pci_dev *tmp; + struct hfc_card *hfctmp = NULL; +@@ -959,9 +752,9 @@ + } + pci_set_master(tmp); + +- hfctmp = kmalloc(sizeof(struct hfc_card), GFP_KERNEL); ++ hfctmp = vmalloc(sizeof(struct hfc_card)); + if (!hfctmp) { +- printk(KERN_WARNING "zaphfc: unable to kmalloc!\n"); ++ printk(KERN_WARNING "zaphfc: unable to vmalloc!\n"); + pci_disable_device(tmp); + multi_hfc = NULL; + return -ENOMEM; +@@ -969,6 +762,7 @@ + memset(hfctmp, 0x0, sizeof(struct hfc_card)); + spin_lock_init(&hfctmp->lock); + ++ hfctmp->active=0; + hfctmp->pcidev = tmp; + hfctmp->pcibus = tmp->bus->number; + hfctmp->pcidevfn = tmp->devfn; +@@ -982,44 +776,34 @@ + hfctmp->pci_io = (char *) tmp->resource[1].start; + if (!hfctmp->pci_io) { + printk(KERN_WARNING "zaphfc: no iomem!\n"); +- kfree(hfctmp); ++ vfree(hfctmp); + pci_disable_device(tmp); + multi_hfc = NULL; + return -1; + } +- +- hfctmp->fifomem = kmalloc(65536, GFP_KERNEL); +- if (!hfctmp->fifomem) { +- printk(KERN_WARNING "zaphfc: unable to kmalloc fifomem!\n"); +- kfree(hfctmp); ++ ++ hfctmp->fifos=(void *)__get_free_pages(GFP_KERNEL,log2(hfc_FIFO_MEM_SIZE_PAGES)); ++ if (!hfctmp->fifos) { ++ printk(KERN_WARNING "zaphfc: unable to __get_free_pages fifomem!\n"); ++ vfree(hfctmp); + pci_disable_device(tmp); + multi_hfc = NULL; + return -ENOMEM; + } else { +- memset(hfctmp->fifomem, 0x0, 65536); +- hfctmp->fifos = (void *)(((ulong) hfctmp->fifomem) & ~0x7FFF) + 0x8000; + pci_write_config_dword(hfctmp->pcidev, 0x80, (u_int) virt_to_bus(hfctmp->fifos)); + hfctmp->pci_io = ioremap((ulong) hfctmp->pci_io, 256); + } + +-#ifdef RTAITIMING +- /* we need no stinking irq */ +- hfctmp->irq = 0; +-#else + if (request_irq(hfctmp->irq, &hfc_interrupt, ZAP_IRQ_SHARED, "zaphfc", hfctmp)) { + printk(KERN_WARNING "zaphfc: unable to register irq\n"); +- kfree(hfctmp->fifomem); +- kfree(hfctmp); ++ free_pages((unsigned long)hfctmp->fifos,log2(hfc_FIFO_MEM_SIZE_PAGES)); ++ vfree(hfctmp); + iounmap((void *) hfctmp->pci_io); + pci_disable_device(tmp); + multi_hfc = NULL; + return -EIO; + } +-#endif + +-#ifdef RTAITIMING +- rtai_register_hfc(hfctmp); +-#endif + printk(KERN_INFO + "zaphfc: %s %s configured at mem %lx fifo %lx(%#x) IRQ %d HZ %d\n", + vendor_name, card_name, +@@ -1041,11 +825,21 @@ + hfctmp->regs.nt_mode = 0; + } + +- zthfc = kmalloc(sizeof(struct zt_hfc),GFP_KERNEL); ++ if(sync_slave&(1<regs.mst_mode=hfc_MST_MODE_SLAVE|hfc_MST_MODE_F0_LONG_DURATION; ++ hfctmp->regs.mst_emod=hfc_MST_EMOD_SLOW_CLOCK_ADJ; ++ }else{ ++ printk(KERN_INFO "zaphfc: Card %d configured for master mode\n",hfc_dev_count); ++ hfctmp->regs.mst_mode=hfc_MST_MODE_MASTER|hfc_MST_MODE_F0_LONG_DURATION; ++ hfctmp->regs.mst_emod=0; ++ } ++ ++ zthfc = vmalloc(sizeof(struct zt_hfc)); + if (!zthfc) { +- printk(KERN_CRIT "zaphfc: unable to kmalloc!\n"); ++ printk(KERN_CRIT "zaphfc: unable to vmalloc!\n"); + hfc_shutdownCard(hfctmp); +- kfree(hfctmp); ++ vfree(hfctmp); + multi_hfc = NULL; + return -ENOMEM; + } +@@ -1079,58 +872,42 @@ + return 0; + } + +- +- + int init_module(void) { + int i = 0; +-#ifdef RTAITIMING +- RTIME tick_period; +- for (i=0; i < hfc_MAX_CARDS; i++) { +- rtai_hfc_list[i] = NULL; ++ if(jitterbuffer<1){ ++ printk(KERN_INFO "zaphfc: invalid jitterbuffer size specified: %d - changing to minimum of 1\n",jitterbuffer); ++ jitterbuffer=1; ++ }else if(jitterbuffer>500){ ++ printk(KERN_INFO "zaphfc: invalid jitterbuffer size specified: %d - changing to maximum of 500\n",jitterbuffer); ++ jitterbuffer=500; + } +- rt_set_periodic_mode(); +-#endif +- i = 0; ++ printk(KERN_INFO "zaphfc: jitterbuffer size: %d\n",jitterbuffer); + while (id_list[i].vendor_id) { + multi_hfc = NULL; + hfc_findCards(id_list[i].vendor_id, id_list[i].device_id, id_list[i].vendor_name, id_list[i].card_name); + i++; + } +-#ifdef RTAITIMING +- for (i=0; i < hfc_MAX_CARDS; i++) { +- if (rtai_hfc_list[i]) { +- printk(KERN_INFO +- "zaphfc: configured %d at mem %#x fifo %#x(%#x) for realtime servicing\n", +- rtai_hfc_list[i]->cardno, +- (u_int) rtai_hfc_list[i]->pci_io, +- (u_int) rtai_hfc_list[i]->fifos, +- (u_int) virt_to_bus(rtai_hfc_list[i]->fifos)); +- +- } +- } +- rt_task_init(&rt_task, rtai_loop, 1, STACK_SIZE, TASK_PRIORITY, 0, 0); +- tick_period = start_rt_timer(nano2count(TICK_PERIOD)); +- rt_task_make_periodic(&rt_task, rt_get_time() + tick_period, tick_period); +-#endif + printk(KERN_INFO "zaphfc: %d hfc-pci card(s) in this box.\n", hfc_dev_count); + return 0; + } + + void cleanup_module(void) { + struct hfc_card *tmpcard; +-#ifdef RTAITIMING +- stop_rt_timer(); +- rt_task_delete(&rt_task); +-#endif ++ + printk(KERN_INFO "zaphfc: stop\n"); + // spin_lock(®isterlock); ++ tmpcard=hfc_dev_list; ++ while(tmpcard){ ++ hfc_shutdownCard1(tmpcard); ++ tmpcard=tmpcard->next; ++ } + while (hfc_dev_list != NULL) { + if (hfc_dev_list == NULL) break; +- hfc_shutdownCard(hfc_dev_list); ++ hfc_shutdownCard2(hfc_dev_list); + tmpcard = hfc_dev_list; + hfc_dev_list = hfc_dev_list->next; + if (tmpcard != NULL) { +- kfree(tmpcard); ++ vfree(tmpcard); + tmpcard = NULL; + printk(KERN_INFO "zaphfc: freed one card.\n"); + } +@@ -1141,11 +918,17 @@ + + + #ifdef LINUX26 +-module_param(modes, int, 0600); ++module_param(modes, int, 0400); + module_param(debug, int, 0600); ++module_param(sync_slave, int, 0400); ++module_param(timer_card, int, 0400); ++module_param(jitterbuffer, int, 0400); + #else + MODULE_PARM(modes,"i"); + MODULE_PARM(debug,"i"); ++MODULE_PARM(sync_slave,"i"); ++MODULE_PARM(timer_card,"i"); ++MODULE_PARM(jitterbuffer,"i"); + #endif + + MODULE_DESCRIPTION("HFC-S PCI A Zaptel Driver"); +@@ -1153,3 +936,6 @@ + #ifdef MODULE_LICENSE + MODULE_LICENSE("GPL"); + #endif ++ ++/* vim:set sw=4: */ ++ +diff -urN bristuff-0.4.0-test1/zaphfc/zaphfc.h zaphfc_0.4.0-test1_florz-13/zaphfc.h +--- bristuff-0.4.0-test1/kernel/zaphfc/zaphfc.h 2005-02-26 23:30:32.000000000 +0100 ++++ zaphfc_0.4.0-test1_florz-13/kernel/zaphfc/zaphfc.h 2005-03-02 20:43:04.000000000 +0100 +@@ -135,8 +135,12 @@ + /* bits in HFCD_MST_MODE */ + #define hfc_MST_MODE_MASTER 0x01 + #define hfc_MST_MODE_SLAVE 0x00 ++#define hfc_MST_MODE_F0_LONG_DURATION 0x08 + /* remaining bits are for codecs control */ + ++/* bits in HFCD_MST_EMOD */ ++#define hfc_MST_EMOD_SLOW_CLOCK_ADJ 0x01 ++ + /* bits in HFCD_SCTRL */ + #define hfc_SCTRL_B1_ENA 0x01 + #define hfc_SCTRL_B2_ENA 0x02 +@@ -236,6 +240,9 @@ + #define hfc_BTRANS_THRESHOLD 128 + #define hfc_BTRANS_THRESMASK 0x00 + ++#define hfc_FIFO_MEM_SIZE_BYTES (32*1024) ++#define hfc_FIFO_MEM_SIZE_PAGES ((hfc_FIFO_MEM_SIZE_BYTES+PAGE_SIZE-1)/PAGE_SIZE) ++ + /* Structures */ + + typedef struct hfc_regs { +@@ -249,20 +256,67 @@ + unsigned char connect; + unsigned char trm; + unsigned char mst_mode; ++ unsigned char mst_emod; + unsigned char bswapped; + unsigned char nt_mode; + unsigned char int_drec; + } hfc_regs; + ++struct bch { ++ int fill_fifo,checkcnt,initialized; ++ struct { ++ u16 z2; ++ struct { ++ volatile u16 *z1p; ++ volatile u8 *fifo_base; ++ int filled; ++ } c[2]; ++ int diff; ++ } rx; ++ struct { ++ u16 z1; ++ struct { ++ volatile u16 *z1p,*z2p; ++ volatile u8 *fifo_base; ++ int filled; ++ } c[2]; ++ int diff; ++ } tx; ++}; ++ ++struct dch { ++ struct { ++ struct { ++ volatile u8 *p; ++ } f1; ++ struct { ++ u8 v; ++ struct { ++ u16 v; ++ } z2; ++ } f2; ++ } rx; ++ struct { ++ struct { ++ u8 v; ++ volatile u8 *p; ++ struct { ++ u16 v; ++ } z1; ++ } f1; ++ struct { ++ volatile u8 *p; ++ } f2; ++ } tx; ++}; ++ + typedef struct hfc_card { + spinlock_t lock; + unsigned int irq; + unsigned int iomem; + int ticks; +- int clicks; + unsigned char *pci_io; +- void *fifomem; // start of the shared mem +- volatile void *fifos; // 32k aligned mem for the fifos ++ void *fifos; // 32k aligned mem for the fifos + struct hfc_regs regs; + unsigned int pcibus; + unsigned int pcidevfn; +@@ -274,6 +328,9 @@ + unsigned char brecbuf[2][ZT_CHUNKSIZE]; + unsigned char btransbuf[2][ZT_CHUNKSIZE]; + unsigned char cardno; ++ int active; ++ struct bch bch; ++ struct dch dch; + struct hfc_card *next; + } hfc_card; + +@@ -284,6 +341,5 @@ + struct hfc_card *card; + } zt_hfc; + +-/* tune this */ +-#define hfc_BCHAN_BUFFER 8 +-#define hfc_MAX_CARDS 8 ++/* vim:set sw=4: */ ++ --- zaptel-1.4.11~dfsg.orig/debian/patches/oslec_zaptel +++ zaptel-1.4.11~dfsg/debian/patches/oslec_zaptel @@ -0,0 +1,45 @@ +## oslec_zaptel by Tzafrir Cohen +## +## A minimal patch to zaptel-base.c to integrate the oslec echo +## canceller. The zaptap device is not included. + +diff -urNad zaptel-1.4.3~dfsg~/zaptel-base.c zaptel-1.4.3~dfsg/zaptel-base.c +--- zaptel-1.4.3~dfsg~/kernel/zaptel-base.c 2007-06-16 21:33:45.000000000 +0300 ++++ zaptel-1.4.3~dfsg/kernel/zaptel-base.c 2007-06-16 21:33:47.000000000 +0300 +@@ -424,6 +424,14 @@ + #include "kb1ec.h" + #elif defined(ECHO_CAN_MG2) + #include "mg2ec.h" ++#elif defined(ECHO_CAN_OSLEC) ++#include "oslec/oslec.h" ++#define echo_can_create_noparams oslec_echo_can_create ++#define echo_can_free oslec_echo_can_free ++#define echo_can_update oslec_echo_can_update ++#define echo_can_traintap oslec_echo_can_traintap ++#define echo_can_identify oslec_echo_can_identify ++#define ECHO_CAN_NO_PARAMS + #elif defined(ECHO_CAN_JP1) + #include "jpah.h" + #else +@@ -425,6 +425,21 @@ + memset(conf_sums_next, 0, maxconfs * sizeof(sumtype)); + } + ++#ifdef ECHO_CAN_NO_PARAMS ++/* A wrapper to the original interface, with no parameters */ ++static int echo_can_create(struct zt_echocanparams *ecp, struct zt_echocanparam *p, ++ struct echo_can_state **ec) ++{ ++ /* ignore the parameters */ ++ /* TODO: maybe fail if the user passes us parameters? or at ++ * least list their number? */ ++ *ec = echo_can_create_noparams(ecp->tap_length, 0); ++ if (!ec) ++ return -EINVAL; /* an arbitrary error */ ++ return 0; ++} ++#endif ++ + /* return quiescent (idle) signalling states, for the various signalling types */ + static int zt_q_sig(struct zt_chan *chan) + { --- zaptel-1.4.11~dfsg.orig/debian/patches/sigcap_dacs +++ zaptel-1.4.11~dfsg/debian/patches/sigcap_dacs @@ -0,0 +1,28 @@ +The asterisk-gui uses ztscan to detect if a span is digital: if its +first B channel supports (as in its sigcap) ZT_SIG_DACS. This patch adds +the ZT_SIG_DACS capability for a number of channel drivers. It seems +that those channels would support it anyway (although it rarely gets +tested). + +--- zaptel-1.4.10.1~dfsg.orig/kernel/vzaphfc/vzaphfc_main.c ++++ zaptel-1.4.10.1~dfsg/kernel/vzaphfc/vzaphfc_main.c +@@ -1005,7 +1005,7 @@ static int hfc_zap_initialize(struct hfc + ZT_SIG_FXSLS | ZT_SIG_FXSGS | + ZT_SIG_FXSKS | ZT_SIG_FXOLS | + ZT_SIG_FXOGS | ZT_SIG_FXOKS | +- ZT_SIG_CAS | ZT_SIG_SF; ++ ZT_SIG_CAS | ZT_SIG_SF | ZT_SIG_DACS; + + card->zt_chans[i].chanpos = i + 1; + } +--- zaptel-1.4.10.1~dfsg.orig/kernel/zaphfc/zaphfc.c ++++ zaptel-1.4.10.1~dfsg/kernel/zaphfc/zaphfc.c +@@ -721,7 +721,7 @@ static int zthfc_initialize(struct zt_hf + memset(&(zthfc->chans[i]), 0x0, sizeof(struct zt_chan)); + sprintf(zthfc->chans[i].name, "ZTHFC%d/%d/%d", hfc_dev_count + 1,0,i + 1); + zthfc->chans[i].pvt = zthfc; +- zthfc->chans[i].sigcap = ZT_SIG_EM | ZT_SIG_CLEAR | ZT_SIG_FXSLS | ZT_SIG_FXSGS | ZT_SIG_FXSKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_FXOKS | ZT_SIG_CAS | ZT_SIG_SF; ++ zthfc->chans[i].sigcap = ZT_SIG_EM | ZT_SIG_CLEAR | ZT_SIG_FXSLS | ZT_SIG_FXSGS | ZT_SIG_FXSKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_FXOKS | ZT_SIG_CAS | ZT_SIG_SF | ZT_SIG_DACS; + zthfc->chans[i].chanpos = i + 1; + } + --- zaptel-1.4.11~dfsg.orig/debian/patches/Makefile_opt_level +++ zaptel-1.4.11~dfsg/debian/patches/Makefile_opt_level @@ -0,0 +1,27 @@ +## Makefile_opt_level.dpatch by Kilian Krause +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: Use -O2 in accordance with Debian Policy + +@DPATCH@ +diff -urNad zaptel-1.4.5.1~dfsg~/Makefile zaptel-1.4.5.1~dfsg/Makefile +--- zaptel-1.4.5.1~dfsg~/Makefile 2007-08-21 20:11:37.000000000 +0200 ++++ zaptel-1.4.5.1~dfsg/Makefile 2007-08-26 12:06:19.000000000 +0200 +@@ -138,7 +138,7 @@ + ALL_MODULES+=$(patsubst %,xpp/%.ko,xpp_usb xpd_fxo xpd_fxs xpp) + endif + +-CFLAGS+=-I. -O4 -g -Wall -DBUILDING_TONEZONE #-DTONEZONE_DRIVER ++CFLAGS+=-I. -O2 -g -Wall -DBUILDING_TONEZONE #-DTONEZONE_DRIVER + ifneq (,$(findstring ppc,$(UNAME_M))) + CFLAGS_PPC:=-fsigned-char + endif +@@ -147,7 +147,7 @@ + endif + CFLAGS+=$(CFLAGS_PPC) $(CFLAGS_x86_64) + LCFLAGS=-fPIC $(CFLAGS) -DBUILDING_TONEZONE +-KFLAGS=-I$(KINCLUDES) -O6 ++KFLAGS=-I$(KINCLUDES) -O2 + 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)) --- zaptel-1.4.11~dfsg.orig/debian/patches/zaptel_perl +++ zaptel-1.4.11~dfsg/debian/patches/zaptel_perl @@ -0,0 +1,19 @@ +## zaptel_perl by Tzafrir Cohen +## +## Point Zaptel PERL modules to the right directory under +## which to install perl modules. +## (sitelib in Debian is under /usr/local. We don't want to install +## there) + +diff -urNad zaptel-1.4.5~dfsg~/xpp/utils/Makefile zaptel-1.4.5~dfsg/xpp/utils/Makefile +--- zaptel-1.4.5~dfsg~/kernel/xpp/utils/Makefile 2007-08-01 23:41:21.000000000 +0100 ++++ zaptel-1.4.5~dfsg/kernel/xpp/utils/Makefile 2007-08-19 20:09:48.000000000 +0100 +@@ -23,7 +23,7 @@ + MANDIR = $(mandir)/man8 + HOTPLUG_USB_DIR = /etc/hotplug/usb + UDEV_RULES_DIR = /etc/udev/rules.d +-PERLLIBDIR := $(shell eval `perl -V:sitelib`; echo "$$sitelib") ++PERLLIBDIR := $(shell eval `perl -V:vendorlib`; echo "$$vendorlib") + PERL_DIRS := $(shell cd zconf; find * -name '[A-Z]*' -type d| xargs) + PERL_MODS_PAT := *.pm $(PERL_DIRS:%=%/*.pm) + PERL_MODS := $(shell cd zconf; echo $(PERL_MODS_PAT)) --- zaptel-1.4.11~dfsg.orig/debian/patches/beronet +++ zaptel-1.4.11~dfsg/debian/patches/beronet @@ -0,0 +1,473 @@ +## beronet by Tzafrir Cohen +## +## Support bero.net cards in qozap and cwain Zaptel drivers. +## Source: http://blog.eth0.cc/zaptel-patchwork/ + +diff -urNad zaptel-1.4.7.1~dfsg~/cwain/cwain.c zaptel-1.4.7.1~dfsg/cwain/cwain.c +--- zaptel-1.4.7.1~dfsg~/kernel/cwain/cwain.c 2007-12-30 10:26:20.000000000 +0200 ++++ zaptel-1.4.7.1~dfsg/kernel/cwain/cwain.c 2007-12-30 10:44:27.000000000 +0200 +@@ -12,6 +12,13 @@ + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * ++ * Patched to recognize and support BN1E1 and BN2E1(+) boards ++ * by Daniel Scheller or ++ * ++ * The patch is (C) 2007 LocaNet oHG. It is free software and ++ * may be modified and distributed under the terms of the GNU ++ * General Public License. ++ * + */ + #include + #include +@@ -153,7 +160,7 @@ + if (!(cwaintmp->span.flags & ZT_FLAG_RUNNING)) { + return; + } +- if ((cwaintmp->type == 0xb553) || (cwaintmp->type == 0xb554)) { ++ if ((cwaintmp->type == 0xb553) || (cwaintmp->type == 0xb554) || (cwaintmp->type == 0xb563) || (cwaintmp->type == 0xb564) || (cwaintmp->type == 0xb565)) { + /* sync status */ + if (((cwaintmp->sync_sta & 0x07) == 0x07) && cwaintmp->sync) { + cwaintmp->leds[0] = 1; +@@ -294,7 +301,7 @@ + } + spin_unlock(&cwain_span_registerlock); + +- if (cwainspan->type == 0xb553) { ++ if ((cwainspan->type == 0xb553) || (cwainspan->type == 0xb563)) { + cwaintmp = kmalloc(sizeof(struct zt_cwain_card),GFP_KERNEL); + if (!cwaintmp) { + printk(KERN_WARNING "cwain: unable to kmalloc!\n"); +@@ -309,10 +316,20 @@ + cwaintmp->spans = 1; + cwaintmp->cardID = cwainspan->cardID; + cwain_register_card(cwaintmp); +- printk(KERN_INFO +- "cwain: Junghanns.NET singleE1 PCI ISDN card configured at mem %lx IRQ %d HZ %d CardID %d\n", +- (unsigned long) cwainspan->pci_io, +- cwaintmp->span[0]->irq, HZ, cwainspan->cardID); ++ ++ switch(cwainspan->type) ++ { ++ case 0xb553 : printk(KERN_INFO ++ "cwain: Junghanns.NET singleE1 PCI ISDN card configured at mem %lx IRQ %d HZ %d CardID %d\n", ++ (unsigned long) cwainspan->pci_io, ++ cwaintmp->span[0]->irq, HZ, cwainspan->cardID); ++ break; ++ case 0xb563 : printk(KERN_INFO ++ "cwain: BeroNet BN1E1 (single HFC-E1) PCI ISDN card configured at mem %lx IRQ %d HZ %d CardID %d\n", ++ (unsigned long) cwainspan->pci_io, ++ cwaintmp->span[0]->irq, HZ, cwainspan->cardID); ++ break; ++ } + } else { + cwaintmp = cwain_get_card(cwainspan->pcibus); + if (!cwaintmp) { +@@ -325,13 +342,21 @@ + + spin_lock_init(&cwaintmp->lock); + cwaintmp->pcibus = cwainspan->pcibus; +- cwaintmp->spans = cwainspan->type - 46419; ++ cwaintmp->spans = 1; + cwaintmp->span[0] = cwainspan; + cwaintmp->cardID = cwainspan->cardID; + cwaintmp->syncs[0] = -1; + cwain_register_card(cwaintmp); + } else { +- cwaintmp->spans = cwainspan->type - 46418; ++ switch (cwainspan->type) { ++ case 0xb554 : ++ case 0xb564 : ++ case 0xb565 : cwaintmp->spans = 2; ++ break; ++ default : cwaintmp->spans = 0; ++ break; ++ } ++ + if (cwainspan->cardID < cwaintmp->cardID) { + cwaintmp->cardID = cwainspan->cardID; + cwaintmp->span[1] = cwaintmp->span[0]; +@@ -341,10 +366,30 @@ + cwaintmp->span[1] = cwainspan; + cwaintmp->syncs[1] = -1; + } +- printk(KERN_INFO +- "cwain: Junghanns.NET doubleE1 PCI ISDN card configured at mem (%lx / %lx) IRQ %d HZ %d CardID (%d / %d) bus %#x\n", +- (unsigned long) cwaintmp->span[0]->pci_io, (unsigned long) cwaintmp->span[1]->pci_io, +- cwaintmp->span[0]->irq, HZ, cwaintmp->span[0]->cardID, cwaintmp->span[1]->cardID, cwaintmp->pcibus); ++ ++ switch (cwainspan->type) { ++ case 0xb554 : printk(KERN_INFO ++ "cwain: Junghanns.NET doubleE1 PCI ISDN card configured at mem (%lx / %lx) IRQ %d HZ %d CardID (%d / %d) bus %#x\n", ++ (unsigned long) cwaintmp->span[0]->pci_io, (unsigned long) cwaintmp->span[1]->pci_io, ++ cwaintmp->span[0]->irq, HZ, cwaintmp->span[0]->cardID, cwaintmp->span[1]->cardID, cwaintmp->pcibus); ++ break; ++ case 0xb564 : printk(KERN_INFO ++ "cwain: BeroNet BN2E1 (dual HFC-E1) PCI ISDN card configured at mem (%lx / %lx) IRQ %d HZ %d CardID (%d / %d) bus %#x\n", ++ (unsigned long) cwaintmp->span[0]->pci_io, (unsigned long) cwaintmp->span[1]->pci_io, ++ cwaintmp->span[0]->irq, HZ, cwaintmp->span[0]->cardID, cwaintmp->span[1]->cardID, cwaintmp->pcibus); ++ break; ++ case 0xb565 : printk(KERN_INFO ++ "cwain: BeroNet BN2E1 (+) (dual HFC-E1 w/failover) PCI ISDN card configured at mem (%lx / %lx) IRQ %d HZ %d CardID (%d / %d) bus %#x\n", ++ (unsigned long) cwaintmp->span[0]->pci_io, (unsigned long) cwaintmp->span[1]->pci_io, ++ cwaintmp->span[0]->irq, HZ, cwaintmp->span[0]->cardID, cwaintmp->span[1]->cardID, cwaintmp->pcibus); ++ break; ++ default : printk(KERN_INFO ++ "cwain: Unknown HFC-E1 PCI ISDN card configured at mem (%lx / %lx) IRQ %d HZ %d CardID (%d / %d) bus %#x\n", ++ (unsigned long) cwaintmp->span[0]->pci_io, (unsigned long) cwaintmp->span[1]->pci_io, ++ cwaintmp->span[0]->irq, HZ, cwaintmp->span[0]->cardID, cwaintmp->span[1]->cardID, cwaintmp->pcibus); ++ break; ++ } ++ + } + } + return 0; +@@ -1232,6 +1277,15 @@ + case 0xb554: + sprintf(cwainspan->span.desc,"Junghanns.NET doubleE1 PCI ISDN Card %d (cardID %d) (1 E1 port) SYNCED",cwainspan->cardno,cwainspan->cardID); + break; ++ case 0xb563: ++ sprintf(cwainspan->span.desc,"BeroNet BN1E1 (single HFC-E1) PCI ISDN Card %d (cardID %d) SYNCED",cwainspan->cardno,cwainspan->cardID); ++ break; ++ case 0xb564: ++ sprintf(cwainspan->span.desc,"BeroNet BN2E1 (dual HFC-E1) PCI ISDN Card %d (cardID %d) (1 E1 port) SYNCED",cwainspan->cardno,cwainspan->cardID); ++ break; ++ case 0xb565: ++ sprintf(cwainspan->span.desc,"BeroNet BN2E1 (+) (dual HFC-E1 w/failover) PCI ISDN Card %d (cardID %d) (1 E1 port) SYNCED",cwainspan->cardno,cwainspan->cardID); ++ break; + } + } else { + switch (cwainspan->type) { +@@ -1241,6 +1295,15 @@ + case 0xb554: + sprintf(cwainspan->span.desc,"Junghanns.NET doubleE1 PCI ISDN Card %d (cardID %d) (1 E1 port) NO SYNC (sync_sta = %#x)",cwainspan->cardno,cwainspan->cardID, sync_sta); + break; ++ case 0xb563: ++ sprintf(cwainspan->span.desc,"BeroNet BN1E1 (single HFC-E1) PCI ISDN Card %d (cardID %d) NO SYNC (sync_sta = %#x)",cwainspan->cardno,cwainspan->cardID, sync_sta); ++ break; ++ case 0xb564: ++ sprintf(cwainspan->span.desc,"BeroNet BN2E1 (dual HFC-E1) PCI ISDN Card %d (cardID %d) (1 E1 port) NO SYNC (sync_sta = %#x)",cwainspan->cardno,cwainspan->cardID, sync_sta); ++ break; ++ case 0xb565: ++ sprintf(cwainspan->span.desc,"BeroNet BN2E1 (+) (dual HFC-E1 w/failover) PCI ISDN Card %d (cardID %d) (1 E1 port) NO SYNC (sync_sta = %#x)",cwainspan->cardno,cwainspan->cardID, sync_sta); ++ break; + } + } + cwain_doLEDs(cwainspan); +@@ -1862,6 +1925,15 @@ + case 0xb554: + sprintf(cwaintmp->span.desc,"Junghanns.NET doubleE1 PCI ISDN Card %d (cardID %d) (1 E1 port)",cwaintmp->cardno,cwaintmp->cardID); + break; ++ case 0xb563: ++ sprintf(cwaintmp->span.desc,"BeroNet BN1E1 (single HFC-E1) PCI ISDN Card %d (cardID %d)",cwaintmp->cardno,cwaintmp->cardID); ++ break; ++ case 0xb564: ++ sprintf(cwaintmp->span.desc,"BeroNet BN2E1 (dual HFC-E1) PCI ISDN Card %d (cardID %d) (1 E1 port)",cwaintmp->cardno,cwaintmp->cardID); ++ break; ++ case 0xb565: ++ sprintf(cwaintmp->span.desc,"BeroNet BN2E1 (+) (dual HFC-E1 w/failover) PCI ISDN Card %d (cardID %d) (1 E1 port)",cwaintmp->cardno,cwaintmp->cardID); ++ break; + default: + return -1; + } +@@ -2038,7 +2110,7 @@ + /* disable interrupts */ + cwain_outb(cwaintmp,cwain_R_IRQ_CTRL, 0); + +- if (((tmp->subsystem_device==0xb553) || (tmp->subsystem_device==0xb554))&& (pcidid == PCI_DEVICE_ID_CCD_E)) { ++ if (((tmp->subsystem_device==0xb553) || (tmp->subsystem_device==0xb554) || (tmp->subsystem_device==0xb563) || (tmp->subsystem_device==0xb564) || (tmp->subsystem_device==0xb565))&& (pcidid == PCI_DEVICE_ID_CCD_E)) { + dips = (cwain_inb(cwaintmp,cwain_R_GPI_IN0) >> 5); + cid = 7; + for (i=0;i<3;i++) { +diff -urNad qozap/qozap.c qozap/qozap.c +--- a/kernel/qozap/qozap.c 2007-12-30 10:26:21.000000000 +0200 ++++ b/kernel/qozap/qozap.c 2007-12-30 10:44:21.000000000 +0200 +@@ -9,6 +9,14 @@ + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * ++ * Patched to recognize and support BN2S0, BN4S0, BN8S0 and BN8S0+ ++ * by Daniel Scheller or and ++ * Henning Holtschneider ++ * ++ * The patch is (C) 2006, 2007 LocaNet oHG. It is free software and ++ * may be modified and distributed under the terms of the GNU Public ++ * License. ++ * + */ + #include + #include +@@ -112,7 +120,7 @@ + unsigned long flags; + spin_lock_irqsave(&qoztmp->lock,flags); + +- if ((qoztmp->type == 0xb520) && (qoztmp->stports == 4)){ ++ if (((qoztmp->type == 0xb520) || (qoztmp->type == 0xb560)) && (qoztmp->stports == 4)){ + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0x20 | 0x10); + qoz_outb(qoztmp,qoz_R_GPIO_EN1,0xf); + qoz_outb(qoztmp,qoz_R_GPIO_OUT1,(qoztmp->leds[0] | (qoztmp->leds[1] << 1) | (qoztmp->leds[2] << 2) | (qoztmp->leds[3] << 3))); +@@ -147,7 +155,7 @@ + */ + qoz_outb(qoztmp,qoz_R_GPIO_OUT1, leds); + +- } else if (((qoztmp->type == 0xb556) || (qoztmp->type == 0xb751)) && (qoztmp->stports == 2)){ ++ } else if (((qoztmp->type == 0xb556) || (qoztmp->type == 0xb566) || (qoztmp->type == 0xb751)) && (qoztmp->stports == 2)){ + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0x80 | 0x40 | 0x20 | 0x10); + qoz_outb(qoztmp,qoz_R_GPIO_EN1,0xff); + leds = 0x0; +@@ -159,7 +167,7 @@ + } + qoz_outb(qoztmp,qoz_R_GPIO_OUT1, leds); + +- } else if (qoztmp->type == 0xb558) { ++ } else if ((qoztmp->type == 0xb558) || (qoztmp->type == 0xb568)) { + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0x80 | 0x40 | 0x20); + qoz_outb(qoztmp,qoz_R_GPIO_EN1,0x80 | 0x40 | 0x20 | 0x4); + if (qoztmp->leds[0] == 1) { +@@ -182,7 +190,7 @@ + gpio_15 2 + gpio_14 3 + */ +- } else if (qoztmp->type == 0xb55b) { ++ } else if ((qoztmp->type == 0xb55b) || (qoztmp->type == 0xb56b)) { + qoz_outb(qoztmp,qoz_R_BRG_PCM_CFG,0x21); + /* 0x01 g1 st1 + 0x02 g2 st2 +@@ -302,9 +310,9 @@ + + /* set S0 amplitude */ + qoz_outb(qoztmp,qoz_R_PWM_MD,0xa0); +- if (qoztmp->type == 0xb552) { ++ if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb562)) { + qoz_outb(qoztmp,qoz_R_PWM0,0x19); +- } else if (qoztmp->type == 0xb55b) { ++ } else if ((qoztmp->type == 0xb55b) || (qoztmp->type == 0xb56b)) { + qoz_outb(qoztmp,qoz_R_PWM0,0x19); + } else { + qoz_outb(qoztmp,qoz_R_PWM0,0x1E); +@@ -318,9 +326,9 @@ + /* all state changes */ + qoz_outb(qoztmp,qoz_R_SCI_MSK, 0xff); + +- if (qoztmp->type == 0xb552) { ++ if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb562)) { + qoz_outb(qoztmp,qoz_R_FIFO_MD,0x16); +- } else if (qoztmp->type == 0xb55b) { ++ } else if ((qoztmp->type == 0xb55b) || (qoztmp->type == 0xb56b)) { + qoz_outb(qoztmp,qoz_R_FIFO_MD,0x16); + } else { + qoz_outb(qoztmp,qoz_R_FIFO_MD,0x26); +@@ -360,11 +368,11 @@ + qoztmp->leds[6] = 0x0; + qoztmp->leds[7] = 0x0; + +- if (qoztmp->type == 0xb552) { ++ if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb562)) { + qoztmp->stports = 8; +- } else if (qoztmp->type == 0xb55b) { ++ } else if ((qoztmp->type == 0xb55b) || (qoztmp->type == 0xb56b)) { + qoztmp->stports = 8; +- } else if (qoztmp->type == 0xb556) { ++ } else if ((qoztmp->type == 0xb556) || (qoztmp->type == 0xb566)) { + qoztmp->stports = 2; + } else if (qoztmp->type == 0xb557) { + qoztmp->stports = 2; +@@ -373,7 +381,7 @@ + } + qoztmp->ticks = 0; + qoztmp->clicks = 0; +- if ((qoztmp->type == 0xb550) || (qoztmp->type == 0xb556)) { ++ if ((qoztmp->type == 0xb550) || (qoztmp->type == 0xb556) || (qoztmp->type == 0xb566)) { + printk(KERN_INFO "qozap: Starting hardware watchdog.\n"); + qoztmp->wdp = 2; + } else { +@@ -427,9 +435,9 @@ + unsigned long flags; + + +- if (qoztmp->type == 0xb552) { ++ if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb562)) { + offset = 24; +- } else if (qoztmp->type == 0xb55b) { ++ } else if ((qoztmp->type == 0xb55b) || (qoztmp->type == 0xb56b)) { + offset = 24; + } else { + offset = 28; +@@ -493,9 +501,9 @@ + char offset = 0; + unsigned long flags; + +- if (qoztmp->type == 0xb552) { ++ if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb562)) { + offset = 24; +- } else if (qoztmp->type == 0xb55b) { ++ } else if ((qoztmp->type == 0xb55b) || (qoztmp->type == 0xb56b)) { + offset = 24; + } else { + offset = 28; +@@ -526,7 +534,7 @@ + while ((oz1 != z1) && (oz2 != z2)) { + oz1 = z1; + oz2 = z2; +- if ((qoztmp->type != 0xb552) && (qoztmp->type != 0xb55b)){ ++ if ((qoztmp->type != 0xb552) && (qoztmp->type != 0xb55b) && (qoztmp->type != 0xb562) && (qoztmp->type != 0xb56b)){ + z1 = qoz_inw(qoztmp,qoz_A_Z1) & 0x7ff; + z2 = qoz_inw(qoztmp,qoz_A_Z2) & 0x7ff; + } else { +@@ -535,7 +543,7 @@ + } + } + +- if ((qoztmp->type == 0xb552)|| (qoztmp->type == 0xb55b)) { ++ if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb55b) || (qoztmp->type == 0xb562) || (qoztmp->type == 0xb56b)) { + len = z1 - z2; + if (len < 0) { + len += qoz_DFIFO_SIZE8; +@@ -944,9 +952,9 @@ + if (debug > 1) { + printk(KERN_INFO "qozap: card %d R_BERT_STA = %#x\n", qoztmp->cardno, qoz_inb(qoztmp, qoz_R_BERT_STA) & 7); + } +- if (qoztmp->type == 0xb552) { ++ if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb562)) { + offset = 24; +- } else if (qoztmp->type == 0xb55b) { ++ } else if ((qoztmp->type == 0xb55b) || (qoztmp->type == 0xb56b)) { + offset = 24; + } else { + offset = 28; +@@ -1131,7 +1139,7 @@ + /* fifo irq */ + spin_lock_irqsave(&(qoztmp->lock), flags); + irq_foview = qoz_inb(qoztmp,qoz_R_IRQ_OVIEW); +- if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb55b)) { ++ if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb55b) || (qoztmp->type == 0xb562) || (qoztmp->type == 0xb56b)) { + if (irq_foview & 0x60) { + offset = 0; + fi = qoz_inb(qoztmp,qoz_R_IRQ_FIFO_BL6); +@@ -1278,9 +1286,9 @@ + // irqs off + qoz_outb(qoztmp,qoz_R_IRQ_CTRL, 0); + +- if (qoztmp->type == 0xb552) { ++ if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb562)) { + offset = 24; +- } else if (qoztmp->type == 0xb55b) { ++ } else if ((qoztmp->type == 0xb55b) || (qoztmp->type == 0xb56b)) { + offset = 24; + } else { + offset = 28; +@@ -1411,9 +1419,9 @@ + + spin_lock_irqsave(&qoztmp->lock,flags); + // turn off irqs for all fifos +- if (qoztmp->type == 0xb552) { ++ if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb562)) { + offset = 24; +- } else if (qoztmp->type == 0xb55b) { ++ } else if ((qoztmp->type == 0xb55b) || (qoztmp->type == 0xb56b)) { + offset = 24; + } else { + offset = 28; +@@ -1642,7 +1650,7 @@ + qoz_resetCard(qoztmp); + + +- if ((tmp->subsystem_device==0xb520) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { ++ if (((tmp->subsystem_device==0xb520) || (tmp->subsystem_device==0xb560)) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + // printk(KERN_INFO "MODES = %#x.\n",modes); + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0x80 | 0x40); + qoz_outb(qoztmp,qoz_R_GPIO_EN1,0x00); +@@ -1670,7 +1678,7 @@ + } + } + +- } else if ((tmp->subsystem_device==0xb556) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { ++ } else if (((tmp->subsystem_device==0xb556) || (tmp->subsystem_device==0xb566)) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + /* duoBRI */ + + /* gpi27 1 gpi23 2 */ +@@ -1686,7 +1694,7 @@ + qoz_outw_io(qoztmp,0x0,0x0); */ + + +- } else if ((tmp->subsystem_device==0xb558) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { ++ } else if (((tmp->subsystem_device==0xb558) || (tmp->subsystem_device==0xb568)) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + /* quadBRI minipCI */ + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0xF0); + qoz_outb(qoztmp,qoz_R_GPIO_EN0,0x00); +@@ -1706,7 +1714,7 @@ + } + // printk(KERN_INFO "DIPS = %#x CID= %#x\n",dips,cid); + +- } else if ((tmp->subsystem_device==0xb55b) && (pcidid == PCI_DEVICE_ID_CCD_M)){ ++ } else if (((tmp->subsystem_device==0xb55b) || (tmp->subsystem_device==0xb56b)) && (pcidid == PCI_DEVICE_ID_CCD_M)){ + qoz_outb(qoztmp,qoz_R_BRG_PCM_CFG,0x21); + + dips = ~qoz_inb_io(qoztmp,0x4000); +@@ -1730,7 +1738,7 @@ + } + + if (ports == -1) { +- if ((tmp->subsystem_device==0xb520) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { ++ if (((tmp->subsystem_device==0xb520) || (tmp->subsystem_device==0xb560)) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + modes = qoz_inb(qoztmp,qoz_R_GPI_IN3) >> 4; + } else if ((tmp->subsystem_device==0xb550) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0xf0); +@@ -1743,7 +1751,7 @@ + printk(KERN_CRIT "gpi_in3 %#x \n", qoz_inb(qoztmp,qoz_R_GPI_IN3)); + } + modes = qoz_inb(qoztmp,qoz_R_GPI_IN3) >> 4; +- } else if ((tmp->subsystem_device==0xb558) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { ++ } else if (((tmp->subsystem_device==0xb558) || (tmp->subsystem_device==0xb568)) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0xf0); + qoz_outb(qoztmp,qoz_R_GPIO_EN0,0x00); + if (debug) { +@@ -1754,7 +1762,7 @@ + printk(KERN_CRIT "gpi_in3 %#x \n", qoz_inb(qoztmp,qoz_R_GPI_IN3)); + } + modes = qoz_inb(qoztmp,qoz_R_GPI_IN3) >> 4; +- } else if ((tmp->subsystem_device==0xb556) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { ++ } else if (((tmp->subsystem_device==0xb556) || (tmp->subsystem_device==0xb566)) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0xf0); + qoz_outb(qoztmp,qoz_R_GPIO_EN0,0x00); + if (debug) { +@@ -1817,6 +1825,20 @@ + qoztmp->irq, HZ, cid); + totalBRIs += 4; + break; ++ case 0xb560: ++ printk(KERN_INFO ++ "qozap: BeroNet BN4S0 card configured at io port %#x IRQ %d HZ %d CardID %d\n", ++ (u_int) qoztmp->ioport, ++ qoztmp->irq, HZ, cid); ++ totalBRIs += 4; ++ break; ++ case 0xb566: ++ printk(KERN_INFO ++ "qozap: BeroNet BN2S0 card configured at io port %#x IRQ %d HZ %d CardID %d\n", ++ (u_int) qoztmp->ioport, ++ qoztmp->irq, HZ, cid); ++ totalBRIs += 2; ++ break; + } + } else { + switch (tmp->subsystem_device) { +@@ -1832,6 +1854,18 @@ + (u_int) qoztmp->ioport, + qoztmp->irq, HZ); + break; ++ case 0xb562: ++ printk(KERN_INFO ++ "qozap: BeroNet BN8S0 card configured at io port %#x IRQ %d HZ %d\n", ++ (u_int) qoztmp->ioport, ++ qoztmp->irq, HZ); ++ break; ++ case 0xb56b: ++ printk(KERN_INFO ++ "qozap: BeroNet BN8S0 (+) card configured at io port %#x IRQ %d HZ %d\n", ++ (u_int) qoztmp->ioport, ++ qoztmp->irq, HZ); ++ break; + default: + printk(KERN_INFO + "qozap: wtf\n"); --- zaptel-1.4.11~dfsg.orig/debian/patches/series +++ zaptel-1.4.11~dfsg/debian/patches/series @@ -0,0 +1,12 @@ +oslec_zaptel +#bristuff +bri_dchan +proc_read +zt_alarm_notify_no_master_change +ztcfg-start_stop +zaphfc-florz +zaptel_perl +#sigcap_dacs +chan_release_check +florz-vmalloc +unsafe_asciidoc --- zaptel-1.4.11~dfsg.orig/debian/patches/bri_dchan +++ zaptel-1.4.11~dfsg/debian/patches/bri_dchan @@ -0,0 +1,160 @@ +# Translate the D channels to a standard channel data. +# The HFC chipset provides us the D channel as data, but +# Zaptel expects it as a standard channel with 1000 samples +# per second. + +Index: zaptel-1.2.22/kernel/zaptel.h +=================================================================== +--- zaptel-1.2.22.orig/kernel/zaptel.h 2007-12-04 10:40:30.000000000 +0100 ++++ zaptel-1.2.22/kernel/zaptel.h 2007-12-04 14:35:14.545043151 +0100 +@@ -1056,6 +1056,13 @@ + int do_ppp_error; + struct sk_buff_head ppp_rq; + #endif ++#ifdef CONFIG_ZAPATA_BRI_DCHANS ++ int bytes2receive; ++ int maxbytes2transmit; /* size of the tx buffer in the card driver */ ++ int bytes2transmit; ++ int eofrx; ++ int eoftx; ++#endif + spinlock_t lock; + char name[40]; /* Name */ + /* Specified by zaptel */ +@@ -1294,6 +1301,10 @@ + #define ZT_FLAG_T1PPP (1 << 15) + #define ZT_FLAG_SIGFREEZE (1 << 16) /* Freeze signalling */ + ++#if defined(CONFIG_ZAPATA_BRI_DCHANS) ++#define ZT_FLAG_BRIDCHAN (1 << 20) ++#endif ++ + struct zt_span { + spinlock_t lock; + void *pvt; /* Private stuff */ +Index: zaptel-1.2.22/kernel/zconfig.h +=================================================================== +--- zaptel-1.2.22.orig/kernel/zconfig.h 2007-12-04 10:40:29.000000000 +0100 ++++ zaptel-1.2.22/kernel/zconfig.h 2007-12-04 14:35:01.329541263 +0100 +@@ -166,4 +166,10 @@ + */ + /* #define FXSFLASH */ + ++/* ++ * Uncomment the following for BRI D channels ++ * ++ */ ++#define CONFIG_ZAPATA_BRI_DCHANS ++ + #endif +Index: zaptel-1.2.22/kernel/zaptel-base.c +=================================================================== +--- zaptel-1.2.22.orig/kernel/zaptel-base.c 2007-12-04 10:40:31.000000000 +0100 ++++ zaptel-1.2.22/kernel/zaptel-base.c 2007-12-04 14:35:14.553042850 +0100 +@@ -5226,11 +5255,40 @@ + *(txb++) = fasthdlc_tx_run_nocheck(&ms->txhdlc); + } + bytes -= left; ++#ifdef CONFIG_ZAPATA_BRI_DCHANS ++ } else if (ms->flags & ZT_FLAG_BRIDCHAN) { ++ /* ++ * Let's get this right, we want to transmit complete frames only. ++ * The card driver will do the dirty HDLC work for us. ++ * txb (transmit buffer) is supposed to be big enough to store one frame ++ * we will make this as big as the D fifo (1KB or 2KB) ++ */ ++ ++ /* there are 'left' bytes in the user buffer left to transmit */ ++ left = ms->writen[ms->outwritebuf] - ms->writeidx[ms->outwritebuf] - 2; ++ if (left > ms->maxbytes2transmit) { ++ memcpy(txb, buf + ms->writeidx[ms->outwritebuf], ms->maxbytes2transmit); ++ ms->writeidx[ms->outwritebuf] += ms->maxbytes2transmit; ++ txb += ms->maxbytes2transmit; ++ ms->bytes2transmit = ms->maxbytes2transmit; ++ ms->eoftx = 0; ++ } else { ++ memcpy(txb, buf + ms->writeidx[ms->outwritebuf], left); ++ ms->writeidx[ms->outwritebuf] += left + 2; ++ txb += left + 2; ++ ms->bytes2transmit = left; ++ ms->eoftx = 1; ++ } ++ bytes = 0; ++#endif + } else { + memcpy(txb, buf + ms->writeidx[ms->outwritebuf], left); + ms->writeidx[ms->outwritebuf]+=left; + txb += left; + bytes -= left; ++#if defined(CONFIG_ZAPATA_BRI_DCHANS) ++ ms->bytes2transmit=ZT_CHUNKSIZE; ++#endif + } + /* Check buffer status */ + if (ms->writeidx[ms->outwritebuf] >= ms->writen[ms->outwritebuf]) { +@@ -5275,6 +5333,17 @@ + /* Transmit a flag if this is an HDLC channel */ + if (ms->flags & ZT_FLAG_HDLC) + fasthdlc_tx_frame_nocheck(&ms->txhdlc); ++#if defined(CONFIG_ZAPATA_BRI_DCHANS) ++ if(ms->flags & ZT_FLAG_BRIDCHAN) { ++ // if (ms->bytes2transmit > 0) { ++ // txb += 2; ++ // ms->bytes2transmit -= 2; ++ bytes=0; ++ ms->eoftx = 1; ++// printk(KERN_CRIT "zaptel EOF(%d) bytes2transmit %d\n",ms->eoftx,ms->bytes2transmit); ++ // } ++ } ++#endif + #ifdef CONFIG_ZAPATA_NET + if (ms->flags & ZT_FLAG_NETDEV) + netif_wake_queue(ztchan_to_dev(ms)); +@@ -5335,6 +5404,12 @@ + memset(txb, 0xFF, bytes); + } + bytes = 0; ++#if defined(CONFIG_ZAPATA_BRI_DCHANS) ++ } else if(ms->flags & ZT_FLAG_BRIDCHAN) { ++ ms->bytes2transmit = 0; ++ ms->eoftx = 0; ++ bytes = 0; ++#endif + } else { + memset(txb, ZT_LIN2X(0, ms), bytes); /* Lastly we use silence on telephony channels */ + bytes = 0; +@@ -6153,6 +6228,14 @@ + int res; + int left, x; + ++#if defined(CONFIG_ZAPATA_BRI_DCHANS) ++ if (ms->flags & ZT_FLAG_BRIDCHAN) { ++ bytes = ms->bytes2receive; ++ if (bytes < 1) return; ++// printk(KERN_CRIT "bytes2receive %d\n",ms->bytes2receive); ++ } ++#endif ++ + while(bytes) { + #if defined(CONFIG_ZAPATA_NET) || defined(CONFIG_ZAPATA_PPP) + skb = NULL; +@@ -6210,6 +6293,19 @@ + } + } + } ++#ifdef CONFIG_ZAPATA_BRI_DCHANS ++ } else if (ms->flags & ZT_FLAG_BRIDCHAN) { ++ memcpy(buf + ms->readidx[ms->inreadbuf], rxb, left); ++ rxb += left; ++ ms->readidx[ms->inreadbuf] += left; ++ bytes -= left; ++ if (ms->eofrx == 1) { ++ eof=1; ++ } ++// printk(KERN_CRIT "receiving %d bytes\n",ms->bytes2receive); ++ ms->bytes2receive = 0; ++ ms->eofrx = 0; ++#endif + } else { + /* Not HDLC */ + memcpy(buf + ms->readidx[ms->inreadbuf], rxb, left); --- zaptel-1.4.11~dfsg.orig/debian/patches/oslec_zaptap +++ zaptel-1.4.11~dfsg/debian/patches/oslec_zaptap @@ -0,0 +1,264 @@ +## oslec_zaptap by Tzafrir Cohen +## +## The zaptap device for sampling echo. Part of the oslec echo canceller. + +diff -urNad zaptel-1.2.17.1.xpp.r3965~/zaptel-base.c zaptel-1.2.17.1.xpp.r3965/zaptel-base.c +--- zaptel-1.2.17.1.xpp.r3965~/kernel/zaptel-base.c 2007-06-16 07:10:17.000000000 +0300 ++++ zaptel-1.2.17.1.xpp.r3965/kernel/zaptel-base.c 2007-06-16 07:55:33.000000000 +0300 +@@ -5708,6 +5708,74 @@ + spin_unlock_irqrestore(&chan->lock, flags); + } + ++#ifdef USE_ZAPTAP ++/* Zaptap code -----------------------------------------------------------*/ ++ ++#define SAMPLE_BUF_SZ 1000 ++#define SAMPLE_IDLE 0 ++#define SAMPLE_PING 1 ++#define SAMPLE_PONG 2 ++ ++DECLARE_WAIT_QUEUE_HEAD(sample_wait); ++static int sample_state = 0; ++static int samples = 0; ++static short *psample; ++static short ping[3*SAMPLE_BUF_SZ]; ++static short pong[3*SAMPLE_BUF_SZ]; ++static int sample_ch = 1; ++static int sample_impulse = 0; ++static int tmp1,tmp2; ++ ++static inline void sample_echo_before(int channo, short rxlin, short txlin) { ++ /* Sample echo canceller signals ++ * Notes: ++ * 1. Samples are multiplexed in buffer: ++ * tx sample ++ * rx sample ++ * ec sample ++ * 2. We needs to sample rx here before echo can as it is ++ * overwritten. ++ */ ++ tmp1++; ++ tmp2 = channo; ++ if ((sample_state != SAMPLE_IDLE) && (channo == sample_ch)) { ++ *psample++ = txlin; ++ *psample++ = rxlin; ++ } ++} ++ ++static inline void sample_echo_after(int channo, short rxlin) { ++ ++ if ((sample_state != SAMPLE_IDLE) && (channo == sample_ch)) { ++ ++ *psample++ = rxlin; ++ ++ /* sample collection ping-pong buffer logic */ ++ ++ samples++; ++ if (samples >= SAMPLE_BUF_SZ) { ++ /* time to swap buffers */ ++ samples = 0; ++ ++ if (sample_state == SAMPLE_PING) { ++ sample_state = SAMPLE_PONG; ++ psample = pong; ++ } ++ else { ++ sample_state = SAMPLE_PING; ++ psample = ping; ++ } ++ wake_up_interruptible(&sample_wait); ++ } ++ } ++} ++ ++/* end Zaptap code -----------------------------------------------------*/ ++#else /* USE_ZAPTAP */ ++#define sample_echo_before(a,b,c) ++#define sample_echo_after(a,b) ++#endif /* USE_ZAPTAP */ ++ + static inline void __zt_ec_chunk(struct zt_chan *ss, unsigned char *rxchunk, const unsigned char *txchunk) + { + short rxlin, txlin; +@@ -5758,7 +5826,9 @@ + #if !defined(ZT_EC_ARRAY_UPDATE) + for (x=0;xchanno, rxlin, ZT_XLAW(txchunk[x], ss)); /* Zaptap code */ + rxlin = echo_can_update(ss->ec, ZT_XLAW(txchunk[x], ss), rxlin); ++ sample_echo_after(ss->channo, rxlin); /* Zaptap code */ + rxchunk[x] = ZT_LIN2X((int)rxlin, ss); + } + #else /* defined(ZT_EC_ARRAY_UPDATE) */ +@@ -6505,6 +6575,10 @@ + static void __zt_transmit_chunk(struct zt_chan *chan, unsigned char *buf) + { + unsigned char silly[ZT_CHUNKSIZE]; ++#ifdef USE_ZAPTAP ++ int x; ++#endif ++ + /* Called with chan->lock locked */ + if (!buf) + buf = silly; +@@ -6519,6 +6593,22 @@ + kernel_fpu_end(); + #endif + } ++ ++#ifdef USE_ZAPTAP ++ /* Start Zaptap code -----------------------------------------*/ ++ if (sample_impulse && (samples == 0)) { ++ ++ /* option impulse insertion, tx stream becomes one */ ++ /* impulse followed by SAMPLE_BUF_SZ-1 0's */ ++ ++ buf[0] = ZT_LIN2MU(10000); ++ for (x=1;xspan && chans[unit]->span->close) + res = chans[unit]->span->close(chans[unit]); +- clear_bit(ZT_FLAGBIT_OPEN, &chans[unit]->flags); ++ ++ /* The channel might be destroyed on close: */ ++ if (chans[unit]) ++ clear_bit(ZT_FLAGBIT_OPEN, &chans[unit]->flags); + } else + res = -ENXIO; + #ifndef LINUX26 --- zaptel-1.4.11~dfsg.orig/debian/patches/bristuff +++ zaptel-1.4.11~dfsg/debian/patches/bristuff @@ -0,0 +1,518 @@ +## bristuff by Faidon Liambotis +## +## bristuff 0.4.0-test4 zaptel.patch +## no changes whatsoever; for new versions just insert the patch below @DPATCH@ + +diff -urN zaptel-1.4.4.orig/zaptel-base.c zaptel-1.4.4/zaptel-base.c +--- zaptel-1.4.4.orig/zaptel-base.c 2007-07-05 20:10:29.000000000 +0200 ++++ zaptel-1.4.4/zaptel-base.c 2007-07-18 14:33:17.880802801 +0200 +@@ -138,6 +138,7 @@ + EXPORT_SYMBOL(zt_qevent_lock); + EXPORT_SYMBOL(zt_hooksig); + EXPORT_SYMBOL(zt_alarm_notify); ++EXPORT_SYMBOL(zt_alarm_notify_no_master_change); + EXPORT_SYMBOL(zt_set_dynamic_ioctl); + EXPORT_SYMBOL(zt_ec_chunk); + EXPORT_SYMBOL(zt_ec_span); +@@ -595,6 +596,10 @@ + len += sprintf(page + len, "\tIRQ misses: %d\n", spans[span]->irqmisses); + len += sprintf(page + len, "\n"); + ++ if (spans[span]->proc_read) { ++ len += spans[span]->proc_read(spans[span], page + len); ++ } ++ + + for (x=1;xalarms &= ~ZT_ALARM_LOOPBACK; ++ /* Determine maint status */ ++ if (span->maintstat || span->mainttimer) ++ span->alarms |= ZT_ALARM_LOOPBACK; ++ /* DON'T CHANGE THIS AGAIN. THIS WAS DONE FOR A REASON. ++ The expression (a != b) does *NOT* do the same thing ++ as ((!a) != (!b)) */ ++ /* if change in general state */ ++ if ((!span->alarms) != (!span->lastalarms)) { ++ if (span->alarms) ++ j = ZT_EVENT_ALARM; ++ else ++ j = ZT_EVENT_NOALARM; ++ span->lastalarms = span->alarms; ++ for (x=0;x < span->channels;x++) ++ zt_qevent_lock(&span->chans[x], j); ++ } ++} ++ + #define VALID_SPAN(j) do { \ + if ((j >= ZT_MAX_SPANS) || (j < 1)) \ + return -EINVAL; \ +@@ -5226,11 +5255,40 @@ + *(txb++) = fasthdlc_tx_run_nocheck(&ms->txhdlc); + } + bytes -= left; ++#ifdef CONFIG_ZAPATA_BRI_DCHANS ++ } else if (ms->flags & ZT_FLAG_BRIDCHAN) { ++ /* ++ * Let's get this right, we want to transmit complete frames only. ++ * The card driver will do the dirty HDLC work for us. ++ * txb (transmit buffer) is supposed to be big enough to store one frame ++ * we will make this as big as the D fifo (1KB or 2KB) ++ */ ++ ++ /* there are 'left' bytes in the user buffer left to transmit */ ++ left = ms->writen[ms->outwritebuf] - ms->writeidx[ms->outwritebuf] - 2; ++ if (left > ms->maxbytes2transmit) { ++ memcpy(txb, buf + ms->writeidx[ms->outwritebuf], ms->maxbytes2transmit); ++ ms->writeidx[ms->outwritebuf] += ms->maxbytes2transmit; ++ txb += ms->maxbytes2transmit; ++ ms->bytes2transmit = ms->maxbytes2transmit; ++ ms->eoftx = 0; ++ } else { ++ memcpy(txb, buf + ms->writeidx[ms->outwritebuf], left); ++ ms->writeidx[ms->outwritebuf] += left + 2; ++ txb += left + 2; ++ ms->bytes2transmit = left; ++ ms->eoftx = 1; ++ } ++ bytes = 0; ++#endif + } else { + memcpy(txb, buf + ms->writeidx[ms->outwritebuf], left); + ms->writeidx[ms->outwritebuf]+=left; + txb += left; + bytes -= left; ++#if defined(CONFIG_ZAPATA_BRI_DCHANS) ++ ms->bytes2transmit=ZT_CHUNKSIZE; ++#endif + } + /* Check buffer status */ + if (ms->writeidx[ms->outwritebuf] >= ms->writen[ms->outwritebuf]) { +@@ -5275,6 +5333,17 @@ + /* Transmit a flag if this is an HDLC channel */ + if (ms->flags & ZT_FLAG_HDLC) + fasthdlc_tx_frame_nocheck(&ms->txhdlc); ++#if defined(CONFIG_ZAPATA_BRI_DCHANS) ++ if(ms->flags & ZT_FLAG_BRIDCHAN) { ++ // if (ms->bytes2transmit > 0) { ++ // txb += 2; ++ // ms->bytes2transmit -= 2; ++ bytes=0; ++ ms->eoftx = 1; ++// printk(KERN_CRIT "zaptel EOF(%d) bytes2transmit %d\n",ms->eoftx,ms->bytes2transmit); ++ // } ++ } ++#endif + #ifdef CONFIG_ZAPATA_NET + if (ms->flags & ZT_FLAG_NETDEV) + netif_wake_queue(ztchan_to_dev(ms)); +@@ -5285,7 +5354,7 @@ + tasklet_schedule(&ms->ppp_calls); + } + #endif +- } ++ } + } else if (ms->curtone && !(ms->flags & ZT_FLAG_PSEUDO)) { + left = ms->curtone->tonesamples - ms->tonep; + if (left > bytes) +@@ -5335,6 +5404,12 @@ + memset(txb, 0xFF, bytes); + } + bytes = 0; ++#if defined(CONFIG_ZAPATA_BRI_DCHANS) ++ } else if(ms->flags & ZT_FLAG_BRIDCHAN) { ++ ms->bytes2transmit = 0; ++ ms->eoftx = 0; ++ bytes = 0; ++#endif + } else { + memset(txb, ZT_LIN2X(0, ms), bytes); /* Lastly we use silence on telephony channels */ + bytes = 0; +@@ -6153,6 +6228,14 @@ + int res; + int left, x; + ++#if defined(CONFIG_ZAPATA_BRI_DCHANS) ++ if (ms->flags & ZT_FLAG_BRIDCHAN) { ++ bytes = ms->bytes2receive; ++ if (bytes < 1) return; ++// printk(KERN_CRIT "bytes2receive %d\n",ms->bytes2receive); ++ } ++#endif ++ + while(bytes) { + #if defined(CONFIG_ZAPATA_NET) || defined(CONFIG_ZAPATA_PPP) + skb = NULL; +@@ -6210,6 +6293,19 @@ + } + } + } ++#ifdef CONFIG_ZAPATA_BRI_DCHANS ++ } else if (ms->flags & ZT_FLAG_BRIDCHAN) { ++ memcpy(buf + ms->readidx[ms->inreadbuf], rxb, left); ++ rxb += left; ++ ms->readidx[ms->inreadbuf] += left; ++ bytes -= left; ++ if (ms->eofrx == 1) { ++ eof=1; ++ } ++// printk(KERN_CRIT "receiving %d bytes\n",ms->bytes2receive); ++ ms->bytes2receive = 0; ++ ms->eofrx = 0; ++#endif + } else { + /* Not HDLC */ + memcpy(buf + ms->readidx[ms->inreadbuf], rxb, left); +diff -urN zaptel-1.4.4.orig/zaptel.h zaptel-1.4.4/zaptel.h +--- zaptel-1.4.4.orig/zaptel.h 2007-04-24 20:32:49.000000000 +0200 ++++ zaptel-1.4.4/zaptel.h 2007-07-18 14:33:17.884803750 +0200 +@@ -1106,6 +1106,13 @@ + int do_ppp_error; + struct sk_buff_head ppp_rq; + #endif ++#ifdef CONFIG_ZAPATA_BRI_DCHANS ++ int bytes2receive; ++ int maxbytes2transmit; /* size of the tx buffer in the card driver */ ++ int bytes2transmit; ++ int eofrx; ++ int eoftx; ++#endif + spinlock_t lock; + char name[40]; /* Name */ + /* Specified by zaptel */ +@@ -1181,7 +1188,7 @@ + int txbufpolicy; /* Buffer policy */ + int rxbufpolicy; /* Buffer policy */ + int txdisable; /* Disable transmitter */ +- int rxdisable; /* Disable receiver */ ++ int rxdisable; /* Disable receiver */ + + + /* Tone zone stuff */ +@@ -1346,6 +1353,10 @@ + #define ZT_FLAG_NOSTDTXRX (1 << 17) /* Do NOT do standard transmit and receive on every interrupt */ + #define ZT_FLAG_LOOPED (1 << 18) /* Loopback the receive data from the channel to the transmit */ + ++#if defined(CONFIG_ZAPATA_BRI_DCHANS) ++#define ZT_FLAG_BRIDCHAN (1 << 19) ++#endif ++ + struct zt_span { + spinlock_t lock; + void *pvt; /* Private stuff */ +@@ -1449,6 +1460,10 @@ + int watchcounter; + int watchstate; + #endif ++#ifdef CONFIG_PROC_FS ++ /* Allow subordinate drivers to print out their own stuff */ ++ int (*proc_read)(struct zt_span *span, char *start); ++#endif + }; + + struct zt_transcoder_channel { +@@ -1581,6 +1596,9 @@ + /* Notify a change possible change in alarm status */ + void zt_alarm_notify(struct zt_span *span); + ++/* Notify a change possible change in alarm status, DONT change the zaptel master! */ ++extern void zt_alarm_notify_no_master_change(struct zt_span *span); ++ + /* Initialize a tone state */ + void zt_init_tone_state(struct zt_tone_state *ts, struct zt_tone *zt); + +diff -urN zaptel-1.4.4.orig/zconfig.h zaptel-1.4.4/zconfig.h +--- zaptel-1.4.4.orig/zconfig.h 2007-04-24 20:32:49.000000000 +0200 ++++ zaptel-1.4.4/zconfig.h 2007-07-18 14:33:17.884803750 +0200 +@@ -184,4 +184,10 @@ + */ + /* #define FXSFLASH */ + ++/* ++ * Uncomment the following for BRI D channels ++ * ++ */ ++#define CONFIG_ZAPATA_BRI_DCHANS ++ + #endif +diff -urN zaptel-1.4.4.orig/ztcfg.c zaptel-1.4.4/ztcfg.c +--- zaptel-1.4.4.orig/ztcfg.c 2007-02-07 17:51:27.000000000 +0100 ++++ zaptel-1.4.4/ztcfg.c 2007-07-18 14:33:17.884803750 +0200 +@@ -96,6 +96,10 @@ + + static int stopmode = 0; + ++static int stopwhich = -1; ++ ++static int startwhich = -1; ++ + static int numdynamic = 0; + + static char zonestoload[ZT_TONE_ZONE_MAX][10]; +@@ -1261,7 +1265,8 @@ + " -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" ++ " -s[span] -- Deactivate/shutdown span [span]. Deactivates all spans if no [span] is given.\n" ++ " -a[span] -- Activate/start span [span]. Activates all spans if no [span] is given.\n" + " -t -- Test mode only, do not apply\n" + " -v -- Verbose (more -v's means more verbose)\n" + ,c); +@@ -1274,7 +1279,7 @@ + char *buf; + char *key, *value; + int x,found; +- while((c = getopt(argc, argv, "fthc:vsd::")) != -1) { ++ while((c = getopt(argc, argv, "fthc:vs::a::d::")) != -1) { + switch(c) { + case 'c': + filename=optarg; +@@ -1295,8 +1300,20 @@ + fo_real = 0; + break; + case 's': ++ if (optarg) { ++ stopwhich = atoi(optarg); ++ } else { ++ stopwhich = -1; ++ } + stopmode = 1; + break; ++ case 'a': ++ if (optarg) { ++ startwhich = atoi(optarg); ++ } else { ++ startwhich = -1; ++ } ++ break; + case 'd': + if (optarg) + debug = atoi(optarg); +@@ -1358,29 +1375,42 @@ + ioctl(fd, ZT_DYNAMIC_DESTROY, &zds[x]); + } + if (stopmode) { +- for (x=0;x 0){ ++ if ((stopwhich > spans) || (ioctl(fd, ZT_SHUTDOWN, &lc[stopwhich - 1].span))) { ++ fprintf(stderr, "Zaptel shutdown failed: %s\n", strerror(errno)); ++ close(fd); ++ exit(1); ++ } else { ++ if (verbose) { ++ printf("Deactivated span %d\n", stopwhich); ++ } ++ } ++ } else { ++ for (x=0;x -1) { ++ } ++ 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 0) { ++ if ((startwhich > spans) || (ioctl(fd, ZT_STARTUP, &lc[startwhich - 1].span))) { ++ fprintf(stderr, "Zaptel startup failed: %s\n", strerror(errno)); ++ close(fd); ++ exit(1); ++ } else { ++ if (verbose) { ++ printf("Activated span %d\n", startwhich); ++ } ++ } ++ } else { ++ for (x=0;x ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "zaptel.h" ++ ++#define SIZE 8000 ++ ++ ++ ++void doit(int fd, int stdinfd) { ++ fd_set fds; ++ char inbuffer[4096]; ++ char outbuffer[4096]; ++ int res = 0; ++ int i = 0; ++ ++// fprintf(stderr, "fd %d stdin fd %d\n", fd, stdinfd); ++ ++ for (;;) { ++ FD_ZERO(&fds); ++ FD_SET(fd, &fds); ++ FD_SET(stdinfd, &fds); ++ /* Wait for *some* sort of I/O */ ++ res = select(stdinfd + 1, &fds, NULL, NULL, NULL); ++ if (res < 0) { ++ fprintf(stderr, "Error in select: %s\n", strerror(errno)); ++ return; ++ } ++ if (FD_ISSET(stdinfd, &fds)) { ++ res = read(stdinfd, inbuffer, sizeof(inbuffer)); ++ if (res > 0) { ++// fprintf(stderr, "read %d bytes from stdin\n", res); ++ if (res > 0) { ++ for (i=0; i < res ; i++) { ++ if (inbuffer[i] == '\n') { ++ if ((i > 0) && (inbuffer[i-1] == ' ')) { ++ inbuffer[i-1] = 0x1a; ++ } ++ inbuffer[i] = 0xd; ++ } ++ } ++ res = write(fd, inbuffer, res+2); ++// res = write(STDOUT_FILENO, inbuffer, res); ++// fprintf(stderr, "wrote %d bytes to stdout\n", res); ++ } ++ } ++ } ++ if (FD_ISSET(fd, &fds)) { ++ res = read(fd, outbuffer, sizeof(outbuffer)); ++// fprintf(stderr, "read %d bytes from fd\n", res); ++ if (res > 0) { ++ res = write(STDOUT_FILENO, outbuffer, res); ++// fprintf(stderr, "wrote %d bytes to stdout\n", res); ++ } ++ } ++ } ++ ++ ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int fd; ++ int stdinfd; ++ struct zt_params p; ++ struct zt_bufferinfo bi; ++ int blocksize=0; ++ fd = open(argv[1], O_RDWR | O_NONBLOCK); ++ if (fd < 0) { ++ fprintf(stderr, "Unable to open zap interface: %s\n", strerror(errno)); ++ exit(1); ++ } ++ if (ioctl(fd, ZT_GET_PARAMS, &p)) { ++ fprintf(stderr, "Unable to get parameters on '%s': %s\n", argv[1], strerror(errno)); ++ exit(1); ++ } ++ if ((p.sigtype != ZT_SIG_HDLCRAW) && (p.sigtype != ZT_SIG_HDLCFCS)) { ++ fprintf(stderr, "%s is in %d signalling, not FCS HDLC or RAW HDLC mode\n", argv[1], p.sigtype); ++ exit(1); ++ } ++ ++ if (ioctl(fd, ZT_GET_BLOCKSIZE, &blocksize)) { ++ fprintf(stderr, "Unable to get blocksize on '%s': %s\n", argv[1], strerror(errno)); ++ exit(1); ++ } else { ++// fprintf(stderr, "blocksize %d\n", blocksize); ++ } ++ ++ bi.txbufpolicy = ZT_POLICY_IMMEDIATE; ++ bi.rxbufpolicy = ZT_POLICY_IMMEDIATE; ++ bi.numbufs = 16; ++ bi.bufsize = 1024; ++ if (ioctl(fd, ZT_SET_BUFINFO, &bi)) { ++ fprintf(stderr, "Unable to set buffer info on '%s': %s\n", argv[1], strerror(errno)); ++ exit(1); ++ } ++ ++ stdinfd = open("/dev/stdin", O_RDONLY | O_NONBLOCK); ++ ++ ++ doit(fd, stdinfd); ++ close(stdinfd); ++ close(fd); ++ return 0; ++} --- zaptel-1.4.11~dfsg.orig/debian/patches/unsafe_asciidoc +++ zaptel-1.4.11~dfsg/debian/patches/unsafe_asciidoc @@ -0,0 +1,14 @@ +Run asciidoc with --unsafe . This is a workaround for Debian bug +#487011 which is aparantly an asciidoc bug. + +--- a/Makefile ++++ b/Makefile +@@ -384,6 +384,8 @@ zaptel.conf.asciidoc: zaptel.conf.sample + 'if (/^#($$|\s)(.*)/){ if (!$$in_doc){print "\n"}; $$in_doc=1; print "$$2\n" } else { if ($$in_doc){print "\n"}; $$in_doc=0; print " $$_" }' \ + $< >$@ + ++ASCIIDOC += --unsafe ++ + README.html: README zaptel.conf.asciidoc tonezones.txt + $(ASCIIDOC) -n -a toc -a toclevels=3 $< + --- zaptel-1.4.11~dfsg.orig/debian/patches/florz-vmalloc +++ zaptel-1.4.11~dfsg/debian/patches/florz-vmalloc @@ -0,0 +1,13 @@ +Include the missing vmalloc.h header missing from florz's patch. +Seems to be included implicitly on x86. + +--- zaptel-1.4.11~dfsg.orig/kernel/zaphfc/zaphfc.c ++++ zaptel-1.4.11~dfsg/kernel/zaphfc/zaphfc.c +@@ -22,6 +22,7 @@ + + #include + #include ++#include + #include + #include + #include --- zaptel-1.4.11~dfsg.orig/debian/patches/ztcfg-start_stop +++ zaptel-1.4.11~dfsg/debian/patches/ztcfg-start_stop @@ -0,0 +1,161 @@ +## DP: ztcfg: Add options to activate a specific span (or all of them) or +## DP: shut down just a specific span (not just all of them). + +@DPATCH@ +diff -urN zaptel-1.4.4.orig/ztcfg.c zaptel-1.4.4/ztcfg.c +--- zaptel-1.4.4.orig/ztcfg.c 2007-02-07 17:51:27.000000000 +0100 ++++ zaptel-1.4.4/ztcfg.c 2007-07-18 14:33:17.884803750 +0200 +@@ -96,6 +96,10 @@ + + static int stopmode = 0; + ++static int stopwhich = -1; ++ ++static int startwhich = -1; ++ + static int numdynamic = 0; + + static char zonestoload[ZT_TONE_ZONE_MAX][10]; +@@ -1261,7 +1265,8 @@ + " -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" ++ " -s[span] -- Deactivate/shutdown span [span]. Deactivates all spans if no [span] is given.\n" ++ " -a[span] -- Activate/start span [span]. Activates all spans if no [span] is given.\n" + " -t -- Test mode only, do not apply\n" + " -v -- Verbose (more -v's means more verbose)\n" + ,c); +@@ -1274,7 +1279,7 @@ + char *buf; + char *key, *value; + int x,found; +- while((c = getopt(argc, argv, "fthc:vsd::")) != -1) { ++ while((c = getopt(argc, argv, "fthc:vs::a::d::")) != -1) { + switch(c) { + case 'c': + filename=optarg; +@@ -1295,8 +1300,20 @@ + fo_real = 0; + break; + case 's': ++ if (optarg) { ++ stopwhich = atoi(optarg); ++ } else { ++ stopwhich = -1; ++ } + stopmode = 1; + break; ++ case 'a': ++ if (optarg) { ++ startwhich = atoi(optarg); ++ } else { ++ startwhich = -1; ++ } ++ break; + case 'd': + if (optarg) + debug = atoi(optarg); +@@ -1358,29 +1375,42 @@ + ioctl(fd, ZT_DYNAMIC_DESTROY, &zds[x]); + } + if (stopmode) { +- for (x=0;x 0){ ++ if ((stopwhich > spans) || (ioctl(fd, ZT_SHUTDOWN, &lc[stopwhich - 1].span))) { ++ fprintf(stderr, "Zaptel shutdown failed: %s\n", strerror(errno)); ++ close(fd); ++ exit(1); ++ } else { ++ if (verbose) { ++ printf("Deactivated span %d\n", stopwhich); ++ } ++ } ++ } else { ++ for (x=0;x -1) { ++ } ++ 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 0) { ++ if ((startwhich > spans) || (ioctl(fd, ZT_STARTUP, &lc[startwhich - 1].span))) { ++ fprintf(stderr, "Zaptel startup failed: %s\n", strerror(errno)); ++ close(fd); ++ exit(1); ++ } else { ++ if (verbose) { ++ printf("Activated span %d\n", startwhich); ++ } ++ } ++ } else { ++ for (x=0;xalarms &= ~ZT_ALARM_LOOPBACK; ++ /* Determine maint status */ ++ if (span->maintstat || span->mainttimer) ++ span->alarms |= ZT_ALARM_LOOPBACK; ++ /* DON'T CHANGE THIS AGAIN. THIS WAS DONE FOR A REASON. ++ The expression (a != b) does *NOT* do the same thing ++ as ((!a) != (!b)) */ ++ /* if change in general state */ ++ if ((!span->alarms) != (!span->lastalarms)) { ++ if (span->alarms) ++ j = ZT_EVENT_ALARM; ++ else ++ j = ZT_EVENT_NOALARM; ++ span->lastalarms = span->alarms; ++ for (x=0;x < span->channels;x++) ++ zt_qevent_lock(&span->chans[x], j); ++ } ++} ++ + #define VALID_SPAN(j) do { \ + if ((j >= ZT_MAX_SPANS) || (j < 1)) \ + return -EINVAL; \ --- zaptel-1.4.11~dfsg.orig/debian/patches/proc_read +++ zaptel-1.4.11~dfsg/debian/patches/proc_read @@ -0,0 +1,40 @@ +# Adds an extra Zaptel span method: proc_read. +# This allows a span to add some arbitrary text at the end +# of the /proc/zaptel/NNN file. +# +# This is currently only used by cwain. +# +# My personal opinion is that such methods are not recommended. +# sysfs support should be used instead (Tzafrir). It is likely to break +# zapconf and genzaptelconf for drivers using it. + +Index: zaptel-1.2.22/kernel/zaptel.h +=================================================================== +--- zaptel-1.2.22.orig/kernel/zaptel.h 2007-12-04 10:40:30.000000000 +0100 ++++ zaptel-1.2.22/kernel/zaptel.h 2007-12-04 14:35:14.545043151 +0100 +@@ -1394,6 +1405,10 @@ + int watchcounter; + int watchstate; + #endif ++#ifdef CONFIG_PROC_FS ++ /* Allow subordinate drivers to print out their own stuff */ ++ int (*proc_read)(struct zt_span *span, char *start); ++#endif + }; + + struct zt_transcoder_channel { +Index: zaptel-1.2.22/kernel/zaptel-base.c +=================================================================== +--- zaptel-1.2.22.orig/kernel/zaptel-base.c 2007-12-04 10:40:31.000000000 +0100 ++++ zaptel-1.2.22/kernel/zaptel-base.c 2007-12-04 14:35:14.553042850 +0100 +@@ -584,6 +585,10 @@ + len += sprintf(page + len, "\tIRQ misses: %d\n", spans[span]->irqmisses); + len += sprintf(page + len, "\n"); + ++ if (spans[span]->proc_read) { ++ len += spans[span]->proc_read(spans[span], page + len); ++ } ++ + + for (x=1;x +## +## Don't try to run clean on empty dir + +diff -urNad zaptel-1.4.5.1~dfsg~/Makefile zaptel-1.4.5.1~dfsg/Makefile +--- zaptel-1.4.5.1~dfsg~/Makefile 2007-08-21 20:11:37.000000000 +0200 ++++ zaptel-1.4.5.1~dfsg/Makefile 2007-08-26 11:56:49.000000000 +0200 +@@ -601,7 +601,9 @@ + rm -f *.o ztcfg tzdriver sethdlc sethdlc-new + rm -f $(LTZ_SO) $(LTZ_A) *.lo + ifeq ($(BUILDVER),linux26) ++ifdef KSRC + $(MAKE) -C $(KSRC) SUBDIRS=$(PWD) clean ++endif + $(MAKE) -C xpp/utils clean + else + $(MAKE) -C wct4xxp clean --- zaptel-1.4.11~dfsg.orig/debian/TODO.Debian +++ zaptel-1.4.11~dfsg/debian/TODO.Debian @@ -0,0 +1,6 @@ +* Set the list of utilities to build. Probably through some menuselect var. +* Rename sethdlc-new to sethdlc. +* Missing man page for fxstest . +* Missing man page for ztdiag . +* Add ds1x1f.c to the list of modules (note: it breaks kernel 2.4 building). +* Apply the conglomerate_fixes patch from trunk. --- zaptel-1.4.11~dfsg.orig/debian/changelog +++ zaptel-1.4.11~dfsg/debian/changelog @@ -0,0 +1,1035 @@ +zaptel (1:1.4.11~dfsg-1~bpo40+1) etch-backports; urgency=low + + * Rebuild for etch-backports. + * Used backported version of asciidoc ( 8.2.7-1~bpo40+1 ). + * Patch unsafe_asciidoc: Work around #487011 ( Tzafrir Cohen ). + + -- Victor Seva Fri, 25 Jul 2008 08:28:32 +0200 + +zaptel (1:1.4.11~dfsg-1) unstable; urgency=medium + + [ Faidon Liambotis ] + * Update qozap from bristuff-0.4.0-RC1. + - fixed duoBRI miniPCI detection in qozap. + - added support for PCIe variants of duoBRI and quadBRI. + * Update cwain from bristuff-0.4.0-RC1. + * Update ztgsm from bristuff-0.4.0-RC1. + - reduced baudrate (serial interface to gsm modules) to be more robust + against irq misses in loaded systems + - improved serial debug output + - added module parameter "baudrate" (the default is 19200) + - added AT commands for SIM card selection + - added AT commands for shutting down/starting GSM modules + + [ Tzafrir Cohen ] + * New upstream release. + * Patch xpp_fix_t1 dropped: merged upstream. + * Finally removing 00list. + * Patch sigcap_dacs: tell zaphfc and vzaphfc to report support for + ZT_SIG_DACS. Makes ztscan report then as digital spans. + * Patch bri_dchan: change ZT_FLAG_BRIDCHAN as ZT_FLAG_MTP2 is also + defined to be 19 as of zaptel 1.4.11 . + * Patch chan_release_check: fixes a regression from 1.4.10.1 that causes + a panic if disconnecting an Astribank device with a channel still open. + * Patch florz-vmalloc: proper includes for zaphfc on non-x86 platforms. + (Closes: #488513) + * oslec_wrap.c: cycles code for powerpc by Stelios Koroneos. + * And dummy cycles code for other architectures. + + [ Mark Purcell ] + * Build-Depends: asciidoc (>= 8.2.6-1.1) + - asciidoc 8.2.6-1 is broken + - FTBFS: ERROR: unsafe: include file: + /etc/asciidoc/./javascripts/toc.js (Closes: #487011) + * Urgency medium as we fix RC bug + + -- Mark Purcell Mon, 30 Jun 2008 20:22:37 +1000 + +zaptel (1:1.4.10.1~dfsg-1) unstable; urgency=low + + [ Tzafrir Cohen ] + * New upstream bugfix release. + * Note the buid "error" from http://bugs.digium.com/12426 . + * Fix the generation of tonezone.txt. + * Patch xpp_fix_t1: allow the xpp pri module to initialize as T1. + * Set OSLEC as the default echo canceller. + * The "unload" init.d script operation will now also attempt to unload + oslec. + + -- Tzafrir Cohen Thu, 8 May 2008 02:03:34 +0300 + +zaptel (1:1.4.10~dfsg-1) unstable; urgency=low + + [ Mark Purcell ] + * New upstream release + + [ Tzafrir Cohen ] + * Added NEWS.Debian about zaptel modules incompatibility. + * Do run 'make dist-clean' on modules build. It prints an ugly error, but + only after the actualy relevant clean. + * Watching downloads.digium.com directly again. + + -- Tzafrir Cohen Mon, 14 Apr 2008 14:07:54 +0300 + +zaptel (1:1.4.9.2~dfsg-1) unstable; urgency=low + + * New upstream release (Closes: #464900). + - All kernel modules moved to under kernel/ + * Converting patches to quilt (because bristuff uses them). + * Include the separate xpp changelog file. + * Fix a possible crash with oslec when a fax tone is detected: + http://sourceforge.net/mailarchive/message.php?msg_name=20080217212421.GT15415%40xorcom.com + (Closes: #447245). + * Adjusted lintian overrides: mknod is now called from a function. + * Adjust vzaphfc to netdevice API changes in kernel 2.6.24. + * Once again ignoring 'make distclean' errors: it will fail if we don't + have kernel sources / headers for current kernel :-( . + * Remove some unnecessary changes from the florz zaphfc patch - fixes zaphfc + warning. + + -- Tzafrir Cohen Thu, 20 Mar 2008 16:31:25 +0200 + +zaptel (1:1.4.8~dfsg-1) unstable; urgency=low + + * New upstream release + * bristuff.dpatch broken to smaller patches, as in bristuff 0.4.0-test6. + - ztpty.c is added as a source file. + - bri_dchan.dpatch, proc_read.dpatch, ztcfg-start_stop.dpatch and + zt_alarm_notify_no_master_change.dpatch . + * beronet.dpatch: Support for Bero.net cards (Closes: #453496). + * Adapted ztcfg-start_stop.dpatch to zaptel 1.4.8 . + * Removing xpp_m_subdirs.dpatch: merged upstream. + * tones.h is no longer generated. + * kbuild_src.dpatch: A small build issue already fixed upstream. + * oslec_zaptel.dpatch: Add a compatibility function for the old + echo_can_create() of zaptel < 1.4.8 . + * Also copy the new wcte12xp/ to the source tarball. + * Delete old created files on purge (Closes: #454388). + + -- Tzafrir Cohen Tue, 29 Jan 2008 13:32:11 +0200 + +zaptel (1:1.4.7.1~dfsg-1) unstable; urgency=low + + [ Tzafrir Cohen ] + * New upstream release. + - New upstream version 1.4.6 (Closes: #452315, #442414). + * Add Voicetronix OpenPCI Zaptel driver (wcopenpci.c) . + * Explicitly delete depmod results (modules.dep at el.) + * ztdiag.dpatch: removed (applied by upstream). + * xpp_m_subdirs.dpatch: removed (applied by upstream). + * Makefile_fix_clean.dpatch: removed (applied by upstream). + * Makefile_opt_level.dpatch: removed (applied by upstream, except for 2.4 + kernel modules). + * Call 'make dist-clean' rather than 'make clean'. + * Don't run 'make dist-clean' on zaptel-modules build. + * Document zaptel-modules build test. + * Don't run ztcfg if zaptel was not loaded. But do fail if ztcfg has failed. + (Closes: #407996). + * Don't build wcopenpci on big endian platforms (Module gives #error there). + * Actually fix building xpp with M instead of SUBDIRS. + * Updates to bristuff zap modules from bristuff-0.3.0-1y-l: + - Fix build warnings. + - Allow sharing interrupts. + * Update cwain from recent bristuff. + + [ Faidon Liambotis ] + * Don't delete old device nodes on installations since it's needed only for + upgrades from <= sarge which isn't supported. Shuts up lintian error. + * Correctly detect udev/devfsd and chrooted environments at postinst time. + * Fix debian/watch by using a pkg-voip wrapper to avoid upstream's silly + redirections. (Closes: #449673) + * Fix OSLEC so that audio works again on x86_64 systems. + * Update Standards-Version to 3.7.3, no changes needed. + * Refresh Uploaders: add myself, remove Jose Carlos Garcia Sogo, Santiago + Garcia Mantinan and Santiago Ruano Rincon. + + -- Faidon Liambotis Fri, 28 Dec 2007 17:59:57 +0200 + +zaptel (1:1.4.5.1~dfsg-2) unstable; urgency=low + + [ Kilian Krause ] + * Update oslec to r942. Add mmx.h (disabled for now). + * Do export oslec_echo_can_identify from oslec.ko (Closes: #439814). + * Add Homepage field as added in dpkg-dev 1.14.6. + * Fix debian-rules-uses-pwd + + [ Mark Purcell ] + * Remove Build-Depends restrictions for packages in stable + + [ Tzafrir Cohen ] + * vzaphfc: update to 1.44, and apply our fixes. + * vzaphfc: fix building with Sarge 2.6 kernels. + * Fix asciidoc buildign in Sarge (temporary fix, until we get upstream fix + in 1.4.6). + * Don't build oslec if it is not the Zaptel echo canceller. + * Adjust line numbers in Lintian ignore file. + * Include oslec-ctrl-panel.sh as an example (Closes: #443363). + + -- Kilian Krause Sat, 22 Sep 2007 12:08:52 +0200 + +zaptel (1:1.4.5.1~dfsg-1) unstable; urgency=low + + * New upstream release. + + [ Kilian Krause ] + * Add dpkg-dev (>= 1.13.19) to Build-Depends for binary:Version + * Correct the zaptel.init to point to correct path of fxotune + (Closes: #439310) + * Put opt level back to -O2 in accordance with Debian Policy. + + [ Tzafrir Cohen ] + * Pass extra modules / subdirs to modules make. + * Re-add zaptel_perl.dpatch. Clarified patch's description in hope to aviod + re-re-re-removal of this patch. + * There's now an HTML version of README.Astribank . And upstream Makefile is + better at cleaning. + * Don't install man pages. 'make install' does that already (Closes: #422943) + * Fixed building vzaphfc, opvx1200p and ds1x1f of kernel >=2.6.22 + (>= 2.6.19?). + * xpp_m_subdirs.dpatch: Fixed building of xpp modules with M= . + * modulestest -r: test current kernel. + + -- Kilian Krause Sun, 26 Aug 2007 12:08:10 +0200 + +zaptel (1:1.4.5~dfsg-1) unstable; urgency=low + + * New Upstream Maintenance Release + - support for Digium's new 32 channel hardware echo canceler + (VPMADT032) for the TDM800P and TDM2400P + + [ Tzafrir Cohen ] + * zaptel_perl.dpatch: install perl modules to vendorlib rather than to + sitelib. This fixes the zaptel-perl utilities (e.g: zaptel_hardware). + * Build-depend on asciidoc to produce some package documentation. + * Mark doc-base documentation. + * Simplify Astribank initialization with zaptel-perl. + * provide symlinks for headers files: compatibility with zaptel 1.2 + locations of zaptel.h and tonezone.h . + + [ Faidon Liambotis ] + * bristuff 0.4.0-test4 + - Add zaptel.patch as debian/patches/bristuff.dpatch. + - Update zaphfc, qozap, cwain and ztgsm. + * Add florz' patch to zaphfc; removes RTAI support. (Closes: #382227) + * Don't fail on mknod errors; helps on vserver setups. (Closes: #411850) + + [ Mark Purcell ] + * Remove echocan_env.dpatch - merged upstream + * Remove zaptel_perl.dpatch - merged upstream + * Add debian/patches/inlcude.dpatch - upstream typo + * debian/rules: upstream change INSTALL_PREFIX->DESTDIR + * debian/zaptel.doc-base.readme add Index: doc-base-file-no-index + * Ship new wctdm24xxp modules + + -- Mark Purcell Sat, 18 Aug 2007 13:20:31 +0100 + +zaptel (1:1.4.4~dfsg-1) unstable; urgency=low + + * New Upstream Release + + [ Tzafrir Cohen ] + * echocan_env.dpatch: set the echo canceller from the environment. + * oslec/ The bulk of the oslec files. + * oslec_zaptel.dpatch: Minimal changes to zaptel to add oslec, beyond + the oslec directory. + * oslec_zaptap.dpatch: The oslec zaptap echo sampling device (probably + still does not apply). + * man_fixes.dpatch: Documentation fixes from upstream. + * Removing unrequired/harmful and useless modprobe / modutils config. + * README.Debian updates. + + [ Mark Purcell ] + * debian/patches/zaptel_perl.dpatch now included upstream + * Switch to ${binary:Version} substvar-source-version-is-deprecated + + -- Mark Purcell Wed, 18 Jul 2007 21:41:51 +0100 + +zaptel (1:1.4.3~dfsg-2) unstable; urgency=low + + [ Tzafrir Cohen ] + * Fixed and re-added zaptel_perl. + * And added zaptel_hardware (zaptel hardware lister), just for fun. + + -- Tzafrir Cohen Sat, 09 Jun 2007 03:36:17 +0300 + +zaptel (1:1.4.3~dfsg-1) unstable; urgency=low + + * New upstream release + - A fix for the potential for a rare deadlock between zaptel and the + wct4xxp, wcte11xp, and wct1xxp drivers + - Fixes for the VPM450M module on FC6 to correct a potential stack + overflow scenario at load time. + - Many updates to the Astribank driver + * disable debian/patches/zaptel_perl as it doesnt apply cleanly + + -- Mark Purcell Sat, 09 Jun 2007 00:01:55 +0100 + +zaptel (1:1.4.2.1~dfsg-2) unstable; urgency=low + + * Include debian/compat in zaptel-sources + - missing debian/compat file in archive (Closes: #422153) + + -- Mark Purcell Sat, 02 Jun 2007 10:22:04 +0100 + +zaptel (1:1.4.2.1~dfsg-1) unstable; urgency=low + + * New upstream release + - Added the ability to monitor pre-echo cancellation audio with ztmonitor + - Fixed some places where there was the potential for memory corruption + on SMP systems + - FTBFS with 2.6.19-1 (Closes: #405562) + * zaptel 1.4 provides wcfxs->wctdm alias + - No mention of wcfxs -> wctdm module rename (Closes: #419161) + - Missing modutils/modprobe rules for wctdm (Closes: #419162) + * provide debian/compat fixes lintian: debian-rules-sets-DH_COMPAT + * Cleanup debian/patches/ + + -- Mark Purcell Thu, 26 Apr 2007 09:07:48 +1000 + +zaptel (1:1.4.1~dfsg-3) unstable; urgency=low + + [ TzafrirCohen ] + * Better shape for ztdiag.dpatch. Don't forget to free. + + -- Mark Purcell Wed, 04 Apr 2007 22:51:24 +0100 + +zaptel (1:1.4.1~dfsg-2) experimental; urgency=low + + * $(MAKE) install-libs needed to install libs to libtonezone + + -- Mark Purcell Sat, 24 Mar 2007 14:17:22 +0000 + +zaptel (1:1.4.1~dfsg-1) experimental; urgency=low + + [ Tzafrir Cohen ] + * New upstream release. + * Fix installation of tonezone.h . + * merges from trunk: + * Update standards version to 3.7.2 . + * ztdiag.dpatch: fix the ioctl ZT_CHANDIAG and eable it. + * Should fix modules building: restore generated files that were + removed by a clean (Closes: #415280). + * Update debian/watch for ~ versions + * zaptel_perl.dpatch: Do install perl modules and scripts. + * zaptel_perl.dpatch: a few other fixes (until next upstream release). + * Add Makefile.kernel26 and wctc4xxp/ to source tarball. + * Don't install /etc/zaptel.conf by default (Closes: #383081) . + * List the tonezones supported by ztcfg and libtonezone in tonezones.txt + (Closes: #379108). + * Remove man pages from debian/, as they are included in upstream. + + -- Tzafrir Cohen Sat, 24 Mar 2007 10:03:54 +0200 + +zaptel (1:1.4.0~dfsg-1) experimental; urgency=low + + * Upgrading to 1.4. + * Watch for 1.4.x tarballs, rather than 1.2.x tarballs. + * Disable most patches: + - ukcid fails to apply. + - bristuff fails to apply. + - No point in patching the Makefile. + * Saving my attempts to apply bristuff. + * .h files moved to /usr/include/zaptel + + -- Tzafrir Cohen Mon, 1 Jan 2007 21:31:18 +0200 + +zaptel (1:1.2.12~dfsg-2) UNRELEASED; urgency=low + + * NOT RELEASED YET + * Remove -O4 from all Makefiles (Closes: #391840) + * Remove gcc hardcoded as HOSTCC + + -- Kilian Krause Thu, 28 Dec 2006 13:12:16 +0100 + +zaptel (1:1.2.12~dfsg-1) unstable; urgency=low + + * New upstream release. (Closes: #403326) + + -- Kilian Krause Wed, 27 Dec 2006 23:23:40 +0100 + +zaptel (1:1.2.11.dfsg-1) unstable; urgency=low + + [ Tzafrir Cohen ] + * Reverting my changes from 1:1.2.9.1.dfsg-2. Moved to the experimental + branch. + + [ Kilian Krause ] + * Remove bogus zaptel-modules from being Recommends (Closes: #387961) + * Update vzaphfc as proposed by Jens Wilke + + [ Mark Purcell ] + * New Upstream Release + - Fixes: Fails to build with pristine upstream kernel, very recent version + (Closes: #400705) + - Fixes: Please package version 1.2.11 (Closes: #399634) + - Fixes: vzaphfc: error: 'CHECKSUM_HW' undeclared (Closes: #386498) + * Cleanup debian/patches/wct4xxp-dfsg.dpatch + * debian/rules call dh_installmodules from binary_modules: + - Fixes: I had to do depmod -a manually after doing m-a a-i zaptel (Closes: + #332787) + * Update debian/patches/Makefile_uname.dpatch to force -O2 + - Fixes: Cannot initiate a call to BRI (Closes: #386052) + * Remove Depends: zaptel from debian/control.modules.in + - please don't depend on zaptel (Closes: #391826) + + -- Mark Purcell Sat, 2 Dec 2006 14:33:30 +0000 + +zaptel (1:1.2.10.dfsg-2) unstable; urgency=low + + * bristuff-0.3.0-PRE-1v + * Remove redundant GPL LICENCE text + + -- Mark Purcell Tue, 24 Oct 2006 22:41:01 +0100 + +zaptel (1:1.2.10.dfsg-1) unstable; urgency=low + + * New upstream release + + -- Mark Purcell Sun, 22 Oct 2006 20:27:19 +0100 + +zaptel (1:1.2.9.1.dfsg-2) unstable; urgency=low + + [ Tzafrir Cohen ] + * zaptel 1.4 compatibility changes: + - place zaptel.h and tonezone.h in /usr/include/zaptel (through symlinks) + - zaptelh_14.dpatch: declare some zaptel 1.4 interfaces (not implemented + anywhere, though). + + [ Mark Purcell ] + * debian/rules patch from Robert Millan + - the package doesn't compile (Closes: #390903) + * add debian/patches/dbug391840.dpatch + - ztcfg segfaults because of -O4 (Closes: #391840) + * add debian/patches/wct4xxp-dfsg.dpatch + - wct4xxp and other modules are not built anymore on zaptel- + 1.2.8.dfsg-1 (Closes: #388756) + + -- Mark Purcell Tue, 10 Oct 2006 09:36:58 +1000 + +zaptel (1:1.2.9.1.dfsg-1) unstable; urgency=low + + * New Upstream Release + * firmware removed from wct4xxp/OCT6114-128D.ima + * Lintian cleanup; spelling-error-in-copyright + + -- Mark Purcell Sat, 23 Sep 2006 13:58:15 +0100 + +zaptel (1:1.2.8.dfsg-1) unstable; urgency=low + + * New Upstream Release + + -- Mark Purcell Wed, 23 Aug 2006 07:30:22 +0100 + +zaptel (1:1.2.7.dfsg-4) unstable; urgency=low + + * Install zaptel.conf.sample as a confile under /etc + * Add Recommends: zaptel-modules + * Improve error handling and conf file checking in init.d. (Closes: + Bug#382604) + + -- Mark Purcell Thu, 17 Aug 2006 08:34:43 +0100 + +zaptel (1:1.2.7.dfsg-3) unstable; urgency=low + + [ Kilian Krause ] + * Simplified vzaphfc patch. + + [ Mark Purcell ] + * Build-Depends: debhelper (>= 5.0.37) and dh_installmodules makes + zaptel-source.postinst & zaptel-modules.post{inst,rm} obsolete + Fixes: postinst/postrm depmod -- update templates to use dh_installmodules + instead (Closes: #381754) + * postinst failure (Closes: #361312) + * zaptel-modules from testing don't compile on Sarge (Closes: #376719) + * pciradio.c:1810: error: syntax error before string constant (Closes: + #368145) + * Can't recompile zaptel modules on Sarge (Closes: #375581) + * zaptel-modules from testing don't compile on Sarge (Closes: #376719) + + -- Mark Purcell Thu, 10 Aug 2006 23:39:58 +0100 + +zaptel (1:1.2.7.dfsg-2) unstable; urgency=low + + * Fix get-orig-source target to make dfsg repacking work + * Fix zaptel-source to build without firmware again. Required dropping + wct4xxp module. Added vzaphfc to linux-2.6 modules. (Closes: #381123) + + -- Kilian Krause Thu, 3 Aug 2006 11:48:14 +0000 + +zaptel (1:1.2.7.dfsg-1) unstable; urgency=high + + * Urgency high as this is blocking a security fix for asterisk + * Remove non-modifiable firmware to make DFSG compliant. Does + anyone need this firmware? (Closes: #379458) + + -- Mark Purcell Tue, 1 Aug 2006 15:27:09 +0100 + +zaptel (1:1.2.7-2) unstable; urgency=low + + * Copying Makefile as before to the source package, + Copying some extra files now needed for building (Closes: #378864) + + -- Mark Purcell Tue, 1 Aug 2006 06:29:39 +0100 + +zaptel (1:1.2.7-1) unstable; urgency=low + + * New upstream release + + [ Kilian Krause ] + * Add vzaphfc driver (enhanced zaphfc) by Jens Wilke. + + [ Tzafrir Cohen ] + * Separating ZapBRI modules to directories, rather than patches + * Example configs moved from zaptel-source to zaptel + * Removing some unneeded dirs from zaptel-source + * debian/patches/Makefile_kbuild: a small part of the original one. + Fixes building on Sarge + * genzaptelconf is now in zaptel + * xpp/utils/Makefile has a decent install target + * debian/rules: Use CURDIR + * debian/modulestest: Building modules for -3 kernels + * fix x bit of files in /usr/share/zaptel + * removed genzaptelconf from debian/ + * Added support for the OpenVox A1200P card (http://www.openvox.com.cn/) + * debian/control: require libusb-dev for building xpp firmware loader. + * debian/control: Recommend package xpp-firmware (should be added to + non-free) + * bristuff_local_zaptelh.dpatch: Build bristuff modules with correct + zaptel.conf (in Sarge) + * Makefile_uname.dpatch: Updated. Note: watch for PWD issues. + * Makefile_bristuff.dpatch: updated to reflect Makefile change. + + -- Mark Purcell Mon, 17 Jul 2006 21:48:21 +0100 + +zaptel (1:1.2.6-2) unstable; urgency=high + + * Urgency high as this is blocking a security fix for asterisk [CVE-2006-2898] + + * Add debian/libtonezone-dev.links + - Realy fix: missing libtonezone.so.1 symlink (Closes: #372887) + + -- Mark Purcell Wed, 14 Jun 2006 13:40:31 +1000 + +zaptel (1:1.2.6-1) unstable; urgency=high + + [ Mark Purcell ] + * Urgency high as this is blocking a security fix for asterisk [CVE-2006-2898] + + * New upstream release. + - can't find zaptel.h during build (Closes: #330137) + - errors in fxotune.c (Closes: #370213) + - Cannot make zaptel-source: SUBDIR not found (Closes: #368561) + + [ Kilian Krause ] + * Weed out old unused patches. Add comments which patches have been included + upstream for next release. + + [ Lionel Elie Mamane ] + * Load ztdummy when needed, not when not needed. + + [ Tzafrir Cohen ] + * bristuff: 0.3.0-PRE1p + * We have another ZapBRI module: ztgsm + * Experimental support in genzaptelconf for ztgsm (from sample files) + * genzaptelconf: 0.5.1 (does not require restart of asterisk) + * zaptel.init: 'unload' operation. Better support for Astribank + * moduletest script fixes + * bristuff added ztpty + * genzaptelconf: wait for xpp (astribank) module to register after loadin it + * minor xpp driver fixes (already in 1.2 branch) + + [ Julien BLACHE ] + * debian/libtonezone1.links: + + Create the libtonezone.so.1 symlink (closes: #372887). + + -- Mark Purcell Wed, 14 Jun 2006 10:59:52 +1000 + +zaptel (1:1.2.5-1) unstable; urgency=low + + [ Tzafrir Cohen ] + * New upstream version + * Only build xpp for i386, as it currently crashes on other arches. + * Fix compilation of xpp for 2.6.14 + + [ Kilian Krause ] + * Fix gendigits to write to stdout. + + -- Kilian Krause Thu, 30 Mar 2006 23:52:38 +0300 + +zaptel (1:1.2.4-1) unstable; urgency=low + + * New upstrream release (Closes: #353094) + * removing xpp.dpatch: merged in upstream + * removing dot_version: bug fixed upstream + * Makefile_kbuild.dpatch: modified, as it was not properly merged in + upstream + * Makefile_bristuff.dpatch: really build zaptel modules again + * Makefile_xpp: fixed. + * debian/modulestest: a script for postbuild of zaptel modules from a svn + build + * zaptel-source: removing unnecessary dependency on dpatch + + -- Tzafrir Cohen Thu, 23 Feb 2006 09:40:47 +0200 + +zaptel (1:1.2.3-2) unstable; urgency=low + + * bristuff 0.3.0-PRE1k. Also, renamed the dpatch to simply "bristuff" + * updated version in dot_version.dpatch. + * Include build_tools and .version in copied files + * newer versions of genzaptelconf and xpp.dpatch + + -- Tzafrir Cohen Mon, 6 Feb 2006 15:30:06 +0200 + +zaptel (1:1.2.3-1) unstable; urgency=low + + * new upstrream release + * ukcid.dpatch: for UK Caller ID support (Zaptel part, closes: #302380) + * newer versions of genzaptelconf and xpp.dpatch + * Makefile_pscmd.dpatch disabled due to a small upstream change. Revive it? + * dot_version.dpatch: the tarball is missing the .version file. Remove + it when it provides one + + -- Tzafrir Cohen Mon, 6 Feb 2006 14:02:04 +0200 + +zaptel (1:1.2.1-3) unstable; urgency=low + + * Fix compilation with binary-only mode. + * bristuff 0.3.0-PRE-1f + * make lintian override apply + + -- Tzafrir Cohen Sat, 7 Jan 2006 20:39:33 +0200 + +zaptel (1:1.2.1-2) unstable; urgency=low + + * Added bristuff 0.3.0-PRE1d patch. bristuff re-enabled. + (Closes: #340627, #344432) + * Documentation fixes (Closes: #316801) + * Makefile_targets.dpatch is ba its small self + * readded bristuff examples. with cwain this time + * zaptel.init: a slightly different test for a zaptel timing source + * Depend on procps due to using ps in postinst (Closes: #342699) + + -- Tzafrir Cohen Fri, 30 Dec 2005 19:12:54 +0200 + +zaptel (1:1.2.1-1) unstable; urgency=low + + * New upstream release + * Disable bristuff for new upstream + + -- Mark Purcell Wed, 7 Dec 2005 21:28:23 +0000 + +zaptel (1:1.2.0-2) unstable; urgency=low + + [ Kilian Krause ] + * Added bristuff 0.3.0-PRE1 for Asterisk 1.2.0 support. + + [Tzafrir Cohen] + * fix Makefile_deps_kern.dpatch + * remove .rej from Makefile.uname.dpatch + * do install genzaptelconf man page + * update genzaptelconf and its man page + * echocan_env.dpatch: allow changing the echo canceller at zaptel-modules + build time + * Makefile_kbuild.dpatch: use kbuild for 2.6 modules build. used for: + * Makefile_xpp.dpatch: (not applied by default) + a small patch to enable the build of: + * xpp.dpatch: drivers for Xorcom Asteribank + + [ Mark Purcell ] + * Build and package libtonezone.so + + -- Mark Purcell Wed, 30 Nov 2005 16:28:51 +0000 + +zaptel (1:1.2.0-1) unstable; urgency=low + + * New upstream release + * Remove Makefile_deps_kern.dpatch as it doesnt apply upstream + + -- Mark Purcell Thu, 17 Nov 2005 17:50:00 +0000 + +zaptel (1:1.2.0-rc2-1) experimental; urgency=low + + * New upstream release + + -- Mark Purcell Sun, 13 Nov 2005 18:24:17 +0000 + +zaptel (1:1.2.0-rc1-1) experimental; urgency=low + + * New upstream release + * Update Makefile_uname.dpatch + * FTBFS: missing or incorrect directory modexamples/ (Closes: #329084) + * debian/rules export DH_COMPAT=4 + + -- Mark Purcell Wed, 9 Nov 2005 21:37:47 +0000 + +zaptel (1:1.2.0-beta2-3) experimental; urgency=low + + * Not Released Yet + * Copyright audit to debian/copyright + + -- Mark Purcell Mon, 7 Nov 2005 19:19:27 +0000 + +zaptel (1:1.0.9.2-1) unstable; urgency=low + + * New Upstream Release + + -- Mark Purcell Tue, 8 Nov 2005 20:47:48 +0000 + +zaptel (1:1.2.0-beta2-2) experimental; urgency=low + + * Suggestions from Tzafrir Cohen + - Makefile_man.dpatch can be removed: fixed by upstream + - fxotune.tmpfile.dpatch can be removed: fixed by upstream + - a small manual fix to Makefile_targets.dpatch: s/ manpages$// + - debian/rules: dh_installman: 's/debian/doc/' a number of times + (don't use doc/*.8 as there is no reason to install the pathetic + torisatool.8 man page) + + -- Mark Purcell Tue, 1 Nov 2005 21:26:36 +0000 + +zaptel (1:1.2.0-beta2-1) experimental; urgency=low + + * New upstream release + * Update Makefile_targets.dpatch + * Update Makefile_man + * Update fxotune_tmpfile.dpatch + + -- Mark Purcell Tue, 1 Nov 2005 20:51:02 +0000 + +zaptel (1:1.2.0-0beta1-1) experimental; urgency=low + + * New upstream release + * Disable bristuff for experimental upload + * Apply patch from Tzafrir Cohen for 1.2.0 build + + -- Mark Purcell Sun, 18 Sep 2005 12:48:59 +0100 + +zaptel (1:1.0.9.1-4) unstable; urgency=low + + (NOT YET RELEASED - needs still some tweaking with florz' patches) + * debian/patches/ztcfg_init.dpatch: Make ztcfg not kill the channels when + executed twice. + * debian/patches/zaphfc_0.2.0-RC8n_florz-8.dpatch: Add florz' improved + zaphfc implementation as zaphfc-florz. This should reduce system IO load + among other improvements. Taken from http://zaphfc.florz.dyndns.org/ + + -- Kilian Krause Sat, 27 Aug 2005 21:32:50 +0200 + +zaptel (1:1.0.9.1-3) unstable; urgency=low + + * debian/control: fixed overrides disparity with zaptel-source belonging to + devel rather than comm. + + -- Kilian Krause Sat, 27 Aug 2005 14:35:48 +0200 + +zaptel (1:1.0.9.1-2) unstable; urgency=low + + * Closes: #302836: zaptel-source: zaphfc module missing after + compiling modules. + * Closes: #323753: zaptel-source: cannot compile zaphfc in unstable + with gcc-4.0.1. + + -- Santiago Ruano Rincon Fri, 19 Aug 2005 00:40:56 -0500 + +zaptel (1:1.0.9.1-1) unstable; urgency=low + + * New upstream release + * Update debian/watch + * Please package version 1.0.9.1 (Closes: #320600) + * FXO hardware stops working after 25 days (Closes: #321239) + + -- Mark Purcell Mon, 8 Aug 2005 18:34:10 +0100 + +zaptel (1:1.0.9-5) unstable; urgency=low + + * Import bristuff-0.2.0-RC8l.dpatch + + -- Santiago Ruano Rincon Sat, 30 Jul 2005 11:26:42 -0500 + +zaptel (1:1.0.9-4) unstable; urgency=low + + Santiago Ruano Rincon: + * Man pages are builded from sgml's using docbook-utils + Deleted the *.8 files + * Closes: #317297: Applied a patch to improve the ztdummy + accuracy on kernel 2.6 + + Mark Purcell: + * Reinstate debian/zaptel.install + - Closes: #318575: this package does not install ztcfg, ztmonitor, + ztspeed, zttest, zttool. + + -- Mark Purcell Sun, 17 Jul 2005 07:11:27 +1000 + +zaptel (1:1.0.9-3) unstable; urgency=low + + * Import bristuff-0.2.0-RC8j.dpatch + * Closes: #315251: zaptel should be in group comm + * Note that the cloned report is still active against ftp.debian.org + * Closes: #316800: zaptel package 1.0.9 ships headers + + -- Mark Purcell Thu, 14 Jul 2005 12:19:10 +0100 + +zaptel (1:1.0.9-2) unstable; urgency=low + + * Import bristuff-0.2.0-RC8h.dpatch + * Enable rtia.dpatch + + -- Mark Purcell Mon, 4 Jul 2005 02:35:37 +0100 + +zaptel (1:1.0.9-1) unstable; urgency=low + + * New upstream release + * Disable bristuff to allow 1.0.9 upload + * Disable rtia to allow 1.0.9 upload + + -- Mark Purcell Sun, 3 Jul 2005 15:51:32 +0100 + +zaptel (1:1.0.7-5) unstable; urgency=low + + * ACK NMUs. Thanks for helping with this. (Closes: #305731, #310150) + * Actually doesn't fail if dpatch is not installed when building modules. + * zaptel-modules.postinst: New. Run depmod -a on modules install + * zaptel: should build-dep on debhelper (>= 4.0.4). (Closes: #310788) + * zaptel: should build-dep on dpatch >= 2.0.9 (Closes: #314549) + * zaptel: bashism in postinst (Closes: #314552) + * zaptel-source: compilation error in zaphfc.c (Closes: #305193) + * zaptel-source Build-Depends on dpatch, should Depend on + it though. (Closes: #309258) + * zaptel-source: Fails to enable RTAI support (Closes: #304648) + + -- Kilian Krause Sun, 19 Jun 2005 15:38:25 +0200 + +zaptel (1:1.0.7-4.1) unstable; urgency=high + + * Non-maintainer upload. + * High-urgency upload for sarge-targetted RC bugfix + * Make sure directories are created mode 755 instead of mode 644, as + this otherwise causes problems for building (apparently on xfs + filesystems). Closes: #310150. + * Tweak debian/patches/Makefile.dpatch fix from the previous NMU so + that it isn't unnecessarily fragile: -fsigned-char is *always* + either a no-op or required, so lose the architecture checking and + enable it unconditionally. Closes: #305731. + + -- Steve Langasek Sun, 22 May 2005 02:48:44 -0700 + +zaptel (1:1.0.7-4) unstable; urgency=high + + * NMU as VOIP team taking so long. Fix compiler flags so that ztcfg + works. (Closes: #305731) + + -- Matthew Grant Fri, 22 Apr 2005 07:35:28 +1200 + +zaptel (1:1.0.7-3) unstable; urgency=medium + + * Closes: #302903: libtonezone1 package is empty + * Closes: #302833: binary files missing, e.g. /sbin/ztcfg + * Move debian/*.files -> debian/*.install + * Closes: #302847: zaptel command ztcfg freezes on Debian PowerPC + causing boot failure. + + -- Mark Purcell Sun, 3 Apr 2005 19:44:25 +0100 + +zaptel (1:1.0.7-2) unstable; urgency=medium + + * Debian VoIP Team upload. + * Jose Carlos: + + Working support for module-assistant included in zaptel-source. + Thanks to Eduard Bloch for his help (Closes: #301665) + + debian/control.modules.in: + - make generated modules package depend on zaptel binary package. + - updated description to refer to module-assistant. + + debian/control: + - build-depend on bzip2. + - zaptel-source depends on module-assistant tool and bzip2. + - updated and improved descriptions. + + debian/rules: + - remaked with the new m-a stuff. + - don't need dpatch installed for building the modules (Closes: #301666) + + debian/postinst: doesn't output garbage (Closes: #296958) + + debian/postrm: don't remove creeated devices. Only box admin can do + that, per Policy 10.6 + + Removed zaphfc and qozap examples from zaptel-source. Only ship them + in zaptel binary package. + + README.Debian: file added. Talk about how compile modules and + use them with udev (Closes: #163857) + + Don't install zaptel.h file in zaptel-modules packages. (Closes: #297306) + + * Kilian Krause: + + Increased urgency for fixing RC-bug and this is the last deb to + allow the whole Debian VoIP suit proceed to testing. + + -- Jose Carlos Garcia Sogo Sat, 2 Apr 2005 01:14:23 +0200 + +zaptel (1:1.0.7-1) unstable; urgency=low + + * New upstream version. + + -- Kilian Krause Sat, 19 Mar 2005 23:28:07 +0100 + +zaptel (1:1.0.6-1) unstable; urgency=low + + * New upstream version. (zaptel 1.0.6, bristuff RC7k) + * added zaphfc and qozap modules. + + -- Kilian Krause Sat, 5 Mar 2005 20:05:35 +0100 + +zaptel (1:1.0.4-3) unstable; urgency=low + + * Debian VoIP team upload. + * debian/rules, debian/zaptel-source.files: fixed zaptel.h includes problem + * debian/patches/Makefile.dpatch: imported from old package (now dpatch + instead directly in the diff). Fixed building on hosts with differring + userland and kernel arch. Now also including symlink for SONAME. + * debian/patches/bristuff.dpatch: imported bristuff patch to include zaphfc. + (Closes: #294183) + * debian/zaptel.postinst: Fixed permissions issue problem. + * debian/zaptel.modprobe.d: Added zaphfc RC7f. + + -- Kilian Krause Thu, 24 Feb 2005 01:42:36 +0100 + +zaptel (1:1.0.4-2) experimental; urgency=low + + * Better "use" of uname -r in Makefile for zaptel-source + + -- Santiago Ruano Rincon Mon, 21 Feb 2005 00:27:14 -0500 + +zaptel (1:1.0.4-1) experimental; urgency=low + + * New upstream release (zaptel-1.0.4) + * Included zttest and ztspeed binaries + * Added Depends on debhelper and Recomends zaptel for zaptel-source + * Added /etc/modprobe.d/zaptel and corrected the path for the binaries, + /sbin instead of /usr/sbin + + -- Santiago Ruano Rincon Wed, 26 Jan 2005 23:05:20 -0500 + +zaptel (1:1.0.2-2) unstable; urgency=low + + * libtonezone out of zaptel-source + * /dev/zap/ are now created by zaptel.postinst and deleted by + zaptel.postrm. Now, the zap devices match with the upstream version + (Closes: #274384). + * Added lintian overrides for mknod-in-maintainer-script warnings + * docbook-to-man out of the Build-Depends + + -- Santiago Ruano Rincon Wed, 24 Nov 2004 22:05:52 -0500 + +zaptel (1:1.0.2-1) unstable; urgency=low + + * New upstream release (zaptel-1.0.2) + + -- Santiago Ruano Rincon Sat, 30 Oct 2004 00:51:54 -0500 + +zaptel (1:1.0.0-2) unstable; urgency=low + + * New maintainer (Closes: #251938). + * Man pages created for ztcfg, ztmonitor and zttool + (Closes: #274632, #274633, #274634). + * Mark Purcell made the package for version 1.0 + (Closes: #273255, #251929). + * zaptel-modules can be build from zaptel-source with + make-kpkg (Closes: #274085). + * Now it compiles for 2.6 Kernels (Closes: #251930). + + -- Santiago Ruano Rincon Sun, 26 Sep 2004 02:05:44 -0500 + +zaptel (1:1.0.0-1) unstable; urgency=low + + * NMU (See Bug#251938) + * New upstream release + + -- Mark Purcell Fri, 24 Sep 2004 22:46:55 +1000 + +zaptel (1:0.8.1+1.0-RC2-1) unstable; urgency=low + + * New upstream release + + -- Mark Purcell Thu, 9 Sep 2004 19:17:28 +1000 + +zaptel (1:0.8.1+1.0-RC1-1) unstable; urgency=low + + * New upstream release + * Add a debian/watch file + + -- Mark Purcell Wed, 21 Jul 2004 17:51:22 +1000 + +zaptel (1:0.8.1-1) unstable; urgency=low + + * New upstream release + + -- Matt Zimmerman Wed, 11 Feb 2004 15:29:20 -0800 + +zaptel (1:0.8.0-2) unstable; urgency=low + + * Create usr/include ahead of time so that tonezone.h is installed + correctly (Closes: #227795) + + -- Matt Zimmerman Wed, 14 Jan 2004 17:24:26 -0800 + +zaptel (1:0.8.0-1) unstable; urgency=low + + * New upstream release + + -- Matt Zimmerman Tue, 13 Jan 2004 14:44:56 -0800 + +zaptel (1:0.6.0-2) unstable; urgency=low + + * Rebuild with new libnewt + + -- Matt Zimmerman Mon, 30 Jun 2003 22:51:18 -0400 + +zaptel (1:0.6.0-1) unstable; urgency=low + + * New upstream release, needed for new asterisk (Closes: #189661) + + -- Matt Zimmerman Sat, 19 Apr 2003 23:56:59 -0400 + +zaptel (1:0.4.0-2) unstable; urgency=low + + * Break out into {build,install,binary}-indep targets + (Closes: #184528) + * libtonezone-dev Section: libdevel + * Escape $ properly in instructions in postinst + + -- Matt Zimmerman Wed, 12 Mar 2003 19:16:10 -0500 + +zaptel (1:0.4.0-1) unstable; urgency=low + + * New upstream release + + -- Matt Zimmerman Sun, 16 Feb 2003 15:12:02 -0500 + +zaptel (0.cvs.20021029-1) unstable; urgency=low + + * New upstream CVS + * Use MARK2 echo canceller + + -- Matt Zimmerman Tue, 29 Oct 2002 10:37:53 -0500 + +zaptel (0.cvs.20020729-1) unstable; urgency=low + + * New upstream CVS + * Include ztmonitor binary + + -- Matt Zimmerman Mon, 29 Jul 2002 12:36:58 -0400 + +zaptel (0.cvs.20020708-1) unstable; urgency=low + + * New upstream CVS + + -- Matt Zimmerman Mon, 8 Jul 2002 15:32:20 -0400 + +zaptel (0.cvs.20020624-2) unstable; urgency=low + + * Include Makefile in the -source package (oops, Closes: #152014) + + -- Matt Zimmerman Fri, 5 Jul 2002 11:00:08 -0400 + +zaptel (0.cvs.20020624-1) unstable; urgency=low + + * Initial Release (Closes: #150874) + + -- Matt Zimmerman Mon, 17 Jun 2002 10:31:21 -0400 + --- zaptel-1.4.11~dfsg.orig/debian/copyright +++ zaptel-1.4.11~dfsg/debian/copyright @@ -0,0 +1,103 @@ +This package was debianized by Matt Zimmerman on +Mon, 17 Jun 2002 10:31:21 -0400. + +It was downloaded from + http://www.asterisk.org/ +bristuff patch was downloaded from + http://www.junghanns.net/download/ +florz' patch for the zaphfc driver was downloaded from + http://zaphfc.florz.dyndns.org/ + +Upstream source has been modified to comply with the Debian Free +Software Guildlines (DFSG), by the removal of the firmware files: + + wct4xxp/OCT6114-128D.ima + +xpp/LICENSE.firmware permits redistribution but does not mention +modification, which is a requirement of Debian Policy 2.1 ("Derived works"). + + +Upstream Authors: Jim Dixon / Zapate Telephony, Linux Support Services, Inc. + +Copyright (from zaptel.c): + + * 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 the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + +On Debian systems, a copy of the GNU General Public License may be found in +/usr/share/common-licenses/GPL. + +Other Portions: + + * Copyright (C) 2001-2005, Digium, Inc. + * Copyright (C) 2002 Steve Underwood +sethdlc.c: * Copyright (C) 2000 Krzysztof Halasa +ztd-loc.c: * Copyright (C) 2004, Axialys Interactive +ztdummy.c: * Copyright (C) 2002, Hermes Softlab + +debian/patches/bristuff.dpatch, zaphfc, qozap, cwain, ztgsm: + * Copyright (C) 2002, 2003, 2004, 2005 Junghanns.NET GmbH + +debian/patches/zaphfc-florz.dpatch: + * Copyright (C) 2004, 2005, 2006 Florian Zumbiehl + +vzaphfc is derived from bristuff and since enhanced by: + * Copyright (C) 2004-2006, Daniele "Vihai" Orlandi + * Copyright (C) 2006, headissue GmbH; Jens Wilke + +Files in the xpp/ subdirectory: + +* Written by Oron Peled +* Copyright (C) 2004-2006, Xorcom + +opvxa1200.c: +* OpenVox A1200P FXS/FXO Interface Driver for Zapata Telephony interface +* +* Modify from wctdm.c by MiaoLin + +opvxa1200.c is available for download from +http://www.openvox.com.cn/members_downloads.php +(requires no login. Does require javascript) + +wcopenpci.c is from the Voicetronix zaptel distribution at +http://www.voicetronix.com/Downloads/asterisk/ +Copyright (C) 2001, Linux Support Services, Inc. +Copyright (C) 2005 - 2007, Voicetronix + + +Files in oslec/ subdirectory and oslec patches: +Part of the Open Source Line Echo Canceller (OSLEC) project: +http://www.rowetel.com/ucasterisk/oslec.html +Current version downloaded from the subversion repository at: +http://svn.rowetel.com/software/oslec/trunk + +oslec/echo.c: * Copyright (C) 2001, 2003 Steve Underwood, 2007 David Rowe +oslec/oslec-ctrl-panel.sh:# Copyright (C) 2007 David Rowe +oslec/oslec.h: Copyright (C) 2007 David Rowe +oslec/oslec_wrap.c: Copyright (C) 2007 David Rowe +oslec/spandsp/fir.h: * Copyright (C) 2002 Steve Underwood +oslec/spandsp/bit_operations.h: * Copyright (C) 2006 Steve Underwood +oslec/spandsp/echo.h: * Copyright (C) 2001 Steve Underwood and 2007 David Rowe + +oslec/spandsp/mmx.h: +* mmx.h + * Copyright (C) 1997-2001 H. Dietz and R. Fisher + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. --- zaptel-1.4.11~dfsg.orig/debian/zaptel.postinst +++ zaptel-1.4.11~dfsg/debian/zaptel.postinst @@ -0,0 +1,68 @@ +#! /bin/sh + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-deconfigure' `in-favour' +# `removing' +# + +mknod_safe() { + if [ -c $1 ]; then return; fi + mknod "$@" || true +} + +# stolen from udev +chrooted() { + if [ "$(stat -c %d/%i /)" = "$(stat -Lc %d/%i /proc/1/root 2>/dev/null)" ]; + then + # the devicenumber/inode pair of / is the same as that of /sbin/init's + # root, so we're *not* in a chroot and hence return false. + return 1 + fi + return 0 +} + +case "$1" in + configure) + + #MAKEDEV zaptel + + if [ ! chrooted -a ! -e /dev/.devfsd -a ! -e /dev/.udevdb -a ! -e /dev/.udev ]; then + mkdir -p /dev/zap + mknod_safe /dev/zap/ctl c 196 0 + mknod_safe /dev/zap/transcode c 196 250 + mknod_safe /dev/zap/timer c 196 253 + mknod_safe /dev/zap/channel c 196 254 + mknod_safe /dev/zap/pseudo c 196 255 + for N in `seq 249`; do + mknod_safe /dev/zap/$N c 196 $N + done + chown 0:dialout /dev/zap/ -R + chmod 0660 /dev/zap/* + fi + + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + --- zaptel-1.4.11~dfsg.orig/debian/zaptel.doc-base.readme +++ zaptel-1.4.11~dfsg/debian/zaptel.doc-base.readme @@ -0,0 +1,13 @@ +Document: zaptel +Title: Zapata Telephony Interface Driver +Author: Tzafrir Cohen +Abstract: Basic documentation of the Zaptel telephony interface +Section: Comm + +Format: HTML +Index: /usr/share/doc/zaptel/README.html +Files: /usr/share/doc/zaptel/README.html + +Format: text +Files: /usr/share/doc/zaptel/README.gz + --- zaptel-1.4.11~dfsg.orig/debian/libtonezone1.links +++ zaptel-1.4.11~dfsg/debian/libtonezone1.links @@ -0,0 +1,2 @@ +usr/lib/libtonezone.so.1.0 usr/lib/libtonezone.so.1 + --- zaptel-1.4.11~dfsg.orig/debian/control +++ zaptel-1.4.11~dfsg/debian/control @@ -0,0 +1,51 @@ +Source: zaptel +Section: comm +Priority: optional +Maintainer: Debian VoIP Team +Uploaders: Kilian Krause , Mark Purcell , Tzafrir Cohen , Faidon Liambotis +Build-Depends: debhelper, libnewt-dev, quilt, bzip2, libusb-dev, asciidoc (>= 8.2.6-1.1) +Standards-Version: 3.7.3 +Homepage: http://www.asterisk.org/ +Vcs-Svn: svn://svn.debian.org/pkg-voip/zaptel/trunk/ +Vcs-Browser: http://svn.debian.org/wsvn/pkg-voip/zaptel/?op=log + +Package: zaptel +Section: comm +Architecture: any +# Xorcom packages depend on xpp-firmware. Debian zaptel will probably +# just recommend it. +Depends: ${shlibs:Depends}, procps, fxload +Description: zapata telephony utilities + Userspace utilities for configuring the Zapata telephony kernel driver, + which supports various telephony hardware, such as Wildcard series of + interface cards (X100P, T400P, E400P, S100P, S100U). + . + Includes ztcfg and zttool utils. + +Package: libtonezone1 +Section: libs +Architecture: any +Depends: ${shlibs:Depends} +Description: tonezone library (runtime) + A library for generating tones used for telephone signalling. + +Package: libtonezone-dev +Section: libdevel +Architecture: any +Depends: libtonezone1 (= ${binary:Version}) +Description: tonezone library (development) + A library for generating tones used for telephone signalling. + . + This package contains the development files. + +Package: zaptel-source +Section: devel +Architecture: all +Depends: debhelper (>> 4.0), module-assistant (>= 0.8.1), bzip2 +Recommends: zaptel +Description: Zapata telephony interface (source code for kernel driver) + This package contains the source code for zaptel kernel module providing + device drivers for various telephony hardware, including the Wildcard + series of interface cards (X100P, T400P, E400P, S100P, S100U). + . + It is normally used to build kernel modules package: m-a a-i zaptel --- zaptel-1.4.11~dfsg.orig/debian/zaptel.init +++ zaptel-1.4.11~dfsg/debian/zaptel.init @@ -0,0 +1,149 @@ +#! /bin/sh + +### BEGIN INIT INFO +# Provides: zaptel +# Required-Start: +# Required-Stop: +# Should-Start: $local_fs hotplug module-init-tools +# Should-Stop: $local_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Configures zaptel kernel modules. +# Description: Configures zaptel kernel modules. Waits until +# they are fully loaded or loads ztdummy (if modules +# are availble). +### END INIT INFO + +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin +DAEMON=/sbin/ztcfg +ZAPCONF_FILE=/etc/zaptel.conf +NAME=zaptel +DESC="Zaptel telephony kernel driver" +FXOTUNE=/sbin/fxotune + +# Include am defaults if available +if [ -f /etc/default/zaptel ] ; then + . /etc/default/zaptel +fi + +if [ ! -x $DAEMON ] ; then + echo >&2 $NAME ":" $DAEMON "fails test for exists and executable" ; + exit 0 +fi + +if [ ! -f $ZAPCONF_FILE ] ; then + echo >&2 $NAME ":" $ZAPCONF_FILE "fails test for exists and readable"; + exit 0 +fi + +# defined in /etc/default/zaptel +# ZAPTEL_MODS is a list of modules to be loaded at startup + +set -e + +# Initialize the Xorcom Astribank (xpp/) using perl utiliites: +# intended to replace all the the three functions below if user has +# installed the zaptel-perl utilities. +xpp_startup() { + # do nothing if there are no astribank devices: + if ! grep -q connected /proc/xpp/xbuses 2>/dev/null; then return 0; fi + + echo "Waiting for Astribank devices to initialize:" + cat /proc/xpp/XBUS-[0-9]*/waitfor_xpds 2>/dev/null || true + + # overriding locales for the above two, as perl can be noisy + # when locales are missing. + # No register all the devices if they didn't auto-register: + LC_ALL=C zt_registration on + + # this one could actually be run after ztcfg: + LC_ALL=C xpp_sync "$XPP_SYNC" +} + +# recursively unload a module and its dependencies, if possible. +# where's modprobe -r when you need it? +# inputs: module to unload. +unload_module() { + set +e + module="$1" + line=`lsmod 2>/dev/null | grep "^$module "` + if [ "$line" = '' ]; then return; fi # module was not loaded + + set -- $line + # $1: the original module, $2: size, $3: refcount, $4: deps list + mods=`echo $4 | tr , ' '` + # xpp_usb keeps the xpds below busy if an xpp hardware is + # connected. Hence must be removed before them: + case "$module" in xpd_*) mods="xpp_usb $mods";; esac + for mod in $mods; do + # run in a subshell, so it won't step over our vars: + (unload_module $mod) + done + rmmod $module || true + set -e +} + +# this checks that: +# A. /dev/zap/pseudo exists (if devfs/udev is in use, it implies zaptel loaded) +# B. Readable to the user running this script. No poblem if run by root. +# C. There is an active zaptel timing source. Either a zaptel hardware +# device or ztdummy. +test_zaptel_timing() { + head -c 0 /dev/zap/pseudo 2>/dev/null +} + +case "$1" in + start|reload) + echo -n "$DESC: " + #for module in $ZAPTEL_MODS + #do + # modprobe $module + #done + xpp_startup + + # If there is no zaptel timing source, load + # ztdummy. Other modules should have been loaded by + # now. + if ! test_zaptel_timing + then modprobe ztdummy || true # will fail if there is no module package + fi + + # configure existing modules: + + #If we got here and we have no zaptel modules, there's + # no point to fail if zaptel-modules-version> is not + # installed. + if [ -d /proc/zaptel ]; then + $DAEMON $DAEMON_OPTS + echo "$NAME." + fi + + if [ -r /etc/fxotune.conf ] && [ -x $FXOTUNE ]; then + $FXOTUNE -s + fi + ;; + stop) + : # do nothing + ;; + unload) + unload_module zaptel + # if oslec is the echo canceller, loading zaptel will + # also load oslec. Thus we need to be able to unload it + # as well: + unload_module oslec + ;; + status) + check_zaptel_timing + ;; + force-reload|restart) + # there's no 'stop' + $0 start + ;; + *) + N=/etc/init.d/$NAME + echo "Usage: $N {start|stop|restart|force-reload|status|unload}" >&2 + exit 1 + ;; +esac + +exit 0 --- zaptel-1.4.11~dfsg.orig/debian/libtonezone1.install +++ zaptel-1.4.11~dfsg/debian/libtonezone1.install @@ -0,0 +1 @@ +usr/lib/lib*.so.* --- zaptel-1.4.11~dfsg.orig/debian/README.Debian +++ zaptel-1.4.11~dfsg/debian/README.Debian @@ -0,0 +1,161 @@ +Building kernel modules +----------------------- +First, install zaptel-source package if you have not yet done so. + +You can build and install the modules package (as root) with the +following command: +# module-assistant a-i zaptel + +It may be handy (for e.g., testing purposes) to build the module packages +for all of the kernel-header packages installed on your system. Something +in the lines of: + + m-a -u . -t -i -f -k "`echo usr/src/kernel-headers-*`" build zaptel + +You can also use the environment variable TARBALL to build the modules +with a custom zaptel.tar.bz2 tarball with some local modifications. + +At the moment the 'clean' operation gives there the following error, +which is known, and is harmless (happens after cleaning the kernel dir), +so ignore it for now: + + make: *** menuselect: No such file or directory. Stop. + make[1]: [clean] Error 2 (ignored) + make: *** ppp: No such file or directory. Stop. + make[1]: *** [clean] Error 2 + + +Using udev +---------- +If you are using udev, zaptel devices will be created when kernel modules +are loaded by using modprobe, or devices are detected by hotplug at boot time. + +If you cannot access the zap/ctl device, check which user asterisk is +running as and add these permissions to your permissions file +(i.e. /etc/udev/permissions.d/50-udev.permissions): +# zaptel devices -- asterisk is expected to be part of 'dialout' group +zap/*:root:dialout:660 + +Note, however, that beginning with Sarge, the default udev settings should +include those lines. + + +Bristuff +-------- +This version has the the bristuff patch and kernel modules. The bristuffed +modules require a bristuffed Asterisk to work. For the older asterisk 1.2 +this is the package asterisk-bristuff . Current asterisk 1.4 packages +include support for bristuff. + + +FXOTune +------- +FXOTune is a utility provided by Digium for fine-tuning parameters of the +FXO modules of their TDM cards and compatibles, as well as of those of the +Xorcom Astribank. + +This package includes the fxotune utility. It will also load configuration +from /etc/fxotune.conf if fxotune was used to tune the FXO modules. Note +that fxotune will not work with X100P and similar cards. + + +/etc/zaptel.conf +---------------- +A sample /etc/zaptel.conf is no longer installed by default. You should +generate it manually (or automatically with genzaptelconf) if and when +you actually have zaptel hardware and installed a zaptel-modules package +for your kernel version. + + +Supported tone-zones +-------------------- +Zaptel is capable of playing ring tone, buy tone etc. for a large variety of +countries. This is done using the loadzone and defaultzone settings in +zaptel.conf. A list of tone-zones supported by zaptel and libtonezone is +included in the file tonezones.txt in the documentation directory. + +Example section of zaptel.conf: +loadzone=us,uk,de +defaultzone=us + + +Echo Canceller +-------------- +Open Source Line Echo Canceller (OSLEC) is an alternative echo canceller +developed outside the main Zaptel tree. It is currently labelled "Beta". +For more information see http://www.rowetel.com/ucasterisk/oslec.html . +It generally works much better than the default upstream echo canceller +(MG2). On the downside, it has a higher CPU consumption. + +We now include OSLEC and make it our default echo canceller. Unlike the +original distribution of oslec, we include it as part of the Zaptel +package. It will get built unless we set an alternative echo canceller. +If you still want to use the original echo MG2 echo canceller, build +the zaptel-modules package using: + + ECHO_CAN_NAME=MG2 m-a a-i zaptel + +The oslec code is the package required some minor changes to the code in +from the oslec repository. Specifically, I have not included the full copy +of spandsp here. Instead I picked only the relevant parts. I also placed +all the files in the directory oslec/ except the changes required to the +zaptel sources and makefiles. I have also separated the implementation +of the zaptap device to a separate patch, as it has a better chances of +failing to apply with upcoming version of Zaptel and its functionality +seems to me less important and not as clean. + +The oslec echo canceller can be controlled at runtime through files +under /proc/oslec . A dialg-based interface is available under +/usr/share/zaptel/examples/oslec-ctrl-panel.sh . + +According to early tests by OSLEC users, the default Asterisk echo +canceller size of 128 taps (16ms) should be good enough for most +installations, and 256 taps (32 ms) should cover just about any case. +For phones connected to FXS ports you can use substatially lower values +in order to reduce CPU consumption. e.g: 8ms or even 4 ms (64 taps or 32 +taps). + +Setting the number of taps for a channel in Asterisk's chan_zap is done +using the following in Asterisk's zapata.conf: + + echocancell=NNN + +where NNN is the number of taps. See Asterisk sample zapata.conf . + + +Build-time Tests +---------------- +One sanity check to run when making changes in the package is to make +(besides the usual lintian/linda) is to make sure that the modules +packages still builds. + +For this to work you should first have a version of zaptel-source +installed (any version. modules-assitant needs a few files from it in +place). This allows you to use the script debian/modulestest as a +post-build hook: + + svn-buildpackage --svn-postbuild='debian/modulestest -r' --svn-ignore-new -uc -us + +This will rebuild the zaptel-modules for your current kernel, or spend +much more time with: + + svn-buildpackage --svn-postbuild='debian/modulestest -a' --svn-ignore-new -uc -us + +to build zaptel-modules for all of your currently-installed +zaptel-modules packages. The log from the build will go to the file +zaptel-modules-build-.log in the build-area directory. e.g: +zaptel-modules-build-1.4.7~dfsg-1.log . + +The script should also run ls for the module packages built. You should +chack that all packages were indeed built and that their size looks sane +(no driver was left unbuilt). + + +If you get the "error": + + zconfig.h:91:41: error: missing binary operator before token "(" + +It is probably harmless. See http://bugs.digium.com/12426 . + + +-- Debian VoIP Team 16 June 2007 --- zaptel-1.4.11~dfsg.orig/debian/zaptel.doc-base.astribank +++ zaptel-1.4.11~dfsg/debian/zaptel.doc-base.astribank @@ -0,0 +1,13 @@ +Document: zaptel.astribank +Title: Xorcom Astribank Documentation +Author: Tzafrir Cohen +Abstract: Technical Documentation ofd the Xorcom Astribank +Section: Comm + +Format: HTML +Index: /usr/share/doc/zaptel/README.Astribank.html +Files: /usr/share/doc/zaptel/README.Astribank.html + +Format: text +Files: /usr/share/doc/zaptel/README.Astribank.gz + --- zaptel-1.4.11~dfsg.orig/debian/modulestest +++ zaptel-1.4.11~dfsg/debian/modulestest @@ -0,0 +1,71 @@ +#!/bin/sh + +# debian/modulestest: a postbuild script to build zaptel modules +# example usage: +# +# svn-buildpackage --svn-postbuild='debian/modulestest -r' --svn-ignore-new + +# At least one of the following two must be set to a sensible value: +# If both are empty, the script does nothing useful) +# +# kernel versions: Comma-separated. Use those if you have their +# kernel-headers/linux-headers packages installed +# + +# Full pathes to trees: +# Use this to provide a full path to a custom kernel tree: +#KERNEL_SOURCES=$HOME/Proj/Debs/Kernel/SwSusp/linux-2.6.15-rc5 +KERNEL_HEADERS= +KERNEL_SOURCES= +# run module-asustant with environment params that will generate +# .changes files even without signing +MODASS="env SIGNCHANGES=1 DEBSIGNCOMMAND=not_an_executable m-a" +me=`basename $0` + +# workaround for silly bash parsing issue in our build scripts: +if [ "$#" -lt 1 ]; then + set -- $MODULESTEST_ARGS +fi + +while getopts ah:rs:t arg +do + case "$arg" in + a) # All of the kernel-headers packages installed: + KERNEL_HEADERS=`COLUMNS=160 dpkg -l 'kernel-headers-2.[46].*-*-*' | awk '/^.i/{print $2}' | sed -e 's/^kernel-headers-//'| xargs| tr ' ' ,` + ;; + h) KERNEL_HEADERS=$OPTARG;; + s) KERNEL_SOURCES=$OPTARG;; + r) KERNEL_HEADERS=`uname -r`;; + t) # TODO: make this test per-distro or something + KERNEL_HEADERS=2.4.27-2-386,2.6.8-2-686-smp + ;; + esac +done +shift $(( $OPTIND-1 )) + +echo "Building for: Headers: $KERNEL_HEADERS, Sources: $KERNEL_SOURCES" + +if [ "$KERNEL_HEADERS" != '' ]; then hdrs_sw="-l $KERNEL_HEADERS"; fi +if [ "$KERNEL_SOURCES" != '' ]; then srcs_sw="-k $KERNEL_SOURCES"; fi + +# must be absolute for m-a ta accept TARBALL: +# Also note that $PWD is ugly and is about to be deleted. We need +# $PWD/.. +TOP_DIR=`dirname $PWD` +MODS_DIR=$TOP_DIR/modules +TAR_BALL=$MODS_DIR/usr/src/zaptel.tar.bz2 +DEB=$TOP_DIR/$PACKAGE-source_${TAG_VERSION}_all.deb +LOG_FILE=$TOP_DIR/$PACKAGE-modules-build-$TAG_VERSION.log + +rm -f $LOG_FILE +dpkg -x $DEB $MODS_DIR + +if [ "$hdrs_sw" != '' ]; then + TARBALL=$TAR_BALL $MODASS -u $TOP_DIR -t -i -f $hdrs_sw build $PACKAGE >>$LOG_FILE +fi +if [ "$srcs_sw" != '' ]; then + TARBALL=$TAR_BALL $MODASS -u $TOP_DIR -t -i -f $srcs_sw build $PACKAGE >>$LOG_FILE +fi + +ls -l $TOP_DIR/$PACKAGE-modules-*_$TAG_VERSION+*.deb +echo "$me: Log file: $LOG_FILE" --- zaptel-1.4.11~dfsg.orig/ztpty.c +++ zaptel-1.4.11~dfsg/ztpty.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "zaptel.h" + +#define SIZE 8000 + + + +void doit(int fd, int stdinfd) { + fd_set fds; + char inbuffer[4096]; + char outbuffer[4096]; + int res = 0; + int i = 0; + +// fprintf(stderr, "fd %d stdin fd %d\n", fd, stdinfd); + + for (;;) { + FD_ZERO(&fds); + FD_SET(fd, &fds); + FD_SET(stdinfd, &fds); + /* Wait for *some* sort of I/O */ + res = select(stdinfd + 1, &fds, NULL, NULL, NULL); + if (res < 0) { + fprintf(stderr, "Error in select: %s\n", strerror(errno)); + return; + } + if (FD_ISSET(stdinfd, &fds)) { + res = read(stdinfd, inbuffer, sizeof(inbuffer)); + if (res > 0) { +// fprintf(stderr, "read %d bytes from stdin\n", res); + if (res > 0) { + for (i=0; i < res ; i++) { + if (inbuffer[i] == '\n') { + if ((i > 0) && (inbuffer[i-1] == ' ')) { + inbuffer[i-1] = 0x1a; + } + inbuffer[i] = 0xd; + } + } + res = write(fd, inbuffer, res+2); +// res = write(STDOUT_FILENO, inbuffer, res); +// fprintf(stderr, "wrote %d bytes to stdout\n", res); + } + } + } + if (FD_ISSET(fd, &fds)) { + res = read(fd, outbuffer, sizeof(outbuffer)); +// fprintf(stderr, "read %d bytes from fd\n", res); + if (res > 0) { + res = write(STDOUT_FILENO, outbuffer, res); +// fprintf(stderr, "wrote %d bytes to stdout\n", res); + } + } + } + + +} + +int main(int argc, char *argv[]) +{ + int fd; + int stdinfd; + struct zt_params p; + struct zt_bufferinfo bi; + int blocksize=0; + fd = open(argv[1], O_RDWR | O_NONBLOCK); + if (fd < 0) { + fprintf(stderr, "Unable to open zap interface: %s\n", strerror(errno)); + exit(1); + } + if (ioctl(fd, ZT_GET_PARAMS, &p)) { + fprintf(stderr, "Unable to get parameters on '%s': %s\n", argv[1], strerror(errno)); + exit(1); + } + if ((p.sigtype != ZT_SIG_HDLCRAW) && (p.sigtype != ZT_SIG_HDLCFCS)) { + fprintf(stderr, "%s is in %d signalling, not FCS HDLC or RAW HDLC mode\n", argv[1], p.sigtype); + exit(1); + } + + if (ioctl(fd, ZT_GET_BLOCKSIZE, &blocksize)) { + fprintf(stderr, "Unable to get blocksize on '%s': %s\n", argv[1], strerror(errno)); + exit(1); + } else { +// fprintf(stderr, "blocksize %d\n", blocksize); + } + + bi.txbufpolicy = ZT_POLICY_IMMEDIATE; + bi.rxbufpolicy = ZT_POLICY_IMMEDIATE; + bi.numbufs = 16; + bi.bufsize = 1024; + if (ioctl(fd, ZT_SET_BUFINFO, &bi)) { + fprintf(stderr, "Unable to set buffer info on '%s': %s\n", argv[1], strerror(errno)); + exit(1); + } + + stdinfd = open("/dev/stdin", O_RDONLY | O_NONBLOCK); + + + doit(fd, stdinfd); + close(stdinfd); + close(fd); + return 0; +} --- zaptel-1.4.11~dfsg.orig/kernel/opvxa1200.c +++ zaptel-1.4.11~dfsg/kernel/opvxa1200.c @@ -0,0 +1,2718 @@ +/* + * OpenVox A1200P FXS/FXO Interface Driver for Zapata Telephony interface + * + * Modify from wctdm.c by MiaoLin + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* Rev histroy + * + * Rev 0.10 initial version + * Rev 0.11 + * fixed the led light on/off bug. + * modify some wctdm print to opvxa1200 + * support firmware version 1.2, faster i/o operation, and better LED control. + * + * Rev 0.12 patched to support new pci id 0x8519 + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "proslic.h" +#include "wctdm.h" + +//miaolin +#include +#include // get_fs(), set_fs(), KERNEL_DS +#include // fput() +//miaolin + + +/* + * Define for audio vs. register based ring detection + * + */ +/* #define AUDIO_RINGCHECK */ + +/* + Experimental max loop current limit for the proslic + Loop current limit is from 20 mA to 41 mA in steps of 3 + (according to datasheet) + So set the value below to: + 0x00 : 20mA (default) + 0x01 : 23mA + 0x02 : 26mA + 0x03 : 29mA + 0x04 : 32mA + 0x05 : 35mA + 0x06 : 37mA + 0x07 : 41mA +*/ +static int loopcurrent = 20; + +static int reversepolarity = 0; + +static alpha indirect_regs[] = +{ +{0,255,"DTMF_ROW_0_PEAK",0x55C2}, +{1,255,"DTMF_ROW_1_PEAK",0x51E6}, +{2,255,"DTMF_ROW2_PEAK",0x4B85}, +{3,255,"DTMF_ROW3_PEAK",0x4937}, +{4,255,"DTMF_COL1_PEAK",0x3333}, +{5,255,"DTMF_FWD_TWIST",0x0202}, +{6,255,"DTMF_RVS_TWIST",0x0202}, +{7,255,"DTMF_ROW_RATIO_TRES",0x0198}, +{8,255,"DTMF_COL_RATIO_TRES",0x0198}, +{9,255,"DTMF_ROW_2ND_ARM",0x0611}, +{10,255,"DTMF_COL_2ND_ARM",0x0202}, +{11,255,"DTMF_PWR_MIN_TRES",0x00E5}, +{12,255,"DTMF_OT_LIM_TRES",0x0A1C}, +{13,0,"OSC1_COEF",0x7B30}, +{14,1,"OSC1X",0x0063}, +{15,2,"OSC1Y",0x0000}, +{16,3,"OSC2_COEF",0x7870}, +{17,4,"OSC2X",0x007D}, +{18,5,"OSC2Y",0x0000}, +{19,6,"RING_V_OFF",0x0000}, +{20,7,"RING_OSC",0x7EF0}, +{21,8,"RING_X",0x0160}, +{22,9,"RING_Y",0x0000}, +{23,255,"PULSE_ENVEL",0x2000}, +{24,255,"PULSE_X",0x2000}, +{25,255,"PULSE_Y",0x0000}, +//{26,13,"RECV_DIGITAL_GAIN",0x4000}, // playback volume set lower +{26,13,"RECV_DIGITAL_GAIN",0x2000}, // playback volume set lower +{27,14,"XMIT_DIGITAL_GAIN",0x4000}, +//{27,14,"XMIT_DIGITAL_GAIN",0x2000}, +{28,15,"LOOP_CLOSE_TRES",0x1000}, +{29,16,"RING_TRIP_TRES",0x3600}, +{30,17,"COMMON_MIN_TRES",0x1000}, +{31,18,"COMMON_MAX_TRES",0x0200}, +{32,19,"PWR_ALARM_Q1Q2",0x07C0}, +{33,20,"PWR_ALARM_Q3Q4",0x2600}, +{34,21,"PWR_ALARM_Q5Q6",0x1B80}, +{35,22,"LOOP_CLOSURE_FILTER",0x8000}, +{36,23,"RING_TRIP_FILTER",0x0320}, +{37,24,"TERM_LP_POLE_Q1Q2",0x008C}, +{38,25,"TERM_LP_POLE_Q3Q4",0x0100}, +{39,26,"TERM_LP_POLE_Q5Q6",0x0010}, +{40,27,"CM_BIAS_RINGING",0x0C00}, +{41,64,"DCDC_MIN_V",0x0C00}, +{42,255,"DCDC_XTRA",0x1000}, +{43,66,"LOOP_CLOSE_TRES_LOW",0x1000}, +}; + +static struct fxo_mode { + char *name; + /* FXO */ + int ohs; + int ohs2; + int rz; + int rt; + int ilim; + int dcv; + int mini; + int acim; + int ring_osc; + int ring_x; +} fxo_modes[] = +{ + { "FCC", 0, 0, 0, 1, 0, 0x3, 0, 0, }, /* US, Canada */ + { "TBR21", 0, 0, 0, 0, 1, 0x3, 0, 0x2, 0x7e6c, 0x023a, }, + /* Austria, Belgium, Denmark, Finland, France, Germany, + Greece, Iceland, Ireland, Italy, Luxembourg, Netherlands, + Norway, Portugal, Spain, Sweden, Switzerland, and UK */ + { "ARGENTINA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "AUSTRALIA", 1, 0, 0, 0, 0, 0, 0x3, 0x3, }, + { "AUSTRIA", 0, 1, 0, 0, 1, 0x3, 0, 0x3, }, + { "BAHRAIN", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "BELGIUM", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "BRAZIL", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "BULGARIA", 0, 0, 0, 0, 1, 0x3, 0x0, 0x3, }, + { "CANADA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "CHILE", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "CHINA", 0, 0, 0, 0, 0, 0, 0x3, 0xf, }, + { "COLUMBIA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "CROATIA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "CYRPUS", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "CZECH", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "DENMARK", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "ECUADOR", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "EGYPT", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "ELSALVADOR", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "FINLAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "FRANCE", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "GERMANY", 0, 1, 0, 0, 1, 0x3, 0, 0x3, }, + { "GREECE", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "GUAM", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "HONGKONG", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "HUNGARY", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "ICELAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "INDIA", 0, 0, 0, 0, 0, 0x3, 0, 0x4, }, + { "INDONESIA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "IRELAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "ISRAEL", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "ITALY", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "JAPAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "JORDAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "KAZAKHSTAN", 0, 0, 0, 0, 0, 0x3, 0, }, + { "KUWAIT", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "LATVIA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "LEBANON", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "LUXEMBOURG", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "MACAO", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "MALAYSIA", 0, 0, 0, 0, 0, 0, 0x3, 0, }, /* Current loop >= 20ma */ + { "MALTA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "MEXICO", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "MOROCCO", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "NETHERLANDS", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "NEWZEALAND", 0, 0, 0, 0, 0, 0x3, 0, 0x4, }, + { "NIGERIA", 0, 0, 0, 0, 0x1, 0x3, 0, 0x2, }, + { "NORWAY", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "OMAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "PAKISTAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "PERU", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "PHILIPPINES", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "POLAND", 0, 0, 1, 1, 0, 0x3, 0, 0, }, + { "PORTUGAL", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "ROMANIA", 0, 0, 0, 0, 0, 3, 0, 0, }, + { "RUSSIA", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "SAUDIARABIA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "SINGAPORE", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "SLOVAKIA", 0, 0, 0, 0, 0, 0x3, 0, 0x3, }, + { "SLOVENIA", 0, 0, 0, 0, 0, 0x3, 0, 0x2, }, + { "SOUTHAFRICA", 1, 0, 1, 0, 0, 0x3, 0, 0x3, }, + { "SOUTHKOREA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "SPAIN", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "SWEDEN", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "SWITZERLAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "SYRIA", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "TAIWAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "THAILAND", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "UAE", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "UK", 0, 1, 0, 0, 1, 0x3, 0, 0x5, }, + { "USA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "YEMEN", 0, 0, 0, 0, 0, 0x3, 0, 0, }, +}; + +#ifdef STANDALONE_ZAPATA +#include "zaptel.h" +#else +#include +#endif + +#ifdef LINUX26 +#include +#endif + +#define NUM_FXO_REGS 60 + +#define WC_MAX_IFACES 128 + +#define DELAY 0x0 /* 30 = 15 cycles, 10 = 8 cycles, 0 = 3 cycles */ +#define WC_OFFSET 4 /* Offset between transmit and receive, in bytes. */ +#define WC_SYNCFLAG 0xca1ef1ac + +#define WC_CNTL 0x00 +#define WC_OPER 0x01 +#define WC_AUXC 0x02 +#define WC_AUXD 0x03 +#define WC_MASK0 0x04 +#define WC_MASK1 0x05 +#define WC_INTSTAT 0x06 +#define WC_AUXR 0x07 + +#define WC_DMAWS 0x08 +#define WC_DMAWI 0x0c +#define WC_DMAWE 0x10 +#define WC_DMARS 0x18 +#define WC_DMARI 0x1c +#define WC_DMARE 0x20 + +#define WC_AUXFUNC 0x2b +#define WC_SERCTL 0x2d +#define WC_FSCDELAY 0x2f + +#define WC_REGBASE 0xc0 + +#define WC_VER 0x0 +#define WC_CS 0x1 +#define WC_SPICTRL 0x2 +#define WC_SPIDATA 0x3 + +#define BIT_SPI_BYHW (1 << 0) +#define BIT_SPI_BUSY (1 << 1) // 0=can read/write spi, 1=spi working. +#define BIT_SPI_START (1 << 2) + + +#define BIT_LED_CLK (1 << 0) // MiaoLin add to control the led. +#define BIT_LED_DATA (1 << 1) // MiaoLin add to control the led. + +#define BIT_CS (1 << 2) +#define BIT_SCLK (1 << 3) +#define BIT_SDI (1 << 4) +#define BIT_SDO (1 << 5) + +#define FLAG_EMPTY 0 +#define FLAG_WRITE 1 +#define FLAG_READ 2 + +/* the constants below control the 'debounce' periods enforced by the + check_hook routines; these routines are called once every 4 interrupts + (the interrupt cycles around the four modules), so the periods are + specified in _4 millisecond_ increments +*/ +#define RING_DEBOUNCE 4 /* Ringer Debounce (64 ms) */ +#define DEFAULT_BATT_DEBOUNCE 4 /* Battery debounce (64 ms) */ +#define POLARITY_DEBOUNCE 4 /* Polarity debounce (64 ms) */ +#define DEFAULT_BATT_THRESH 3 /* Anything under this is "no battery" */ + +#define OHT_TIMER 6000 /* How long after RING to retain OHT */ + +#define FLAG_3215 (1 << 0) + +//modify by MiaoLin from 4 to 12; +//#define NUM_CARDS 4 +#define NUM_CARDS 12 +#define NUM_FLAG 4 //number of flag channels. + +// if you want to record the last 8 sec voice before the driver unload, uncomment it and rebuild. +//#define TEST_LOG_INCOME_VOICE + +#define MAX_ALARMS 10 + +#define MOD_TYPE_FXS 0 +#define MOD_TYPE_FXO 1 + +#define MINPEGTIME 10 * 8 /* 30 ms peak to peak gets us no more than 100 Hz */ +#define PEGTIME 50 * 8 /* 50ms peak to peak gets us rings of 10 Hz or more */ +#define PEGCOUNT 5 /* 5 cycles of pegging means RING */ + +#define NUM_CAL_REGS 12 + +struct calregs { + unsigned char vals[NUM_CAL_REGS]; +}; + +enum proslic_power_warn { + PROSLIC_POWER_UNKNOWN = 0, + PROSLIC_POWER_ON, + PROSLIC_POWER_WARNED, +}; + +#define voc_buffer_size (8000*8) + +struct wctdm { + struct pci_dev *dev; + char *variety; + struct zt_span span; + unsigned char ios; + int usecount; + unsigned int intcount; + int dead; + int pos; + int flags[NUM_CARDS]; + int freeregion; + int alt; + int curcard; + int cardflag; /* Bit-map of present cards */ + enum proslic_power_warn proslic_power; + spinlock_t lock; + + union { + struct { +#ifdef AUDIO_RINGCHECK + unsigned int pegtimer; + int pegcount; + int peg; + int ring; +#else + int wasringing; +#endif + int ringdebounce; + int offhook; + int battdebounce; + int nobatttimer; + int battery; + int lastpol; + int polarity; + int polaritydebounce; + } fxo; + struct { + int oldrxhook; + int debouncehook; + int lastrxhook; + int debounce; + int ohttimer; + int idletxhookstate; /* IDLE changing hook state */ + int lasttxhook; + int palarms; + struct calregs calregs; + } fxs; + } mod[NUM_CARDS]; + + /* Receive hook state and debouncing */ + int modtype[NUM_CARDS]; + unsigned char reg0shadow[NUM_CARDS]; + unsigned char reg1shadow[NUM_CARDS]; + + unsigned long ioaddr; + unsigned long mem_region; /* 32 bit Region allocated to tiger320 */ + unsigned long mem_len; /* Length of 32 bit region */ + volatile unsigned long mem32; /* Virtual representation of 32 bit memory area */ + + dma_addr_t readdma; + dma_addr_t writedma; + volatile unsigned char *writechunk; /* Double-word aligned write memory */ + volatile unsigned char *readchunk; /* Double-word aligned read memory */ + struct zt_chan chans[NUM_CARDS]; + +#ifdef TEST_LOG_INCOME_VOICE + //unsigned char tempo[NUM_CARDS + NUM_FLAG]; + char * voc_buf[NUM_CARDS + NUM_FLAG]; + int voc_ptr[NUM_CARDS + NUM_FLAG]; +#endif + //int offset; + int lastchan; + unsigned short ledstate; + unsigned char fwversion; +}; + + +struct wctdm_desc { + char *name; + int flags; +}; + +static struct wctdm_desc wctdme = { "OpenVox A1200P", 0 }; +static int acim2tiss[16] = { 0x0, 0x1, 0x4, 0x5, 0x7, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x2, 0x0, 0x3 }; + +static struct wctdm *ifaces[WC_MAX_IFACES]; + +static void wctdm_release(struct wctdm *wc); + +static int battdebounce = DEFAULT_BATT_DEBOUNCE; +static int battthresh = DEFAULT_BATT_THRESH; +static int debug = 0; +//static int debug = 1; +static int robust = 0; +static int timingonly = 0; +static int lowpower = 0; +static int boostringer = 0; +static int _opermode = 0; +static char *opermode = "FCC"; +static int fxshonormode = 0; +static int alawoverride = 0; +static int spibyhw = 1; // MiaoLin add; +static int usememio = 1; + +static int wctdm_init_proslic(struct wctdm *wc, int card, int fast , int manual, int sane); + +static void wctdm_set_led(struct wctdm* wc, int card, int onoff) +{ + int i; + unsigned char c; + + wc->ledstate &= ~(0x01<ledstate |= (onoff<ioaddr + WC_AUXD)&~BIT_LED_CLK)|BIT_LED_DATA; + outb( c, wc->ioaddr + WC_AUXD); + for(i=NUM_CARDS-1; i>=0; i--) + { + if(wc->ledstate & (0x0001<fwversion == 0x11) + c &= ~BIT_LED_DATA; + else + c |= BIT_LED_DATA; + else + if(wc->fwversion == 0x11) + c |= BIT_LED_DATA; + else + c &= ~BIT_LED_DATA; + + outb( c, wc->ioaddr + WC_AUXD); + outb( c|BIT_LED_CLK, wc->ioaddr + WC_AUXD); + outb( (c&~BIT_LED_CLK)|BIT_LED_DATA, wc->ioaddr + WC_AUXD); + } +} + + +static inline void wctdm_transmitprep(struct wctdm *wc, unsigned char ints) +{ + int x, y, chan_offset, pos; + volatile unsigned char *txbuf; + + if (ints & 0x04 /*0x01*/) + /* Write is at interrupt address. Start writing from normal offset */ + txbuf = wc->writechunk; + else + txbuf = wc->writechunk + ZT_CHUNKSIZE * (NUM_CARDS+NUM_FLAG); + + /* Calculate Transmission */ + zt_transmit(&wc->span); + + if(wc->lastchan == -1) // not in sync. + return; + + chan_offset = (wc->lastchan*4 + 4 ) % (NUM_CARDS+NUM_FLAG); + + //for (x=0;xoffset;x++) + // txbuf[x] = wc->tempo[x]; + for (y=0;yoffset; + // /* Put channel number as outgoing data */ + // if (pos < (NUM_CARDS+NUM_FLAG) * ZT_CHUNKSIZE) + // txbuf[pos] = wc->chans[x].writechunk[y]; + // else + // wc->tempo[pos - (NUM_CARDS+NUM_FLAG) * ZT_CHUNKSIZE] = wc->chans[x].writechunk[y]; + //} + //printk("\n"); + for (x=0;x<(NUM_CARDS+NUM_FLAG);x++) { + pos = y * (NUM_CARDS+NUM_FLAG) + ((x + chan_offset + NUM_CARDS+NUM_FLAG /*+ wc->offset*/ - WC_OFFSET)&0x0f); + if(xchans[x].writechunk[y]; + else + txbuf[pos] = 0; + //if(x==2) + // txbuf[pos] = 0x55;//trans_count; + //else + // txbuf[pos] = 0; + } + + /*for (x=0;x<(NUM_CARDS+NUM_FLAG);x++) { + pos = y * (NUM_CARDS+NUM_FLAG) + x + chan_offset + wc->offset - WC_OFFSET; + if ( pos<(NUM_CARDS+NUM_FLAG)*ZT_CHUNKSIZE ) + { + if(xchans[x].writechunk[y]; + else + txbuf[pos] = 0; + } + else + { + if(xtempo[pos - (NUM_CARDS+NUM_FLAG) * ZT_CHUNKSIZE] = wc->chans[x].writechunk[y]; + else + wc->tempo[pos - (NUM_CARDS+NUM_FLAG) * ZT_CHUNKSIZE] = 0; + } + }*/ +#endif + } + +} + +#ifdef AUDIO_RINGCHECK +static inline void ring_check(struct wctdm *wc, int card) +{ + int x; + short sample; + if (wc->modtype[card] != MOD_TYPE_FXO) + return; + wc->mod[card].fxo.pegtimer += ZT_CHUNKSIZE; + for (x=0;xchans[card].readchunk[x], (&(wc->chans[card]))); + if ((sample > 10000) && (wc->mod[card].fxo.peg != 1)) { + if (debug > 1) printk("High peg!\n"); + if ((wc->mod[card].fxo.pegtimer < PEGTIME) && (wc->mod[card].fxo.pegtimer > MINPEGTIME)) + wc->mod[card].fxo.pegcount++; + wc->mod[card].fxo.pegtimer = 0; + wc->mod[card].fxo.peg = 1; + } else if ((sample < -10000) && (wc->mod[card].fxo.peg != -1)) { + if (debug > 1) printk("Low peg!\n"); + if ((wc->mod[card].fxo.pegtimer < (PEGTIME >> 2)) && (wc->mod[card].fxo.pegtimer > (MINPEGTIME >> 2))) + wc->mod[card].fxo.pegcount++; + wc->mod[card].fxo.pegtimer = 0; + wc->mod[card].fxo.peg = -1; + } + } + if (wc->mod[card].fxo.pegtimer > PEGTIME) { + /* Reset pegcount if our timer expires */ + wc->mod[card].fxo.pegcount = 0; + } + /* Decrement debouncer if appropriate */ + if (wc->mod[card].fxo.ringdebounce) + wc->mod[card].fxo.ringdebounce--; + if (!wc->mod[card].fxo.offhook && !wc->mod[card].fxo.ringdebounce) { + if (!wc->mod[card].fxo.ring && (wc->mod[card].fxo.pegcount > PEGCOUNT)) { + /* It's ringing */ + if (debug) + printk("RING on %d/%d!\n", wc->span.spanno, card + 1); + if (!wc->mod[card].fxo.offhook) + zt_hooksig(&wc->chans[card], ZT_RXSIG_RING); + wc->mod[card].fxo.ring = 1; + } + if (wc->mod[card].fxo.ring && !wc->mod[card].fxo.pegcount) { + /* No more ring */ + if (debug) + printk("NO RING on %d/%d!\n", wc->span.spanno, card + 1); + zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK); + wc->mod[card].fxo.ring = 0; + } + } +} +#endif + + +static inline void wctdm_receiveprep(struct wctdm *wc, unsigned char ints) +{ + volatile unsigned char *rxbuf; + int x, y, chan_offset; + + + if (ints & /*0x08*/0x04) + /* Read is at interrupt address. Valid data is available at normal offset */ + rxbuf = wc->readchunk; + else + rxbuf = wc->readchunk + ZT_CHUNKSIZE * (NUM_CARDS+NUM_FLAG); + + //search for the flag channel + for(x=0; x<4; x++) + { + //printk("0x%08x ", *(int*)(rxbuf+x*4)); + if( *(int*)(rxbuf+x*4) == WC_SYNCFLAG) + { + //printk(" found at %d ", x); + break; + } + } + + if(x==4) + { + printk("buffer sync misseed!\n"); + wc->lastchan = -1; + return; + } + else if(wc->lastchan != x) + { + + printk("buffer re-sync occur from %d to %d\n", wc->lastchan, x); + wc->lastchan = x; + } + chan_offset = (wc->lastchan*4 + 4 ) % (NUM_CARDS+NUM_FLAG); + + for (x=0;xcardflag & (1 << y)) + wc->chans[y].readchunk[x] = rxbuf[(NUM_CARDS+NUM_FLAG) * x + ((y + chan_offset ) & 0x0f)]; +#ifdef TEST_LOG_INCOME_VOICE + wc->voc_buf[y][wc->voc_ptr[y]] = rxbuf[(NUM_CARDS+NUM_FLAG) * x + ((y + chan_offset) & 0x0f)]; + wc->voc_ptr[y]++; + if(wc->voc_ptr[y] >= voc_buffer_size) + wc->voc_ptr[y] = 0; +#endif + } + +#endif + } +#ifdef AUDIO_RINGCHECK + for (x=0;xcards;x++) + ring_check(wc, x); +#endif + /* XXX We're wasting 8 taps. We should get closer :( */ + for (x = 0; x < NUM_CARDS; x++) { + if (wc->cardflag & (1 << x)) + zt_ec_chunk(&wc->chans[x], wc->chans[x].readchunk, wc->chans[x].writechunk); + } + zt_receive(&wc->span); +} + +static void wctdm_stop_dma(struct wctdm *wc); +static void wctdm_reset_tdm(struct wctdm *wc); +static void wctdm_restart_dma(struct wctdm *wc); + + +static unsigned char __wctdm_getcreg(struct wctdm *wc, unsigned char reg); +static void __wctdm_setcreg(struct wctdm *wc, unsigned char reg, unsigned char val); + + +static inline void __write_8bits(struct wctdm *wc, unsigned char bits) +{ + if(spibyhw == 0) + { + int x; + /* Drop chip select */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + wc->ios &= ~BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + for (x=0;x<8;x++) { + /* Send out each bit, MSB first, drop SCLK as we do so */ + if (bits & 0x80) + wc->ios |= BIT_SDI; + else + wc->ios &= ~BIT_SDI; + wc->ios &= ~BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Now raise SCLK high again and repeat */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + bits <<= 1; + } + /* Finally raise CS back high again */ + wc->ios |= BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + } + else + { + __wctdm_setcreg(wc, WC_SPIDATA, bits); + __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW | BIT_SPI_START); + while ((__wctdm_getcreg(wc, WC_SPICTRL) & BIT_SPI_BUSY) != 0); + __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW); + } +} + + +static inline void __reset_spi(struct wctdm *wc) +{ + __wctdm_setcreg(wc, WC_SPICTRL, 0); + + /* Drop chip select and clock once and raise and clock once */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + wc->ios &= ~BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + wc->ios |= BIT_SDI; + wc->ios &= ~BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Now raise SCLK high again and repeat */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Finally raise CS back high again */ + wc->ios |= BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Clock again */ + wc->ios &= ~BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Now raise SCLK high again and repeat */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + + __wctdm_setcreg(wc, WC_SPICTRL, spibyhw); + +} + +static inline unsigned char __read_8bits(struct wctdm *wc) +{ + unsigned char res=0, c; + int x; + if(spibyhw == 0) + { + wc->ios &= ~BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Drop chip select */ + wc->ios &= ~BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + for (x=0;x<8;x++) { + res <<= 1; + /* Get SCLK */ + wc->ios &= ~BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + /* Read back the value */ + c = inb(wc->ioaddr + WC_AUXR); + if (c & BIT_SDO) + res |= 1; + /* Now raise SCLK high again */ + wc->ios |= BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + } + /* Finally raise CS back high again */ + wc->ios |= BIT_CS; + outb(wc->ios, wc->ioaddr + WC_AUXD); + wc->ios &= ~BIT_SCLK; + outb(wc->ios, wc->ioaddr + WC_AUXD); + } + else + { + __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW | BIT_SPI_START); + while ((__wctdm_getcreg(wc, WC_SPICTRL) & BIT_SPI_BUSY) != 0); + res = __wctdm_getcreg(wc, WC_SPIDATA); + __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW); + } + + /* And return our result */ + return res; +} + +static void __wctdm_setcreg_mem(struct wctdm *wc, unsigned char reg, unsigned char val) +{ + unsigned int *p = (unsigned int*)(wc->mem32 + WC_REGBASE + ((reg & 0xf) << 2)); + *p = val; +} + +static unsigned char __wctdm_getcreg_mem(struct wctdm *wc, unsigned char reg) +{ + unsigned int *p = (unsigned int*)(wc->mem32 + WC_REGBASE + ((reg & 0xf) << 2)); + return (*p)&0x00ff; +} + + +static void __wctdm_setcreg(struct wctdm *wc, unsigned char reg, unsigned char val) +{ + if(usememio) + __wctdm_setcreg_mem(wc, reg, val); + else + outb(val, wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2)); +} + +static unsigned char __wctdm_getcreg(struct wctdm *wc, unsigned char reg) +{ + if(usememio) + return __wctdm_getcreg_mem(wc, reg); + else + return inb(wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2)); +} + +static inline void __wctdm_setcard(struct wctdm *wc, int card) +{ + if (wc->curcard != card) { + __wctdm_setcreg(wc, WC_CS, card); + wc->curcard = card; + //printk("Select card %d\n", card); + } +} + +static void __wctdm_setreg(struct wctdm *wc, int card, unsigned char reg, unsigned char value) +{ + __wctdm_setcard(wc, card); + if (wc->modtype[card] == MOD_TYPE_FXO) { + __write_8bits(wc, 0x20); + __write_8bits(wc, reg & 0x7f); + } else { + __write_8bits(wc, reg & 0x7f); + } + __write_8bits(wc, value); +} + +static void wctdm_setreg(struct wctdm *wc, int card, unsigned char reg, unsigned char value) +{ + unsigned long flags; + spin_lock_irqsave(&wc->lock, flags); + __wctdm_setreg(wc, card, reg, value); + spin_unlock_irqrestore(&wc->lock, flags); +} + +static unsigned char __wctdm_getreg(struct wctdm *wc, int card, unsigned char reg) +{ + __wctdm_setcard(wc, card); + if (wc->modtype[card] == MOD_TYPE_FXO) { + __write_8bits(wc, 0x60); + __write_8bits(wc, reg & 0x7f); + } else { + __write_8bits(wc, reg | 0x80); + } + return __read_8bits(wc); +} + +static inline void reset_spi(struct wctdm *wc, int card) +{ + unsigned long flags; + spin_lock_irqsave(&wc->lock, flags); + __wctdm_setcard(wc, card); + __reset_spi(wc); + __reset_spi(wc); + spin_unlock_irqrestore(&wc->lock, flags); +} + +static unsigned char wctdm_getreg(struct wctdm *wc, int card, unsigned char reg) +{ + unsigned long flags; + unsigned char res; + spin_lock_irqsave(&wc->lock, flags); + res = __wctdm_getreg(wc, card, reg); + spin_unlock_irqrestore(&wc->lock, flags); + return res; +} + +static int __wait_access(struct wctdm *wc, int card) +{ + unsigned char data = 0; + long origjiffies; + int count = 0; + + #define MAX 6000 /* attempts */ + + + origjiffies = jiffies; + /* Wait for indirect access */ + while (count++ < MAX) + { + data = __wctdm_getreg(wc, card, I_STATUS); + + if (!data) + return 0; + + } + + if(count > (MAX-1)) printk(" ##### Loop error (%02x) #####\n", data); + + return 0; +} + +static unsigned char translate_3215(unsigned char address) +{ + int x; + for (x=0;xflags[card] & FLAG_3215) { + address = translate_3215(address); + if (address == 255) + return 0; + } + spin_lock_irqsave(&wc->lock, flags); + if(!__wait_access(wc, card)) { + __wctdm_setreg(wc, card, IDA_LO,(unsigned char)(data & 0xFF)); + __wctdm_setreg(wc, card, IDA_HI,(unsigned char)((data & 0xFF00)>>8)); + __wctdm_setreg(wc, card, IAA,address); + res = 0; + }; + spin_unlock_irqrestore(&wc->lock, flags); + return res; +} + +static int wctdm_proslic_getreg_indirect(struct wctdm *wc, int card, unsigned char address) +{ + unsigned long flags; + int res = -1; + char *p=NULL; + /* Translate 3215 addresses */ + if (wc->flags[card] & FLAG_3215) { + address = translate_3215(address); + if (address == 255) + return 0; + } + spin_lock_irqsave(&wc->lock, flags); + if (!__wait_access(wc, card)) { + __wctdm_setreg(wc, card, IAA, address); + if (!__wait_access(wc, card)) { + unsigned char data1, data2; + data1 = __wctdm_getreg(wc, card, IDA_LO); + data2 = __wctdm_getreg(wc, card, IDA_HI); + res = data1 | (data2 << 8); + } else + p = "Failed to wait inside\n"; + } else + p = "failed to wait\n"; + spin_unlock_irqrestore(&wc->lock, flags); + if (p) + printk(p); + return res; +} + +static int wctdm_proslic_init_indirect_regs(struct wctdm *wc, int card) +{ + unsigned char i; + + for (i=0; iflags[card] & FLAG_3215) || (indirect_regs[i].altaddr != 255))) + { + printk("!!!!!!! %s iREG %X = %X should be %X\n", + indirect_regs[i].name,indirect_regs[i].address,j,initial ); + passed = 0; + } + } + + if (passed) { + if (debug) + printk("Init Indirect Registers completed successfully.\n"); + } else { + printk(" !!!!! Init Indirect Registers UNSUCCESSFULLY.\n"); + return -1; + } + return 0; +} + +static inline void wctdm_proslic_recheck_sanity(struct wctdm *wc, int card) +{ + int res; + /* Check loopback */ + res = wc->reg1shadow[card]; + if (!res && (res != wc->mod[card].fxs.lasttxhook)) { + res = wctdm_getreg(wc, card, 8); + if (res) { + printk("Ouch, part reset, quickly restoring reality (%d)\n", card); + wctdm_init_proslic(wc, card, 1, 0, 1); + } else { + if (wc->mod[card].fxs.palarms++ < MAX_ALARMS) { + printk("Power alarm on module %d, resetting!\n", card + 1); + if (wc->mod[card].fxs.lasttxhook == 4) + wc->mod[card].fxs.lasttxhook = 1; + wctdm_setreg(wc, card, 64, wc->mod[card].fxs.lasttxhook); + } else { + if (wc->mod[card].fxs.palarms == MAX_ALARMS) + printk("Too many power alarms on card %d, NOT resetting!\n", card + 1); + } + } + } +} + +static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) +{ +#ifndef AUDIO_RINGCHECK + unsigned char res; +#endif + signed char b; + int poopy = 0; + /* Try to track issues that plague slot one FXO's */ + b = wc->reg0shadow[card]; + if ((b & 0x2) || !(b & 0x8)) { + /* Not good -- don't look at anything else */ + if (debug) + printk("Poopy (%02x) on card %d!\n", b, card + 1); + poopy++; + } + b &= 0x9b; + if (wc->mod[card].fxo.offhook) { + if (b != 0x9) + wctdm_setreg(wc, card, 5, 0x9); + } else { + if (b != 0x8) + wctdm_setreg(wc, card, 5, 0x8); + } + if (poopy) + return; +#ifndef AUDIO_RINGCHECK + if (!wc->mod[card].fxo.offhook) { + res = wc->reg0shadow[card]; + if ((res & 0x60) && wc->mod[card].fxo.battery) { + wc->mod[card].fxo.ringdebounce += (ZT_CHUNKSIZE * 16); + if (wc->mod[card].fxo.ringdebounce >= ZT_CHUNKSIZE * 64) { + if (!wc->mod[card].fxo.wasringing) { + wc->mod[card].fxo.wasringing = 1; + zt_hooksig(&wc->chans[card], ZT_RXSIG_RING); + if (debug) + printk("RING on %d/%d!\n", wc->span.spanno, card + 1); + } + wc->mod[card].fxo.ringdebounce = ZT_CHUNKSIZE * 64; + } + } else { + wc->mod[card].fxo.ringdebounce -= ZT_CHUNKSIZE * 4; + if (wc->mod[card].fxo.ringdebounce <= 0) { + if (wc->mod[card].fxo.wasringing) { + wc->mod[card].fxo.wasringing = 0; + zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK); + if (debug) + printk("NO RING on %d/%d!\n", wc->span.spanno, card + 1); + } + wc->mod[card].fxo.ringdebounce = 0; + } + + } + } +#endif + b = wc->reg1shadow[card]; +#if 0 + { + static int count = 0; + if (!(count++ % 100)) { + printk("Card %d: Voltage: %d Debounce %d\n", card + 1, + b, wc->mod[card].fxo.battdebounce); + } + } +#endif + if (abs(b) < battthresh) { + wc->mod[card].fxo.nobatttimer++; +#if 0 + if (wc->mod[card].fxo.battery) + printk("Battery loss: %d (%d debounce)\n", b, wc->mod[card].fxo.battdebounce); +#endif + if (wc->mod[card].fxo.battery && !wc->mod[card].fxo.battdebounce) { + if (debug) + printk("NO BATTERY on %d/%d!\n", wc->span.spanno, card + 1); + wc->mod[card].fxo.battery = 0; +#ifdef JAPAN + if ((!wc->ohdebounce) && wc->offhook) { + zt_hooksig(&wc->chans[card], ZT_RXSIG_ONHOOK); + if (debug) + printk("Signalled On Hook\n"); +#ifdef ZERO_BATT_RING + wc->onhook++; +#endif + } +#else + zt_hooksig(&wc->chans[card], ZT_RXSIG_ONHOOK); +#endif + wc->mod[card].fxo.battdebounce = battdebounce; + } else if (!wc->mod[card].fxo.battery) + wc->mod[card].fxo.battdebounce = battdebounce; + } else if (abs(b) > battthresh) { + if (!wc->mod[card].fxo.battery && !wc->mod[card].fxo.battdebounce) { + if (debug) + printk("BATTERY on %d/%d (%s)!\n", wc->span.spanno, card + 1, + (b < 0) ? "-" : "+"); +#ifdef ZERO_BATT_RING + if (wc->onhook) { + wc->onhook = 0; + zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK); + if (debug) + printk("Signalled Off Hook\n"); + } +#else + zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK); +#endif + wc->mod[card].fxo.battery = 1; + wc->mod[card].fxo.nobatttimer = 0; + wc->mod[card].fxo.battdebounce = battdebounce; + } else if (wc->mod[card].fxo.battery) + wc->mod[card].fxo.battdebounce = battdebounce; + + if (wc->mod[card].fxo.lastpol >= 0) { + if (b < 0) { + wc->mod[card].fxo.lastpol = -1; + wc->mod[card].fxo.polaritydebounce = POLARITY_DEBOUNCE; + } + } + if (wc->mod[card].fxo.lastpol <= 0) { + if (b > 0) { + wc->mod[card].fxo.lastpol = 1; + wc->mod[card].fxo.polaritydebounce = POLARITY_DEBOUNCE; + } + } + } else { + /* It's something else... */ + wc->mod[card].fxo.battdebounce = battdebounce; + } + if (wc->mod[card].fxo.battdebounce) + wc->mod[card].fxo.battdebounce--; + if (wc->mod[card].fxo.polaritydebounce) { + wc->mod[card].fxo.polaritydebounce--; + if (wc->mod[card].fxo.polaritydebounce < 1) { + if (wc->mod[card].fxo.lastpol != wc->mod[card].fxo.polarity) { + if (debug) + printk("%lu Polarity reversed (%d -> %d)\n", jiffies, + wc->mod[card].fxo.polarity, + wc->mod[card].fxo.lastpol); + if (wc->mod[card].fxo.polarity) + zt_qevent_lock(&wc->chans[card], ZT_EVENT_POLARITY); + wc->mod[card].fxo.polarity = wc->mod[card].fxo.lastpol; + } + } + } +} + +static inline void wctdm_proslic_check_hook(struct wctdm *wc, int card) +{ + char res; + int hook; + + /* For some reason we have to debounce the + hook detector. */ + + res = wc->reg0shadow[card]; + hook = (res & 1); + if (hook != wc->mod[card].fxs.lastrxhook) { + /* Reset the debounce (must be multiple of 4ms) */ + wc->mod[card].fxs.debounce = 8 * (4 * 8); +#if 0 + printk("Resetting debounce card %d hook %d, %d\n", card, hook, wc->mod[card].fxs.debounce); +#endif + } else { + if (wc->mod[card].fxs.debounce > 0) { + wc->mod[card].fxs.debounce-= 16 * ZT_CHUNKSIZE; +#if 0 + printk("Sustaining hook %d, %d\n", hook, wc->mod[card].fxs.debounce); +#endif + if (!wc->mod[card].fxs.debounce) { +#if 0 + printk("Counted down debounce, newhook: %d...\n", hook); +#endif + wc->mod[card].fxs.debouncehook = hook; + } + if (!wc->mod[card].fxs.oldrxhook && wc->mod[card].fxs.debouncehook) { + /* Off hook */ +#if 1 + if (debug) +#endif + printk("opvxa1200: Card %d Going off hook\n", card); + zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK); + if (robust) + wctdm_init_proslic(wc, card, 1, 0, 1); + wc->mod[card].fxs.oldrxhook = 1; + + } else if (wc->mod[card].fxs.oldrxhook && !wc->mod[card].fxs.debouncehook) { + /* On hook */ +#if 1 + if (debug) +#endif + printk("opvxa1200: Card %d Going on hook\n", card); + zt_hooksig(&wc->chans[card], ZT_RXSIG_ONHOOK); + wc->mod[card].fxs.oldrxhook = 0; + } + } + } + wc->mod[card].fxs.lastrxhook = hook; +} + +ZAP_IRQ_HANDLER(wctdm_interrupt) +{ + struct wctdm *wc = dev_id; + unsigned char ints; + int x, y, z; + int mode; + + ints = inb(wc->ioaddr + WC_INTSTAT); + outb(ints, wc->ioaddr + WC_INTSTAT); + + if (!ints) +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + + if (ints & 0x10) { + /* Stop DMA, wait for watchdog */ + printk("TDM PCI Master abort\n"); + wctdm_stop_dma(wc); +#ifdef LINUX26 + return IRQ_RETVAL(1); +#else + return; +#endif + } + + if (ints & 0x20) { + printk("PCI Target abort\n"); +#ifdef LINUX26 + return IRQ_RETVAL(1); +#else + return; +#endif + } + + for (x=0;x<4*3;x++) { + if (wc->cardflag & (1 << x) && + (wc->modtype[x] == MOD_TYPE_FXS)) { + if (wc->mod[x].fxs.lasttxhook == 0x4) { + /* RINGing, prepare for OHT */ + wc->mod[x].fxs.ohttimer = OHT_TIMER << 3; + if (reversepolarity) + wc->mod[x].fxs.idletxhookstate = 0x6; /* OHT mode when idle */ + else + wc->mod[x].fxs.idletxhookstate = 0x2; + } else { + if (wc->mod[x].fxs.ohttimer) { + wc->mod[x].fxs.ohttimer-= ZT_CHUNKSIZE; + if (!wc->mod[x].fxs.ohttimer) { + if (reversepolarity) + wc->mod[x].fxs.idletxhookstate = 0x5; /* Switch to active */ + else + wc->mod[x].fxs.idletxhookstate = 0x1; + if ((wc->mod[x].fxs.lasttxhook == 0x2) || (wc->mod[x].fxs.lasttxhook = 0x6)) { + /* Apply the change if appropriate */ + if (reversepolarity) + wc->mod[x].fxs.lasttxhook = 0x5; + else + wc->mod[x].fxs.lasttxhook = 0x1; + wctdm_setreg(wc, x, 64, wc->mod[x].fxs.lasttxhook); + } + } + } + } + } + } + + if (ints & 0x0f) { + wc->intcount++; + z = wc->intcount & 0x3; + mode = wc->intcount & 0xc; + for(y=0; y<3; y++) + { + x = z + y*4; + if (wc->cardflag & (1 << x ) ) + { + switch(mode) + { + case 0: + /* Rest */ + break; + case 4: + /* Read first shadow reg */ + if (wc->modtype[x] == MOD_TYPE_FXS) + wc->reg0shadow[x] = wctdm_getreg(wc, x, 68); + else if (wc->modtype[x] == MOD_TYPE_FXO) + wc->reg0shadow[x] = wctdm_getreg(wc, x, 5); + break; + case 8: + /* Read second shadow reg */ + if (wc->modtype[x] == MOD_TYPE_FXS) + wc->reg1shadow[x] = wctdm_getreg(wc, x, 64); + else if (wc->modtype[x] == MOD_TYPE_FXO) + wc->reg1shadow[x] = wctdm_getreg(wc, x, 29); + break; + case 12: + /* Perform processing */ + if (wc->modtype[x] == MOD_TYPE_FXS) { + wctdm_proslic_check_hook(wc, x); + if (!(wc->intcount & 0xf0)) + wctdm_proslic_recheck_sanity(wc, x); + } else if (wc->modtype[x] == MOD_TYPE_FXO) { + wctdm_voicedaa_check_hook(wc, x); + } + break; + } + } + } + if (!(wc->intcount % 10000)) { + /* Accept an alarm once per 10 seconds */ + for (x=0;x<4*3;x++) + if (wc->modtype[x] == MOD_TYPE_FXS) { + if (wc->mod[x].fxs.palarms) + wc->mod[x].fxs.palarms--; + } + } + wctdm_receiveprep(wc, ints); + wctdm_transmitprep(wc, ints); + } +#ifdef LINUX26 + return IRQ_RETVAL(1); +#endif + +} + +static int wctdm_voicedaa_insane(struct wctdm *wc, int card) +{ + int blah; + blah = wctdm_getreg(wc, card, 2); + if (blah != 0x3) + return -2; + blah = wctdm_getreg(wc, card, 11); + if (debug) + printk("VoiceDAA System: %02x\n", blah & 0xf); + return 0; +} + +static int wctdm_proslic_insane(struct wctdm *wc, int card) +{ + int blah,insane_report; + insane_report=0; + + blah = wctdm_getreg(wc, card, 0); + if (debug) + printk("ProSLIC on module %d, product %d, version %d\n", card, (blah & 0x30) >> 4, (blah & 0xf)); + +#if 0 + if ((blah & 0x30) >> 4) { + printk("ProSLIC on module %d is not a 3210.\n", card); + return -1; + } +#endif + if (((blah & 0xf) == 0) || ((blah & 0xf) == 0xf)) { + /* SLIC not loaded */ + return -1; + } + if ((blah & 0xf) < 2) { + printk("ProSLIC 3210 version %d is too old\n", blah & 0xf); + return -1; + } + if ((blah & 0xf) == 2) { + /* ProSLIC 3215, not a 3210 */ + wc->flags[card] |= FLAG_3215; + } + blah = wctdm_getreg(wc, card, 8); + if (blah != 0x2) { + printk("ProSLIC on module %d insane (1) %d should be 2\n", card, blah); + return -1; + } else if ( insane_report) + printk("ProSLIC on module %d Reg 8 Reads %d Expected is 0x2\n",card,blah); + + blah = wctdm_getreg(wc, card, 64); + if (blah != 0x0) { + printk("ProSLIC on module %d insane (2)\n", card); + return -1; + } else if ( insane_report) + printk("ProSLIC on module %d Reg 64 Reads %d Expected is 0x0\n",card,blah); + + blah = wctdm_getreg(wc, card, 11); + if (blah != 0x33) { + printk("ProSLIC on module %d insane (3)\n", card); + return -1; + } else if ( insane_report) + printk("ProSLIC on module %d Reg 11 Reads %d Expected is 0x33\n",card,blah); + + /* Just be sure it's setup right. */ + wctdm_setreg(wc, card, 30, 0); + + if (debug) + printk("ProSLIC on module %d seems sane.\n", card); + return 0; +} + +static int wctdm_proslic_powerleak_test(struct wctdm *wc, int card) +{ + unsigned long origjiffies; + unsigned char vbat; + + /* Turn off linefeed */ + wctdm_setreg(wc, card, 64, 0); + + /* Power down */ + wctdm_setreg(wc, card, 14, 0x10); + + /* Wait for one second */ + origjiffies = jiffies; + + while((vbat = wctdm_getreg(wc, card, 82)) > 0x6) { + if ((jiffies - origjiffies) >= (HZ/2)) + break;; + } + + if (vbat < 0x06) { + printk("Excessive leakage detected on module %d: %d volts (%02x) after %d ms\n", card, + 376 * vbat / 1000, vbat, (int)((jiffies - origjiffies) * 1000 / HZ)); + return -1; + } else if (debug) { + printk("Post-leakage voltage: %d volts\n", 376 * vbat / 1000); + } + return 0; +} + +static int wctdm_powerup_proslic(struct wctdm *wc, int card, int fast) +{ + unsigned char vbat; + unsigned long origjiffies; + int lim; + + /* Set period of DC-DC converter to 1/64 khz */ + wctdm_setreg(wc, card, 92, 0xff /* was 0xff */); + + /* Wait for VBat to powerup */ + origjiffies = jiffies; + + /* Disable powerdown */ + wctdm_setreg(wc, card, 14, 0); + + /* If fast, don't bother checking anymore */ + if (fast) + return 0; + + while((vbat = wctdm_getreg(wc, card, 82)) < 0xc0) { + /* Wait no more than 500ms */ + if ((jiffies - origjiffies) > HZ/2) { + break; + } + } + + if (vbat < 0xc0) { + if (wc->proslic_power == PROSLIC_POWER_UNKNOWN) + printk("ProSLIC on module %d failed to powerup within %d ms (%d mV only)\n\n -- DID YOU REMEMBER TO PLUG IN THE HD POWER CABLE TO THE A1200P??\n", + card, (int)(((jiffies - origjiffies) * 1000 / HZ)), + vbat * 375); + wc->proslic_power = PROSLIC_POWER_WARNED; + return -1; + } else if (debug) { + printk("ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n", + card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ))); + } + wc->proslic_power = PROSLIC_POWER_ON; + + /* Proslic max allowed loop current, reg 71 LOOP_I_LIMIT */ + /* If out of range, just set it to the default value */ + lim = (loopcurrent - 20) / 3; + if ( loopcurrent > 41 ) { + lim = 0; + if (debug) + printk("Loop current out of range! Setting to default 20mA!\n"); + } + else if (debug) + printk("Loop current set to %dmA!\n",(lim*3)+20); + wctdm_setreg(wc,card,LOOP_I_LIMIT,lim); + + /* Engage DC-DC converter */ + wctdm_setreg(wc, card, 93, 0x19 /* was 0x19 */); +#if 0 + origjiffies = jiffies; + while(0x80 & wctdm_getreg(wc, card, 93)) { + if ((jiffies - origjiffies) > 2 * HZ) { + printk("Timeout waiting for DC-DC calibration on module %d\n", card); + return -1; + } + } + +#if 0 + /* Wait a full two seconds */ + while((jiffies - origjiffies) < 2 * HZ); + + /* Just check to be sure */ + vbat = wctdm_getreg(wc, card, 82); + printk("ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n", + card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ))); +#endif +#endif + return 0; + +} + +static int wctdm_proslic_manual_calibrate(struct wctdm *wc, int card){ + unsigned long origjiffies; + unsigned char i; + + wctdm_setreg(wc, card, 21, 0);//(0) Disable all interupts in DR21 + wctdm_setreg(wc, card, 22, 0);//(0)Disable all interupts in DR21 + wctdm_setreg(wc, card, 23, 0);//(0)Disable all interupts in DR21 + wctdm_setreg(wc, card, 64, 0);//(0) + + wctdm_setreg(wc, card, 97, 0x18); //(0x18)Calibrations without the ADC and DAC offset and without common mode calibration. + wctdm_setreg(wc, card, 96, 0x47); //(0x47) Calibrate common mode and differential DAC mode DAC + ILIM + + origjiffies=jiffies; + while( wctdm_getreg(wc,card,96)!=0 ){ + if((jiffies-origjiffies)>80) + return -1; + } +//Initialized DR 98 and 99 to get consistant results. +// 98 and 99 are the results registers and the search should have same intial conditions. + +/*******************************The following is the manual gain mismatch calibration****************************/ +/*******************************This is also available as a function *******************************************/ + // Delay 10ms + origjiffies=jiffies; + while((jiffies-origjiffies)<1); + wctdm_proslic_setreg_indirect(wc, card, 88,0); + wctdm_proslic_setreg_indirect(wc,card,89,0); + wctdm_proslic_setreg_indirect(wc,card,90,0); + wctdm_proslic_setreg_indirect(wc,card,91,0); + wctdm_proslic_setreg_indirect(wc,card,92,0); + wctdm_proslic_setreg_indirect(wc,card,93,0); + + wctdm_setreg(wc, card, 98,0x10); // This is necessary if the calibration occurs other than at reset time + wctdm_setreg(wc, card, 99,0x10); + + for ( i=0x1f; i>0; i--) + { + wctdm_setreg(wc, card, 98,i); + origjiffies=jiffies; + while((jiffies-origjiffies)<4); + if((wctdm_getreg(wc,card,88)) == 0) + break; + } // for + + for ( i=0x1f; i>0; i--) + { + wctdm_setreg(wc, card, 99,i); + origjiffies=jiffies; + while((jiffies-origjiffies)<4); + if((wctdm_getreg(wc,card,89)) == 0) + break; + }//for + +/*******************************The preceding is the manual gain mismatch calibration****************************/ +/**********************************The following is the longitudinal Balance Cal***********************************/ + wctdm_setreg(wc,card,64,1); + while((jiffies-origjiffies)<10); // Sleep 100? + + wctdm_setreg(wc, card, 64, 0); + wctdm_setreg(wc, card, 23, 0x4); // enable interrupt for the balance Cal + wctdm_setreg(wc, card, 97, 0x1); // this is a singular calibration bit for longitudinal calibration + wctdm_setreg(wc, card, 96,0x40); + + wctdm_getreg(wc,card,96); /* Read Reg 96 just cause */ + + wctdm_setreg(wc, card, 21, 0xFF); + wctdm_setreg(wc, card, 22, 0xFF); + wctdm_setreg(wc, card, 23, 0xFF); + + /**The preceding is the longitudinal Balance Cal***/ + return(0); + +} +#if 1 +static int wctdm_proslic_calibrate(struct wctdm *wc, int card) +{ + unsigned long origjiffies; + int x; + /* Perform all calibrations */ + wctdm_setreg(wc, card, 97, 0x1f); + + /* Begin, no speedup */ + wctdm_setreg(wc, card, 96, 0x5f); + + /* Wait for it to finish */ + origjiffies = jiffies; + while(wctdm_getreg(wc, card, 96)) { + if ((jiffies - origjiffies) > 2 * HZ) { + printk("Timeout waiting for calibration of module %d\n", card); + return -1; + } + } + + if (debug) { + /* Print calibration parameters */ + printk("Calibration Vector Regs 98 - 107: \n"); + for (x=98;x<108;x++) { + printk("%d: %02x\n", x, wctdm_getreg(wc, card, x)); + } + } + return 0; +} +#endif + +static void wait_just_a_bit(int foo) +{ + long newjiffies; + newjiffies = jiffies + foo; + while(jiffies < newjiffies); +} + +static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual, int sane) +{ + unsigned char reg16=0, reg26=0, reg30=0, reg31=0; + long newjiffies; + wc->modtype[card] = MOD_TYPE_FXO; + + /* Sanity check the ProSLIC */ + reset_spi(wc, card); + if (!sane && wctdm_voicedaa_insane(wc, card)) + return -2; + + /* Software reset */ + wctdm_setreg(wc, card, 1, 0x80); + + /* Wait just a bit */ + wait_just_a_bit(HZ/10); + + /* Enable PCM, ulaw */ + if (alawoverride) + wctdm_setreg(wc, card, 33, 0x20); + else + wctdm_setreg(wc, card, 33, 0x28); + + /* Set On-hook speed, Ringer impedence, and ringer threshold */ + reg16 |= (fxo_modes[_opermode].ohs << 6); + reg16 |= (fxo_modes[_opermode].rz << 1); + reg16 |= (fxo_modes[_opermode].rt); + wctdm_setreg(wc, card, 16, reg16); + + /* Set DC Termination: + Tip/Ring voltage adjust, minimum operational current, current limitation */ + reg26 |= (fxo_modes[_opermode].dcv << 6); + reg26 |= (fxo_modes[_opermode].mini << 4); + reg26 |= (fxo_modes[_opermode].ilim << 1); + wctdm_setreg(wc, card, 26, reg26); + + /* Set AC Impedence */ + reg30 = (fxo_modes[_opermode].acim); + wctdm_setreg(wc, card, 30, reg30); + + /* Misc. DAA parameters */ + reg31 = 0xa3; + reg31 |= (fxo_modes[_opermode].ohs2 << 3); + wctdm_setreg(wc, card, 31, reg31); + + /* Set Transmit/Receive timeslot */ + //printk("set card %d to %d\n", card, (3-(card%4)) * 8 + (card/4) * 64); + wctdm_setreg(wc, card, 34, (3-(card%4)) * 8 + (card/4) * 64); + wctdm_setreg(wc, card, 35, 0x00); + wctdm_setreg(wc, card, 36, (3-(card%4)) * 8 + (card/4) * 64); + wctdm_setreg(wc, card, 37, 0x00); + + /* Enable ISO-Cap */ + wctdm_setreg(wc, card, 6, 0x00); + + /* Wait 1000ms for ISO-cap to come up */ + newjiffies = jiffies; + newjiffies += 2 * HZ; + while((jiffies < newjiffies) && !(wctdm_getreg(wc, card, 11) & 0xf0)) + wait_just_a_bit(HZ/10); + + if (!(wctdm_getreg(wc, card, 11) & 0xf0)) { + printk("VoiceDAA did not bring up ISO link properly!\n"); + return -1; + } + if (debug) + printk("ISO-Cap is now up, line side: %02x rev %02x\n", + wctdm_getreg(wc, card, 11) >> 4, + (wctdm_getreg(wc, card, 13) >> 2) & 0xf); + /* Enable on-hook line monitor */ + wctdm_setreg(wc, card, 5, 0x08); + + /* NZ -- crank the tx gain up by 7 dB */ + if (!strcmp(fxo_modes[_opermode].name, "NEWZEALAND")) { + printk("Adjusting gain\n"); + wctdm_setreg(wc, card, 38, 0x7); + } + + return 0; + +} + +static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, int sane) +{ + + unsigned short tmp[5]; + unsigned char r19; + int x; + int fxsmode=0; + + + /* By default, don't send on hook */ + if (reversepolarity) + wc->mod[card].fxs.idletxhookstate = 5; + else + wc->mod[card].fxs.idletxhookstate = 1; + + /* Sanity check the ProSLIC */ + if (!sane && wctdm_proslic_insane(wc, card)) + return -2; + + if (sane) { + /* Make sure we turn off the DC->DC converter to prevent anything from blowing up */ + wctdm_setreg(wc, card, 14, 0x10); + } + + if (wctdm_proslic_init_indirect_regs(wc, card)) { + printk(KERN_INFO "Indirect Registers failed to initialize on module %d.\n", card); + return -1; + } + + /* Clear scratch pad area */ + wctdm_proslic_setreg_indirect(wc, card, 97,0); + + /* Clear digital loopback */ + wctdm_setreg(wc, card, 8, 0); + + /* Revision C optimization */ + wctdm_setreg(wc, card, 108, 0xeb); + + /* Disable automatic VBat switching for safety to prevent + Q7 from accidently turning on and burning out. */ + wctdm_setreg(wc, card, 67, 0x17); + + /* Turn off Q7 */ + wctdm_setreg(wc, card, 66, 1); + + /* Flush ProSLIC digital filters by setting to clear, while + saving old values */ + for (x=0;x<5;x++) { + tmp[x] = wctdm_proslic_getreg_indirect(wc, card, x + 35); + wctdm_proslic_setreg_indirect(wc, card, x + 35, 0x8000); + } + + /* Power up the DC-DC converter */ + if (wctdm_powerup_proslic(wc, card, fast)) { + printk("Unable to do INITIAL ProSLIC powerup on module %d\n", card); + return -1; + } + + if (!fast) { + + /* Check for power leaks */ + if (wctdm_proslic_powerleak_test(wc, card)) { + printk("ProSLIC module %d failed leakage test. Check for short circuit\n", card); + } + /* Power up again */ + if (wctdm_powerup_proslic(wc, card, fast)) { + printk("Unable to do FINAL ProSLIC powerup on module %d\n", card); + return -1; + } +#ifndef NO_CALIBRATION + /* Perform calibration */ + if(manual) { + if (wctdm_proslic_manual_calibrate(wc, card)) { + //printk("Proslic failed on Manual Calibration\n"); + if (wctdm_proslic_manual_calibrate(wc, card)) { + printk("Proslic Failed on Second Attempt to Calibrate Manually. (Try -DNO_CALIBRATION in Makefile)\n"); + return -1; + } + printk("Proslic Passed Manual Calibration on Second Attempt\n"); + } + } + else { + if(wctdm_proslic_calibrate(wc, card)) { + //printk("ProSlic died on Auto Calibration.\n"); + if (wctdm_proslic_calibrate(wc, card)) { + printk("Proslic Failed on Second Attempt to Auto Calibrate\n"); + return -1; + } + printk("Proslic Passed Auto Calibration on Second Attempt\n"); + } + } + /* Perform DC-DC calibration */ + wctdm_setreg(wc, card, 93, 0x99); + r19 = wctdm_getreg(wc, card, 107); + if ((r19 < 0x2) || (r19 > 0xd)) { + printk("DC-DC cal has a surprising direct 107 of 0x%02x!\n", r19); + wctdm_setreg(wc, card, 107, 0x8); + } + + /* Save calibration vectors */ + for (x=0;xmod[card].fxs.calregs.vals[x] = wctdm_getreg(wc, card, 96 + x); +#endif + + } else { + /* Restore calibration registers */ + for (x=0;xmod[card].fxs.calregs.vals[x]); + } + /* Calibration complete, restore original values */ + for (x=0;x<5;x++) { + wctdm_proslic_setreg_indirect(wc, card, x + 35, tmp[x]); + } + + if (wctdm_proslic_verify_indirect_regs(wc, card)) { + printk(KERN_INFO "Indirect Registers failed verification.\n"); + return -1; + } + + +#if 0 + /* Disable Auto Power Alarm Detect and other "features" */ + wctdm_setreg(wc, card, 67, 0x0e); + blah = wctdm_getreg(wc, card, 67); +#endif + +#if 0 + if (wctdm_proslic_setreg_indirect(wc, card, 97, 0x0)) { // Stanley: for the bad recording fix + printk(KERN_INFO "ProSlic IndirectReg Died.\n"); + return -1; + } +#endif + + if (alawoverride) + wctdm_setreg(wc, card, 1, 0x20); + else + wctdm_setreg(wc, card, 1, 0x28); + // U-Law 8-bit interface + + //printk("set card %d to %d\n", card, (3-(card%4)) * 8 + (card/4) * 64); + wctdm_setreg(wc, card, 2, (3-(card%4)) * 8 + (card/4) * 64); // Tx Start count low byte 0 + wctdm_setreg(wc, card, 3, 0); // Tx Start count high byte 0 + wctdm_setreg(wc, card, 4, (3-(card%4)) * 8 + (card/4) * 64); // Rx Start count low byte 0 + wctdm_setreg(wc, card, 5, 0); // Rx Start count high byte 0 + wctdm_setreg(wc, card, 18, 0xff); // clear all interrupt + wctdm_setreg(wc, card, 19, 0xff); + wctdm_setreg(wc, card, 20, 0xff); + wctdm_setreg(wc, card, 73, 0x04); + if (fxshonormode) { + fxsmode = acim2tiss[fxo_modes[_opermode].acim]; + wctdm_setreg(wc, card, 10, 0x08 | fxsmode); + if (fxo_modes[_opermode].ring_osc) + wctdm_proslic_setreg_indirect(wc, card, 20, fxo_modes[_opermode].ring_osc); + if (fxo_modes[_opermode].ring_x) + wctdm_proslic_setreg_indirect(wc, card, 21, fxo_modes[_opermode].ring_x); + } + if (lowpower) + wctdm_setreg(wc, card, 72, 0x10); + +#if 0 + wctdm_setreg(wc, card, 21, 0x00); // enable interrupt + wctdm_setreg(wc, card, 22, 0x02); // Loop detection interrupt + wctdm_setreg(wc, card, 23, 0x01); // DTMF detection interrupt +#endif + +#if 0 + /* Enable loopback */ + wctdm_setreg(wc, card, 8, 0x2); + wctdm_setreg(wc, card, 14, 0x0); + wctdm_setreg(wc, card, 64, 0x0); + wctdm_setreg(wc, card, 1, 0x08); +#endif + + /* Beef up Ringing voltage to 89V */ + if (boostringer) { + if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x1d1)) + return -1; + printk("Boosting ringinger on slot %d (89V peak)\n", card + 1); + } else if (lowpower) { + if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x108)) + return -1; + printk("Reducing ring power on slot %d (50V peak)\n", card + 1); + } + wctdm_setreg(wc, card, 64, 0x01); + return 0; +} + + +static int wctdm_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) +{ + struct wctdm_stats stats; + struct wctdm_regs regs; + struct wctdm_regop regop; + struct wctdm_echo_coefs echoregs; + struct wctdm *wc = chan->pvt; + int x; + switch (cmd) { + case ZT_ONHOOKTRANSFER: + if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) + return -EINVAL; + if (get_user(x, (int *)data)) + return -EFAULT; + wc->mod[chan->chanpos - 1].fxs.ohttimer = x << 3; + if (reversepolarity) + wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 0x6; /* OHT mode when idle */ + else + wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 0x2; + if (wc->mod[chan->chanpos - 1].fxs.lasttxhook == 0x1) { + /* Apply the change if appropriate */ + if (reversepolarity) + wc->mod[chan->chanpos - 1].fxs.lasttxhook = 0x6; + else + wc->mod[chan->chanpos - 1].fxs.lasttxhook = 0x2; + wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos - 1].fxs.lasttxhook); + } + break; + case ZT_SETPOLARITY: + if (get_user(x, (int *)data)) + return -EFAULT; + if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) + return -EINVAL; + /* Can't change polarity while ringing or when open */ + if ((wc->mod[chan->chanpos -1 ].fxs.lasttxhook == 0x04) || + (wc->mod[chan->chanpos -1 ].fxs.lasttxhook == 0x00)) + return -EINVAL; + + if ((x && !reversepolarity) || (!x && reversepolarity)) + wc->mod[chan->chanpos - 1].fxs.lasttxhook |= 0x04; + else + wc->mod[chan->chanpos - 1].fxs.lasttxhook &= ~0x04; + wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos - 1].fxs.lasttxhook); + break; + case WCTDM_GET_STATS: + if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) { + stats.tipvolt = wctdm_getreg(wc, chan->chanpos - 1, 80) * -376; + stats.ringvolt = wctdm_getreg(wc, chan->chanpos - 1, 81) * -376; + stats.batvolt = wctdm_getreg(wc, chan->chanpos - 1, 82) * -376; + } else if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) { + stats.tipvolt = (signed char)wctdm_getreg(wc, chan->chanpos - 1, 29) * 1000; + stats.ringvolt = (signed char)wctdm_getreg(wc, chan->chanpos - 1, 29) * 1000; + stats.batvolt = (signed char)wctdm_getreg(wc, chan->chanpos - 1, 29) * 1000; + } else + return -EINVAL; + if (copy_to_user((struct wctdm_stats *)data, &stats, sizeof(stats))) + return -EFAULT; + break; + case WCTDM_GET_REGS: + if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) { + for (x=0;xchanpos -1, x); + for (x=0;xchanpos - 1, x); + } else { + memset(®s, 0, sizeof(regs)); + for (x=0;xchanpos - 1, x); + } + if (copy_to_user((struct wctdm_regs *)data, ®s, sizeof(regs))) + return -EFAULT; + break; + case WCTDM_SET_REG: + if (copy_from_user(®op, (struct wctdm_regop *)data, sizeof(regop))) + return -EFAULT; + if (regop.indirect) { + if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) + return -EINVAL; + printk("Setting indirect %d to 0x%04x on %d\n", regop.reg, regop.val, chan->chanpos); + wctdm_proslic_setreg_indirect(wc, chan->chanpos - 1, regop.reg, regop.val); + } else { + regop.val &= 0xff; + printk("Setting direct %d to %04x on %d\n", regop.reg, regop.val, chan->chanpos); + wctdm_setreg(wc, chan->chanpos - 1, regop.reg, regop.val); + } + break; + case WCTDM_SET_ECHOTUNE: + printk("-- Setting echo registers: \n"); + if (copy_from_user(&echoregs, (struct wctdm_echo_coefs*)data, sizeof(echoregs))) + return -EFAULT; + + if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) { + /* Set the ACIM register */ + wctdm_setreg(wc, chan->chanpos - 1, 30, echoregs.acim); + + /* Set the digital echo canceller registers */ + wctdm_setreg(wc, chan->chanpos - 1, 45, echoregs.coef1); + wctdm_setreg(wc, chan->chanpos - 1, 46, echoregs.coef2); + wctdm_setreg(wc, chan->chanpos - 1, 47, echoregs.coef3); + wctdm_setreg(wc, chan->chanpos - 1, 48, echoregs.coef4); + wctdm_setreg(wc, chan->chanpos - 1, 49, echoregs.coef5); + wctdm_setreg(wc, chan->chanpos - 1, 50, echoregs.coef6); + wctdm_setreg(wc, chan->chanpos - 1, 51, echoregs.coef7); + wctdm_setreg(wc, chan->chanpos - 1, 52, echoregs.coef8); + + printk("-- Set echo registers successfully\n"); + + break; + } else { + return -EINVAL; + + } + break; + default: + return -ENOTTY; + } + return 0; + +} + +static int wctdm_open(struct zt_chan *chan) +{ + struct wctdm *wc = chan->pvt; + if (!(wc->cardflag & (1 << (chan->chanpos - 1)))) + return -ENODEV; + if (wc->dead) + return -ENODEV; + wc->usecount++; +#ifndef LINUX26 + MOD_INC_USE_COUNT; +#else + try_module_get(THIS_MODULE); +#endif + return 0; +} + +static int wctdm_watchdog(struct zt_span *span, int event) +{ + printk("opvxa1200: Restarting DMA\n"); + wctdm_restart_dma(span->pvt); + return 0; +} + +static int wctdm_close(struct zt_chan *chan) +{ + struct wctdm *wc = chan->pvt; + wc->usecount--; +#ifndef LINUX26 + MOD_DEC_USE_COUNT; +#else + module_put(THIS_MODULE); +#endif + if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) { + if (reversepolarity) + wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 5; + else + wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 1; + } + /* If we're dead, release us now */ + if (!wc->usecount && wc->dead) + wctdm_release(wc); + return 0; +} + +static int wctdm_hooksig(struct zt_chan *chan, zt_txsig_t txsig) +{ + struct wctdm *wc = chan->pvt; + int reg=0; + if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) { + /* XXX Enable hooksig for FXO XXX */ + switch(txsig) { + case ZT_TXSIG_START: + case ZT_TXSIG_OFFHOOK: + wc->mod[chan->chanpos - 1].fxo.offhook = 1; + wctdm_setreg(wc, chan->chanpos - 1, 5, 0x9); + break; + case ZT_TXSIG_ONHOOK: + wc->mod[chan->chanpos - 1].fxo.offhook = 0; + wctdm_setreg(wc, chan->chanpos - 1, 5, 0x8); + break; + default: + printk("wcfxo: Can't set tx state to %d\n", txsig); + } + } else { + switch(txsig) { + case ZT_TXSIG_ONHOOK: + switch(chan->sig) { + case ZT_SIG_EM: + case ZT_SIG_FXOKS: + case ZT_SIG_FXOLS: + wc->mod[chan->chanpos-1].fxs.lasttxhook = wc->mod[chan->chanpos-1].fxs.idletxhookstate; + break; + case ZT_SIG_FXOGS: + wc->mod[chan->chanpos-1].fxs.lasttxhook = 3; + break; + } + break; + case ZT_TXSIG_OFFHOOK: + switch(chan->sig) { + case ZT_SIG_EM: + wc->mod[chan->chanpos-1].fxs.lasttxhook = 5; + break; + default: + wc->mod[chan->chanpos-1].fxs.lasttxhook = wc->mod[chan->chanpos-1].fxs.idletxhookstate; + break; + } + break; + case ZT_TXSIG_START: + wc->mod[chan->chanpos-1].fxs.lasttxhook = 4; + break; + case ZT_TXSIG_KEWL: + wc->mod[chan->chanpos-1].fxs.lasttxhook = 0; + break; + default: + printk("opvxa1200: Can't set tx state to %d\n", txsig); + } + if (debug) + printk("Setting FXS hook state to %d (%02x)\n", txsig, reg); + +#if 1 + wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos-1].fxs.lasttxhook); +#endif + } + return 0; +} + +static int wctdm_initialize(struct wctdm *wc) +{ + int x; + + /* Zapata stuff */ + sprintf(wc->span.name, "OPVXA1200/%d", wc->pos); + sprintf(wc->span.desc, "%s Board %d", wc->variety, wc->pos + 1); + if (alawoverride) { + printk("ALAW override parameter detected. Device will be operating in ALAW\n"); + wc->span.deflaw = ZT_LAW_ALAW; + } else + wc->span.deflaw = ZT_LAW_MULAW; + for (x = 0; x < NUM_CARDS; x++) { + sprintf(wc->chans[x].name, "OPVXA1200/%d/%d", wc->pos, x); + wc->chans[x].sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_SF | ZT_SIG_EM | ZT_SIG_CLEAR; + wc->chans[x].sigcap |= ZT_SIG_FXSKS | ZT_SIG_FXSLS | ZT_SIG_SF | ZT_SIG_CLEAR; + wc->chans[x].chanpos = x+1; + wc->chans[x].pvt = wc; + } + wc->span.chans = wc->chans; + wc->span.channels = NUM_CARDS; + wc->span.hooksig = wctdm_hooksig; + wc->span.open = wctdm_open; + wc->span.close = wctdm_close; + wc->span.flags = ZT_FLAG_RBS; + wc->span.ioctl = wctdm_ioctl; + wc->span.watchdog = wctdm_watchdog; + init_waitqueue_head(&wc->span.maintq); + + wc->span.pvt = wc; + if (zt_register(&wc->span, 0)) { + printk("Unable to register span with zaptel\n"); + return -1; + } + return 0; +} + +static void wctdm_post_initialize(struct wctdm *wc) +{ + int x; + /* Finalize signalling */ + for (x = 0; x < NUM_CARDS; x++) { + if (wc->cardflag & (1 << x)) { + if (wc->modtype[x] == MOD_TYPE_FXO) + wc->chans[x].sigcap = ZT_SIG_FXSKS | ZT_SIG_FXSLS | ZT_SIG_SF | ZT_SIG_CLEAR; + else + wc->chans[x].sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_SF | ZT_SIG_EM | ZT_SIG_CLEAR; + } + } +} + +static int wctdm_hardware_init(struct wctdm *wc) +{ + /* Hardware stuff */ + unsigned char ver; + unsigned char x,y; + int failed; + //long origjiffies; //ml. + + /* Signal Reset */ + //printk("before raise reset\n"); + outb(0x01, wc->ioaddr + WC_CNTL); + + /* Wait for 2 second */ + /* + origjiffies = jiffies; + + while(1) + { + if ((jiffies - origjiffies) >= (HZ*5)) + break;; + } + + printk("after raise reset\n");*/ + + /* Check OpenVox chip */ + x=inb(wc->ioaddr + WC_CNTL); + ver = __wctdm_getcreg(wc, WC_VER); + wc->fwversion = ver; + printk("OpenVox A1200P version: %01x.%01x\n", ver>>4, ver&0x0f); + failed = 0; + if (ver != 0x00) { + for (x=0;x<16;x++) { + /* Test registers */ + __wctdm_setcreg(wc, WC_CS, x); + y = __wctdm_getcreg(wc, WC_CS) & 0x0f; + if (x != y) { + printk("%02x != %02x\n", x, y); + failed++; + } + } + + if (!failed) { + printk("OpenVox A1200P passed register test\n"); + } else { + printk("OpenVox A1200P failed register test\n"); + return -1; + } + } else { + printk("No OpenVox chip %02x\n", ver); + } + + if (spibyhw) + __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW); // spi controled by hw MiaoLin; + else + __wctdm_setcreg(wc, WC_SPICTRL, 0); + + /* Reset PCI Interface chip and registers (and serial) */ + outb(0x06, wc->ioaddr + WC_CNTL); + /* Setup our proper outputs for when we switch for our "serial" port */ + wc->ios = BIT_CS | BIT_SCLK | BIT_SDI; + + outb(wc->ios, wc->ioaddr + WC_AUXD); + + /* Set all to outputs except AUX 5, which is an input */ + outb(0xdf, wc->ioaddr + WC_AUXC); + + /* Select alternate function for AUX0 *///MiaoLin modify it to normal io line. + //outb(0x4, wc->ioaddr + WC_AUXFUNC); + + /* Wait 1/4 of a sec */ + wait_just_a_bit(HZ/4); + + /* Back to normal, with automatic DMA wrap around */ + outb(0x30 | 0x01, wc->ioaddr + WC_CNTL); + wc->ledstate = 0; + wctdm_set_led(wc, 0, 0); + + /* Make sure serial port and DMA are out of reset */ + outb(inb(wc->ioaddr + WC_CNTL) & 0xf9, WC_CNTL); + + /* Configure serial port for MSB->LSB operation */ + //outb(0xc1, wc->ioaddr + WC_SERCTL); + outb(0xc1, wc->ioaddr + WC_SERCTL); + + /* Delay FSC by 0 so it's properly aligned */ + //outb(0x0, wc->ioaddr + WC_FSCDELAY); + outb(0x01, wc->ioaddr + WC_FSCDELAY); + + /* Setup DMA Addresses */ + outl(wc->writedma, wc->ioaddr + WC_DMAWS); /* Write start */ + outl(wc->writedma + ZT_CHUNKSIZE * 4 * 4 - 4, wc->ioaddr + WC_DMAWI); /* Middle (interrupt) */ + outl(wc->writedma + ZT_CHUNKSIZE * 8 * 4 - 4, wc->ioaddr + WC_DMAWE); /* End */ + + outl(wc->readdma, wc->ioaddr + WC_DMARS); /* Read start */ + outl(wc->readdma + ZT_CHUNKSIZE * 4 * 4 - 4, wc->ioaddr + WC_DMARI); /* Middle (interrupt) */ + outl(wc->readdma + ZT_CHUNKSIZE * 8 * 4 - 4, wc->ioaddr + WC_DMARE); /* End */ + + /* Clear interrupts */ + outb(0xff, wc->ioaddr + WC_INTSTAT); + + /* Wait 1/4 of a second more */ + wait_just_a_bit(HZ/4); + + for (x = 0; x < NUM_CARDS; x++) { + int sane=0,ret=0,readi=0; +#if 1 + /* Init with Auto Calibration */ + if (!(ret=wctdm_init_proslic(wc, x, 0, 0, sane))) { + wc->cardflag |= (1 << x); + if (debug) { + readi = wctdm_getreg(wc,x,LOOP_I_LIMIT); + printk("Proslic module %d loop current is %dmA\n",x, + ((readi*3)+20)); + } + printk("Module %d: Installed -- AUTO FXS/DPO\n",x); + wctdm_set_led(wc, (unsigned int)x, 1); + } else { + if(ret!=-2) { + sane=1; + + printk("Init ProSlic with Manual Calibration \n"); + /* Init with Manual Calibration */ + if (!wctdm_init_proslic(wc, x, 0, 1, sane)) { + wc->cardflag |= (1 << x); + if (debug) { + readi = wctdm_getreg(wc,x,LOOP_I_LIMIT); + printk("Proslic module %d loop current is %dmA\n",x, + ((readi*3)+20)); + } + printk("Module %d: Installed -- MANUAL FXS\n",x); + } else { + printk("Module %d: FAILED FXS (%s)\n", x, fxshonormode ? fxo_modes[_opermode].name : "FCC"); + } + } else if (!(ret = wctdm_init_voicedaa(wc, x, 0, 0, sane))) { + wc->cardflag |= (1 << x); + printk("Module %d: Installed -- AUTO FXO (%s mode)\n",x, fxo_modes[_opermode].name); + wctdm_set_led(wc, (unsigned int)x, 1); + } else + printk("Module %d: Not installed\n", x); + } +#endif + } + + /* Return error if nothing initialized okay. */ + if (!wc->cardflag && !timingonly) + return -1; + //__wctdm_setcreg(wc, WC_SYNC, (wc->cardflag << 1) | 0x1); + return 0; +} + +static void wctdm_enable_interrupts(struct wctdm *wc) +{ + /* Clear interrupts */ + outb(0xff, wc->ioaddr + WC_INTSTAT); + + /* Enable interrupts (we care about all of them) */ + outb(0x3c /*0x3f*/, wc->ioaddr + WC_MASK0); + /* No external interrupts */ + outb(0x00, wc->ioaddr + WC_MASK1); +} + +static void wctdm_restart_dma(struct wctdm *wc) +{ + /* Reset Master and TDM */ + outb(0x01, wc->ioaddr + WC_CNTL); + outb(0x01, wc->ioaddr + WC_OPER); +} + +static void wctdm_start_dma(struct wctdm *wc) +{ + /* Reset Master and TDM */ + outb(0x0f, wc->ioaddr + WC_CNTL); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + outb(0x01, wc->ioaddr + WC_CNTL); + outb(0x01, wc->ioaddr + WC_OPER); +} + +static void wctdm_stop_dma(struct wctdm *wc) +{ + outb(0x00, wc->ioaddr + WC_OPER); +} + +static void wctdm_reset_tdm(struct wctdm *wc) +{ + /* Reset TDM */ + outb(0x0f, wc->ioaddr + WC_CNTL); +} + +static void wctdm_disable_interrupts(struct wctdm *wc) +{ + outb(0x00, wc->ioaddr + WC_MASK0); + outb(0x00, wc->ioaddr + WC_MASK1); +} + +static int __devinit wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int res; + struct wctdm *wc; + struct wctdm_desc *d = (struct wctdm_desc *)ent->driver_data; + int x; + int y; + + static int initd_ifaces=0; + + if(initd_ifaces){ + memset((void *)ifaces,0,(sizeof(struct wctdm *))*WC_MAX_IFACES); + initd_ifaces=1; + } + for (x=0;x= WC_MAX_IFACES) { + printk("Too many interfaces\n"); + return -EIO; + } + + if (pci_enable_device(pdev)) { + res = -EIO; + } else { + wc = kmalloc(sizeof(struct wctdm), GFP_KERNEL); + if (wc) { + int cardcount = 0; + + //wc->offset = 12; // miaolin add. + wc->lastchan = -1; // first channel offset = -1; + wc->ledstate = 0; + + ifaces[x] = wc; + memset(wc, 0, sizeof(struct wctdm)); + spin_lock_init(&wc->lock); + wc->curcard = -1; + wc->ioaddr = pci_resource_start(pdev, 0); + wc->mem_region = pci_resource_start(pdev, 1); + wc->mem_len = pci_resource_len(pdev, 1); + wc->mem32 = (unsigned long)ioremap(wc->mem_region, wc->mem_len); + wc->dev = pdev; + wc->pos = x; + wc->variety = d->name; + for (y=0;yflags[y] = d->flags; + /* Keep track of whether we need to free the region */ + if (request_region(wc->ioaddr, 0xff, "opvxa1200")) + wc->freeregion = 1; + else + wc->freeregion = 0; + + if (request_mem_region(wc->mem_region, wc->mem_len, "opvxa1200")) + wc->freeregion |= 0x02; + + /* Allocate enough memory for two zt chunks, receive and transmit. Each sample uses + 8 bits. */ + wc->writechunk = pci_alloc_consistent(pdev, ZT_MAX_CHUNKSIZE * (NUM_CARDS+NUM_FLAG) * 2 * 2, &wc->writedma); + if (!wc->writechunk) { + printk("opvxa1200: Unable to allocate DMA-able memory\n"); + if (wc->freeregion & 0x01) + release_region(wc->ioaddr, 0xff); + if (wc->freeregion & 0x02); + { + release_mem_region(wc->mem_region, wc->mem_len); + iounmap((void *)wc->mem32); + } + return -ENOMEM; + } + + wc->readchunk = wc->writechunk + ZT_MAX_CHUNKSIZE * (NUM_CARDS+NUM_FLAG) * 2; /* in bytes */ + wc->readdma = wc->writedma + ZT_MAX_CHUNKSIZE * (NUM_CARDS+NUM_FLAG) * 2; /* in bytes */ + + if (wctdm_initialize(wc)) { + printk("opvxa1200: Unable to intialize FXS\n"); + /* Set Reset Low */ + x=inb(wc->ioaddr + WC_CNTL); + outb((~0x1)&x, wc->ioaddr + WC_CNTL); + /* Free Resources */ + free_irq(pdev->irq, wc); + if (wc->freeregion & 0x01) + release_region(wc->ioaddr, 0xff); + if (wc->freeregion & 0x02); + { + release_mem_region(wc->mem_region, wc->mem_len); + iounmap((void *)wc->mem32); + } + pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * (NUM_CARDS+NUM_FLAG) * 2 * 2, (void *)wc->writechunk, wc->writedma); + kfree(wc); + return -EIO; + } + + /* Enable bus mastering */ + pci_set_master(pdev); + + /* Keep track of which device we are */ + pci_set_drvdata(pdev, wc); + + if (request_irq(pdev->irq, wctdm_interrupt, ZAP_IRQ_SHARED, "opvxa1200", wc)) { + printk("opvxa1200: Unable to request IRQ %d\n", pdev->irq); + if (wc->freeregion & 0x01) + release_region(wc->ioaddr, 0xff); + if (wc->freeregion & 0x02); + { + release_mem_region(wc->mem_region, wc->mem_len); + iounmap((void *)wc->mem32); + } + pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * (NUM_CARDS+NUM_FLAG) * 2 * 2, (void *)wc->writechunk, wc->writedma); + pci_set_drvdata(pdev, NULL); + kfree(wc); + return -EIO; + } + + + if (wctdm_hardware_init(wc)) { + unsigned char x; + + /* Set Reset Low */ + x=inb(wc->ioaddr + WC_CNTL); + outb((~0x1)&x, wc->ioaddr + WC_CNTL); + /* Free Resources */ + free_irq(pdev->irq, wc); + if (wc->freeregion & 0x01) + release_region(wc->ioaddr, 0xff); + if (wc->freeregion & 0x02); + { + release_mem_region(wc->mem_region, wc->mem_len); + iounmap((void *)wc->mem32); + } + pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * (NUM_CARDS+NUM_FLAG) * 2 * 2, (void *)wc->writechunk, wc->writedma); + pci_set_drvdata(pdev, NULL); + zt_unregister(&wc->span); + kfree(wc); + return -EIO; + + } + +#ifdef TEST_LOG_INCOME_VOICE + for(i=0; ivoc_buf[i] = kmalloc(voc_buffer_size, GFP_KERNEL); + wc->voc_ptr[i] = 0; + } +#endif + + wctdm_post_initialize(wc); + + /* Enable interrupts */ + wctdm_enable_interrupts(wc); + /* Initialize Write/Buffers to all blank data */ + memset((void *)wc->writechunk,0, ZT_MAX_CHUNKSIZE * (NUM_CARDS+NUM_FLAG) * 2 * 2); + + /* Start DMA */ + wctdm_start_dma(wc); + + for (x = 0; x < NUM_CARDS; x++) { + if (wc->cardflag & (1 << x)) + cardcount++; + } + + printk("Found a OpenVox A1200P: Version %01x.%01x (%d modules)\n", wc->fwversion>>4, wc->fwversion&0x0f, cardcount); + + res = 0; + } else + res = -ENOMEM; + } + return res; +} + +static void wctdm_release(struct wctdm *wc) +{ +#ifdef TEST_LOG_INCOME_VOICE + struct file * f = NULL; + mm_segment_t orig_fs; + int i; + char fname[20]; +#endif + + zt_unregister(&wc->span); + if (wc->freeregion & 0x01) + release_region(wc->ioaddr, 0xff); + if (wc->freeregion & 0x02); + { + release_mem_region(wc->mem_region, wc->mem_len); + iounmap((void *)wc->mem32); + } + +#ifdef TEST_LOG_INCOME_VOICE + for(i=0; if_op || !f->f_op->read) + { + printk("WARNING: File (read) object is a null pointer!!!\n"); + continue; + } + + f->f_pos = 0; + + orig_fs = get_fs(); + set_fs(KERNEL_DS); + + if(wc->voc_buf[i]) + { + f->f_op->write(f, wc->voc_buf[i], voc_buffer_size, &f->f_pos); + kfree(wc->voc_buf[i]); + } + + set_fs(orig_fs); + fput(f); + } +#endif + + kfree(wc); + printk("Freed a OpenVox A1200 card\n"); +} + +static void __devexit wctdm_remove_one(struct pci_dev *pdev) +{ + struct wctdm *wc = pci_get_drvdata(pdev); + if (wc) { + + /* Stop any DMA */ + wctdm_stop_dma(wc); + wctdm_reset_tdm(wc); + + /* In case hardware is still there */ + wctdm_disable_interrupts(wc); + + /* Immediately free resources */ + pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * (NUM_CARDS+NUM_FLAG) * 2 * 2, (void *)wc->writechunk, wc->writedma); + free_irq(pdev->irq, wc); + + /* Reset PCI chip and registers */ + if(wc->fwversion > 0x11) + outb(0x0e, wc->ioaddr + WC_CNTL); + else + { + wc->ledstate = 0; + wctdm_set_led(wc,0,0); // power off all leds. + } + + /* Release span, possibly delayed */ + if (!wc->usecount) + wctdm_release(wc); + else + wc->dead = 1; + } +} + +static struct pci_device_id wctdm_pci_tbl[] = { + { 0xe159, 0x0001, 0x9100, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, + { 0xe159, 0x0001, 0x9519, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, + { 0xe159, 0x0001, 0x95D9, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, + { 0xe159, 0x0001, 0x8519, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, + { 0 } +}; + +MODULE_DEVICE_TABLE(pci, wctdm_pci_tbl); + +static struct pci_driver wctdm_driver = { + name: "opvxa1200", + probe: wctdm_init_one, +#ifdef LINUX26 + remove: __devexit_p(wctdm_remove_one), +#else + remove: wctdm_remove_one, +#endif + suspend: NULL, + resume: NULL, + id_table: wctdm_pci_tbl, +}; + +static int __init wctdm_init(void) +{ + int res; + int x; + for (x=0;x<(sizeof(fxo_modes) / sizeof(fxo_modes[0])); x++) { + if (!strcmp(fxo_modes[x].name, opermode)) + break; + } + if (x < sizeof(fxo_modes) / sizeof(fxo_modes[0])) { + _opermode = x; + } else { + printk("Invalid/unknown operating mode '%s' specified. Please choose one of:\n", opermode); + for (x=0;x"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +module_init(wctdm_init); +module_exit(wctdm_cleanup); --- zaptel-1.4.11~dfsg.orig/kernel/ds1x1f.c +++ zaptel-1.4.11~dfsg/kernel/ds1x1f.c @@ -0,0 +1,2089 @@ +//======================================================================= +// T1/E1 PCI card with failover DS1x1F card Driver +// +// Ver: 2.14 Date: 17.Jan.2007 +// +// based of work by: +// Mark Spencer +// Matthew Fredrickson +// William Meadows +// +// Copyright (c)2006,2007 TCDG Corp. +// All rights reserved. +// +// http://www.tc-dg.net +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +//======================================================================= + + +#include +#include +#include +#include + +#ifdef STANDALONE_ZAPATA +#include "zaptel.h" +#else +#include +#endif + + +#define WC_MAX_CARDS 32 +#define NUM_REGS 0xa9 +#define NUM_PCI 12 + +#define OFS_FALC 0x0000 // pci address space offset for FALC registers 256 * 32 bit words (only 8 LSB used) +#define OFS_TDM_WR 0x0800 // pci address space offset for TDM media shift register buffers 8 * 32 bit words +#define OFS_TDM_RD 0x0900 // pci address space offset for TDM media shift register buffers 8 * 32 bit words +#define OFS_CTRL 0x1000 // pci address space offset for card control register 1 * 32 bit +#define OFS_CLK 0x1004 // pci address space offset for card clock / h.100 register 1 * 32 bit + + +#define FLAG_STARTED 0x01 +#define FLAG_NMF 0x02 +#define FLAG_SENDINGYELLOW 0x04 + +#define MODE_SW -1 // mode controlled by DIP switch +#define MODE_T1 0x01 // T1 mode bit.0 = T1 +#define MODE_E1 0x02 // E1 mode bit.1 = E1 +#define MODE_UE1 0x82 // unchannelized E1 bit.7 = unchannelized +#define MODE_J1 0x41 // J1 mode bit.6 = j1mode + + +#define DS1_SET_REL 7 // new MAINT commands for controlling the bypass relay and monitoring, hot-standby +#define DS1_CLR_REL 8 +#define DS1_SET_RXMON 9 +#define DS1_CLR_RXMON 10 +#define DS1_SET_TXEN 11 +#define DS1_CLR_TXEN 12 + + + +//======================================= Control register bit definitions ================================ + +#define CTRL_LOFF 0x0000 // All LED off +#define CTRL_LGRN 0x0001 // Green LED control bit +#define CTRL_LRED 0x0002 // Red LED control bit +#define CTRL_REL 0x0004 // bypass relay L = bypass H = normal operation +#define CTRL_TEST 0x0008 // relay presence test bit L = normal operation H= presence test +#define CTRL_FRST 0x0010 // Falc reset bit L = normal operation H = Falc Reset +#define CTRL_INTA 0x0100 // Interrupt acknowledge bit writing a '1' resets IRQ and IRM +#define CTRL_IRQ 0x0100 // Interrupt request bit +#define CTRL_IRM 0x0400 // Interrupt missed bit = previous interrupt has not yet been serviced when new one was triggered +#define CTRL_RELI 0x0800 // relay presence bit H = no relay L = relay present (valid only when CTRL_TEST = H) +#define CTRL_SW1 0x1000 // DIP switch #1 +#define CTRL_SW2 0x2000 // DIP switch #2 +#define CTRL_SW3 0x4000 // DIP switch #3 +#define CTRL_SW4 0x8000 // DIP switch #4 + +//==================================== FALC-56 PEF-2256 register definitions =============================== + +#define FR_XFIFO 0x00 +#define FR_RFIFO 0x00 +#define FR_CMDR 0x02 +#define FR_MODE 0x03 +#define FR_RAH1 0x04 +#define FR_RAH2 0x05 +#define FR_RAL1 0x06 +#define FR_RAL2 0x07 +#define FR_IPC 0x08 +#define FR_CCR1 0x09 +#define FR_CCR2 0x0a +#define FR_RTR1 0x0c +#define FR_RTR2 0x0d +#define FR_RTR3 0x0e +#define FR_RTR4 0x0f +#define FR_TTR1 0x10 +#define FR_TTR2 0x11 +#define FR_TTR3 0x12 +#define FR_TTR4 0x13 +#define FR_IMR0 0x14 +#define FR_IMR1 0x15 +#define FR_IMR2 0x16 +#define FR_IMR3 0x17 +#define FR_IMR4 0x18 +#define FR_IMR5 0x19 +#define FR_IERR 0x1b +#define FR_FMR0 0x1c +#define FR_FMR1 0x1d +#define FR_FMR2 0x1e +#define FR_LOOP 0x1f +#define FR_FMR4 0x20 +#define FR_FMR5 0x21 +#define FR_XSW 0x20 +#define FR_XSP 0x21 +#define FR_XC0 0x22 +#define FR_XC1 0x23 +#define FR_RC0 0x24 +#define FR_RC1 0x25 +#define FR_XPM0 0x26 +#define FR_XPM1 0x27 +#define FR_XPM2 0x28 +#define FR_TSWM 0x29 +#define FR_IDLE 0x2b +#define FR_XSA4 0x2c +#define FR_XSA5 0x2d +#define FR_XSA6 0x2e +#define FR_XSA7 0x2f +#define FR_XSA8 0x30 +#define FR_FMR3 0x31 +#define FR_CCB1 0x2f +#define FR_CCB2 0x30 +#define FR_CCB3 0x31 +#define FR_ICB1 0x32 +#define FR_ICB2 0x33 +#define FR_ICB3 0x34 +#define FR_ICB4 0x35 +#define FR_LIM0 0x36 +#define FR_LIM1 0x37 +#define FR_PCD 0x38 +#define FR_PCR 0x39 +#define FR_LIM2 0x3a +#define FR_LCR1 0x3b +#define FR_LCR2 0x3c +#define FR_LCR3 0x3d +#define FR_SIC1 0x3e +#define FR_SIC2 0x3f +#define FR_SIC3 0x40 +#define FR_CMR1 0x44 +#define FR_CMR2 0x45 +#define FR_GCR 0x46 +#define FR_ESM 0x47 +#define FR_RBD 0x49 +#define FR_VSTR 0x4a +#define FR_RES 0x4b +#define FR_FRS0 0x4c +#define FR_FRS1 0x4d +#define FR_RSW 0x4e +#define FR_RSP 0x4f +#define FR_FECL 0x50 +#define FR_FECH 0x51 +#define FR_CVCL 0x52 +#define FR_CVCH 0x53 +#define FR_CEC1L 0x54 +#define FR_CEC1H 0x55 +#define FR_EBCL 0x56 +#define FR_EBCH 0x57 +#define FR_CEC2L 0x58 +#define FR_CEC2H 0x59 +#define FR_CEC3L 0x5a +#define FR_CEC3H 0x5b +#define FR_RSA4 0x5c +#define FR_RSA5 0x5d +#define FR_RSA6 0x5e +#define FR_RSA7 0x5f +#define FR_RSA8 0x60 +#define FR_DEC 0x60 +#define FR_RSA6S 0x61 +#define FR_RSP1 0x62 +#define FR_RSP2 0x63 +#define FR_SIS 0x64 +#define FR_RSIS 0x65 +#define FR_RBCL 0x66 +#define FR_RBCH 0x67 +#define FR_ISR0 0x68 +#define FR_ISR1 0x69 +#define FR_ISR2 0x6a +#define FR_ISR3 0x6b +#define FR_ISR4 0x6c +#define FR_ISR5 0x6d +#define FR_GIS 0x6e +#define FR_XS1 0x70 +#define FR_XS2 0x71 +#define FR_XS3 0x72 +#define FR_XS4 0x73 +#define FR_XS5 0x74 +#define FR_XS6 0x75 +#define FR_XS7 0x76 +#define FR_XS8 0x77 +#define FR_XS9 0x78 +#define FR_XS10 0x79 +#define FR_XS11 0x7a +#define FR_XS12 0x7b +#define FR_XS13 0x7c +#define FR_XS14 0x7d +#define FR_XS15 0x7e +#define FR_XS16 0x7f +#define FR_RS1 0x70 +#define FR_RS2 0x71 +#define FR_RS3 0x72 +#define FR_RS4 0x73 +#define FR_RS5 0x74 +#define FR_RS6 0x75 +#define FR_RS7 0x76 +#define FR_RS8 0x77 +#define FR_RS9 0x78 +#define FR_RS10 0x79 +#define FR_RS11 0x7a +#define FR_RS12 0x7b +#define FR_RS13 0x7c +#define FR_RS14 0x7d +#define FR_RS15 0x7e +#define FR_RS16 0x7f +#define FR_PC1 0x80 +#define FR_PC2 0x81 +#define FR_PC3 0x82 +#define FR_PC4 0x83 +#define FR_PC5 0x84 +#define FR_GPC1 0x85 +#define FR_PC6 0x86 +#define FR_CMDR2 0x87 +#define FR_CMDR3 0x88 +#define FR_CMDR4 0x89 +#define FR_CCR3 0x8b +#define FR_CCR4 0x8c +#define FR_CCR5 0x8d +#define FR_MODE2 0x8e +#define FR_MODE3 0x8f +#define FR_RBC2 0x90 +#define FR_RBC3 0x91 +#define FR_GCM1 0x92 +#define FR_GCM2 0x93 +#define FR_GCM3 0x94 +#define FR_GCM4 0x95 +#define FR_GCM5 0x96 +#define FR_GCM6 0x97 +#define FR_GCM7 0x98 +#define FR_GCM8 0x99 +#define FR_SIS2 0x98 +#define FR_RSIS2 0x99 +#define FR_SIS3 0x9a +#define FR_RSIS3 0x9b +#define FR_XFIFO2 0x9c +#define FR_RFIFO2 0x9c +#define FR_XFIFO3 0x9e +#define FR_RFIFO3 0x9e +#define FR_TSEO 0xa0 +#define FR_TSBS1 0xa1 +#define FR_TSBS2 0xa2 +#define FR_TSBS3 0xa3 +#define FR_TSS2 0xa4 +#define FR_TSS3 0xa5 +#define FR_TPC0 0xa8 +#define FR_WID 0xec + +//======================================================================================================== + +struct t4_regs { + unsigned int pci[NUM_PCI]; + unsigned char regs[NUM_REGS]; +}; + + +#define WCT4_GET_REGS _IOW (ZT_CODE, 60, struct t4_regs) + + + +static int clrtab[] = {0x80,0x40,0x20,0x10,0x8,0x4,0x2,0x1}; + +static char name[] = {"ds1x1f"}; + +struct t1 { + struct pci_dev *dev; + spinlock_t lock; + int spantype; + int spanflags; // Span flags + unsigned char txsigs[16]; // Copy of tx sig registers + int num; + int alarmcount; // How much red alarm we've seen + int alarmdebounce; + char *variety; + + unsigned int intcount; + int prescaler; // interrupt divider + + int usecount; + int blinktimer; + int alarmtimer; + int loopupcnt; + int loopdowncnt; + unsigned char ledtestreg; + + void * cnaddr; // PCI and driver related parameters + void * ioaddr; + unsigned long iostart; + unsigned long iolen; + unsigned long cnstart; + unsigned long cnlen; + struct resource * cres; + struct resource * ires; + + unsigned char txchunkptr; // pointer for rd/wr audiosamples + unsigned char rxchunkptr; + + unsigned char ec_chunk1[32][ZT_CHUNKSIZE]; // echocanceller memory buffers + unsigned char ec_chunk2[32][ZT_CHUNKSIZE]; + + struct zt_span span; // Span + struct zt_chan chans[32]; // Channels +}; + + + // module paramter variables +static int debug = 0; // defines debug modes 0,1,2 +static int cardmode = -1; // forces card into T1, E1, J1, UE1 mode , -1 = use DIP switches +static int alarmdebounce = 0; +static int loopback = 0; +static int frames = -1; // sets the size of the cards framebuffer 1-8 frames (preferred are 1,2,4,8) +static int extclk = -1; // controls the clock synchronization via H.100 bus connector +static int monitor = 0; // monitor = 3 puts the card in a Hi-Z mode and enabling it to monitor T1/E1 traffic + +static struct t1 *cards[WC_MAX_CARDS]; + + +static inline void start_alarm(struct t1 *wc) +{ + wc->blinktimer = 0; +} + + +static int ds1_open(struct zt_chan *chan) +{ + struct t1 *wc = chan->pvt; + + wc->usecount++; + + try_module_get(THIS_MODULE); + + return 0; +} + +//------------------------------------------------------------------------------------------------------------------------------------------- +static inline unsigned int led_write_reg(struct t1 *wc, unsigned int val) // write to the cards 32 bit control register +{ + unsigned int x; + + x = (ioread32(wc->ioaddr + OFS_CTRL) & 0xfc) | (val & 0x03); // combine LED content with all other register data + + iowrite32(x, wc->ioaddr + OFS_CTRL); // write back register content + + return x; +} + +//------------------------------------------------------------------------------------------------------------------------------------------- +static inline unsigned int ctrl_set_reg(struct t1 *wc, unsigned int val) // set bits in control register +{ + unsigned x; + + x = (ioread32(wc->ioaddr + OFS_CTRL) & 0xff) | val; // OR newcontent with all other register data + + iowrite32(x, wc->ioaddr + OFS_CTRL); // write back register content + + return x; +} + +//------------------------------------------------------------------------------------------------------------------------------------------- +static inline unsigned int ctrl_clr_reg(struct t1 *wc, unsigned int val) // clear bits in control register +{ + unsigned x; + + x = (ioread32(wc->ioaddr + OFS_CTRL) & 0xff) & (val ^ 0xff); // OR newcontent with all other register data + + iowrite32(x, wc->ioaddr + OFS_CTRL); // write back register content + + return x; +} + +//------------------------------------------------------------------------------------------------------------------------------------------- +static inline unsigned int ctrl_write_reg(struct t1 *wc, unsigned int val) // write to the cards 32 bit control register +{ + iowrite32(val, wc->ioaddr + OFS_CTRL); // write back register content + + return val; +} + + +//------------------------------------------------------------------------------------------------------------------------------------------- +static inline unsigned int ctrl_read_reg(struct t1 *wc) // read control register +{ + return (ioread32(wc->ioaddr + OFS_CTRL)); // return content of control register +} + + +//------------------------------------------------------------------------------------------------------------------------------------------- +static inline unsigned int clk_write_reg(struct t1 *wc, unsigned int val) // write to the cards 32 bit clock/ h.100 bus register +{ + iowrite32(val, wc->ioaddr + OFS_CLK); // write back register content + + return val; +} + + +//------------------------------------------------------------------------------------------------------------------------------------------- +static inline unsigned int clk_read_reg(struct t1 *wc) // read clock / h.100 bus register +{ + return (ioread32(wc->ioaddr + OFS_CLK)); // return content of control register +} + + +//------------------------------------------------------------------------------------------------------------------------------------------- +static inline unsigned int __t1_framer_in(struct t1 *wc, const unsigned int reg) // read a Falc framer register +{ + int loc = ((reg & 0x00ff) << 2) + OFS_FALC; // FALC registers are on doubleword boundaries + + return ((ioread32(wc->ioaddr + loc)) & 0xff); // return only the 8 lsb +} + + + +static inline unsigned int t1_framer_in(struct t1 *wc, const unsigned int addr) // spinlock wrapper +{ + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&wc->lock, flags); + + ret = __t1_framer_in(wc, addr); + + spin_unlock_irqrestore(&wc->lock, flags); + + return ret; +} + + +static inline void __t1_framer_out(struct t1 *wc, const unsigned int reg, const unsigned int val) // write to Falc framer register +{ + int loc = ((reg & 0x00ff) << 2) + OFS_FALC; // FALC registers are on doubleword boundaries + int ret; + + iowrite32(val, wc->ioaddr + loc); // write to register + + ret = (ioread32(wc->ioaddr + loc)) & 0xff; // for debugging read back register content + + if (debug > 1) + { + if (ret == val) // check if write was successful + printk("Wrote: %02x to Adr: %02x \n", val, reg); // and print on the console + else + printk("Wrote: %02x to Adr: %02x but read back: %02x !!!\n", val, reg, ret); // print results on the console + } +} + + +static inline void t1_framer_out(struct t1 *wc, const unsigned int addr, const unsigned int value) // spinlock wrapper +{ + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + + __t1_framer_out(wc, addr, value); + + spin_unlock_irqrestore(&wc->lock, flags); +} + +//------------------------------------------------------------------------------------------------------------------------------------------- +static void ds1_release(struct t1 *wc) +{ + zt_unregister(&wc->span); + + kfree(wc); + + printk("Freed a DS1x1F card\n"); +} + + +static int ds1_close(struct zt_chan *chan) +{ + struct t1 *wc = chan->pvt; + wc->usecount--; + + module_put(THIS_MODULE); + + if (!wc->usecount) // If we're dead, release us now + ds1_release(wc); + + return 0; +} + +//---------------------------------------------------------------------------------------------------------------------------------------- +// HARDWARE INTERRUPT CONTROL Enable / Disable + +static void ds1_enable_interrupts(struct t1 *wc) // enable 8 kHz TDM interrupts from the pci card +{ + iowrite32(0x00000001, wc->cnaddr + 0x1ec); // enable internal interrupt + + ctrl_set_reg(wc, CTRL_INTA); // clear interrupt status bit + + if (debug) + printk("Enabled interrupts!\n"); +} + +static void ds1_disable_interrupts(struct t1 *wc) // disable all interrupts from the card +{ + ctrl_set_reg(wc, CTRL_INTA); // clear interrupt status bit + + iowrite32(0x00000000, wc->cnaddr + 0x1ec); // disable internal interrupt + + if (debug) + printk("Disabled interrupts!\n"); +} + +//---------------------------------------------------------------------------------------------------------------------------------------- +static void __ds1_set_clear(struct t1 *wc) // SELECT CLEAR CHANNEL MODE - FOR T1 ONLY +{ + int i; + unsigned short val; + + val = 0; + for (i=0; i<8; i++) // go through channel 0-7 + { + if (wc->span.chans[i ].flags & ZT_FLAG_CLEAR) // check if this channel is marked Clear channel + val |= clrtab[i]; + } + __t1_framer_out(wc, FR_CCB1, val); // write register CCB1 + + val = 0; + for (i=0; i<8; i++) // go through channel 8-15 + { + if (wc->span.chans[i+ 8].flags & ZT_FLAG_CLEAR) // check if this channel is marked Clear channel + val |= clrtab[i]; + } + __t1_framer_out(wc, FR_CCB2, val); // write register CCB2 + + val = 0; + for (i=0; i<8; i++) // go through channel 16-23 + { + if (wc->span.chans[i+16].flags & ZT_FLAG_CLEAR) // check if this channel is marked Clear channel + val |= clrtab[i]; + } + __t1_framer_out(wc, FR_CCB3, val); // write register CCB3 + + + +} + +//---------------------------------------------------------------------------------------------------------------------------------------- +static int ds1_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) +{ + struct t4_regs regs; + int x; + struct t1 *wc; + + wc = chan->pvt; + + switch(cmd) + { + case WCT4_GET_REGS: + for (x=0; xioaddr + (x << 2))); + + for (x=0; xpvt; + + if (wc->spantype == MODE_E1) + { + switch(cmd) + { + case ZT_MAINT_NONE: + printk("XXX Turn off local and remote loops E1 XXX\n"); + break; + + case ZT_MAINT_LOCALLOOP: + printk("XXX Turn on local loopback E1 XXX\n"); + break; + + case ZT_MAINT_REMOTELOOP: + printk("XXX Turn on remote loopback E1 XXX\n"); + break; + + case ZT_MAINT_LOOPUP: + printk("XXX Send loopup code E1 XXX\n"); + break; + + case ZT_MAINT_LOOPDOWN: + printk("XXX Send loopdown code E1 XXX\n"); + break; + + case ZT_MAINT_LOOPSTOP: + printk("XXX Stop sending loop codes E1 XXX\n"); + break; + + case DS1_SET_REL: + ctrl_set_reg (wc, CTRL_REL); + printk("XXX Turn off E1 bypass XXX\n"); + break; + + case DS1_CLR_REL: + ctrl_clr_reg (wc, CTRL_REL); + printk("XXX Turn on E1 bypass XXX\n"); + break; + + default: + printk("DS1x1F: Unknown E1 maintainance command: %d\n", cmd); + break; + } + } + else + { + switch(cmd) + { + case ZT_MAINT_NONE: + printk("XXX Turn off local and remote loops T1 XXX\n"); + break; + + case ZT_MAINT_LOCALLOOP: + printk("XXX Turn on local loop and no remote loop XXX\n"); + break; + + case ZT_MAINT_REMOTELOOP: + printk("XXX Turn on remote loopup XXX\n"); + break; + + case ZT_MAINT_LOOPUP: + t1_framer_out(wc, FR_FMR5, 0x50); /* FMR5: Nothing but RBS mode */ + break; + + case ZT_MAINT_LOOPDOWN: + t1_framer_out(wc, FR_FMR5, 0x60); /* FMR5: Nothing but RBS mode */ + break; + + case ZT_MAINT_LOOPSTOP: + t1_framer_out(wc, FR_FMR5, 0x40); /* FMR5: Nothing but RBS mode */ + break; + + case DS1_SET_REL: + ctrl_set_reg (wc, CTRL_REL); + printk("XXX Turn off T1 bypass XXX\n"); + break; + + case DS1_CLR_REL: + ctrl_clr_reg (wc, CTRL_REL); + printk("XXX Turn on T1 bypass XXX\n"); + break; + + default: + printk("DS1x1F: Unknown T1 maintainance command: %d\n", cmd); + break; + } + } + return 0; +} + + +static int ds1_rbsbits(struct zt_chan *chan, int bits) +{ + u_char m,c; + int n,b; + struct t1 *wc = chan->pvt; + unsigned long flags; + + if(debug > 2) + printk("Setting CAS signalling bits to: %d for ch: %s\n", bits, chan->name); + + spin_lock_irqsave(&wc->lock, flags); + + if (wc->spantype == MODE_E1) // E1 CAS (R2) signalling + { + if (chan->chanpos == 16) // channel 16 is the signalling channel even for CAS + { + spin_unlock_irqrestore(&wc->lock, flags); + return 0; // so leave it alone + } + + n = chan->chanpos - 1; + + if (chan->chanpos > 15) // move everything above channel 16 one down + n--; + + b = (n % 15); + c = wc->txsigs[b]; + m = (n / 15) << 2; // we have to write one nibble with new bits + c &= (0xf << m); // while keeping the other nibble as is + c |= (bits & 0xf) << (4 - m); + + wc->txsigs[b] = c; + + __t1_framer_out(wc,FR_XS2 + b,c); // In E1 mode XS1 is not used for CAS signalling + + } + else // T1 or J1 CAS mode + { + if (wc->span.lineconfig & ZT_CONFIG_D4) // Old D4 framin selected + { + n = chan->chanpos - 1; + b = (n/4); + c = wc->txsigs[b]; + m = ((3 - (n % 4)) << 1); // D4 only uses 2 CAS bits per channel + c &= ~(0x3 << m); // do the nibble thing + c |= ((bits >> 2) & 0x3) << m; + wc->txsigs[b] = c; + // output them to the chip */ + __t1_framer_out(wc,FR_XS1 + b ,c); // since there are only 2 bits per channel + __t1_framer_out(wc,FR_XS6 + b ,c); // write them into the CAS registers twice + } + else + { + if (wc->span.lineconfig & ZT_CONFIG_ESF) // ESF = CAS 4 bits per channel + { + n = chan->chanpos - 1; + b = (n/2); + c = wc->txsigs[b]; + m = ((n % 2) << 2); // do the nibble thing + c &= (0xf << m); + c |= (bits & 0xf) << (4 - m); + wc->txsigs[b] = c; + // output them to the chip + __t1_framer_out(wc,FR_XS1 + b,c); // write only once + } + } + } + + spin_unlock_irqrestore(&wc->lock, flags); + + if (debug > 2) + printk("Finished setting CAS signalling bits\n"); + + return 0; +} + + +static void __t1_check_sigbits(struct t1 *wc) // read the current CAS bits from framerchip +{ + int a,i,rxs; + + if (!(wc->span.flags & ZT_FLAG_RUNNING)) + return; + + if (wc->spantype == MODE_E1) // E1 CAS mode selected + { + for (i = 0; i < 15; i++) // do this in 16 steps + { + a = __t1_framer_in(wc, FR_RS2 + i); // RS1 is not used for CAS bits + rxs = (a & 0xf); + + if (!(wc->span.chans[i+16].sig & ZT_SIG_CLEAR)) // get lower nibble = channel+16 + { + if (wc->span.chans[i+16].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i+16], rxs); + } + + rxs = (a >> 4) & 0xf; // get upper nibble = channel +0 + + if (!(wc->span.chans[i].sig & ZT_SIG_CLEAR)) + { + if (wc->span.chans[i].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i], rxs); + } + } + } + else // T1 or J1 mode selected + { + if (wc->span.lineconfig & ZT_CONFIG_D4) // D4 framing selected + { // D4 uses only 2 CAS bits per channel + for (i = 0; i < 24; i+=4) // do it in 6 steps + { + a = __t1_framer_in(wc, FR_RS1 + (i>>2)); + + rxs = (a & 0x3) << 2; // get bits.0 and .1 = channel +3 + if (!(wc->span.chans[i+3].sig & ZT_SIG_CLEAR)) + { + if (wc->span.chans[i+3].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i+3], rxs); + } + + rxs = (a & 0xc); // get bits.2 and .3 = channel +2 + if (!(wc->span.chans[i+2].sig & ZT_SIG_CLEAR)) + { + if (wc->span.chans[i+2].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i+2], rxs); + } + + rxs = (a >> 2) & 0xc; // get bits.4 and .5 = channel +1 + if (!(wc->span.chans[i+1].sig & ZT_SIG_CLEAR)) + { + if (wc->span.chans[i+1].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i+1], rxs); + } + + rxs = (a >> 4) & 0xc; // get bits.6 and .7 = channel +0 + if (!(wc->span.chans[i].sig & ZT_SIG_CLEAR)) + { + if (wc->span.chans[i].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i], rxs); + } + } + } + else // ESF framing uses 4 CAS bits per channel + { + for (i = 0; i < 24; i+=2) // do this in 12 steps + { + a = __t1_framer_in(wc, FR_RS1 + (i>>1)); + + rxs = (a & 0xf); // get lower nibble = channel +1 + if (!(wc->span.chans[i+1].sig & ZT_SIG_CLEAR)) + { + if (wc->span.chans[i+1].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i+1], rxs); + } + + rxs = (a >> 4) & 0xf; // get upper nibble = channel +0 + if (!(wc->span.chans[i].sig & ZT_SIG_CLEAR)) + { + if (wc->span.chans[i].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i], rxs); + } + } + } + } +} + + +static void t4_serial_setup(struct t1 *wc) +{ + unsigned int RC,XC; + + switch (wc->spantype) + { + case MODE_E1: // E1 mode + printk("DS1x1F: Setting global parameters for E1 \n"); + break; + + case MODE_UE1: // NEW - Unframed E1 mode + printk("DS1x1F: Setting global parameters for Unchannelized E1 \n"); + break; + + case MODE_J1: // J1 mode + printk("DS1x1F: Setting global parameters for J1 \n"); + break; + + default: // All others default to T1 + printk("DS1x1F: Setting global parameters for T1 \n"); + break; + } + + + RC = 0x000; // Rt = 4 x 125 ns cycle + XC = 0x004; // Xt = 0 x 125 nS cycles + + t1_framer_out(wc, FR_GPC1, 0xe0); // GPC1: Multiplex mode enabled, FSC is output, active low, RCLK from channel 0 + t1_framer_out(wc, FR_IPC, 0x05); // IPC: Interrupt push/pull active low + + t1_framer_out(wc, FR_GCM1, 0x66); + t1_framer_out(wc, FR_GCM2, 0x0e); + t1_framer_out(wc, FR_GCM3, 0x3f); + t1_framer_out(wc, FR_GCM4, 0x0f); + t1_framer_out(wc, FR_GCM5, 0x04); + t1_framer_out(wc, FR_GCM6, 0x3c); + t1_framer_out(wc, FR_GCM7, 0x9c); + t1_framer_out(wc, FR_GCM8, 0x90); + + t1_framer_out(wc, FR_GCR, 0x40); // GCR: Interrupt on Activation/Deactivation of AIX, LOS + t1_framer_out(wc, FR_SIC1, 0x82); // SIC1: 2.048 Mhz clock/bus, double buffer receive / transmit, byte interleaved + t1_framer_out(wc, FR_SIC2, 0x00); // SIC2: No FFS, no center receive eliastic buffer, phase 0 + t1_framer_out(wc, FR_SIC3, 0x08); // SIC3: Edges for capture tx latches with rising edge / rx changes with falling edge + t1_framer_out(wc, FR_CMR1, 0x30); // CMR1: RCLK is at 8.192 Mhz dejittered + t1_framer_out(wc, FR_CMR2, 0x25); // CMR2: sync and clock for tx and rx provided by FALC + + t1_framer_out(wc, FR_XC0, 0x00 | ((XC >> 8) & 0x07)); // XC0: Normal operation of Sa-bits + t1_framer_out(wc, FR_XC1, XC & 0xff); // XC1: tx offset + + t1_framer_out(wc, FR_RC0, 0x00 | ((RC >> 8) & 0x07)); // RC0: rx offset + t1_framer_out(wc, FR_RC1, RC & 0xff); // RC1: + + // ------------- Configure ports --------------- + t1_framer_out(wc, FR_PC1, 0x10); // PC1: FMR/SPYX output/input on RPA/XPA + t1_framer_out(wc, FR_PC2, 0x65); // PC2: unused + t1_framer_out(wc, FR_PC3, 0x65); // PC3: unused + t1_framer_out(wc, FR_PC4, 0x35); // PC4: unused + t1_framer_out(wc, FR_PC5, 0x33); // PC5: XMFS active low, SCLKR is input, RCLK is output + t1_framer_out(wc, FR_PC6, 0x01); // PC6: CLK1 is Tx Clock output, CLK2 is 2.048 Mhz from DCO-R + + t1_framer_out(wc, FR_LCR1, 0x00); // Clear LCR1 + + printk("DS1x1F: Successfully initialized card\n"); +} + + +static void __t1_configure_t1(struct t1 *wc, int lineconfig, int txlevel) +{ + char *mode, *frame, *lcode; + char as1; + char xlt; + + __t1_framer_out(wc, FR_FMR1, 0xbc); // FMR1: Mode 1, T1 mode, CRC on for ESF, 2.048 Mhz system data rate, no XAIS + + switch (monitor) // module parameter monitor + { + case 1: + as1 = 0x00; // Tx = normal / Rx = Hi-Z + xlt = 0x00; + break; + + case 2: + as1 = 0x02; // Tx = Hi-Z / Rx = 100 Ohm + xlt = 0x40; + break; + + case 3: + as1 = 0x00; // Tx = Hi-Z / Rx = Hi-Z + xlt = 0x40; + break; + + default: // Tx = normal / Rx = 100 Ohm + as1 = 0x02; + xlt = 0x00; + break; + } + + /* Configure line interface */ + if (lineconfig & ZT_CONFIG_AMI) + { + lcode = "AMI"; + __t1_framer_out(wc, FR_FMR0, 0xa0); + } + else + { + lcode = "B8ZS"; + __t1_framer_out(wc, FR_FMR0, 0xf0); + } + + if (!(lineconfig & ZT_CONFIG_D4) && !(lineconfig & ZT_CONFIG_ESF)) // support for F4 4 frame format + { + frame = "F4"; + if (loopback) + __t1_framer_out(wc, FR_FMR2, 0x26); + else + __t1_framer_out(wc, FR_FMR2, 0x22); + + if (wc->spantype == MODE_J1) + __t1_framer_out(wc, FR_FMR4, 0x1d); + else + __t1_framer_out(wc, FR_FMR4, 0x0d); + } + + if (!(lineconfig & ZT_CONFIG_D4) && (lineconfig & ZT_CONFIG_ESF)) // standard ESF 24 frame format + { + frame = "ESF"; + + if (loopback) + __t1_framer_out(wc, FR_FMR2, 0xe6); + else + __t1_framer_out(wc, FR_FMR2, 0xe2); + + if (wc->spantype == MODE_J1) + __t1_framer_out(wc, FR_FMR4, 0x1e); + else + __t1_framer_out(wc, FR_FMR4, 0x0e); + } + + if ((lineconfig & ZT_CONFIG_D4) && !(lineconfig & ZT_CONFIG_ESF)) // standard D4 12 frame format + { + frame = "D4"; + if (loopback) + __t1_framer_out(wc, FR_FMR2, 0x26); + else + __t1_framer_out(wc, FR_FMR2, 0x22); + + if (wc->spantype == MODE_J1) + __t1_framer_out(wc, FR_FMR4, 0x1c); + else + __t1_framer_out(wc, FR_FMR4, 0x0c); + } + + if ((lineconfig & ZT_CONFIG_D4) && (lineconfig & ZT_CONFIG_ESF)) // support for SLC96 framing mode used in TR-08 circuits + { + frame = "SLC96"; + if (loopback) + __t1_framer_out(wc, FR_FMR2, 0x26); + else + __t1_framer_out(wc, FR_FMR2, 0x22); + + if (wc->spantype == MODE_J1) + __t1_framer_out(wc, FR_FMR4, 0x1f); + else + __t1_framer_out(wc, FR_FMR4, 0x0f); + } + + + __t1_framer_out(wc, FR_FMR5, 0x40); // FMR5: Enable RBS mode */ + + __t1_framer_out(wc, FR_LIM1, 0xf8); // LIM1: Clear data in case of LOS, Set receiver threshold (0.5V), No remote loop, no DRS + __t1_framer_out(wc, FR_LIM0, 0x08); // LIM0: Enable auto long haul mode, no local loop (must be set after LIM1) + + __t1_framer_out(wc, FR_CMDR, 0x50); // CMDR: Reset the receiver and transmitter line interface + __t1_framer_out(wc, FR_CMDR, 0x00); // CMDR: Reset the receiver and transmitter line interface + + __t1_framer_out(wc, FR_PCD, 0x0a); // PCD: LOS after 176 consecutive "zeros" + __t1_framer_out(wc, FR_PCR, 0x15); // PCR: 22 "ones" clear LOS + + if (wc->spantype == MODE_J1) + { + mode = "J1"; + __t1_framer_out(wc, FR_RC0, 0x80); // J1 overide + } + else + { + mode = "T1"; + } + + switch (txlevel) // Set Tx pulse mask + Rx line build out + { + case 7: + __t1_framer_out(wc, FR_LIM2, 0xe1 | as1); // LIM2: LBO=3, RST=50%, LOS1=1 + __t1_framer_out(wc, FR_XPM0, 0x07); // XPM0 + __t1_framer_out(wc, FR_XPM1, 0x01); // XPM1 + __t1_framer_out(wc, FR_XPM2, 0x00 | xlt); // XPM2 + break; + + case 6: + __t1_framer_out(wc, FR_LIM2, 0xa1 | as1); // LIM2: LBO=2, RST=50%, LOS1=1 + __t1_framer_out(wc, FR_XPM0, 0x8c); // XPM0 + __t1_framer_out(wc, FR_XPM1, 0x11); // XPM1 + __t1_framer_out(wc, FR_XPM2, 0x01 | xlt); // XPM2 + break; + + case 5: + __t1_framer_out(wc, FR_LIM2, 0x61 | as1); // LIM2: LBO=1, RST=50%, LOS1=1 + __t1_framer_out(wc, FR_XPM0, 0x8c); // XPM0 + __t1_framer_out(wc, FR_XPM1, 0x01); // XPM1 + __t1_framer_out(wc, FR_XPM2, 0x00 | xlt); // XPM2 + break; + + default: + __t1_framer_out(wc, FR_LIM2, 0x21 | as1); // LIM2: LBO=0, RST=50%, LOS1=1 + __t1_framer_out(wc, FR_XPM0, 0xd7); // XPM0 + __t1_framer_out(wc, FR_XPM1, 0x22); // XPM1 + __t1_framer_out(wc, FR_XPM2, 0x01 | xlt); // XPM2 + break; + } + + + printk("DS1x1F: Configured Mode: %s Framing: %s Linecode: %s\n", mode, frame, lcode); +} + + +static void __t1_configure_e1(struct t1 *wc, int lineconfig) +{ + unsigned int fmr2, fmr1; + unsigned int cas = 0; + char *crc4 = ""; + char *frame, *lcode; + char as1; + char xlt; + + fmr1 = 0x44; /* FMR1: E1 mode, Automatic force resync, PCM30 mode, 2.048 Mhz backplane, no XAIS */ + fmr2 = 0x03; /* FMR2: Auto transmit remote alarm, auto loss of multiframe recovery, no payload loopback */ + + switch (monitor) // module parameter monitor + { + case 1: + as1 = 0x00; // Tx = normal / Rx = Hi-Z + xlt = 0x00; + break; + + case 2: + as1 = 0x02; // Tx = Hi-Z / Rx = 100 Ohm + xlt = 0x40; + break; + + case 3: + as1 = 0x00; // Tx = Hi-Z / Rx = Hi-Z + xlt = 0x40; + break; + + default: // Tx = normal / Rx = 100 Ohm + as1 = 0x02; + xlt = 0x00; + break; + } + + + if (wc->spantype == MODE_UE1) + fmr2 |= 0x30; + + if (loopback) + fmr2 |= 0x4; + + if (lineconfig & ZT_CONFIG_CRC4) + { + fmr1 |= 0x08; /* CRC4 transmit */ + fmr2 |= 0xc0; /* CRC4 receive */ + crc4 = "/CRC4"; + } + + __t1_framer_out(wc, FR_FMR1, fmr1); + __t1_framer_out(wc, FR_FMR2, fmr2); + + + if (lineconfig & ZT_CONFIG_AMI) /* Configure line interface */ + { + lcode = "AMI"; + __t1_framer_out(wc, FR_FMR0, 0xa0); + } + else + { + lcode = "HDB3"; + __t1_framer_out(wc, FR_FMR0, 0xf0); + } + + if (lineconfig & ZT_CONFIG_CCS) + { + frame = "CCS"; + } + else + { + frame = "CAS"; + cas = 0x40; + } + + if (wc->spantype == MODE_UE1) + __t1_framer_out(wc, FR_LOOP, 0x40); + + __t1_framer_out(wc, FR_LIM1, 0xf0); // LIM1: Clear data in case of LOS, Set receiver threshold (0.5V), No remote loop, no DRS + __t1_framer_out(wc, FR_LIM0, 0x08); // LIM0: Enable auto long haul mode, no local loop (must be after LIM1) + + __t1_framer_out(wc, FR_CMDR, 0x50); // CMDR: Reset the receiver and transmitter line interface + __t1_framer_out(wc, FR_CMDR, 0x00); // CMDR: Reset the receiver and transmitter line interface + + // Condition receive line interface for E1 after reset + __t1_framer_out(wc, 0xbb, 0x17); + __t1_framer_out(wc, 0xbc, 0x55); + __t1_framer_out(wc, 0xbb, 0x97); + __t1_framer_out(wc, 0xbb, 0x11); + __t1_framer_out(wc, 0xbc, 0xaa); + __t1_framer_out(wc, 0xbb, 0x91); + __t1_framer_out(wc, 0xbb, 0x12); + __t1_framer_out(wc, 0xbc, 0x55); + __t1_framer_out(wc, 0xbb, 0x92); + __t1_framer_out(wc, 0xbb, 0x0c); + __t1_framer_out(wc, 0xbb, 0x00); + __t1_framer_out(wc, 0xbb, 0x8c); + + __t1_framer_out(wc, FR_LIM2, 0x20 | as1); // LIM2: 50% peak amplitude is a "1" + __t1_framer_out(wc, FR_PCD, 0x0a); // PCD: LOS after 176 consecutive "zeros" + __t1_framer_out(wc, FR_PCR, 0x15); // PCR: 22 "ones" clear LOS + + __t1_framer_out(wc, FR_XSW, 0x9f); // XSW: Spare bits all to 1 + + if (wc->spantype == MODE_UE1) + __t1_framer_out(wc, FR_XSP, 0x3c ); + else + __t1_framer_out(wc, FR_XSP, 0x1c | cas); // XSP: E-bit set when async. AXS auto, XSIF to 1 + + // Generate pulse mask for E1 + __t1_framer_out(wc, FR_XPM0, 0x54); // XPM0 + __t1_framer_out(wc, FR_XPM1, 0x02); // XPM1 + __t1_framer_out(wc, FR_XPM2, 0x00 | xlt); // XPM2 + + if (wc->spantype == MODE_UE1) + printk("DS1x1F: Configured Mode: E1-Unchannelized Framing: %s Linecode: %s\n", frame, lcode); + else + printk("DS1x1F: Configured Mode: E1%s Framing: %s Linecode: %s\n", crc4, frame, lcode); + +} +//----------------------------------------------------------------------------------------------------------------------------------------- + + +static void ds1_framer_start(struct t1 *wc, struct zt_span *span) +{ + int alreadyrunning = wc->span.flags & ZT_FLAG_RUNNING; + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + + if (wc->spantype & MODE_E1) + { // configure in E1/ UE1 mode + __t1_configure_e1(wc, span->lineconfig); + } + else + { // configure in T1 / J1 mode + __t1_configure_t1(wc, span->lineconfig, span->txlevel); + __ds1_set_clear(wc); + } + + if (!alreadyrunning) + wc->span.flags |= ZT_FLAG_RUNNING; + + spin_unlock_irqrestore(&wc->lock, flags); +} + + +static int ds1_startup(struct zt_span *span) +{ + struct t1 *wc = span->pvt; + + int alreadyrunning = span->flags & ZT_FLAG_RUNNING; + // initialize the start value for the entire chunk of last ec buffer + // Reset framer with proper parameters and start + ds1_framer_start(wc, span); + printk("Calling startup (flags is %d)\n", span->flags); + + ctrl_write_reg(wc, CTRL_REL | CTRL_LRED); // now enable the relay + + + if (!alreadyrunning) + { // Only if we're not already going + ds1_enable_interrupts(wc); + span->flags |= ZT_FLAG_RUNNING; + } + return 0; +} + + +static int ds1_shutdown(struct zt_span *span) +{ + struct t1 *wc = span->pvt; + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + + __t1_framer_out(wc, FR_GCR, 0x41); // GCR: Interrupt on Activation/Deactivation of AIX, LOS + + ds1_disable_interrupts(wc); + + ctrl_write_reg(wc, CTRL_LRED); // turn off relay and turn on RED LED only + + span->flags &= ~ZT_FLAG_RUNNING; + spin_unlock_irqrestore(&wc->lock, flags); + return 0; +} + + +static int ds1_chanconfig(struct zt_chan *chan, int sigtype) +{ + struct t1 *wc = chan->pvt; + unsigned long flags; + int alreadyrunning = chan->span->flags & ZT_FLAG_RUNNING; + + spin_lock_irqsave(&wc->lock, flags); + + if (alreadyrunning && (wc->spantype != MODE_E1)) + __ds1_set_clear(wc); + + spin_unlock_irqrestore(&wc->lock, flags); + return 0; +} + + +static int ds1_spanconfig(struct zt_span *span, struct zt_lineconfig *lc) +{ + span->lineconfig = lc->lineconfig; + span->txlevel = lc->lbo; + span->rxlevel = 0; + /* Do we want to SYNC on receive or not */ + /* If already running, apply changes immediately */ + if (span->flags & ZT_FLAG_RUNNING) + return ds1_startup(span); + + return 0; +} + + +static int ds1_software_init(struct t1 *wc) +{ + int x; /* Find position */ + + for (x = 0; x < WC_MAX_CARDS; x++) + { + if (!cards[x]) + { + cards[x] = wc; + break; + } + } + + if (x >= WC_MAX_CARDS) + return -1; + + t4_serial_setup(wc); + wc->num = x; + + sprintf(wc->span.name, "WCT1/%d" , wc->num); + sprintf(wc->span.desc, "%s Card %d", wc->variety, wc->num); + + wc->span.spanconfig = ds1_spanconfig; + wc->span.chanconfig = ds1_chanconfig; + wc->span.startup = ds1_startup; + wc->span.shutdown = ds1_shutdown; + wc->span.rbsbits = ds1_rbsbits; + wc->span.maint = ds1_maint; + wc->span.open = ds1_open; + wc->span.close = ds1_close; + + switch (wc->spantype) + { + case MODE_UE1: + wc->span.channels = 32; + break; + + case MODE_E1: + wc->span.channels = 31; + break; + + default: + wc->span.channels = 24; + break; + } + + wc->span.chans = wc->chans; + wc->span.flags = ZT_FLAG_RBS; + wc->span.linecompat = ZT_CONFIG_AMI | ZT_CONFIG_B8ZS | ZT_CONFIG_D4 | ZT_CONFIG_ESF; + wc->span.ioctl = ds1_ioctl; + wc->span.pvt = wc; + + if (wc->spantype & MODE_E1) + wc->span.deflaw = ZT_LAW_ALAW; + else + wc->span.deflaw = ZT_LAW_MULAW; + + init_waitqueue_head(&wc->span.maintq); + + for (x=0;xspan.channels;x++) + { + sprintf(wc->chans[x].name, "WCT1/%d/%d", wc->num, x + 1); + wc->chans[x].sigcap = ZT_SIG_EM | ZT_SIG_CLEAR | ZT_SIG_EM_E1 | + ZT_SIG_FXSLS | ZT_SIG_FXSGS | + ZT_SIG_FXSKS | ZT_SIG_FXOLS | ZT_SIG_DACS_RBS | + ZT_SIG_FXOGS | ZT_SIG_FXOKS | ZT_SIG_CAS | ZT_SIG_SF; + wc->chans[x].pvt = wc; + wc->chans[x].chanpos = x + 1; + } + + if (zt_register(&wc->span, 0)) + { + printk("Unable to register span with zaptel\n"); + return -1; + } + return 0; +} + + +static inline void __handle_leds(struct t1 *wc) +{ + int oldreg; + + wc->blinktimer++; + + if (wc->blinktimer >= 4000) + wc->blinktimer = 0; + + oldreg = wc->ledtestreg; + + if (wc->span.alarms & ZT_ALARM_RED) // RED alarm active + { // LOS = loss of signal + if (wc->blinktimer < 50) + wc->ledtestreg = CTRL_LRED; + else + wc->ledtestreg = CTRL_LOFF; + + if (wc->blinktimer >= 200) // very fast flickering + wc->blinktimer = 0; + + } + else + { + if (wc->span.alarms & ZT_ALARM_YELLOW) // YELLOW ALARM = RED LED flashing + { // other side can't see our signal + if (wc->blinktimer < 250) + wc->ledtestreg = CTRL_LRED; + else + wc->ledtestreg = CTRL_LOFF; + + if (wc->blinktimer >= 500) // flashing + wc->blinktimer = 0; + } + else + { + if (wc->span.alarms & ZT_ALARM_BLUE) // BLUE ALARM + { // T1 failure in front of a repeater down the line + if (wc->blinktimer < 250) + wc->ledtestreg = CTRL_LGRN; // GREEN LED flashing + else + wc->ledtestreg = CTRL_LOFF; + + if (wc->blinktimer >= 500) // flashing + wc->blinktimer = 0; + } + else + { // NO ALARM + if (wc->blinktimer < 150) // slow winking green led heartbeat + wc->ledtestreg = CTRL_LOFF; + else + wc->ledtestreg = CTRL_LGRN; // GREEN LED on most of the time + } + } + } + + if (oldreg != wc->ledtestreg) // only when some bits have changed + led_write_reg(wc, wc->ledtestreg); // write LED bits 0,1 +} + + +//====================================== SEND MEDIA DATA ===================================== +static void ds1_media(struct t1 *wc) +{ + int x,y,ty,ry,shift; + unsigned int tx,rx; + char frm; + + frm = (clk_read_reg(wc) & 0x07) + 1; // see how many frames are to be read by card + + ty = wc->txchunkptr; + ry = wc->rxchunkptr; + + + for (y = 0; y < frm; y++) + { + int reg = y * 32; + + if (ty >= ZT_CHUNKSIZE) + { + ty = 0; + zt_transmit(&wc->span); + } + + rx = ioread32(wc->ioaddr + OFS_TDM_RD + 0 + reg); // read in first 4 channels + tx = 0xff; // FALC-56 uses TS0 for sync purposes only + + for (x=1; x<=wc->span.channels; x++) + { + if ((x % 4) == 0) + { + iowrite32(tx, wc->ioaddr + OFS_TDM_WR + x - 4 + reg); // write 4 bytes (channels) of pcm data + tx = 0; + rx = ioread32(wc->ioaddr + OFS_TDM_RD + x + reg); + } + + shift = ((x % 4) << 3); + tx |= (wc->chans[x-1].writechunk[ty] & 0xff) << shift; + wc->chans[x-1].readchunk [ry] = (rx >> shift) & 0xff; + } + + iowrite32(tx, wc->ioaddr + OFS_TDM_WR + (x & 0xfc) + reg); // write 4 bytes (channels) of pcm data + + ty++; + ry++; + + if (ry >= ZT_CHUNKSIZE) + { + ry = 0; + + for (x=0; xspan.channels; x++) // handle echocanceller memory fill + { + zt_ec_chunk(&wc->chans[x], wc->chans[x].readchunk, wc->ec_chunk2[x]); + + memcpy(wc->ec_chunk2[x], wc->ec_chunk1[x] , ZT_CHUNKSIZE); + memcpy(wc->ec_chunk1[x], wc->chans[x].writechunk, ZT_CHUNKSIZE); + } + + zt_receive(&wc->span); + } + } + + wc->txchunkptr = ty; + wc->rxchunkptr = ry; +} + + + +//=============================================================================================================================================================================== + + +static void __t1_check_alarms(struct t1 *wc) +{ + unsigned char c,d; + int alarms; + int x,j; + + if (!(wc->span.flags & ZT_FLAG_RUNNING)) + return; + + c = __t1_framer_in(wc, FR_FRS0); + d = __t1_framer_in(wc, FR_FRS1); + + /* Assume no alarms */ + alarms = 0; + + /* And consider only carrier alarms */ + wc->span.alarms &= (ZT_ALARM_RED | ZT_ALARM_BLUE | ZT_ALARM_NOTOPEN); + + if (wc->spantype & MODE_E1) + { + if (c & 0x04) + { /* No multiframe found, force RAI high after 400ms only if we haven't found a multiframe since last loss of frame */ + if (!(wc->spanflags & FLAG_NMF)) + { + __t1_framer_out(wc, FR_FMR4, 0x9f | 0x20); /* FMR4: Force RAI High */ + wc->spanflags |= FLAG_NMF; + printk("NMF workaround on!\n"); + } + + __t1_framer_out(wc, FR_FMR2, 0xc3); /* Reset to CRC4 mode */ + __t1_framer_out(wc, FR_FMR0, 0xf2); /* Force Resync */ + __t1_framer_out(wc, FR_FMR0, 0xf0); /* Force Resync */ + } + else + { + if (!(c & 0x02)) + { + if ((wc->spanflags & FLAG_NMF)) + { + __t1_framer_out(wc, FR_FMR4, 0x9f); /* FMR4: Clear forced RAI */ + wc->spanflags &= ~FLAG_NMF; + printk("NMF workaround off!\n"); + } + } + } + } + else + { /* Detect loopup code if we're not sending one */ + if ((!wc->span.mainttimer) && (d & 0x08)) + { + if ((wc->loopupcnt++ > 80) && (wc->span.maintstat != ZT_MAINT_REMOTELOOP)) // Loop-up code detected + { + __t1_framer_out(wc, FR_LIM0, 0x08); // LIM0: Disable any local loop + __t1_framer_out(wc, FR_LIM1, 0xf6); // LIM1: Enable remote loop + + wc->span.maintstat = ZT_MAINT_REMOTELOOP; // maintainance status = REMOTELOOP + } + } + else + wc->loopupcnt = 0; + + /* Same for loopdown code */ + if ((!wc->span.mainttimer) && (d & 0x10)) + { /* Loop-down code detected */ + if ((wc->loopdowncnt++ > 80) && (wc->span.maintstat == ZT_MAINT_REMOTELOOP)) + { + __t1_framer_out(wc, FR_LIM0, 0x08); /* LIM0: Disable any local loop */ + __t1_framer_out(wc, FR_LIM1, 0xf0); /* LIM1: Disable remote loop */ + wc->span.maintstat = ZT_MAINT_NONE; + } + } + else + wc->loopdowncnt = 0; + } + + if (wc->span.lineconfig & ZT_CONFIG_NOTOPEN) + { + for (x=0,j=0;x < wc->span.channels;x++) + { + if ((wc->span.chans[x].flags & ZT_FLAG_OPEN) || (wc->span.chans[x].flags & ZT_FLAG_NETDEV)) + j++; + } + + if (!j) + alarms |= ZT_ALARM_NOTOPEN; + } + + if (c & 0xa0) + { + if (wc->alarmcount >= alarmdebounce) + { + if (!(wc->spantype & 0x80)) + alarms |= ZT_ALARM_RED; + } + else + wc->alarmcount++; + } + else + wc->alarmcount = 0; + + if (c & 0x4) + alarms |= ZT_ALARM_BLUE; + + /* Keep track of recovering */ + if ((!alarms) && wc->span.alarms) + wc->alarmtimer = ZT_ALARMSETTLE_TIME; + + if (wc->alarmtimer) + alarms |= ZT_ALARM_RECOVER; + + /* If receiving alarms, go into Yellow alarm state */ + if (alarms && !(wc->spanflags & FLAG_SENDINGYELLOW)) + { + unsigned char fmr4; + printk("DS1x1F: Setting yellow alarm\n"); + /* We manually do yellow alarm to handle RECOVER and NOTOPEN, otherwise it's auto anyway */ + fmr4 = __t1_framer_in(wc, FR_FMR4); + __t1_framer_out(wc, FR_FMR4, fmr4 | 0x20); + + wc->spanflags |= FLAG_SENDINGYELLOW; + } + else + { + if ((!alarms) && (wc->spanflags & FLAG_SENDINGYELLOW)) + { + unsigned char fmr4; + printk("DS1x1F: Clearing yellow alarm\n"); + // We manually do yellow alarm to handle RECOVER + fmr4 = __t1_framer_in(wc, FR_FMR4); + __t1_framer_out(wc, FR_FMR4, fmr4 & ~0x20); + + wc->spanflags &= ~FLAG_SENDINGYELLOW; + } + } + // Re-check the timing source when we enter/leave alarm, not withstanding yellow alarm + if ((c & 0x10) && !(wc->spantype & 0x80)) + alarms |= ZT_ALARM_YELLOW; + + if (wc->span.mainttimer || wc->span.maintstat) + alarms |= ZT_ALARM_LOOPBACK; + + wc->span.alarms = alarms; + zt_alarm_notify(&wc->span); +} + + +static void __ds1_do_counters(struct t1 *wc) +{ + if (wc->alarmtimer) + { + if (!--wc->alarmtimer) + { + wc->span.alarms &= ~(ZT_ALARM_RECOVER); + zt_alarm_notify(&wc->span); + } + } +} + +///-------------------------------------------------------------------------------------------------------------------------------- +ZAP_IRQ_HANDLER(ds1_interrupt) +{ + struct t1 *wc = dev_id; + unsigned long flags; + unsigned int x; + int y; + + x = ctrl_read_reg(wc); // read statusregister + + if (!(x & CTRL_IRQ)) + return IRQ_NONE; + + ctrl_set_reg(wc, CTRL_INTA); // clear the interrupt flag + + if (!wc->intcount && debug) + printk("DS1x1F: Got interrupt\n"); + + if (x & CTRL_IRM) // we missed at least one interrupt + printk("DS1x1F: Missed an Interrupt !!!\n"); + + ds1_media(wc); // handle media / audio- buffers + + x = clk_read_reg(wc) & 0x07; // get the current frame buffer setting + + spin_lock_irqsave(&wc->lock, flags); + + y = wc->prescaler - 1; + + if (y < 1) + { + y = 8 / (x + 1); + + __handle_leds(wc); + // Count down timers + __ds1_do_counters(wc); + + wc->intcount++; + + x = wc->intcount & 0x0f; // Do some things that we don't have to do very often + + switch(x) + { + case 0: + case 1: + break; + case 2: + __t1_check_sigbits(wc); + break; + case 4: // Check alarms 1/4 as frequently + if (!(wc->intcount & 0x30)) + __t1_check_alarms(wc); + break; + } + } + wc->prescaler = y; + + spin_unlock_irqrestore(&wc->lock, flags); + + return IRQ_RETVAL(1); +} + +static int memory_test(struct t1 *wc, int pattern) +{ + int ret = 0; + int x, y; + + for (x=0; x<31; x++) // clear out tx tdm memory to FF + { + iowrite32 (pattern, wc->ioaddr + OFS_TDM_WR + (8 * x) ); + + y = ioread32( wc->ioaddr + OFS_TDM_WR + (8 * x) ); + + if (y != pattern) + { + ret = 1; + if (debug) + printk("DS1x1F: TDM tx memory failure %08x / %08x @ %02x LSB\n", pattern, y, x); + } + else + if (debug) + printk("DS1x1F: TDM tx memory OK %08x / %08x @ %02x LSB\n", pattern, y, x); + + iowrite32 (pattern, wc->ioaddr + OFS_TDM_RD + (8 * x)); + + y = ioread32( wc->ioaddr + OFS_TDM_RD + (8 * x)); + + if (y != pattern) + { + ret = 1; + if (debug) + printk("DS1x1F: TDM rx memory failure %08x / %08x @ %02x MSB\n", pattern, y, x); + } + else + if (debug) + printk("DS1x1F: TDM rx memory OK %08x / %08x @ %02x MSB\n", pattern, y, x); + + + } + return (ret); +} + + +//-------------------------------------------------------------------------------------------------------------------------------------- +static int ds1_hardware_init(struct t1 *wc) +{ + unsigned int vstr, wid; + unsigned int x; + + if (frames < 1) // setting frames = 0 autoadjust to current ZT_CHUNKSIZE + frames = ZT_CHUNKSIZE; + + if (frames > 8) // maximum framebuffer size is 8 frames + frames = 8; + + if (extclk == -1) // extclk not specified by parameter + { + x = ctrl_read_reg(wc); // read DIP switch + extclk = (x >> 14) & 0x03; // SW 3-4 => clock selection + } + + switch (extclk) // external / H.100 bus clocking options + { + case 1: // clock derived from H.100 bus if available + x = 0x30 | (frames - 1); + break; + + case 2: // this card is bus clock master + x = 0x20 | (frames - 1); + break; + + default: // all other cases - internally clocked / no clk output + x = frames - 1; + break; + } + + clk_write_reg (wc, x); // no loopback + + mdelay(10); // wait for 10 msec. for FDET settle time + + x = clk_read_reg(wc); + + switch (x & 0x30) + { + case 0x20: + printk("DS1x1F: Clocking: Busmaster Framebuffer: %d frames\n",(x & 0x07) + 1); + break; + + case 0x30: + if (x & 0x00010000) + printk("DS1x1F: Clocking: External ( clock present on bus) Framebuffer: %d frames\n",(x & 0x07) + 1); + else + printk("DS1x1F: Clocking: (External) no clock present on bus Framebuffer: %d frames\n",(x & 0x07) + 1); + break; + + default: + printk("DS1x1F: Clocking: Internal Framebuffer: %d frames\n",(x & 0x07) + 1); + break; + } + + ctrl_write_reg(wc, CTRL_INTA | CTRL_TEST | CTRL_FRST); + + mdelay(10); // wait for 10 msec. + + x = ctrl_read_reg(wc); // read in the controlregister + + ctrl_write_reg(wc, CTRL_LRED); + + mdelay(1); // wait for 1 msec. + + if (cardmode != -1) + wc->spantype = cardmode; // read in spanmode paramter + else + switch (x & 0x3000) // check if SW-1 and SW-2 + { + case 0x0000: + wc->spantype = MODE_T1; // OFF - OFF = T1 + break; + + case 0x1000: + wc->spantype = MODE_J1; // OFF - ON = J1 + break; + + case 0x2000: + wc->spantype = MODE_UE1; // ON - OFF = unchannelized E1 + break; + + case 0x3000: + wc->spantype = MODE_E1; // ON - ON = E1 + break; + } + + if (x & CTRL_RELI) + printk("DS1x1 Board Ver: %01x.%01x SW=%01x-%01x-%01x-%01x [%x]\n",(x>>28),((x>>20)&0xf),((x>>15)&1),((x>>14)&1),((x>>13)&1),((x>>12)&1),x); + else + printk("DS1x1F Board Ver: %01x.%01x SW=%01x-%01x-%01x-%01x [%x]\n",(x>>28),((x>>20)&0xf),((x>>15)&1),((x>>14)&1),((x>>13)&1),((x>>12)&1),x); + + vstr = t1_framer_in(wc ,FR_VSTR); // identify FALC framer chip version + wid = t1_framer_in(wc ,FR_WID ); + + if (vstr == 0x05) + { + if (wid & 0xc0) + printk("FALC PEF-2256 Ver: 2.2 detected\n"); + else + printk("FALC PEF-2256 Ver: 2.1 detected\n"); + } + else + { + if ((wid & 0x03) == 0x03) + printk("!!! WARNING !!! Old FALC PEB-2256 Ver: 1.2 detected\n"); + else + printk("!!! WARNING !!! FALC Version unknown: VSTR: %02x WID: %02x\n", vstr, wid); + } + + ds1_disable_interrupts(wc); + + x = 0; + + if (memory_test (wc, 0x5555aaaa)) x = 1; + if (memory_test (wc, 0x01234567)) x = 1; + if (memory_test (wc, 0xaaaa5555)) x = 1; + if (memory_test (wc, 0x76543210)) x = 1; + + if (x) + printk("DS1x1F: Card TDM memory test failed !!!\n"); + else + printk("DS1x1F: Card TDM memory test completed.\n"); + + ds1_enable_interrupts(wc); + + start_alarm(wc); + return 0; + +} + + +static int __devinit ds1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct t1 *wc; + unsigned long end; + + + if (pci_enable_device(pdev)) + { + printk (KERN_ALERT "DS1x1F: PCI enable failed\n"); + pci_disable_device (pdev); + return (-EIO); + } + else + { + wc = kmalloc(sizeof(struct t1), GFP_KERNEL); + + printk("Trying to init an DS1x1F\n"); + + if (wc) + { + memset(wc, 0x0, sizeof(struct t1)); // initialize structure with 0x00 + spin_lock_init(&wc->lock); + + wc->cnstart = pci_resource_start(pdev, 0); // read the assigned memory resources for region 0 + end = pci_resource_end (pdev, 0); + wc->cnlen = end - wc->cnstart + 1; + wc->cres = request_mem_region(wc->cnstart,wc->cnlen,name); + wc->cnaddr = ioremap_nocache (wc->cnstart,wc->cnlen ); + + if(debug) + printk (KERN_INFO "Config resources = [0x%lx-0x%lx] (%ld) \n", wc->cnstart, end, wc->cnlen); + + wc->iostart = pci_resource_start(pdev, 2); // read the assigned memory resources for region 2 + end = pci_resource_end (pdev, 2); + wc->iolen = end - wc->iostart + 1; + wc->ires = request_mem_region(wc->iostart,wc->iolen,name); + wc->ioaddr = ioremap_nocache (wc->iostart,wc->iolen ); + + if(debug) + printk (KERN_INFO "I/O resources = [0x%lx-0x%lx] (%ld) \n", wc->iostart, end, wc->iolen); + + wc->dev = pdev; + + pci_set_drvdata(pdev, wc); // keep track of our device - i.e. for later removal + + if (request_irq(pdev->irq, ds1_interrupt, ZAP_IRQ_SHARED_DISABLED, "ds1x1f", wc)) + { + printk("DS1x1F: Unable to request IRQ %d\n", pdev->irq); + kfree(wc); + return -EIO; + } + + ds1_hardware_init(wc); // Initialize hardware + + wc->variety = "DS1x1F T1/E1-card"; // We now know which version of card we have + ds1_software_init(wc); // Misc. software stuff + + printk("Found an: %s\n", wc->variety); + return (0); + } + else + { + printk (KERN_ALERT "DS1x1F: failed to allocate memory\n"); + return (-ENOMEM); + } + } + return (0); +} + + +static void __devexit ds1_remove(struct pci_dev *pdev) +{ + struct t1 *wc = pci_get_drvdata(pdev); + + if (wc) + { + + ds1_disable_interrupts(wc); // In case hardware is still there + free_irq(pdev->irq, wc); // release the interrupt resource + mdelay(10); + + zt_unregister(&wc->span); + + if (debug) + printk (KERN_ALERT "DS1x1F: resetting relay / falc / red led on\n"); + + ctrl_write_reg(wc, CTRL_FRST | CTRL_LRED); // reset FALC, relay= off, Red LED = on + + if (wc->ioaddr != NULL) // unmap io space + iounmap (wc->ioaddr); + + if (wc->cnaddr != NULL) // unmap control space + iounmap (wc->cnaddr); + + release_mem_region(wc->iostart, wc->iolen); // release io memory region + release_mem_region(wc->cnstart, wc->cnlen); // release control memory region + + kfree(wc); + + printk (KERN_ALERT "DS1x1F: disable pci device\n"); + pci_disable_device (pdev); + } + else + printk (KERN_ALERT "DS1x1F: WARNING Removal Failed !!!\n"); + +} + + +static struct pci_device_id ds1_ids[] = { { PCI_DEVICE( 0x2321, 0x011f) }, { 0, }, }; + + +MODULE_DEVICE_TABLE(pci,ds1_ids); + + +static struct pci_driver ds1_driver = { + name: "ds1x1f", + probe: ds1_probe, + remove: __devexit_p(ds1_remove), + suspend: NULL, + resume: NULL, + id_table: ds1_ids, +}; + + +static int __init ds1_init(void) +{ + int res; + res = zap_pci_module(&ds1_driver); + + if (res) + return -ENODEV; + + return 0; +} + + +static void __exit ds1_cleanup(void) +{ + pci_unregister_driver(&ds1_driver); +} + + + +module_param(alarmdebounce, int, 0600); +module_param(loopback, int, 0600); +module_param(cardmode, int, 0600); +module_param(frames, int, 0600); +module_param(debug, int, 0600); +module_param(extclk, int, 0600); +module_param(monitor, int, 0600); + +MODULE_DESCRIPTION("TC-DG ds1x1f Zaptel Driver"); +MODULE_AUTHOR("TCDG Corp. + * and David Rowe + * + * Copyright (C) 2001, 2003 Steve Underwood, 2007 David Rowe + * + * Based on a bit from here, a bit from there, eye of toad, ear of + * bat, 15 years of failed attempts by David and a few fried brain + * cells. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * 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. + * + * $Id: echo.c,v 1.20 2006/12/01 18:00:48 steveu Exp $ + */ + +/*! \file */ + +/* Implementation Notes + David Rowe + April 2007 + + This code started life as Steve's NLMS algorithm with a tap + rotation algorithm to handle divergence during double talk. I + added a Geigel Double Talk Detector (DTD) [2] and performed some + G168 tests. However I had trouble meeting the G168 requirements, + especially for double talk - there were always cases where my DTD + failed, for example where near end speech was under the 6dB + threshold required for declaring double talk. + + So I tried a two path algorithm [1], which has so far given better + results. The original tap rotation/Geigel algorithm is available + in SVN http://svn.rowetel.com/software/oslec/tags/before_16bit. + It's probably possible to make it work if some one wants to put some + serious work into it. + + At present no special treatment is provided for tones, which + generally cause NLMS algorithms to diverge. Initial runs of a + subset of the G168 tests for tones (e.g ./echo_test 6) show the + current algorithm is passing OK, which is kind of surprising. The + full set of tests needs to be performed to confirm this result. + + One other interesting change is that I have managed to get the NLMS + code to work with 16 bit coefficients, rather than the original 32 + bit coefficents. This reduces the MIPs and storage required. + I evaulated the 16 bit port using g168_tests.sh and listening tests + on 4 real-world samples. + + I also attempted the implementation of a block based NLMS update + [2] but although this passes g168_tests.sh it didn't converge well + on the real-world samples. I have no idea why, perhaps a scaling + problem. The block based code is also available in SVN + http://svn.rowetel.com/software/oslec/tags/before_16bit. If this + code can be debugged, it will lead to further reduction in MIPS, as + the block update code maps nicely onto DSP instruction sets (it's a + dot product) compared to the current sample-by-sample update. + + Steve also has some nice notes on echo cancellers in echo.h + + + References: + + [1] Ochiai, Areseki, and Ogihara, "Echo Canceller with Two Echo + Path Models", IEEE Transactions on communications, COM-25, + No. 6, June + 1977. + http://www.rowetel.com/images/echo/dual_path_paper.pdf + + [2] The classic, very useful paper that tells you how to + actually build a real world echo canceller: + Messerschmitt, Hedberg, Cole, Haoui, Winship, "Digital Voice + Echo Canceller with a TMS320020, + http://www.rowetel.com/images/echo/spra129.pdf + + [3] I have written a series of blog posts on this work, here is + Part 1: http://www.rowetel.com/blog/?p=18 + + [4] The source code http://svn.rowetel.com/software/oslec/ + + [5] A nice reference on LMS filters: + http://en.wikipedia.org/wiki/Least_mean_squares_filter + + Credits: + + Thanks to Steve Underwood, Jean-Marc Valin, and Ramakrishnan + Muthukrishnan for their suggestions and email discussions. Thanks + also to those people who collected echo samples for me such as + Mark, Pawel, and Pavel. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif +#ifdef __KERNEL__ +#include /* We're doing kernel work */ +#include +#include +#include +#define malloc(a) kmalloc((a), GFP_KERNEL) +#define free(a) kfree(a) +#else +#include +#include +#include +#include + +#endif + +#include "spandsp/bit_operations.h" +#include "spandsp/echo.h" + +#if !defined(NULL) +#define NULL (void *) 0 +#endif +#if !defined(FALSE) +#define FALSE 0 +#endif +#if !defined(TRUE) +#define TRUE (!FALSE) +#endif + +#define MIN_TX_POWER_FOR_ADAPTION 64 +#define MIN_RX_POWER_FOR_ADAPTION 64 +#define DTD_HANGOVER 600 /* 600 samples, or 75ms */ +#define DC_LOG2BETA 3 /* log2() of DC filter Beta */ + +/*-----------------------------------------------------------------------*\ + + FUNCTIONS + +\*-----------------------------------------------------------------------*/ + +/* adapting coeffs using the traditional stochastic descent (N)LMS algorithm */ + + +#ifdef __BLACKFIN_ASM__ +static void __inline__ lms_adapt_bg(echo_can_state_t *ec, int clean, int shift) +{ + int i, j; + int offset1; + int offset2; + int factor; + int exp; + int16_t *phist; + int n; + + if (shift > 0) + factor = clean << shift; + else + factor = clean >> -shift; + + /* Update the FIR taps */ + + offset2 = ec->curr_pos; + offset1 = ec->taps - offset2; + phist = &ec->fir_state_bg.history[offset2]; + + /* st: and en: help us locate the assembler in echo.s */ + + //asm("st:"); + n = ec->taps; + for (i = 0, j = offset2; i < n; i++, j++) + { + exp = *phist++ * factor; + ec->fir_taps16[1][i] += (int16_t) ((exp+(1<<14)) >> 15); + } + //asm("en:"); + + /* Note the asm for the inner loop above generated by Blackfin gcc + 4.1.1 is pretty good (note even parallel instructions used): + + R0 = W [P0++] (X); + R0 *= R2; + R0 = R0 + R3 (NS) || + R1 = W [P1] (X) || + nop; + R0 >>>= 15; + R0 = R0 + R1; + W [P1++] = R0; + + A block based update algorithm would be much faster but the + above can't be improved on much. Every instruction saved in + the loop above is 2 MIPs/ch! The for loop above is where the + Blackfin spends most of it's time - about 17 MIPs/ch measured + with speedtest.c with 256 taps (32ms). Write-back and + Write-through cache gave about the same performance. + */ +} + +/* + IDEAS for further optimisation of lms_adapt_bg(): + + 1/ The rounding is quite costly. Could we keep as 32 bit coeffs + then make filter pluck the MS 16-bits of the coeffs when filtering? + However this would lower potential optimisation of filter, as I + think the dual-MAC architecture requires packed 16 bit coeffs. + + 2/ Block based update would be more efficient, as per comments above, + could use dual MAC architecture. + + 3/ Look for same sample Blackfin LMS code, see if we can get dual-MAC + packing. + + 4/ Execute the whole e/c in a block of say 20ms rather than sample + by sample. Processing a few samples every ms is inefficient. +*/ + +#else +static __inline__ void lms_adapt_bg(echo_can_state_t *ec, int clean, int shift) +{ + int i; + + int offset1; + int offset2; + int factor; + int exp; + + if (shift > 0) + factor = clean << shift; + else + factor = clean >> -shift; + + /* Update the FIR taps */ + + offset2 = ec->curr_pos; + offset1 = ec->taps - offset2; + + for (i = ec->taps - 1; i >= offset1; i--) + { + exp = (ec->fir_state_bg.history[i - offset1]*factor); + ec->fir_taps16[1][i] += (int16_t) ((exp+(1<<14)) >> 15); + } + for ( ; i >= 0; i--) + { + exp = (ec->fir_state_bg.history[i + offset2]*factor); + ec->fir_taps16[1][i] += (int16_t) ((exp+(1<<14)) >> 15); + } +} +#endif + +/*- End of function --------------------------------------------------------*/ + +echo_can_state_t *echo_can_create(int len, int adaption_mode) +{ + echo_can_state_t *ec; + int i; + int j; + + ec = (echo_can_state_t *) malloc(sizeof(*ec)); + if (ec == NULL) + return NULL; + memset(ec, 0, sizeof(*ec)); + + ec->taps = len; + ec->log2taps = top_bit(len); + ec->curr_pos = ec->taps - 1; + + for (i = 0; i < 2; i++) + { + if ((ec->fir_taps16[i] = (int16_t *) malloc((ec->taps)*sizeof(int16_t))) == NULL) + { + for (j = 0; j < i; j++) + free(ec->fir_taps16[j]); + free(ec); + return NULL; + } + memset(ec->fir_taps16[i], 0, (ec->taps)*sizeof(int16_t)); + } + + fir16_create(&ec->fir_state, + ec->fir_taps16[0], + ec->taps); + fir16_create(&ec->fir_state_bg, + ec->fir_taps16[1], + ec->taps); + + for(i=0; i<5; i++) { + ec->xvtx[i] = ec->yvtx[i] = ec->xvrx[i] = ec->yvrx[i] = 0; + } + + ec->cng_level = 1000; + echo_can_adaption_mode(ec, adaption_mode); + + ec->snapshot = (int16_t*)malloc(ec->taps*sizeof(int16_t)); + memset(ec->snapshot, 0, sizeof(int16_t)*ec->taps); + + ec->cond_met = 0; + ec->Pstates = 0; + ec->Ltxacc = ec->Lrxacc = ec->Lcleanacc = ec->Lclean_bgacc = 0; + ec->Ltx = ec->Lrx = ec->Lclean = ec->Lclean_bg = 0; + ec->tx_1 = ec->tx_2 = ec->rx_1 = ec->rx_2 = 0; + ec->Lbgn = ec->Lbgn_acc = 0; + ec->Lbgn_upper = 200; + ec->Lbgn_upper_acc = ec->Lbgn_upper << 13; + + return ec; +} +/*- End of function --------------------------------------------------------*/ + +void echo_can_free(echo_can_state_t *ec) +{ + int i; + + fir16_free(&ec->fir_state); + for (i = 0; i < 2; i++) + free(ec->fir_taps16[i]); + free(ec->snapshot); + free(ec); +} +/*- End of function --------------------------------------------------------*/ + +void echo_can_adaption_mode(echo_can_state_t *ec, int adaption_mode) +{ + ec->adaption_mode = adaption_mode; +} +/*- End of function --------------------------------------------------------*/ + +void echo_can_flush(echo_can_state_t *ec) +{ + int i; + + ec->Ltxacc = ec->Lrxacc = ec->Lcleanacc = ec->Lclean_bgacc = 0; + ec->Ltx = ec->Lrx = ec->Lclean = ec->Lclean_bg = 0; + ec->tx_1 = ec->tx_2 = ec->rx_1 = ec->rx_2 = 0; + + ec->Lbgn = ec->Lbgn_acc = 0; + ec->Lbgn_upper = 200; + ec->Lbgn_upper_acc = ec->Lbgn_upper << 13; + + ec->nonupdate_dwell = 0; + + fir16_flush(&ec->fir_state); + fir16_flush(&ec->fir_state_bg); + ec->fir_state.curr_pos = ec->taps - 1; + ec->fir_state_bg.curr_pos = ec->taps - 1; + for (i = 0; i < 2; i++) + memset(ec->fir_taps16[i], 0, ec->taps*sizeof(int16_t)); + + ec->curr_pos = ec->taps - 1; + ec->Pstates = 0; +} +/*- End of function --------------------------------------------------------*/ + +void echo_can_snapshot(echo_can_state_t *ec) { + memcpy(ec->snapshot, ec->fir_taps16[0], ec->taps*sizeof(int16_t)); +} +/*- End of function --------------------------------------------------------*/ + +/* Dual Path Echo Canceller ------------------------------------------------*/ + +int16_t echo_can_update(echo_can_state_t *ec, int16_t tx, int16_t rx) +{ + int32_t echo_value; + int clean_bg; + int tmp; + + /* Input scaling was found be required to prevent problems when tx + starts clipping. Another possible way to handle this would be the + filter coefficent scaling. */ + + ec->tx = tx; ec->rx = rx; + tx >>=1; + rx >>=1; + + /* + Filter DC, 3dB point is 160Hz (I think), note 32 bit precision required + otherwise values do not track down to 0. Zero at DC, Pole at (1-Beta) + only real axis. Some chip sets (like Si labs) don't need + this, but something like a $10 X100P card does. Any DC really slows + down convergence. + + Note: removes some low frequency from the signal, this reduces + the speech quality when listening to samples through headphones + but may not be obvious through a telephone handset. + + Note that the 3dB frequency in radians is approx Beta, e.g. for + Beta = 2^(-3) = 0.125, 3dB freq is 0.125 rads = 159Hz. + */ + + if (ec->adaption_mode & ECHO_CAN_USE_RX_HPF) { + tmp = rx << 15; + ec->rx_1 += -(ec->rx_1>>DC_LOG2BETA) + tmp - ec->rx_2; + rx = ec->rx_1 >> 15; + ec->rx_2 = tmp; + } + + /* Block average of power in the filter states. Used for + adaption power calculation. */ + + { + int new, old; + + /* efficient "out with the old and in with the new" algorithm so + we don't have to recalculate over the whole block of + samples. */ + new = (int)tx * (int)tx; + old = (int)ec->fir_state.history[ec->fir_state.curr_pos] * + (int)ec->fir_state.history[ec->fir_state.curr_pos]; + ec->Pstates += ((new - old) + (1<log2taps)) >> ec->log2taps; + if (ec->Pstates < 0) ec->Pstates = 0; + } + + /* Calculate short term average levels using simple single pole IIRs */ + + ec->Ltxacc += abs(tx) - ec->Ltx; + ec->Ltx = (ec->Ltxacc + (1<<4)) >> 5; + ec->Lrxacc += abs(rx) - ec->Lrx; + ec->Lrx = (ec->Lrxacc + (1<<4)) >> 5; + + /* Foreground filter ---------------------------------------------------*/ + + ec->fir_state.coeffs = ec->fir_taps16[0]; + echo_value = fir16(&ec->fir_state, tx); + ec->clean = rx - echo_value; + ec->Lcleanacc += abs(ec->clean) - ec->Lclean; + ec->Lclean = (ec->Lcleanacc + (1<<4)) >> 5; + + /* Background filter ---------------------------------------------------*/ + + echo_value = fir16(&ec->fir_state_bg, tx); + clean_bg = rx - echo_value; + ec->Lclean_bgacc += abs(clean_bg) - ec->Lclean_bg; + ec->Lclean_bg = (ec->Lclean_bgacc + (1<<4)) >> 5; + + /* Background Filter adaption -----------------------------------------*/ + + /* Almost always adap bg filter, just simple DT and energy + detection to minimise adaption in cases of strong double talk. + However this is not critical for the dual path algorithm. + */ + ec->factor = 0; + ec->shift = 0; + if ((ec->nonupdate_dwell == 0)) { + int P, logP, shift; + + /* Determine: + + f = Beta * clean_bg_rx/P ------ (1) + + where P is the total power in the filter states. + + The Boffins have shown that if we obey (1) we converge + quickly and avoid instability. + + The correct factor f must be in Q30, as this is the fixed + point format required by the lms_adapt_bg() function, + therefore the scaled version of (1) is: + + (2^30) * f = (2^30) * Beta * clean_bg_rx/P + factor = (2^30) * Beta * clean_bg_rx/P ----- (2) + + We have chosen Beta = 0.25 by experiment, so: + + factor = (2^30) * (2^-2) * clean_bg_rx/P + + (30 - 2 - log2(P)) + factor = clean_bg_rx 2 ----- (3) + + To avoid a divide we approximate log2(P) as top_bit(P), + which returns the position of the highest non-zero bit in + P. This approximation introduces an error as large as a + factor of 2, but the algorithm seems to handle it OK. + + Come to think of it a divide may not be a big deal on a + modern DSP, so its probably worth checking out the cycles + for a divide versus a top_bit() implementation. + */ + + P = MIN_TX_POWER_FOR_ADAPTION + ec->Pstates; + logP = top_bit(P) + ec->log2taps; + shift = 30 - 2 - logP; + ec->shift = shift; + + lms_adapt_bg(ec, clean_bg, shift); + } + + /* very simple DTD to make sure we dont try and adapt with strong + near end speech */ + + ec->adapt = 0; + if ((ec->Lrx > MIN_RX_POWER_FOR_ADAPTION) && (ec->Lrx > ec->Ltx)) + ec->nonupdate_dwell = DTD_HANGOVER; + if (ec->nonupdate_dwell) + ec->nonupdate_dwell--; + + /* Transfer logic ------------------------------------------------------*/ + + /* These conditions are from the dual path paper [1], I messed with + them a bit to improve performance. */ + + if ((ec->adaption_mode & ECHO_CAN_USE_ADAPTION) && + (ec->nonupdate_dwell == 0) && + (8*ec->Lclean_bg < 7*ec->Lclean) /* (ec->Lclean_bg < 0.875*ec->Lclean) */ && + (8*ec->Lclean_bg < ec->Ltx) /* (ec->Lclean_bg < 0.125*ec->Ltx) */ ) + { + if (ec->cond_met == 6) { + /* BG filter has had better results for 6 consecutive samples */ + ec->adapt = 1; + memcpy(ec->fir_taps16[0], ec->fir_taps16[1], ec->taps*sizeof(int16_t)); + } + else + ec->cond_met++; + } + else + ec->cond_met = 0; + + /* Non-Linear Processing ---------------------------------------------------*/ + + ec->clean_nlp = ec->clean; + if (ec->adaption_mode & ECHO_CAN_USE_NLP) + { + /* Non-linear processor - a fancy way to say "zap small signals, to avoid + residual echo due to (uLaw/ALaw) non-linearity in the channel.". */ + + if ((16*ec->Lclean < ec->Ltx)) + { + /* Our e/c has improved echo by at least 24 dB (each factor of 2 is 6dB, + so 2*2*2*2=16 is the same as 6+6+6+6=24dB) */ + if (ec->adaption_mode & ECHO_CAN_USE_CNG) + { + ec->cng_level = ec->Lbgn; + + /* Very elementary comfort noise generation. Just random + numbers rolled off very vaguely Hoth-like. DR: This + noise doesn't sound quite right to me - I suspect there + are some overlfow issues in the filtering as it's too + "crackly". TODO: debug this, maybe just play noise at + high level or look at spectrum. + */ + + ec->cng_rndnum = 1664525U*ec->cng_rndnum + 1013904223U; + ec->cng_filter = ((ec->cng_rndnum & 0xFFFF) - 32768 + 5*ec->cng_filter) >> 3; + ec->clean_nlp = (ec->cng_filter*ec->cng_level*8) >> 14; + + } + else if (ec->adaption_mode & ECHO_CAN_USE_CLIP) + { + /* This sounds much better than CNG */ + if (ec->clean_nlp > ec->Lbgn) + ec->clean_nlp = ec->Lbgn; + if (ec->clean_nlp < -ec->Lbgn) + ec->clean_nlp = -ec->Lbgn; + } + else + { + /* just mute the residual, doesn't sound very good, used mainly + in G168 tests */ + ec->clean_nlp = 0; + } + } + else { + /* Background noise estimator. I tried a few algorithms + here without much luck. This very simple one seems to + work best, we just average the level using a slow (1 sec + time const) filter if the current level is less than a + (experimentally derived) constant. This means we dont + include high level signals like near end speech. When + combined with CNG or especially CLIP seems to work OK. + */ + if (ec->Lclean < 40) { + ec->Lbgn_acc += abs(ec->clean) - ec->Lbgn; + ec->Lbgn = (ec->Lbgn_acc + (1<<11)) >> 12; + } + } + } + + /* Roll around the taps buffer */ + if (ec->curr_pos <= 0) + ec->curr_pos = ec->taps; + ec->curr_pos--; + + if (ec->adaption_mode & ECHO_CAN_DISABLE) + ec->clean_nlp = rx; + + /* Output scaled back up again to match input scaling */ + + return (int16_t) ec->clean_nlp << 1; +} + +/*- End of function --------------------------------------------------------*/ + +/* This function is seperated from the echo canceller is it is usually called + as part of the tx process. See rx HP (DC blocking) filter above, it's + the same design. + + Some soft phones send speech signals with a lot of low frequency + energy, e.g. down to 20Hz. This can make the hybrid non-linear + which causes the echo canceller to fall over. This filter can help + by removing any low frequency before it gets to the tx port of the + hybrid. + + It can also help by removing and DC in the tx signal. DC is bad + for LMS algorithms. +*/ + +int16_t echo_can_hpf_tx(echo_can_state_t *ec, int16_t tx) { + int tmp, tmp1; + + if (ec->adaption_mode & ECHO_CAN_USE_TX_HPF) { + tmp = tx << 15; + ec->tx_1 += -(ec->tx_1>>DC_LOG2BETA) + tmp - ec->tx_2; + tmp1 = ec->tx_1 >> 15; + if (tmp1 > 32767) tmp1 = 32767; + if (tmp1 < -32767) tmp1 = -32767; + tx = tmp1; + ec->tx_2 = tmp; + } + + return tx; +} + +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/ --- zaptel-1.4.11~dfsg.orig/kernel/oslec/Kbuild +++ zaptel-1.4.11~dfsg/kernel/oslec/Kbuild @@ -0,0 +1,4 @@ +obj-m = oslec.o +oslec-objs = oslec_wrap.o echo.o + + --- zaptel-1.4.11~dfsg.orig/kernel/oslec/spandsp/echo.h +++ zaptel-1.4.11~dfsg/kernel/oslec/spandsp/echo.h @@ -0,0 +1,220 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * echo.c - A line echo canceller. This code is being developed + * against and partially complies with G168. + * + * Written by Steve Underwood + * and David Rowe + * + * Copyright (C) 2001 Steve Underwood and 2007 David Rowe + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * 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. + * + * $Id: echo.h,v 1.9 2006/10/24 13:45:28 steveu Exp $ + */ + +/*! \file */ + +#if !defined(_ECHO_H_) +#define _ECHO_H_ + +/*! \page echo_can_page Line echo cancellation for voice + +\section echo_can_page_sec_1 What does it do? +This module aims to provide G.168-2002 compliant echo cancellation, to remove +electrical echoes (e.g. from 2-4 wire hybrids) from voice calls. + +\section echo_can_page_sec_2 How does it work? +The heart of the echo cancellor is FIR filter. This is adapted to match the echo +impulse response of the telephone line. It must be long enough to adequately cover +the duration of that impulse response. The signal transmitted to the telephone line +is passed through the FIR filter. Once the FIR is properly adapted, the resulting +output is an estimate of the echo signal received from the line. This is subtracted +from the received signal. The result is an estimate of the signal which originated +at the far end of the line, free from echos of our own transmitted signal. + +The least mean squares (LMS) algorithm is attributed to Widrow and Hoff, and was +introduced in 1960. It is the commonest form of filter adaption used in things +like modem line equalisers and line echo cancellers. There it works very well. +However, it only works well for signals of constant amplitude. It works very poorly +for things like speech echo cancellation, where the signal level varies widely. +This is quite easy to fix. If the signal level is normalised - similar to applying +AGC - LMS can work as well for a signal of varying amplitude as it does for a modem +signal. This normalised least mean squares (NLMS) algorithm is the commonest one used +for speech echo cancellation. Many other algorithms exist - e.g. RLS (essentially +the same as Kalman filtering), FAP, etc. Some perform significantly better than NLMS. +However, factors such as computational complexity and patents favour the use of NLMS. + +A simple refinement to NLMS can improve its performance with speech. NLMS tends +to adapt best to the strongest parts of a signal. If the signal is white noise, +the NLMS algorithm works very well. However, speech has more low frequency than +high frequency content. Pre-whitening (i.e. filtering the signal to flatten +its spectrum) the echo signal improves the adapt rate for speech, and ensures the +final residual signal is not heavily biased towards high frequencies. A very low +complexity filter is adequate for this, so pre-whitening adds little to the +compute requirements of the echo canceller. + +An FIR filter adapted using pre-whitened NLMS performs well, provided certain +conditions are met: + + - The transmitted signal has poor self-correlation. + - There is no signal being generated within the environment being cancelled. + +The difficulty is that neither of these can be guaranteed. + +If the adaption is performed while transmitting noise (or something fairly noise +like, such as voice) the adaption works very well. If the adaption is performed +while transmitting something highly correlative (typically narrow band energy +such as signalling tones or DTMF), the adaption can go seriously wrong. The reason +is there is only one solution for the adaption on a near random signal - the impulse +response of the line. For a repetitive signal, there are any number of solutions +which converge the adaption, and nothing guides the adaption to choose the generalised +one. Allowing an untrained canceller to converge on this kind of narrowband +energy probably a good thing, since at least it cancels the tones. Allowing a well +converged canceller to continue converging on such energy is just a way to ruin +its generalised adaption. A narrowband detector is needed, so adapation can be +suspended at appropriate times. + +The adaption process is based on trying to eliminate the received signal. When +there is any signal from within the environment being cancelled it may upset the +adaption process. Similarly, if the signal we are transmitting is small, noise +may dominate and disturb the adaption process. If we can ensure that the +adaption is only performed when we are transmitting a significant signal level, +and the environment is not, things will be OK. Clearly, it is easy to tell when +we are sending a significant signal. Telling, if the environment is generating a +significant signal, and doing it with sufficient speed that the adaption will +not have diverged too much more we stop it, is a little harder. + +The key problem in detecting when the environment is sourcing significant energy +is that we must do this very quickly. Given a reasonably long sample of the +received signal, there are a number of strategies which may be used to assess +whether that signal contains a strong far end component. However, by the time +that assessment is complete the far end signal will have already caused major +mis-convergence in the adaption process. An assessment algorithm is needed which +produces a fairly accurate result from a very short burst of far end energy. + +\section echo_can_page_sec_3 How do I use it? +The echo cancellor processes both the transmit and receive streams sample by +sample. The processing function is not declared inline. Unfortunately, +cancellation requires many operations per sample, so the call overhead is only a +minor burden. +*/ + +#include "fir.h" + +/* Mask bits for the adaption mode */ + +#define ECHO_CAN_USE_ADAPTION 0x01 +#define ECHO_CAN_USE_NLP 0x02 +#define ECHO_CAN_USE_CNG 0x04 +#define ECHO_CAN_USE_CLIP 0x08 +#define ECHO_CAN_USE_TX_HPF 0x10 +#define ECHO_CAN_USE_RX_HPF 0x20 +#define ECHO_CAN_DISABLE 0x40 + +/*! + G.168 echo canceller descriptor. This defines the working state for a line + echo canceller. +*/ +typedef struct +{ + int16_t tx,rx; + int16_t clean; + int16_t clean_nlp; + + int nonupdate_dwell; + int curr_pos; + int taps; + int log2taps; + int adaption_mode; + + int cond_met; + int32_t Pstates; + int16_t adapt; + int32_t factor; + int16_t shift; + + /* Average levels and averaging filter states */ + int Ltxacc, Lrxacc, Lcleanacc, Lclean_bgacc; + int Ltx, Lrx; + int Lclean; + int Lclean_bg; + int Lbgn, Lbgn_acc, Lbgn_upper, Lbgn_upper_acc; + + /* foreground and background filter states */ + fir16_state_t fir_state; + fir16_state_t fir_state_bg; + int16_t *fir_taps16[2]; + + /* DC blocking filter states */ + int tx_1, tx_2, rx_1, rx_2; + + /* optional High Pass Filter states */ + int32_t xvtx[5], yvtx[5]; + int32_t xvrx[5], yvrx[5]; + + /* Parameters for the optional Hoth noise generator */ + int cng_level; + int cng_rndnum; + int cng_filter; + + /* snapshot sample of coeffs used for development */ + int16_t *snapshot; + +} echo_can_state_t; + +/*! Create a voice echo canceller context. + \param len The length of the canceller, in samples. + \return The new canceller context, or NULL if the canceller could not be created. +*/ +echo_can_state_t *echo_can_create(int len, int adaption_mode); + +/*! Free a voice echo canceller context. + \param ec The echo canceller context. +*/ +void echo_can_free(echo_can_state_t *ec); + +/*! Flush (reinitialise) a voice echo canceller context. + \param ec The echo canceller context. +*/ +void echo_can_flush(echo_can_state_t *ec); + +/*! Set the adaption mode of a voice echo canceller context. + \param ec The echo canceller context. + \param adapt The mode. +*/ +void echo_can_adaption_mode(echo_can_state_t *ec, int adaption_mode); + +void echo_can_snapshot(echo_can_state_t *ec); + +/*! Process a sample through a voice echo canceller. + \param ec The echo canceller context. + \param tx The transmitted audio sample. + \param rx The received audio sample. + \return The clean (echo cancelled) received sample. +*/ +int16_t echo_can_update(echo_can_state_t *ec, int16_t tx, int16_t rx); + +/*! Process to high pass filter the tx signal. + \param ec The echo canceller context. + \param tx The transmitted auio sample. + \return The HP filtered transmit sample, send this to your D/A. +*/ +int16_t echo_can_hpf_tx(echo_can_state_t *ec, int16_t tx); + +#endif +/*- End of file ------------------------------------------------------------*/ --- zaptel-1.4.11~dfsg.orig/kernel/oslec/spandsp/bit_operations.h +++ zaptel-1.4.11~dfsg/kernel/oslec/spandsp/bit_operations.h @@ -0,0 +1,253 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * bit_operations.h - Various bit level operations, such as bit reversal + * + * Written by Steve Underwood + * + * Copyright (C) 2006 Steve Underwood + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * 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. + * + * $Id: bit_operations.h,v 1.11 2006/11/28 15:37:03 steveu Exp $ + */ + +/*! \file */ + +#if !defined(_BIT_OPERATIONS_H_) +#define _BIT_OPERATIONS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__i386__) || defined(__x86_64__) +/*! \brief Find the bit position of the highest set bit in a word + \param bits The word to be searched + \return The bit number of the highest set bit, or -1 if the word is zero. */ +static __inline__ int top_bit(unsigned int bits) +{ + int res; + + __asm__ (" xorl %[res],%[res];\n" + " decl %[res];\n" + " bsrl %[bits],%[res]\n" + : [res] "=&r" (res) + : [bits] "rm" (bits)); + return res; +} +/*- End of function --------------------------------------------------------*/ + +/*! \brief Find the bit position of the lowest set bit in a word + \param bits The word to be searched + \return The bit number of the lowest set bit, or -1 if the word is zero. */ +static __inline__ int bottom_bit(unsigned int bits) +{ + int res; + + __asm__ (" xorl %[res],%[res];\n" + " decl %[res];\n" + " bsfl %[bits],%[res]\n" + : [res] "=&r" (res) + : [bits] "rm" (bits)); + return res; +} +/*- End of function --------------------------------------------------------*/ +#else +static __inline__ int top_bit(unsigned int bits) +{ + int i; + + if (bits == 0) + return -1; + i = 0; + if (bits & 0xFFFF0000) + { + bits &= 0xFFFF0000; + i += 16; + } + if (bits & 0xFF00FF00) + { + bits &= 0xFF00FF00; + i += 8; + } + if (bits & 0xF0F0F0F0) + { + bits &= 0xF0F0F0F0; + i += 4; + } + if (bits & 0xCCCCCCCC) + { + bits &= 0xCCCCCCCC; + i += 2; + } + if (bits & 0xAAAAAAAA) + { + bits &= 0xAAAAAAAA; + i += 1; + } + return i; +} +/*- End of function --------------------------------------------------------*/ + +static __inline__ int bottom_bit(unsigned int bits) +{ + int i; + + if (bits == 0) + return -1; + i = 32; + if (bits & 0x0000FFFF) + { + bits &= 0x0000FFFF; + i -= 16; + } + if (bits & 0x00FF00FF) + { + bits &= 0x00FF00FF; + i -= 8; + } + if (bits & 0x0F0F0F0F) + { + bits &= 0x0F0F0F0F; + i -= 4; + } + if (bits & 0x33333333) + { + bits &= 0x33333333; + i -= 2; + } + if (bits & 0x55555555) + { + bits &= 0x55555555; + i -= 1; + } + return i; +} +/*- End of function --------------------------------------------------------*/ +#endif + +/*! \brief Bit reverse a byte. + \param data The byte to be reversed. + \return The bit reversed version of data. */ +static __inline__ uint8_t bit_reverse8(uint8_t x) +{ +#if defined(__i386__) || defined(__x86_64__) + /* If multiply is fast */ + return ((x*0x0802U & 0x22110U) | (x*0x8020U & 0x88440U))*0x10101U >> 16; +#else + /* If multiply is slow, but we have a barrel shifter */ + x = (x >> 4) | (x << 4); + x = ((x & 0xCC) >> 2) | ((x & 0x33) << 2); + return ((x & 0xAA) >> 1) | ((x & 0x55) << 1); +#endif +} +/*- End of function --------------------------------------------------------*/ + +/*! \brief Bit reverse a 16 bit word. + \param data The word to be reversed. + \return The bit reversed version of data. */ +uint16_t bit_reverse16(uint16_t data); + +/*! \brief Bit reverse a 32 bit word. + \param data The word to be reversed. + \return The bit reversed version of data. */ +uint32_t bit_reverse32(uint32_t data); + +/*! \brief Bit reverse each of the four bytes in a 32 bit word. + \param data The word to be reversed. + \return The bit reversed version of data. */ +uint32_t bit_reverse_4bytes(uint32_t data); + +/*! \brief Find the number of set bits in a 32 bit word. + \param x The word to be searched. + \return The number of set bits. */ +int one_bits32(uint32_t x); + +/*! \brief Create a mask as wide as the number in a 32 bit word. + \param x The word to be searched. + \return The mask. */ +uint32_t make_mask32(uint32_t x); + +/*! \brief Create a mask as wide as the number in a 16 bit word. + \param x The word to be searched. + \return The mask. */ +uint16_t make_mask16(uint16_t x); + +/*! \brief Find the least significant one in a word, and return a word + with just that bit set. + \param x The word to be searched. + \return The word with the single set bit. */ +static __inline__ uint32_t least_significant_one32(uint32_t x) +{ + return (x & (-(int32_t) x)); +} +/*- End of function --------------------------------------------------------*/ + +/*! \brief Find the most significant one in a word, and return a word + with just that bit set. + \param x The word to be searched. + \return The word with the single set bit. */ +static __inline__ uint32_t most_significant_one32(uint32_t x) +{ +#if defined(__i386__) || defined(__x86_64__) + return 1 << top_bit(x); +#else + x = make_mask32(x); + return (x ^ (x >> 1)); +#endif +} +/*- End of function --------------------------------------------------------*/ + +/*! \brief Find the parity of a byte. + \param x The byte to be checked. + \return 1 for odd, or 0 for even. */ +static __inline__ int parity8(uint8_t x) +{ + x = (x ^ (x >> 4)) & 0x0F; + return (0x6996 >> x) & 1; +} +/*- End of function --------------------------------------------------------*/ + +/*! \brief Find the parity of a 16 bit word. + \param x The word to be checked. + \return 1 for odd, or 0 for even. */ +static __inline__ int parity16(uint16_t x) +{ + x ^= (x >> 8); + x = (x ^ (x >> 4)) & 0x0F; + return (0x6996 >> x) & 1; +} +/*- End of function --------------------------------------------------------*/ + +/*! \brief Find the parity of a 32 bit word. + \param x The word to be checked. + \return 1 for odd, or 0 for even. */ +static __inline__ int parity32(uint32_t x) +{ + x ^= (x >> 16); + x ^= (x >> 8); + x = (x ^ (x >> 4)) & 0x0F; + return (0x6996 >> x) & 1; +} +/*- End of function --------------------------------------------------------*/ + +#ifdef __cplusplus +} +#endif + +#endif +/*- End of file ------------------------------------------------------------*/ --- zaptel-1.4.11~dfsg.orig/kernel/oslec/spandsp/mmx.h +++ zaptel-1.4.11~dfsg/kernel/oslec/spandsp/mmx.h @@ -0,0 +1,283 @@ +/* + * mmx.h + * Copyright (C) 1997-2001 H. Dietz and R. Fisher + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef AVCODEC_I386MMX_H +#define AVCODEC_I386MMX_H + +/* + * The type of an value that fits in an MMX register (note that long + * long constant values MUST be suffixed by LL and unsigned long long + * values by ULL, lest they be truncated by the compiler) + */ + +typedef union { + long long q; /* Quadword (64-bit) value */ + unsigned long long uq; /* Unsigned Quadword */ + int d[2]; /* 2 Doubleword (32-bit) values */ + unsigned int ud[2]; /* 2 Unsigned Doubleword */ + short w[4]; /* 4 Word (16-bit) values */ + unsigned short uw[4]; /* 4 Unsigned Word */ + char b[8]; /* 8 Byte (8-bit) values */ + unsigned char ub[8]; /* 8 Unsigned Byte */ + float s[2]; /* Single-precision (32-bit) value */ +} mmx_t; /* On an 8-byte (64-bit) boundary */ + + +#define mmx_i2r(op,imm,reg) \ + __asm__ __volatile__ (#op " %0, %%" #reg \ + : /* nothing */ \ + : "i" (imm) ) + +#define mmx_m2r(op,mem,reg) \ + __asm__ __volatile__ (#op " %0, %%" #reg \ + : /* nothing */ \ + : "m" (mem)) + +#define mmx_r2m(op,reg,mem) \ + __asm__ __volatile__ (#op " %%" #reg ", %0" \ + : "=m" (mem) \ + : /* nothing */ ) + +#define mmx_r2r(op,regs,regd) \ + __asm__ __volatile__ (#op " %" #regs ", %" #regd) + + +#define emms() __asm__ __volatile__ ("emms") + +#define movd_m2r(var,reg) mmx_m2r (movd, var, reg) +#define movd_r2m(reg,var) mmx_r2m (movd, reg, var) +#define movd_r2r(regs,regd) mmx_r2r (movd, regs, regd) + +#define movq_m2r(var,reg) mmx_m2r (movq, var, reg) +#define movq_r2m(reg,var) mmx_r2m (movq, reg, var) +#define movq_r2r(regs,regd) mmx_r2r (movq, regs, regd) + +#define packssdw_m2r(var,reg) mmx_m2r (packssdw, var, reg) +#define packssdw_r2r(regs,regd) mmx_r2r (packssdw, regs, regd) +#define packsswb_m2r(var,reg) mmx_m2r (packsswb, var, reg) +#define packsswb_r2r(regs,regd) mmx_r2r (packsswb, regs, regd) + +#define packuswb_m2r(var,reg) mmx_m2r (packuswb, var, reg) +#define packuswb_r2r(regs,regd) mmx_r2r (packuswb, regs, regd) + +#define paddb_m2r(var,reg) mmx_m2r (paddb, var, reg) +#define paddb_r2r(regs,regd) mmx_r2r (paddb, regs, regd) +#define paddd_m2r(var,reg) mmx_m2r (paddd, var, reg) +#define paddd_r2r(regs,regd) mmx_r2r (paddd, regs, regd) +#define paddw_m2r(var,reg) mmx_m2r (paddw, var, reg) +#define paddw_r2r(regs,regd) mmx_r2r (paddw, regs, regd) + +#define paddsb_m2r(var,reg) mmx_m2r (paddsb, var, reg) +#define paddsb_r2r(regs,regd) mmx_r2r (paddsb, regs, regd) +#define paddsw_m2r(var,reg) mmx_m2r (paddsw, var, reg) +#define paddsw_r2r(regs,regd) mmx_r2r (paddsw, regs, regd) + +#define paddusb_m2r(var,reg) mmx_m2r (paddusb, var, reg) +#define paddusb_r2r(regs,regd) mmx_r2r (paddusb, regs, regd) +#define paddusw_m2r(var,reg) mmx_m2r (paddusw, var, reg) +#define paddusw_r2r(regs,regd) mmx_r2r (paddusw, regs, regd) + +#define pand_m2r(var,reg) mmx_m2r (pand, var, reg) +#define pand_r2r(regs,regd) mmx_r2r (pand, regs, regd) + +#define pandn_m2r(var,reg) mmx_m2r (pandn, var, reg) +#define pandn_r2r(regs,regd) mmx_r2r (pandn, regs, regd) + +#define pcmpeqb_m2r(var,reg) mmx_m2r (pcmpeqb, var, reg) +#define pcmpeqb_r2r(regs,regd) mmx_r2r (pcmpeqb, regs, regd) +#define pcmpeqd_m2r(var,reg) mmx_m2r (pcmpeqd, var, reg) +#define pcmpeqd_r2r(regs,regd) mmx_r2r (pcmpeqd, regs, regd) +#define pcmpeqw_m2r(var,reg) mmx_m2r (pcmpeqw, var, reg) +#define pcmpeqw_r2r(regs,regd) mmx_r2r (pcmpeqw, regs, regd) + +#define pcmpgtb_m2r(var,reg) mmx_m2r (pcmpgtb, var, reg) +#define pcmpgtb_r2r(regs,regd) mmx_r2r (pcmpgtb, regs, regd) +#define pcmpgtd_m2r(var,reg) mmx_m2r (pcmpgtd, var, reg) +#define pcmpgtd_r2r(regs,regd) mmx_r2r (pcmpgtd, regs, regd) +#define pcmpgtw_m2r(var,reg) mmx_m2r (pcmpgtw, var, reg) +#define pcmpgtw_r2r(regs,regd) mmx_r2r (pcmpgtw, regs, regd) + +#define pmaddwd_m2r(var,reg) mmx_m2r (pmaddwd, var, reg) +#define pmaddwd_r2r(regs,regd) mmx_r2r (pmaddwd, regs, regd) + +#define pmulhw_m2r(var,reg) mmx_m2r (pmulhw, var, reg) +#define pmulhw_r2r(regs,regd) mmx_r2r (pmulhw, regs, regd) + +#define pmullw_m2r(var,reg) mmx_m2r (pmullw, var, reg) +#define pmullw_r2r(regs,regd) mmx_r2r (pmullw, regs, regd) + +#define por_m2r(var,reg) mmx_m2r (por, var, reg) +#define por_r2r(regs,regd) mmx_r2r (por, regs, regd) + +#define pslld_i2r(imm,reg) mmx_i2r (pslld, imm, reg) +#define pslld_m2r(var,reg) mmx_m2r (pslld, var, reg) +#define pslld_r2r(regs,regd) mmx_r2r (pslld, regs, regd) +#define psllq_i2r(imm,reg) mmx_i2r (psllq, imm, reg) +#define psllq_m2r(var,reg) mmx_m2r (psllq, var, reg) +#define psllq_r2r(regs,regd) mmx_r2r (psllq, regs, regd) +#define psllw_i2r(imm,reg) mmx_i2r (psllw, imm, reg) +#define psllw_m2r(var,reg) mmx_m2r (psllw, var, reg) +#define psllw_r2r(regs,regd) mmx_r2r (psllw, regs, regd) + +#define psrad_i2r(imm,reg) mmx_i2r (psrad, imm, reg) +#define psrad_m2r(var,reg) mmx_m2r (psrad, var, reg) +#define psrad_r2r(regs,regd) mmx_r2r (psrad, regs, regd) +#define psraw_i2r(imm,reg) mmx_i2r (psraw, imm, reg) +#define psraw_m2r(var,reg) mmx_m2r (psraw, var, reg) +#define psraw_r2r(regs,regd) mmx_r2r (psraw, regs, regd) + +#define psrld_i2r(imm,reg) mmx_i2r (psrld, imm, reg) +#define psrld_m2r(var,reg) mmx_m2r (psrld, var, reg) +#define psrld_r2r(regs,regd) mmx_r2r (psrld, regs, regd) +#define psrlq_i2r(imm,reg) mmx_i2r (psrlq, imm, reg) +#define psrlq_m2r(var,reg) mmx_m2r (psrlq, var, reg) +#define psrlq_r2r(regs,regd) mmx_r2r (psrlq, regs, regd) +#define psrlw_i2r(imm,reg) mmx_i2r (psrlw, imm, reg) +#define psrlw_m2r(var,reg) mmx_m2r (psrlw, var, reg) +#define psrlw_r2r(regs,regd) mmx_r2r (psrlw, regs, regd) + +#define psubb_m2r(var,reg) mmx_m2r (psubb, var, reg) +#define psubb_r2r(regs,regd) mmx_r2r (psubb, regs, regd) +#define psubd_m2r(var,reg) mmx_m2r (psubd, var, reg) +#define psubd_r2r(regs,regd) mmx_r2r (psubd, regs, regd) +#define psubw_m2r(var,reg) mmx_m2r (psubw, var, reg) +#define psubw_r2r(regs,regd) mmx_r2r (psubw, regs, regd) + +#define psubsb_m2r(var,reg) mmx_m2r (psubsb, var, reg) +#define psubsb_r2r(regs,regd) mmx_r2r (psubsb, regs, regd) +#define psubsw_m2r(var,reg) mmx_m2r (psubsw, var, reg) +#define psubsw_r2r(regs,regd) mmx_r2r (psubsw, regs, regd) + +#define psubusb_m2r(var,reg) mmx_m2r (psubusb, var, reg) +#define psubusb_r2r(regs,regd) mmx_r2r (psubusb, regs, regd) +#define psubusw_m2r(var,reg) mmx_m2r (psubusw, var, reg) +#define psubusw_r2r(regs,regd) mmx_r2r (psubusw, regs, regd) + +#define punpckhbw_m2r(var,reg) mmx_m2r (punpckhbw, var, reg) +#define punpckhbw_r2r(regs,regd) mmx_r2r (punpckhbw, regs, regd) +#define punpckhdq_m2r(var,reg) mmx_m2r (punpckhdq, var, reg) +#define punpckhdq_r2r(regs,regd) mmx_r2r (punpckhdq, regs, regd) +#define punpckhwd_m2r(var,reg) mmx_m2r (punpckhwd, var, reg) +#define punpckhwd_r2r(regs,regd) mmx_r2r (punpckhwd, regs, regd) + +#define punpcklbw_m2r(var,reg) mmx_m2r (punpcklbw, var, reg) +#define punpcklbw_r2r(regs,regd) mmx_r2r (punpcklbw, regs, regd) +#define punpckldq_m2r(var,reg) mmx_m2r (punpckldq, var, reg) +#define punpckldq_r2r(regs,regd) mmx_r2r (punpckldq, regs, regd) +#define punpcklwd_m2r(var,reg) mmx_m2r (punpcklwd, var, reg) +#define punpcklwd_r2r(regs,regd) mmx_r2r (punpcklwd, regs, regd) + +#define pxor_m2r(var,reg) mmx_m2r (pxor, var, reg) +#define pxor_r2r(regs,regd) mmx_r2r (pxor, regs, regd) + + +/* 3DNOW extensions */ + +#define pavgusb_m2r(var,reg) mmx_m2r (pavgusb, var, reg) +#define pavgusb_r2r(regs,regd) mmx_r2r (pavgusb, regs, regd) + + +/* AMD MMX extensions - also available in intel SSE */ + + +#define mmx_m2ri(op,mem,reg,imm) \ + __asm__ __volatile__ (#op " %1, %0, %%" #reg \ + : /* nothing */ \ + : "m" (mem), "i" (imm)) +#define mmx_r2ri(op,regs,regd,imm) \ + __asm__ __volatile__ (#op " %0, %%" #regs ", %%" #regd \ + : /* nothing */ \ + : "i" (imm) ) + +#define mmx_fetch(mem,hint) \ + __asm__ __volatile__ ("prefetch" #hint " %0" \ + : /* nothing */ \ + : "m" (mem)) + + +#define maskmovq(regs,maskreg) mmx_r2ri (maskmovq, regs, maskreg) + +#define movntq_r2m(mmreg,var) mmx_r2m (movntq, mmreg, var) + +#define pavgb_m2r(var,reg) mmx_m2r (pavgb, var, reg) +#define pavgb_r2r(regs,regd) mmx_r2r (pavgb, regs, regd) +#define pavgw_m2r(var,reg) mmx_m2r (pavgw, var, reg) +#define pavgw_r2r(regs,regd) mmx_r2r (pavgw, regs, regd) + +#define pextrw_r2r(mmreg,reg,imm) mmx_r2ri (pextrw, mmreg, reg, imm) + +#define pinsrw_r2r(reg,mmreg,imm) mmx_r2ri (pinsrw, reg, mmreg, imm) + +#define pmaxsw_m2r(var,reg) mmx_m2r (pmaxsw, var, reg) +#define pmaxsw_r2r(regs,regd) mmx_r2r (pmaxsw, regs, regd) + +#define pmaxub_m2r(var,reg) mmx_m2r (pmaxub, var, reg) +#define pmaxub_r2r(regs,regd) mmx_r2r (pmaxub, regs, regd) + +#define pminsw_m2r(var,reg) mmx_m2r (pminsw, var, reg) +#define pminsw_r2r(regs,regd) mmx_r2r (pminsw, regs, regd) + +#define pminub_m2r(var,reg) mmx_m2r (pminub, var, reg) +#define pminub_r2r(regs,regd) mmx_r2r (pminub, regs, regd) + +#define pmovmskb(mmreg,reg) \ + __asm__ __volatile__ ("movmskps %" #mmreg ", %" #reg) + +#define pmulhuw_m2r(var,reg) mmx_m2r (pmulhuw, var, reg) +#define pmulhuw_r2r(regs,regd) mmx_r2r (pmulhuw, regs, regd) + +#define prefetcht0(mem) mmx_fetch (mem, t0) +#define prefetcht1(mem) mmx_fetch (mem, t1) +#define prefetcht2(mem) mmx_fetch (mem, t2) +#define prefetchnta(mem) mmx_fetch (mem, nta) + +#define psadbw_m2r(var,reg) mmx_m2r (psadbw, var, reg) +#define psadbw_r2r(regs,regd) mmx_r2r (psadbw, regs, regd) + +#define pshufw_m2r(var,reg,imm) mmx_m2ri(pshufw, var, reg, imm) +#define pshufw_r2r(regs,regd,imm) mmx_r2ri(pshufw, regs, regd, imm) + +#define sfence() __asm__ __volatile__ ("sfence\n\t") + +/* SSE2 */ +#define pshufhw_m2r(var,reg,imm) mmx_m2ri(pshufhw, var, reg, imm) +#define pshufhw_r2r(regs,regd,imm) mmx_r2ri(pshufhw, regs, regd, imm) +#define pshuflw_m2r(var,reg,imm) mmx_m2ri(pshuflw, var, reg, imm) +#define pshuflw_r2r(regs,regd,imm) mmx_r2ri(pshuflw, regs, regd, imm) + +#define pshufd_r2r(regs,regd,imm) mmx_r2ri(pshufd, regs, regd, imm) + +#define movdqa_m2r(var,reg) mmx_m2r (movdqa, var, reg) +#define movdqa_r2m(reg,var) mmx_r2m (movdqa, reg, var) +#define movdqa_r2r(regs,regd) mmx_r2r (movdqa, regs, regd) +#define movdqu_m2r(var,reg) mmx_m2r (movdqu, var, reg) +#define movdqu_r2m(reg,var) mmx_r2m (movdqu, reg, var) +#define movdqu_r2r(regs,regd) mmx_r2r (movdqu, regs, regd) + +#define pmullw_r2m(reg,var) mmx_r2m (pmullw, reg, var) + +#define pslldq_i2r(imm,reg) mmx_i2r (pslldq, imm, reg) +#define psrldq_i2r(imm,reg) mmx_i2r (psrldq, imm, reg) + +#define punpcklqdq_r2r(regs,regd) mmx_r2r (punpcklqdq, regs, regd) +#define punpckhqdq_r2r(regs,regd) mmx_r2r (punpckhqdq, regs, regd) + + +#endif /* AVCODEC_I386MMX_H */ --- zaptel-1.4.11~dfsg.orig/kernel/oslec/spandsp/fir.h +++ zaptel-1.4.11~dfsg/kernel/oslec/spandsp/fir.h @@ -0,0 +1,369 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * fir.h - General telephony FIR routines + * + * Written by Steve Underwood + * + * Copyright (C) 2002 Steve Underwood + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * 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. + * + * $Id: fir.h,v 1.8 2006/10/24 13:45:28 steveu Exp $ + */ + +/*! \page fir_page FIR filtering +\section fir_page_sec_1 What does it do? +???. + +\section fir_page_sec_2 How does it work? +???. +*/ + +#if !defined(_FIR_H_) +#define _FIR_H_ + +/* + Blackfin NOTES & IDEAS: + + A simple dot product function is used to implement the filter. This performs + just one MAC/cycle which is inefficient but was easy to implement as a first + pass. The current Blackfin code also uses an unrolled form of the filter + history to avoid 0 length hardware loop issues. This is wasteful of + memory. + + Ideas for improvement: + + 1/ Rewrite filter for dual MAC inner loop. The issue here is handling + history sample offsets that are 16 bit aligned - the dual MAC needs + 32 bit aligmnent. There are some good examples in libbfdsp. + + 2/ Use the hardware circular buffer facility tohalve memory usage. + + 3/ Consider using internal memory. + + Using less memory might also improve speed as cache misses will be + reduced. A drop in MIPs and memory approaching 50% should be + possible. + + The foreground and background filters currenlty use a total of + about 10 MIPs/ch as measured with speedtest.c on a 256 TAP echo + can. +*/ + +#if defined(USE_MMX) || defined(USE_SSE2) +#include "mmx.h" +#endif + +/*! + 16 bit integer FIR descriptor. This defines the working state for a single + instance of an FIR filter using 16 bit integer coefficients. +*/ +typedef struct +{ + int taps; + int curr_pos; + const int16_t *coeffs; + int16_t *history; +} fir16_state_t; + +/*! + 32 bit integer FIR descriptor. This defines the working state for a single + instance of an FIR filter using 32 bit integer coefficients, and filtering + 16 bit integer data. +*/ +typedef struct +{ + int taps; + int curr_pos; + const int32_t *coeffs; + int16_t *history; +} fir32_state_t; + +/*! + Floating point FIR descriptor. This defines the working state for a single + instance of an FIR filter using floating point coefficients and data. +*/ +typedef struct +{ + int taps; + int curr_pos; + const float *coeffs; + float *history; +} fir_float_state_t; + +#ifdef __cplusplus +extern "C" { +#endif + +static __inline__ const int16_t *fir16_create(fir16_state_t *fir, + const int16_t *coeffs, + int taps) +{ + fir->taps = taps; + fir->curr_pos = taps - 1; + fir->coeffs = coeffs; +#if defined(USE_MMX) || defined(USE_SSE2) || defined(__BLACKFIN_ASM__) + if ((fir->history = malloc(2*taps*sizeof(int16_t)))) + memset(fir->history, 0, 2*taps*sizeof(int16_t)); +#else + if ((fir->history = (int16_t *) malloc(taps*sizeof(int16_t)))) + memset(fir->history, 0, taps*sizeof(int16_t)); +#endif + return fir->history; +} +/*- End of function --------------------------------------------------------*/ + +static __inline__ void fir16_flush(fir16_state_t *fir) +{ +#if defined(USE_MMX) || defined(USE_SSE2) || defined(__BLACKFIN_ASM__) + memset(fir->history, 0, 2*fir->taps*sizeof(int16_t)); +#else + memset(fir->history, 0, fir->taps*sizeof(int16_t)); +#endif +} +/*- End of function --------------------------------------------------------*/ + +static __inline__ void fir16_free(fir16_state_t *fir) +{ + free(fir->history); +} +/*- End of function --------------------------------------------------------*/ + +#ifdef __BLACKFIN_ASM__ +static inline int32_t dot_asm(short *x, short *y, int len) +{ + int dot; + + len--; + + __asm__ + ( + "I0 = %1;\n\t" + "I1 = %2;\n\t" + "A0 = 0;\n\t" + "R0.L = W[I0++] || R1.L = W[I1++];\n\t" + "LOOP dot%= LC0 = %3;\n\t" + "LOOP_BEGIN dot%=;\n\t" + "A0 += R0.L * R1.L (IS) || R0.L = W[I0++] || R1.L = W[I1++];\n\t" + "LOOP_END dot%=;\n\t" + "A0 += R0.L*R1.L (IS);\n\t" + "R0 = A0;\n\t" + "%0 = R0;\n\t" + : "=&d" (dot) + : "a" (x), "a" (y), "a" (len) + : "I0", "I1", "A1", "A0", "R0", "R1" + ); + + return dot; +} +#endif +/*- End of function --------------------------------------------------------*/ + +static __inline__ int16_t fir16(fir16_state_t *fir, int16_t sample) +{ + int32_t y; +#if defined(USE_MMX) + int i; + mmx_t *mmx_coeffs; + mmx_t *mmx_hist; + + fir->history[fir->curr_pos] = sample; + fir->history[fir->curr_pos + fir->taps] = sample; + + mmx_coeffs = (mmx_t *) fir->coeffs; + mmx_hist = (mmx_t *) &fir->history[fir->curr_pos]; + i = fir->taps; + pxor_r2r(mm4, mm4); + /* 8 samples per iteration, so the filter must be a multiple of 8 long. */ + while (i > 0) + { + movq_m2r(mmx_coeffs[0], mm0); + movq_m2r(mmx_coeffs[1], mm2); + movq_m2r(mmx_hist[0], mm1); + movq_m2r(mmx_hist[1], mm3); + mmx_coeffs += 2; + mmx_hist += 2; + pmaddwd_r2r(mm1, mm0); + pmaddwd_r2r(mm3, mm2); + paddd_r2r(mm0, mm4); + paddd_r2r(mm2, mm4); + i -= 8; + } + movq_r2r(mm4, mm0); + psrlq_i2r(32, mm0); + paddd_r2r(mm0, mm4); + movd_r2m(mm4, y); + emms(); +#elif defined(USE_SSE2) + int i; + xmm_t *xmm_coeffs; + xmm_t *xmm_hist; + + fir->history[fir->curr_pos] = sample; + fir->history[fir->curr_pos + fir->taps] = sample; + + xmm_coeffs = (xmm_t *) fir->coeffs; + xmm_hist = (xmm_t *) &fir->history[fir->curr_pos]; + i = fir->taps; + pxor_r2r(xmm4, xmm4); + /* 16 samples per iteration, so the filter must be a multiple of 16 long. */ + while (i > 0) + { + movdqu_m2r(xmm_coeffs[0], xmm0); + movdqu_m2r(xmm_coeffs[1], xmm2); + movdqu_m2r(xmm_hist[0], xmm1); + movdqu_m2r(xmm_hist[1], xmm3); + xmm_coeffs += 2; + xmm_hist += 2; + pmaddwd_r2r(xmm1, xmm0); + pmaddwd_r2r(xmm3, xmm2); + paddd_r2r(xmm0, xmm4); + paddd_r2r(xmm2, xmm4); + i -= 16; + } + movdqa_r2r(xmm4, xmm0); + psrldq_i2r(8, xmm0); + paddd_r2r(xmm0, xmm4); + movdqa_r2r(xmm4, xmm0); + psrldq_i2r(4, xmm0); + paddd_r2r(xmm0, xmm4); + movd_r2m(xmm4, y); +#elif defined(__BLACKFIN_ASM__) + fir->history[fir->curr_pos] = sample; + fir->history[fir->curr_pos + fir->taps] = sample; + y = dot_asm((int16_t*)fir->coeffs, &fir->history[fir->curr_pos], fir->taps); +#else + int i; + int offset1; + int offset2; + + fir->history[fir->curr_pos] = sample; + + offset2 = fir->curr_pos; + offset1 = fir->taps - offset2; + y = 0; + for (i = fir->taps - 1; i >= offset1; i--) + y += fir->coeffs[i]*fir->history[i - offset1]; + for ( ; i >= 0; i--) + y += fir->coeffs[i]*fir->history[i + offset2]; +#endif + if (fir->curr_pos <= 0) + fir->curr_pos = fir->taps; + fir->curr_pos--; + return (int16_t) (y >> 15); +} +/*- End of function --------------------------------------------------------*/ + +static __inline__ const int16_t *fir32_create(fir32_state_t *fir, + const int32_t *coeffs, + int taps) +{ + fir->taps = taps; + fir->curr_pos = taps - 1; + fir->coeffs = coeffs; + fir->history = (int16_t *) malloc(taps*sizeof(int16_t)); + if (fir->history) + memset(fir->history, '\0', taps*sizeof(int16_t)); + return fir->history; +} +/*- End of function --------------------------------------------------------*/ + +static __inline__ void fir32_flush(fir32_state_t *fir) +{ + memset(fir->history, 0, fir->taps*sizeof(int16_t)); +} +/*- End of function --------------------------------------------------------*/ + +static __inline__ void fir32_free(fir32_state_t *fir) +{ + free(fir->history); +} +/*- End of function --------------------------------------------------------*/ + +static __inline__ int16_t fir32(fir32_state_t *fir, int16_t sample) +{ + int i; + int32_t y; + int offset1; + int offset2; + + fir->history[fir->curr_pos] = sample; + offset2 = fir->curr_pos; + offset1 = fir->taps - offset2; + y = 0; + for (i = fir->taps - 1; i >= offset1; i--) + y += fir->coeffs[i]*fir->history[i - offset1]; + for ( ; i >= 0; i--) + y += fir->coeffs[i]*fir->history[i + offset2]; + if (fir->curr_pos <= 0) + fir->curr_pos = fir->taps; + fir->curr_pos--; + return (int16_t) (y >> 15); +} +/*- End of function --------------------------------------------------------*/ + +#ifndef __KERNEL__ +static __inline__ const float *fir_float_create(fir_float_state_t *fir, + const float *coeffs, + int taps) +{ + fir->taps = taps; + fir->curr_pos = taps - 1; + fir->coeffs = coeffs; + fir->history = (float *) malloc(taps*sizeof(float)); + if (fir->history) + memset(fir->history, '\0', taps*sizeof(float)); + return fir->history; +} +/*- End of function --------------------------------------------------------*/ + +static __inline__ void fir_float_free(fir_float_state_t *fir) +{ + free(fir->history); +} +/*- End of function --------------------------------------------------------*/ + +static __inline__ int16_t fir_float(fir_float_state_t *fir, int16_t sample) +{ + int i; + float y; + int offset1; + int offset2; + + fir->history[fir->curr_pos] = sample; + + offset2 = fir->curr_pos; + offset1 = fir->taps - offset2; + y = 0; + for (i = fir->taps - 1; i >= offset1; i--) + y += fir->coeffs[i]*fir->history[i - offset1]; + for ( ; i >= 0; i--) + y += fir->coeffs[i]*fir->history[i + offset2]; + if (fir->curr_pos <= 0) + fir->curr_pos = fir->taps; + fir->curr_pos--; + return (int16_t) y; +} +/*- End of function --------------------------------------------------------*/ +#endif + +#ifdef __cplusplus +} +#endif + +#endif +/*- End of file ------------------------------------------------------------*/ --- zaptel-1.4.11~dfsg.orig/kernel/oslec/oslec.h +++ zaptel-1.4.11~dfsg/kernel/oslec/oslec.h @@ -0,0 +1,48 @@ +/* + oslec.h + David Rowe + 7 Feb 2007 + + Interface for OSLEC module. +*/ + +/* + Copyright (C) 2007 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2, as + published by the Free Software Foundation. + + 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. +*/ + +#ifndef __OSLEC__ + +struct echo_can_state { + void *ec; +}; + +struct echo_can_state *oslec_echo_can_create(int len, int adaption_mode); +void oslec_echo_can_free(struct echo_can_state *ec); +short oslec_echo_can_update(struct echo_can_state *ec, short iref, short isig); +int oslec_echo_can_traintap(struct echo_can_state *ec, int pos, short val); +void oslec_echo_can_identify(char *buf, size_t len); +static inline void echo_can_init(void) {} +static inline void echo_can_shutdown(void) {} +short oslec_hpf_tx(struct echo_can_state *ec, short txlin); + +#define ZAPTEL_ECHO_CANCELLER "OSLEC" + +#define USE_ZAPTAP + +#endif + --- zaptel-1.4.11~dfsg.orig/kernel/oslec/oslec_wrap.c +++ zaptel-1.4.11~dfsg/kernel/oslec/oslec_wrap.c @@ -0,0 +1,451 @@ +/* + oslec_wrap.c + David Rowe + 7 Feb 2007 + + Wrapper for OSLEC to turn it into a kernel module compatable with Zaptel. + + The /proc/oslec interface points to the first echo canceller + instance created. Zaptel appears to create/destroy e/c on a call by + call basis, and with the current echo can function interface it is + difficult to tell which channel is assigned to which e/c. So to + simply the /proc interface (at least in this first implementation) + we limit it to the first echo canceller created. + + So if you only have one call up on a system, /proc/oslec will refer + to that. That should be sufficient for debugging the echo canceller + algorithm, we can extend it to handle multiple simultaneous channels + later. +*/ + +/* + Copyright (C) 2007 David Rowe + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2, as + published by the Free Software Foundation. + 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 +#include +#include +#include +#include +#include +#include + + +#define malloc(a) kmalloc((a), GFP_KERNEL) +#define free(a) kfree(a) + +#include "oslec.h" +#include "spandsp/echo.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +EXPORT_SYMBOL(oslec_echo_can_create); +EXPORT_SYMBOL(oslec_echo_can_free); +EXPORT_SYMBOL(oslec_echo_can_update); +EXPORT_SYMBOL(oslec_echo_can_traintap); +EXPORT_SYMBOL(oslec_echo_can_identify); +EXPORT_SYMBOL(oslec_hpf_tx); +#endif + +/* constants for isr cycle averaging */ + +#define LTC 5 /* base 2 log of TC */ + +/* number of cycles we are using per call */ + +static int cycles_last = 0; +static int cycles_worst = 0; +static int cycles_average = 0; + +#ifdef __BLACKFIN__ +/* sample cycles register of Blackfin */ + +static inline volatile unsigned int cycles(void) { + int ret; + + __asm__ __volatile__ + ( + "%0 = CYCLES;\n\t" + : "=&d" (ret) + : + : "R1" + ); + + return ret; +} +#elif defined(__X86__) || defined (__i386) || defined (__x86_64__) +static __inline__ uint64_t cycles(void) { + uint32_t lo, hi; + /* We cannot use "=A", since this would use %rax on x86_64 */ + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); + return (uint64_t)hi << 32 | lo; +} +#elif (defined(__powerpc__) || defined(__ppc__)) +static inline __attribute__((always_inline)) unsigned long long cycles(void) { + register uint32_t tbu, tbl, tmp; + + __asm__ volatile( + "0:\n\t" + "mftbu %0\n\t" + "mftb %1\n\t" + "mftbu %2\n\t" + "cmpw %0, %2\n\t" + "bne- 0b" + : "=r"(tbu), "=r"(tbl), "=r"(tmp) + : /* nope */ + : "cc"); + + return (((uint64_t)tbu)<<32) + tbl; +} +#else +static inline volatile unsigned int cycles(void) { + /* A dummy implementation for other architectures */ + static unsigned int dummy_cycles = 1; + return dummy_cycles++; +} +#endif + + +/* vars to help us with /proc interface */ + +static echo_can_state_t *mon_ec; +static int num_ec; +static int len_ec; + +/* We need this lock as multiple threads may try to manipulate + the globals used for diagnostics at the same time. + + This lock locks two separate sctivities: + 1/ any access to the global mon_ec pointer and related globals num_ec + len_ec. + 2/ Any use of an echo canceller that does not require a channel lock. */ + +spinlock_t oslec_lock; + +/* Thread safety issues: + + Due to the design of zaptel an e/c instance may be created and + destroyed at any time. So monitoring and controlling a running e/c + instance through /proc/oslec has some special challenges: + + 1/ oslec_echo_can_create() might be interrupted part way through, + and called again by another thread. + + 2/ proc_* might be interrupted and the e/c instance pointed to + by mon_ec destroyed by another thread. + + 3/ Call 1 might be destroyed while Call 2 is still up, mon_ec will + then point to a destroyed instance. + + 4/ What happens if an e/c is destroyed while we are modifying it's + mode? + + The goal is to allow monitoring and control of at least once + instance while maintaining stable multithreaded operation. This is + tricky (at least for me) given zaptel design and the use of globals + here to monitor status. + + Thanks Dmitry for helping point these problems out..... +*/ + +struct echo_can_state *oslec_echo_can_create(int len, int adaption_mode) { + struct echo_can_state *ec; + unsigned long flags; + + + ec = (struct echo_can_state *)malloc(sizeof(struct echo_can_state)); + ec->ec = (void*)echo_can_create(len, ECHO_CAN_USE_ADAPTION + | ECHO_CAN_USE_NLP + | ECHO_CAN_USE_CLIP + | ECHO_CAN_USE_TX_HPF + | ECHO_CAN_USE_RX_HPF); + + + spin_lock_irqsave(&oslec_lock, flags); + num_ec++; + + /* We monitor the first e/c created after mon_ec is set to NULL. If + no other calls exist this will be the first call. If a monitored + call hangs up, we will monitor the next call created, ignoring + any other current calls. Not perfect I know, however this is + just meant to be a development tool. Stability is more important + than comprehensive monitoring abilities. + */ + if (mon_ec == NULL) { + mon_ec = (echo_can_state_t*)(ec->ec); + len_ec = len; + } + + spin_unlock_irqrestore(&oslec_lock, flags); + + return ec; +} + +void oslec_echo_can_free(struct echo_can_state *ec) { + unsigned long flags; + spin_lock_irqsave(&oslec_lock, flags); + + /* if this is the e/c being monitored, disable monitoring */ + + if (mon_ec == ec->ec) + mon_ec = NULL; + + echo_can_free((echo_can_state_t*)(ec->ec)); + num_ec--; + free(ec); + + spin_unlock_irqrestore(&oslec_lock, flags); +} + +/* + This code in re-entrant, and will run in the context of an ISR. No + locking is required for cycles calculation, as only one thread will have + ec->ec == mon_ec at a given time, so there will only ever be one + writer to the cycles_* globals. +*/ + +short oslec_echo_can_update(struct echo_can_state *ec, short iref, short isig) { + short clean; + u32 start_cycles = 0; + + if (ec->ec == mon_ec) { + start_cycles = cycles(); + } + + clean = echo_can_update((echo_can_state_t*)(ec->ec), iref, isig); + + /* + Simple IIR averager: + + -LTC -LTC + y(n) = (1 - 2 )y(n-1) + 2 x(n) + + */ + + if (ec->ec == mon_ec) { + cycles_last = cycles() - start_cycles; + cycles_average += (cycles_last - cycles_average) >> LTC; + + if (cycles_last > cycles_worst) + cycles_worst = cycles_last; + } + + return clean; +} + +int oslec_echo_can_traintap(struct echo_can_state *ec, int pos, short val) +{ + return 1; +} + +void oslec_echo_can_identify(char *buf, size_t len) +{ + strncpy(buf, ZAPTEL_ECHO_CANCELLER, len); +} + + +static int proc_read_info(char *buf, char **start, off_t offset, + int count, int *eof, void *data) +{ + int len; + char mode_str[80]; + unsigned long flags; + + *eof = 1; + + spin_lock_irqsave(&oslec_lock, flags); + + if (mon_ec == NULL) { + len = sprintf(buf, "no echo canceller being monitored - make a new call\n"); + spin_unlock_irqrestore(&oslec_lock, flags); + return len; + } + + if (mon_ec->adaption_mode & ECHO_CAN_USE_ADAPTION) + sprintf(mode_str, "|ADAPTION"); + else + sprintf(mode_str, "| "); + + if (mon_ec->adaption_mode & ECHO_CAN_USE_NLP) + strcat(mode_str, "|NLP"); + else + strcat(mode_str, "| "); + + if (mon_ec->adaption_mode & ECHO_CAN_USE_CNG) + strcat(mode_str, "|CNG"); + else if (mon_ec->adaption_mode & ECHO_CAN_USE_CLIP) + strcat(mode_str, "|CLIP"); + else + strcat(mode_str, "| "); + + if (mon_ec->adaption_mode & ECHO_CAN_USE_TX_HPF) + strcat(mode_str, "|TXHPF"); + else + strcat(mode_str, "| "); + + if (mon_ec->adaption_mode & ECHO_CAN_USE_TX_HPF) + strcat(mode_str, "|RXHPF|"); + else + strcat(mode_str, "| |"); + + len = sprintf(buf, + "channels.......: %d\n" + "length (taps)..: %d\n" + "mode...........: [%0d] %s\n" + "Ltx............: %d\n" + "Lrx............: %d\n" + "Lclean.........: %d\n" + "Lclean_bg......: %d\n" + "shift..........: %d\n" + "Double Talk....: %d\n" + "Lbgn...........: %d\n" + "MIPs (last)....: %d\n" + "MIPs (worst)...: %d\n" + "MIPs (avergage): %d\n", + num_ec, + len_ec, + mon_ec->adaption_mode, mode_str, + mon_ec->Ltx, + mon_ec->Lrx, + mon_ec->Lclean, + mon_ec->Lclean_bg, + mon_ec->shift, + (mon_ec->nonupdate_dwell != 0), + mon_ec->Lbgn, + 8*cycles_last/1000, + 8*cycles_worst/1000, + 8*cycles_average/1000 + ); + + spin_unlock_irqrestore(&oslec_lock, flags); + + return len; +} + +static int proc_read_mode(char *buf, char **start, off_t offset, + int count, int *eof, void *data) +{ + int len; + unsigned long flags; + + *eof = 1; + + spin_lock_irqsave(&oslec_lock, flags); + + if (mon_ec == NULL) { + len = sprintf(buf, "%d\n", 0); + spin_unlock_irqrestore(&oslec_lock, flags); + return len; + } + + len = sprintf(buf, "%d\n", mon_ec->adaption_mode); + + spin_unlock_irqrestore(&oslec_lock, flags); + + return len; +} + +static int proc_write_mode(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + + int new_mode; + char *endbuffer; + unsigned long flags; + + spin_lock_irqsave(&oslec_lock, flags); + + if (mon_ec == NULL) { + printk(KERN_NOTICE "no echo canceller being monitored - make a new call\n"); + spin_unlock_irqrestore(&oslec_lock, flags); + return count; + } + + new_mode = simple_strtol (buffer, &endbuffer, 10); + mon_ec->adaption_mode = new_mode; + + spin_unlock_irqrestore(&oslec_lock, flags); + + return count; +} + +static int proc_write_reset(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + unsigned long flags; + spin_lock_irqsave(&oslec_lock, flags); + + if (mon_ec == NULL) { + printk(KERN_NOTICE "no echo canceller being monitored - make a new call\n"); + spin_unlock_irqrestore(&oslec_lock, flags); + return count; + } + + /* Not sure how thread safe this is, should be OKJ as its only resetting + same state variables. There is a chance this will be interrupted + by ISR that calls oslec_echo_can_update() + */ + echo_can_flush(mon_ec); + + spin_unlock_irqrestore(&oslec_lock, flags); + + return count; +} + +static int __init init_oslec(void) +{ + struct proc_dir_entry *proc_oslec, *proc_mode, *proc_reset; + + printk(KERN_INFO "Open Source Line Echo Canceller Installed\n"); + + num_ec = 0; + mon_ec = NULL; + + proc_oslec = proc_mkdir("oslec", 0); + create_proc_read_entry("oslec/info", 0, NULL, proc_read_info, NULL); + proc_mode = create_proc_read_entry("oslec/mode", 0, NULL, proc_read_mode, NULL); + proc_reset = create_proc_read_entry("oslec/reset", 0, NULL, NULL, NULL); + + proc_mode->write_proc = proc_write_mode; + proc_reset->write_proc = proc_write_reset; + spin_lock_init(&oslec_lock); + + return 0; +} + +static void __exit cleanup_oslec(void) +{ + remove_proc_entry("oslec/reset", NULL); + remove_proc_entry("oslec/info", NULL); + remove_proc_entry("oslec/mode", NULL); + remove_proc_entry("oslec", NULL); + printk(KERN_INFO "Open Source Line Echo Canceller Removed\n"); +} + +short oslec_hpf_tx(struct echo_can_state *ec, short txlin) { + if (ec != NULL) + return echo_can_hpf_tx((echo_can_state_t*)(ec->ec), txlin); + else + return txlin; +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Rowe"); +MODULE_DESCRIPTION("Open Source Line Echo Canceller Zaptel Wrapper"); + +module_init(init_oslec); +module_exit(cleanup_oslec); + --- zaptel-1.4.11~dfsg.orig/kernel/oslec/oslec-ctrl-panel.sh +++ zaptel-1.4.11~dfsg/kernel/oslec/oslec-ctrl-panel.sh @@ -0,0 +1,159 @@ +#!/bin/sh +# ./oslec-ctrl-panel.sh +# +# David Rowe +# Created February 2007 +# +# cdialog based shell script to control Oslec echo canceller in real time +# from a text mode GUI. I am totally useless at shell scripting so please +# feel free to improve on this. + +# +# Copyright (C) 2007 David Rowe +# +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2, as +# published by the Free Software Foundation. +# +# 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. + +nlp_off=off +nlp_mute=off +nlp_cng=off +nlp_clip=on +nlp_mode=1+2+8 + +tmpfile=`mktemp /tmp/oslec_panel_XXXXXX` || exit 1 + +nlp_menu() +{ + nlpstatus=0 + while [ $nlpstatus -eq 0 ] + do + + dialog \ + --radiolist "NLP Mode" 10 70 4 \ + Off "NLP disabled" $nlp_off \ + Mute "Simple mute" $nlp_mute \ + CNG "Synthetic level-matched comfort noise" $nlp_cng \ + Clip "Clip to background noise level" $nlp_clip \ + 2>$tmpfile + nlpstatus=$? + + if [ $nlpstatus -eq 0 ] ; then + nlpmenuitem=`cat $tmpfile` + case $nlpmenuitem in + "Off") nlp_off=on; nlp_mute=off; nlp_cng=off; nlp_clip=off; nlp_mode=0;; + "Mute") nlp_off=off; nlp_mute=on; nlp_cng=off; nlp_clip=off; nlp_mode=2;; + "CNG") nlp_off=off; nlp_mute=off; nlp_cng=on; nlp_clip=off; nlp_mode=`expr 2 + 4`;; + "Clip") nlp_off=off; nlp_mute=off; nlp_cng=off; nlp_clip=on; nlp_mode=`expr 2 + 8`;; + esac + mode=`expr 1 + $nlp_mode` + echo $mode > /proc/oslec/mode + fi + done + + return +} + +filter_menu() +{ + + filterstatus=0 + while [ $filterstatus -eq 0 ] + do + + # extract HPF state from $mode + mode=`cat /proc/oslec/mode` + tx_hpf=$(( $mode & 16 )) + if [ $tx_hpf -eq 16 ] ; then + tx_hpf=on + else + tx_hpf=off + fi + rx_hpf=$(( $mode & 32 )) + if [ $rx_hpf -eq 32 ] ; then + rx_hpf=on + else + rx_hpf=off + fi + + dialog \ + --checklist "High Pass Filters $mode" 10 70 4 \ + Tx "Tx HPF" $tx_hpf \ + Rx "Rx HPF" $rx_hpf \ + 2>$tmpfile + filterstatus=$? + + if [ $filterstatus -eq 0 ] ; then + filtermenuitem=`cat $tmpfile` + if [ `expr match "$filtermenuitem" ".*Tx"` -ne 0 ] ; then + mode=$(( $mode | 16 )) + else + + mode=$(( $mode & 239 )) + fi + + if [ `expr match "$filtermenuitem" ".*Rx"` -ne 0 ] ; then + mode=$(( $mode | 32 )) + else + mode=$(( $mode & 223 )) + fi + echo $mode > /proc/oslec/mode + fi + done + + return +} + +disable() { + mode=`cat /proc/oslec/mode` + mode=$(( $mode | 64 )) + echo $mode > /proc/oslec/mode +} + +enable() { + mode=`cat /proc/oslec/mode` + mode=$(( $mode & 191 )) + echo $mode > /proc/oslec/mode +} + +status=0 +menuitem=Info +while [ $status -eq 0 ] +do + dialog --cancel-label Finish --default-item $menuitem \ + --menu "Oslec Control Panel" 12 60 6 \ + Info "Display echo canceller stats" \ + NLP "Configure Non Linear Processor" \ + Filters "Select High Pass Filters" \ + Reset "Reset echo canceller" \ + Disable "Disable echo canceller" \ + Enable "Enable echo canceller" \ + 2>$tmpfile + + status=$? + + if [ $status -eq 0 ] ; then + menuitem=`cat $tmpfile` + case $menuitem in + "Info") cat /proc/oslec/info>$tmpfile; dialog --textbox $tmpfile 18 60;; + "NLP") nlp_menu;; + "Filters") filter_menu;; + "Reset") echo 1 > /proc/oslec/reset;; + "Disable") disable;; + "Enable") enable;; + esac + fi +done + +rm $tmpfile --- zaptel-1.4.11~dfsg.orig/kernel/vzaphfc/vzaphfc.h +++ zaptel-1.4.11~dfsg/kernel/vzaphfc/vzaphfc.h @@ -0,0 +1,387 @@ +/* + * zaphfc.c - Zaptel driver for HFC-S PCI A based ISDN BRI cards + * + * Copyright (C) 2006 headissue GmbH; Jens Wilke + * Copyright (C) 2004 Daniele Orlandi + * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH + * + * Jens Wilke + * + * Orginal author of this code is + * Daniele "Vihai" Orlandi + * + * Major rewrite of the driver made by + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + */ + +#ifndef _HFC_ZAPHFC_H +#define _HFC_ZAPHFC_H + +#include +#include + +#include "../zaptel.h" + +#define hfc_DRIVER_NAME "vzaphfc" +#define hfc_DRIVER_PREFIX hfc_DRIVER_NAME ": " +#define hfc_DRIVER_DESCR "HFC-S PCI A Zaptel Driver" +#define hfc_DRIVER_VERSION "1.44" +#define hfc_DRIVER_STRING hfc_DRIVER_DESCR " (V" hfc_DRIVER_VERSION ")" + +#define hfc_MAX_BOARDS 32 + +#ifndef LINUX26 +#define IRQ_NONE +#define IRQ_HANDLED +#define irqreturn_t void +#endif + +#ifndef PCI_DMA_32BIT +#define PCI_DMA_32BIT 0x00000000ffffffffULL +#endif + +#ifndef PCI_VENDOR_ID_SITECOM +#define PCI_VENDOR_ID_SITECOM 0x182D +#endif + +#ifndef PCI_DEVICE_ID_SITECOM_3069 +#define PCI_DEVICE_ID_SITECOM_3069 0x3069 +#endif + +#define hfc_RESET_DELAY 20 + +#define hfc_CLKDEL_TE 0x0f /* CLKDEL in TE mode */ +#define hfc_CLKDEL_NT 0x6c /* CLKDEL in NT mode */ + +/* PCI memory mapped I/O */ + +#define hfc_PCI_MEM_SIZE 0x0100 +#define hfc_PCI_MWBA 0x80 + +/* GCI/IOM bus monitor registers */ + +#define hfc_C_I 0x08 +#define hfc_TRxR 0x0C +#define hfc_MON1_D 0x28 +#define hfc_MON2_D 0x2C + + +/* GCI/IOM bus timeslot registers */ + +#define hfc_B1_SSL 0x80 +#define hfc_B2_SSL 0x84 +#define hfc_AUX1_SSL 0x88 +#define hfc_AUX2_SSL 0x8C +#define hfc_B1_RSL 0x90 +#define hfc_B2_RSL 0x94 +#define hfc_AUX1_RSL 0x98 +#define hfc_AUX2_RSL 0x9C + +/* GCI/IOM bus data registers */ + +#define hfc_B1_D 0xA0 +#define hfc_B2_D 0xA4 +#define hfc_AUX1_D 0xA8 +#define hfc_AUX2_D 0xAC + +/* GCI/IOM bus configuration registers */ + +#define hfc_MST_EMOD 0xB4 +#define hfc_MST_MODE 0xB8 +#define hfc_CONNECT 0xBC + + +/* Interrupt and status registers */ + +#define hfc_FIFO_EN 0x44 +#define hfc_TRM 0x48 +#define hfc_B_MODE 0x4C +#define hfc_CHIP_ID 0x58 +#define hfc_CIRM 0x60 +#define hfc_CTMT 0x64 +#define hfc_INT_M1 0x68 +#define hfc_INT_M2 0x6C +#define hfc_INT_S1 0x78 +#define hfc_INT_S2 0x7C +#define hfc_STATUS 0x70 + +/* S/T section registers */ + +#define hfc_STATES 0xC0 +#define hfc_SCTRL 0xC4 +#define hfc_SCTRL_E 0xC8 +#define hfc_SCTRL_R 0xCC +#define hfc_SQ 0xD0 +#define hfc_CLKDEL 0xDC +#define hfc_B1_REC 0xF0 +#define hfc_B1_SEND 0xF0 +#define hfc_B2_REC 0xF4 +#define hfc_B2_SEND 0xF4 +#define hfc_D_REC 0xF8 +#define hfc_D_SEND 0xF8 +#define hfc_E_REC 0xFC + +/* Bits and values in various HFC PCI registers */ + +/* bits in status register (READ) */ +#define hfc_STATUS_PCI_PROC 0x02 +#define hfc_STATUS_NBUSY 0x04 +#define hfc_STATUS_TIMER_ELAP 0x10 +#define hfc_STATUS_STATINT 0x20 +#define hfc_STATUS_FRAMEINT 0x40 +#define hfc_STATUS_ANYINT 0x80 + +/* bits in CTMT (Write) */ +#define hfc_CTMT_TRANSB1 0x01 +#define hfc_CTMT_TRANSB2 0x02 +#define hfc_CTMT_TIMER_CLEAR 0x80 +#define hfc_CTMT_TIMER_MASK 0x1C +#define hfc_CTMT_TIMER_3_125 (0x01 << 2) +#define hfc_CTMT_TIMER_6_25 (0x02 << 2) +#define hfc_CTMT_TIMER_12_5 (0x03 << 2) +#define hfc_CTMT_TIMER_25 (0x04 << 2) +#define hfc_CTMT_TIMER_50 (0x05 << 2) +#define hfc_CTMT_TIMER_400 (0x06 << 2) +#define hfc_CTMT_TIMER_800 (0x07 << 2) +#define hfc_CTMT_AUTO_TIMER 0x20 + +/* bits in CIRM (Write) */ +#define hfc_CIRM_AUX_MSK 0x07 +#define hfc_CIRM_RESET 0x08 +#define hfc_CIRM_B1_REV 0x40 +#define hfc_CIRM_B2_REV 0x80 + +/* bits in INT_M1 and INT_S1 */ +#define hfc_INTS_B1TRANS 0x01 +#define hfc_INTS_B2TRANS 0x02 +#define hfc_INTS_DTRANS 0x04 +#define hfc_INTS_B1REC 0x08 +#define hfc_INTS_B2REC 0x10 +#define hfc_INTS_DREC 0x20 +#define hfc_INTS_L1STATE 0x40 +#define hfc_INTS_TIMER 0x80 + +/* bits in INT_M2 */ +#define hfc_M2_PROC_TRANS 0x01 +#define hfc_M2_GCI_I_CHG 0x02 +#define hfc_M2_GCI_MON_REC 0x04 +#define hfc_M2_IRQ_ENABLE 0x08 +#define hfc_M2_PMESEL 0x80 + +/* bits in STATES */ +#define hfc_STATES_STATE_MASK 0x0F +#define hfc_STATES_LOAD_STATE 0x10 +#define hfc_STATES_ACTIVATE 0x20 +#define hfc_STATES_DO_ACTION 0x40 +#define hfc_STATES_NT_G2_G3 0x80 + +/* bits in HFCD_MST_MODE */ +#define hfc_MST_MODE_MASTER 0x01 +#define hfc_MST_MODE_SLAVE 0x00 +/* remaining bits are for codecs control */ + +/* bits in HFCD_SCTRL */ +#define hfc_SCTRL_B1_ENA 0x01 +#define hfc_SCTRL_B2_ENA 0x02 +#define hfc_SCTRL_MODE_TE 0x00 +#define hfc_SCTRL_MODE_NT 0x04 +#define hfc_SCTRL_LOW_PRIO 0x08 +#define hfc_SCTRL_SQ_ENA 0x10 +#define hfc_SCTRL_TEST 0x20 +#define hfc_SCTRL_NONE_CAP 0x40 +#define hfc_SCTRL_PWR_DOWN 0x80 + +/* bits in SCTRL_E */ +#define hfc_SCTRL_E_AUTO_AWAKE 0x01 +#define hfc_SCTRL_E_DBIT_1 0x04 +#define hfc_SCTRL_E_IGNORE_COL 0x08 +#define hfc_SCTRL_E_CHG_B1_B2 0x80 + +/* bits in SCTRL_R */ +#define hfc_SCTRL_R_B1_ENA 0x01 +#define hfc_SCTRL_R_B2_ENA 0x02 + +/* bits in FIFO_EN register */ +#define hfc_FIFOEN_B1TX 0x01 +#define hfc_FIFOEN_B1RX 0x02 +#define hfc_FIFOEN_B2TX 0x04 +#define hfc_FIFOEN_B2RX 0x08 +#define hfc_FIFOEN_DTX 0x10 +#define hfc_FIFOEN_DRX 0x20 + +#define hfc_FIFOEN_B1 (hfc_FIFOEN_B1TX|hfc_FIFOEN_B1RX) +#define hfc_FIFOEN_B2 (hfc_FIFOEN_B2TX|hfc_FIFOEN_B2RX) +#define hfc_FIFOEN_D (hfc_FIFOEN_DTX|hfc_FIFOEN_DRX) + +/* bits in the CONNECT register */ +#define hfc_CONNECT_B1_HFC_from_ST 0x00 +#define hfc_CONNECT_B1_HFC_from_GCI 0x01 +#define hfc_CONNECT_B1_ST_from_HFC 0x00 +#define hfc_CONNECT_B1_ST_from_GCI 0x02 +#define hfc_CONNECT_B1_GCI_from_HFC 0x00 +#define hfc_CONNECT_B1_GCI_from_ST 0x04 + +#define hfc_CONNECT_B2_HFC_from_ST 0x00 +#define hfc_CONNECT_B2_HFC_from_GCI 0x08 +#define hfc_CONNECT_B2_ST_from_HFC 0x00 +#define hfc_CONNECT_B2_ST_from_GCI 0x10 +#define hfc_CONNECT_B2_GCI_from_HFC 0x00 +#define hfc_CONNECT_B2_GCI_from_ST 0x20 + +/* bits in the TRM register */ +#define hfc_TRM_TRANS_INT_00 0x00 +#define hfc_TRM_TRANS_INT_01 0x01 +#define hfc_TRM_TRANS_INT_10 0x02 +#define hfc_TRM_TRANS_INT_11 0x04 +#define hfc_TRM_ECHO 0x20 +#define hfc_TRM_B1_PLUS_B2 0x40 +#define hfc_TRM_IOM_TEST_LOOP 0x80 + +/* bits in the __SSL and __RSL registers */ +#define hfc_SRSL_STIO 0x40 +#define hfc_SRSL_ENABLE 0x80 +#define hfc_SRCL_SLOT_MASK 0x1f + +/* FIFO memory definitions */ + +#define hfc_FIFO_SIZE 0x8000 + +#define hfc_UGLY_FRAMEBUF 0x2000 + +#define hfc_TX_FIFO_PRELOAD ZT_CHUNKSIZE + 2 +#define hfc_RX_FIFO_PRELOAD 4 + +/* NOTE: FIFO pointers are not declared volatile because accesses to the + * FIFOs are inherently safe. + */ + +#ifdef DEBUG +extern int debug_level; +#endif + +struct hfc_chan; + +struct hfc_chan_simplex { + struct hfc_chan_duplex *chan; + + u8 zaptel_buffer[ZT_CHUNKSIZE]; + + u8 ugly_framebuf[hfc_UGLY_FRAMEBUF]; + int ugly_framebuf_size; + u16 ugly_framebuf_off; + + void *z1_base,*z2_base; + void *fifo_base; + void *z_base; + u16 z_min; + u16 z_max; + u16 fifo_size; + + u8 *f1,*f2; + u8 f_min; + u8 f_max; + u8 f_num; + + unsigned long long frames; + unsigned long long bytes; + unsigned long long fifo_full; + unsigned long long crc; + unsigned long long fifo_underrun; +}; + +enum hfc_chan_status { + free, + open_framed, + open_voice, + sniff_aux, + loopback, +}; + +struct hfc_chan_duplex { + struct hfc_card *card; + + char *name; + int number; + + enum hfc_chan_status status; + int open_by_netdev; + int open_by_zaptel; + + unsigned short protocol; + + spinlock_t lock; + + struct hfc_chan_simplex rx; + struct hfc_chan_simplex tx; + + struct net_device *netdev; + struct net_device_stats net_device_stats; +}; + +typedef struct hfc_card { + int cardnum; + spinlock_t lock; + + int ticks; + + struct pci_dev *pcidev; + + struct proc_dir_entry *proc_dir; + char proc_dir_name[32]; + + struct proc_dir_entry *proc_info; + struct proc_dir_entry *proc_fifos; + struct proc_dir_entry *proc_bufs; + + unsigned long io_bus_mem; + void *io_mem; + + dma_addr_t fifo_bus_mem; + void *fifo_mem; + void *fifos; + + int nt_mode; + int sync_loss_reported; + int late_irqs; + + u8 l1_state; + int fifo_suspended; + int ignore_first_timer_interrupt; + + struct { + u8 m1; + u8 m2; + u8 fifo_en; + u8 trm; + u8 connect; + u8 sctrl; + u8 sctrl_r; + u8 sctrl_e; + u8 ctmt; + u8 cirm; + } regs; + + struct hfc_chan_duplex chans[3]; + int echo_enabled; + + struct zt_span zt_span; + struct zt_chan zt_chans[3]; + + int debug_event; +} hfc_card; + +static inline u8 hfc_inb(struct hfc_card *card, int offset) +{ + return readb(card->io_mem + offset); +} + +static inline void hfc_outb(struct hfc_card *card, int offset, u8 value) +{ + writeb(value, card->io_mem + offset); +} + +#endif --- zaptel-1.4.11~dfsg.orig/kernel/vzaphfc/fifo.h +++ zaptel-1.4.11~dfsg/kernel/vzaphfc/fifo.h @@ -0,0 +1,131 @@ +/* + * zaphfc.c - Zaptel driver for HFC-S PCI A based ISDN BRI cards + * + * Copyright (C) 2004 Daniele Orlandi + * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH + * + * Daniele "Vihai" Orlandi + * + * Major rewrite of the driver made by + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + */ + +#ifndef _HFC_FIFO_H +#define _HFC_FIFO_H + +#include "vzaphfc.h" + +static inline u16 *Z1_F1(struct hfc_chan_simplex *chan) +{ + return chan->z1_base + (*chan->f1 * 4); +} + +static inline u16 *Z2_F1(struct hfc_chan_simplex *chan) +{ + return chan->z2_base + (*chan->f1 * 4); +} + +static inline u16 *Z1_F2(struct hfc_chan_simplex *chan) +{ + return chan->z1_base + (*chan->f2 * 4); +} + +static inline u16 *Z2_F2(struct hfc_chan_simplex *chan) +{ + return chan->z2_base + (*chan->f2 * 4); +} + +static inline u16 Z_inc(struct hfc_chan_simplex *chan, u16 z, u16 inc) +{ + // declared as u32 in order to manage overflows + u32 newz = z + inc; + if (newz > chan->z_max) + newz -= chan->fifo_size; + + return newz; +} + +static inline u8 F_inc(struct hfc_chan_simplex *chan, u8 f, u8 inc) +{ + // declared as u16 in order to manage overflows + u16 newf = f + inc; + if (newf > chan->f_max) + newf -= chan->f_num; + + return newf; +} + +static inline u16 hfc_fifo_used_rx(struct hfc_chan_simplex *chan) +{ + return (*Z1_F2(chan) - *Z2_F2(chan) + chan->fifo_size) % chan->fifo_size; +} + +static inline u16 hfc_fifo_get_frame_size(struct hfc_chan_simplex *chan) +{ + // This +1 is needed because in frame mode the available bytes are Z2-Z1+1 + // while in transparent mode I wouldn't consider the byte pointed by Z2 to + // be available, otherwise, the FIFO would always contain one byte, even + // when Z1==Z2 + + return hfc_fifo_used_rx(chan) + 1; +} + +static inline u8 hfc_fifo_u8(struct hfc_chan_simplex *chan, u16 z) +{ + return *((u8 *)(chan->z_base + z)); +} + +static inline u16 hfc_fifo_used_tx(struct hfc_chan_simplex *chan) +{ + return (*Z1_F1(chan) - *Z2_F1(chan) + chan->fifo_size) % chan->fifo_size; +} + +static inline u16 hfc_fifo_free_rx(struct hfc_chan_simplex *chan) +{ + u16 free_bytes=*Z2_F1(chan) - *Z1_F1(chan); + + if (free_bytes > 0) + return free_bytes; + else + return free_bytes + chan->fifo_size; +} + +static inline u16 hfc_fifo_free_tx(struct hfc_chan_simplex *chan) +{ + u16 free_bytes=*Z2_F1(chan) - *Z1_F1(chan); + + if (free_bytes > 0) + return free_bytes; + else + return free_bytes + chan->fifo_size; +} + +static inline int hfc_fifo_has_frames(struct hfc_chan_simplex *chan) +{ + return *chan->f1 != *chan->f2; +} + +static inline u8 hfc_fifo_used_frames(struct hfc_chan_simplex *chan) +{ + return (*chan->f1 - *chan->f2 + chan->f_num) % chan->f_num; +} + +static inline u8 hfc_fifo_free_frames(struct hfc_chan_simplex *chan) +{ + return (*chan->f2 - *chan->f1 + chan->f_num) % chan->f_num; +} + +int hfc_fifo_get(struct hfc_chan_simplex *chan, void *data, int size); +void hfc_fifo_put(struct hfc_chan_simplex *chan, void *data, int size); +void hfc_fifo_drop(struct hfc_chan_simplex *chan, int size); +int hfc_fifo_get_frame(struct hfc_chan_simplex *chan, void *data, int max_size); +void hfc_fifo_drop_frame(struct hfc_chan_simplex *chan); +void hfc_fifo_put_frame(struct hfc_chan_simplex *chan, void *data, int size); +void hfc_clear_fifo_rx(struct hfc_chan_simplex *chan); +void hfc_clear_fifo_tx(struct hfc_chan_simplex *chan); + +#endif --- zaptel-1.4.11~dfsg.orig/kernel/vzaphfc/fifo.c +++ zaptel-1.4.11~dfsg/kernel/vzaphfc/fifo.c @@ -0,0 +1,366 @@ +/* + * fifo.c - HFC FIFO management routines + * + * Copyright (C) 2006 headissue GmbH; Jens Wilke + * Copyright (C) 2004 Daniele Orlandi + * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH + * + * Original author of this code is + * Daniele "Vihai" Orlandi + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + */ + +#include + +#include "../zaptel.h" + +#include "fifo.h" + +static void hfc_fifo_mem_read(struct hfc_chan_simplex *chan, + int z_start, + void *data, int size) +{ + int bytes_to_boundary = chan->z_max - z_start + 1; + if (bytes_to_boundary >= size) { + memcpy(data, + chan->z_base + z_start, + size); + } else { + // Buffer wrap + memcpy(data, + chan->z_base + z_start, + bytes_to_boundary); + + memcpy(data + bytes_to_boundary, + chan->fifo_base, + size - bytes_to_boundary); + } +} + +static void hfc_fifo_mem_write(struct hfc_chan_simplex *chan, + void *data, int size) +{ + int bytes_to_boundary = chan->z_max - *Z1_F1(chan) + 1; + if (bytes_to_boundary >= size) { + memcpy(chan->z_base + *Z1_F1(chan), + data, + size); + } else { + // FIFO wrap + + memcpy(chan->z_base + *Z1_F1(chan), + data, + bytes_to_boundary); + + memcpy(chan->fifo_base, + data + bytes_to_boundary, + size - bytes_to_boundary); + } +} + +int hfc_fifo_get(struct hfc_chan_simplex *chan, + void *data, int size) +{ + int available_bytes; + + // Some useless statistic + chan->bytes += size; + + available_bytes = hfc_fifo_used_rx(chan); + + if (available_bytes < size && !chan->fifo_underrun++) { + // print the warning only once + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "RX FIFO not enough (%d) bytes to receive!\n", + chan->chan->card->cardnum, + chan->chan->name, + available_bytes); + return -1; + } + + hfc_fifo_mem_read(chan, *Z2_F2(chan), data, size); + *Z2_F2(chan) = Z_inc(chan, *Z2_F2(chan), size); + return available_bytes - size; +} + +/* +static void hfc_fifo_drop(struct hfc_chan_simplex *chan, int size) +{ + int available_bytes = hfc_fifo_used_rx(chan); + if (available_bytes + 1 < size) { + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "RX FIFO not enough (%d) bytes to drop!\n", + chan->chan->card->cardnum, + chan->chan->name, + available_bytes); + + return; + } + + *Z2_F2(chan) = Z_inc(chan, *Z2_F2(chan), size); +} +*/ + +void hfc_fifo_put(struct hfc_chan_simplex *chan, + void *data, int size) +{ + struct hfc_card *card = chan->chan->card; + int used_bytes = hfc_fifo_used_tx(chan); + int free_bytes = hfc_fifo_free_tx(chan); + + if (!used_bytes && !chan->fifo_underrun++) { + // print warning only once, to make timing not worse + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "TX FIFO has become empty\n", + card->cardnum, + chan->chan->name); + } + if (free_bytes < size) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "TX FIFO full!\n", + chan->chan->card->cardnum, + chan->chan->name); + chan->fifo_full++; + hfc_clear_fifo_tx(chan); + } + + hfc_fifo_mem_write(chan, data, size); + chan->bytes += size; + *Z1_F1(chan) = Z_inc(chan, *Z1_F1(chan), size); +} + +int hfc_fifo_get_frame(struct hfc_chan_simplex *chan, void *data, int max_size) +{ + int frame_size; + u16 newz2 ; + + if (*chan->f1 == *chan->f2) { + // nothing received, strange uh? + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "get_frame called with no frame in FIFO.\n", + chan->chan->card->cardnum, + chan->chan->name); + + return -1; + } + + // frame_size includes CRC+CRC+STAT + frame_size = hfc_fifo_get_frame_size(chan); + +#ifdef DEBUG + if(debug_level == 3) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "RX len %2d: ", + chan->chan->card->cardnum, + chan->chan->name, + frame_size); + } else if(debug_level >= 4) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "RX (f1=%02x, f2=%02x, z1=%04x, z2=%04x) len %2d: ", + chan->chan->card->cardnum, + chan->chan->name, + *chan->f1, *chan->f2, *Z1_F2(chan), *Z2_F2(chan), + frame_size); + } + + if(debug_level >= 3) { + int i; + for (i=0; i < frame_size; i++) { + printk("%02x", hfc_fifo_u8(chan, + Z_inc(chan, *Z2_F2(chan), i))); + } + + printk("\n"); + } +#endif + + if (frame_size <= 0) { +#ifdef DEBUG + if (debug_level >= 2) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "invalid (empty) frame received.\n", + chan->chan->card->cardnum, + chan->chan->name); + } +#endif + + hfc_fifo_drop_frame(chan); + return -1; + } + + // STAT is not really received + chan->bytes += frame_size - 1; + + // Calculate beginning of the next frame + newz2 = Z_inc(chan, *Z2_F2(chan), frame_size); + + // We cannot use hfc_fifo_get because of different semantic of + // "available bytes" and to avoid useless increment of Z2 + hfc_fifo_mem_read(chan, *Z2_F2(chan), data, + frame_size < max_size ? frame_size : max_size); + + if (hfc_fifo_u8(chan, Z_inc(chan, *Z2_F2(chan), + frame_size - 1)) != 0x00) { + // CRC not ok, frame broken, skipping +#ifdef DEBUG + if(debug_level >= 2) { + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "Received frame with wrong CRC\n", + chan->chan->card->cardnum, + chan->chan->name); + } +#endif + + chan->crc++; + chan->chan->net_device_stats.rx_errors++; + + hfc_fifo_drop_frame(chan); + return -1; + } + + chan->frames++; + + *chan->f2 = F_inc(chan, *chan->f2, 1); + + // Set Z2 for the next frame we're going to receive + *Z2_F2(chan) = newz2; + + return frame_size; +} + +void hfc_fifo_drop_frame(struct hfc_chan_simplex *chan) +{ + int available_bytes; + u16 newz2; + + if (*chan->f1 == *chan->f2) { + // nothing received, strange eh? + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "skip_frame called with no frame in FIFO.\n", + chan->chan->card->cardnum, + chan->chan->name); + + return; + } + +// chan->drops++; + + available_bytes = hfc_fifo_used_rx(chan) + 1; + + // Calculate beginning of the next frame + newz2 = Z_inc(chan, *Z2_F2(chan), available_bytes); + + *chan->f2 = F_inc(chan, *chan->f2, 1); + + // Set Z2 for the next frame we're going to receive + *Z2_F2(chan) = newz2; +} + +void hfc_fifo_put_frame(struct hfc_chan_simplex *chan, + void *data, int size) +{ + u16 newz1; + int available_frames; + +#ifdef DEBUG + if (debug_level == 3) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "TX len %2d: ", + chan->chan->card->cardnum, + chan->chan->name, + size); + } else if (debug_level >= 4) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "TX (f1=%02x, f2=%02x, z1=%04x, z2=%04x) len %2d: ", + chan->chan->card->cardnum, + chan->chan->name, + *chan->f1, *chan->f2, *Z1_F1(chan), *Z2_F1(chan), + size); + } + + if (debug_level >= 3) { + int i; + for (i=0; i= chan->f_num) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "TX FIFO total number of frames exceeded!\n", + chan->chan->card->cardnum, + chan->chan->name); + + chan->fifo_full++; + + return; + } + + hfc_fifo_put(chan, data, size); + + newz1 = *Z1_F1(chan); + + *chan->f1 = F_inc(chan, *chan->f1, 1); + + *Z1_F1(chan) = newz1; + + chan->frames++; +} + +void hfc_clear_fifo_rx(struct hfc_chan_simplex *chan) +{ + *chan->f2 = *chan->f1; + *Z2_F2(chan) = *Z1_F2(chan); +} + +void hfc_clear_fifo_tx(struct hfc_chan_simplex *chan) +{ + *chan->f1 = *chan->f2; + *Z1_F1(chan) = *Z2_F1(chan); + + if (chan->chan->status == open_voice) { + // Make sure that at least hfc_TX_FIFO_PRELOAD bytes are + // present in the TX FIFOs + + // Create hfc_TX_FIFO_PRELOAD bytes of empty data + // (0x7f is mute audio) + u8 empty_fifo[hfc_TX_FIFO_PRELOAD + ZT_CHUNKSIZE + hfc_RX_FIFO_PRELOAD]; + memset(empty_fifo, 0x7f, sizeof(empty_fifo)); + + hfc_fifo_put(chan, empty_fifo, sizeof(empty_fifo)); + } +} + --- zaptel-1.4.11~dfsg.orig/kernel/vzaphfc/ChangeLog +++ zaptel-1.4.11~dfsg/kernel/vzaphfc/ChangeLog @@ -0,0 +1,13 @@ +1.44 - 2007aug26 - Jens Wilke +- Fix for changes irq callback signature (pt_reqs removed) +- Fix for missing CHECKSUM_COMPLETE in newer kernel version +- Fix for missing linux/config.h in kernel headers +- tested with Linux kernel 2.6.20 + +1.43 - 2006oct03 - Jens Wilke +- fix wrong nt_modes parsing, found with kernel 2.6.15 + +1.42 - 2006jun06 - Jens Wilke +- fixed fifo timing issue +- added statistics for fifo underruns (RXU&TXU) +- maintanance now done by jw --- zaptel-1.4.11~dfsg.orig/kernel/vzaphfc/lapd.h +++ zaptel-1.4.11~dfsg/kernel/vzaphfc/lapd.h @@ -0,0 +1,26 @@ +/* + * lapd.c - net_device LAPD link layer support functionss + * + * Copyright (C) 2004 Daniele Orlandi + * + * Daniele "Vihai" Orlandi + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + */ + +#ifndef _HFC_LAPD_H +#define _HFC_LAPD_H + +#ifndef ARPHRD_LAPD +#define ARPHRD_LAPD 1000 /* LAPD pseudo type */ +#endif + +#ifndef ETH_P_LAPD +#define ETH_P_LAPD 0x0030 /* LAPD pseudo type */ +#endif + +void setup_lapd(struct net_device *netdev); + +#endif --- zaptel-1.4.11~dfsg.orig/kernel/vzaphfc/lapd.c +++ zaptel-1.4.11~dfsg/kernel/vzaphfc/lapd.c @@ -0,0 +1,48 @@ +/* + * lapd.c + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + */ + +#include +#include +#include + +#include "lapd.h" + +static int lapd_change_mtu(struct net_device *dev, int mtu) +{ + return -EINVAL; +} + +static int lapd_mac_addr(struct net_device *dev, void *addr) +{ + return -EINVAL; +} + +void setup_lapd(struct net_device *netdev) +{ + + netdev->change_mtu = lapd_change_mtu; + netdev->set_mac_address = lapd_mac_addr; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + netdev->hard_header = NULL; + netdev->rebuild_header = NULL; + netdev->hard_header_cache = NULL; + netdev->header_cache_update= NULL; +#endif + + netdev->type = ARPHRD_LAPD; + netdev->hard_header_len = 0; + netdev->mtu = 512; + netdev->addr_len = 0; + netdev->tx_queue_len = 10; + + memset(netdev->broadcast, 0x00, sizeof(netdev->broadcast)); + + netdev->flags = IFF_NOARP; +} + + --- zaptel-1.4.11~dfsg.orig/kernel/vzaphfc/Makefile +++ zaptel-1.4.11~dfsg/kernel/vzaphfc/Makefile @@ -0,0 +1,38 @@ +# +# Copyright (C) 2006 headissue GmbH; Jens Wilke +# +# This program is free software and may be modified and +# distributed under the terms of the GNU Public License. +# + +KSOURCE ?= /lib/modules/`uname -r`/build + +ifndef src + src=$(shell pwd) +endif + +ZAPTEL_SOURCE = $(src)/../../bristuff12/zaptel/ + +EXTRA_CFLAGS += -I $(ZAPTEL_SOURCE) + +obj-m := vzaphfc.o +vzaphfc-objs := vzaphfc_main.o fifo.o lapd.o + +all: + $(MAKE) -C $(KSOURCE) SUBDIRS=`pwd` modules + +clean: + $(MAKE) -C $(KSOURCE) SUBDIRS=`pwd` clean + +debug: + $(MAKE) -C $(KSOURCE) modules SUBDIRS=`pwd` EXTRA_CFLAGS="$(EXTRA_CFLAGS) -DDEBUG" CONFIG_DEBUG_INFO=1 + +# 2.4 stuff #### +modules: $(obj-m) + sync + +vzaphfc.o: vzaphfc_main.c fifo.c vzaphfc.h fifo.h lapd.h + $(CC) -c vzaphfc_main.c fifo.c $(CFLAGS) $(EXTRA_CFLAGS) + +install: + install -D -m 644 vzaphfc.ko $(INSTALL_PREFIX)/lib/modules/`uname -r`/misc/vzaphfc.ko --- zaptel-1.4.11~dfsg.orig/kernel/vzaphfc/vzaphfc_main.c +++ zaptel-1.4.11~dfsg/kernel/vzaphfc/vzaphfc_main.c @@ -0,0 +1,2037 @@ +/* + * zaphfc.c - Zaptel driver for HFC-S PCI A based ISDN BRI cards + * + * Copyright (C) 2006, headiisue GmbH; Jens Wilke + * Copyright (C) 2004 Daniele Orlandi + * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH + * + * Jens Wilke + * + * Original author of this code is + * Daniele "Vihai" Orlandi + * + * Major rewrite of the driver made by + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + * Please read the README file for important infos. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vzaphfc.h" +#include "fifo.h" +#include "lapd.h" + +#if CONFIG_PCI + +#define ZT_B1 0 +#define ZT_B2 1 +#define ZT_D 2 + +#define D 0 +#define B1 1 +#define B2 2 + +static int modes = 0; // all TE +static int nt_modes[hfc_MAX_BOARDS] = { [0 ... (hfc_MAX_BOARDS-1)] = -1 }; +static int force_l1_up = 0; +static int sniff_zaptel_d_channel = 0; +static struct proc_dir_entry *hfc_proc_zaphfc_dir; + +#ifdef DEBUG +int debug_level = 0; +#endif + +static struct pci_device_id hfc_pci_ids[] = { + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_2BD0, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B000, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B006, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B007, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B008, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B009, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00C, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_A1T, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_SITECOM, PCI_DEVICE_ID_SITECOM_3069, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, hfc_pci_ids); + +static int __devinit hfc_probe(struct pci_dev *dev + , const struct pci_device_id *ent); +static void __devexit hfc_remove(struct pci_dev *dev); + +struct pci_driver hfc_driver = { + .name = hfc_DRIVER_NAME, + .id_table = hfc_pci_ids, + .probe = hfc_probe, + .remove = hfc_remove, +}; + +/****************************************** + * HW routines + ******************************************/ + +static void hfc_softreset(struct hfc_card *card) +{ + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "resetting\n", + card->cardnum); + + hfc_outb(card, hfc_CIRM, hfc_CIRM_RESET); // softreset on + udelay(6); // wait at least 5.21us + hfc_outb(card, hfc_CIRM, 0); // softreset off + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((hfc_RESET_DELAY * HZ) / 1000); // wait 20 ms +} + +void hfc_resetCard(struct hfc_card *card) +{ + card->regs.m1 = 0; + hfc_outb(card, hfc_INT_M1, card->regs.m1); // no ints + + card->regs.m2 = 0; + hfc_outb(card, hfc_INT_M2, card->regs.m2); // not at all + + hfc_softreset(card); + + card->regs.trm = 0; + hfc_outb(card, hfc_TRM, card->regs.trm); + + // Select the non-capacitive line mode for the S/T interface */ + card->regs.sctrl = hfc_SCTRL_NONE_CAP; + + if (card->nt_mode) { + // ST-Bit delay for NT-Mode + hfc_outb(card, hfc_CLKDEL, hfc_CLKDEL_NT); + + card->regs.sctrl |= hfc_SCTRL_MODE_NT; + } else { + // ST-Bit delay for TE-Mode + hfc_outb(card, hfc_CLKDEL, hfc_CLKDEL_TE); + + card->regs.sctrl |= hfc_SCTRL_MODE_TE; + } + + hfc_outb(card, hfc_SCTRL, card->regs.sctrl); + + // S/T Auto awake + card->regs.sctrl_e = hfc_SCTRL_E_AUTO_AWAKE; + hfc_outb(card, hfc_SCTRL_E, card->regs.sctrl_e); + + // No B-channel enabled at startup + card->regs.sctrl_r = 0; + hfc_outb(card, hfc_SCTRL_R, card->regs.sctrl_r); + + // HFC Master Mode + hfc_outb(card, hfc_MST_MODE, hfc_MST_MODE_MASTER); + + // Connect internal blocks + card->regs.connect = + hfc_CONNECT_B1_HFC_from_ST | + hfc_CONNECT_B1_ST_from_HFC | + hfc_CONNECT_B1_GCI_from_HFC | + hfc_CONNECT_B2_HFC_from_ST | + hfc_CONNECT_B2_ST_from_HFC | + hfc_CONNECT_B2_GCI_from_HFC; + hfc_outb(card, hfc_CONNECT, card->regs.connect); + + // All bchans are HDLC by default, not useful, actually + // since mode is set during open() + hfc_outb(card, hfc_CTMT, 0); + + // bit order + hfc_outb(card, hfc_CIRM, 0); + + // Enable D-rx FIFO. At least one FIFO must be enabled (by specs) + card->regs.fifo_en = hfc_FIFOEN_DRX; + hfc_outb(card, hfc_FIFO_EN, card->regs.fifo_en); + + card->late_irqs=0; + + // Clear already pending ints + hfc_inb(card, hfc_INT_S1); + hfc_inb(card, hfc_INT_S2); + + // Enable IRQ output + card->regs.m1 = hfc_INTS_DREC | hfc_INTS_L1STATE | hfc_INTS_TIMER; + hfc_outb(card, hfc_INT_M1, card->regs.m1); + + card->regs.m2 = hfc_M2_IRQ_ENABLE; + hfc_outb(card, hfc_INT_M2, card->regs.m2); + + // Unlocks the states machine + hfc_outb(card, hfc_STATES, 0); + + // There's no need to explicitly activate L1 now. + // Activation is managed inside the interrupt routine. +} + +static void hfc_update_fifo_state(struct hfc_card *card) +{ + // I'm not sure if irqsave is needed but there could be a race + // condition since hfc_update_fifo_state could be called from + // both the IRQ handler and the *_(open|close) functions + + unsigned long flags; + spin_lock_irqsave(&card->chans[B1].lock, flags); + if (!card->fifo_suspended && + (card->chans[B1].status == open_framed || + card->chans[B1].status == open_voice)) { + + if(!(card->regs.fifo_en & hfc_FIFOEN_B1RX)) { + card->regs.fifo_en |= hfc_FIFOEN_B1RX; + hfc_clear_fifo_rx(&card->chans[B1].rx); + } + + if(!(card->regs.fifo_en & hfc_FIFOEN_B1TX)) { + card->regs.fifo_en |= hfc_FIFOEN_B1TX; + hfc_clear_fifo_tx(&card->chans[B1].tx); + } + } else { + if(card->regs.fifo_en & hfc_FIFOEN_B1RX) + card->regs.fifo_en &= ~hfc_FIFOEN_B1RX; + if(card->regs.fifo_en & hfc_FIFOEN_B1TX) + card->regs.fifo_en &= ~hfc_FIFOEN_B1TX; + } + spin_unlock_irqrestore(&card->chans[B1].lock, flags); + + spin_lock_irqsave(&card->chans[B2].lock, flags); + if (!card->fifo_suspended && + (card->chans[B2].status == open_framed || + card->chans[B2].status == open_voice || + card->chans[B2].status == sniff_aux)) { + + if(!(card->regs.fifo_en & hfc_FIFOEN_B2RX)) { + card->regs.fifo_en |= hfc_FIFOEN_B2RX; + hfc_clear_fifo_rx(&card->chans[B2].rx); + } + + if(!(card->regs.fifo_en & hfc_FIFOEN_B2TX)) { + card->regs.fifo_en |= hfc_FIFOEN_B2TX; + hfc_clear_fifo_tx(&card->chans[B2].tx); + } + } else { + if(card->regs.fifo_en & hfc_FIFOEN_B2RX) + card->regs.fifo_en &= ~hfc_FIFOEN_B2RX; + if(card->regs.fifo_en & hfc_FIFOEN_B2TX) + card->regs.fifo_en &= ~hfc_FIFOEN_B2TX; + } + spin_unlock_irqrestore(&card->chans[B2].lock, flags); + + spin_lock_irqsave(&card->chans[D].lock, flags); + if (!card->fifo_suspended && + card->chans[D].status == open_framed) { + +// if(!(card->regs.fifo_en & hfc_FIFOEN_DRX)) { +// hfc_clear_fifo_rx(&card->chans[B2].rx); +// +// card->chans[B2].rx.ugly_framebuf_size = 0; +// card->chans[B2].rx.ugly_framebuf_off = 0; +// } +// + if(!(card->regs.fifo_en & hfc_FIFOEN_DTX)) { + card->regs.fifo_en |= hfc_FIFOEN_DTX; + + card->chans[D].tx.ugly_framebuf_size = 0; + card->chans[D].tx.ugly_framebuf_off = 0; + } + } else { +// if(card->regs.fifo_en & hfc_FIFOEN_DRX) +// card->regs.fifo_en &= ~hfc_FIFOEN_DRX; + if(card->regs.fifo_en & hfc_FIFOEN_DTX) + card->regs.fifo_en &= ~hfc_FIFOEN_DTX; + } + spin_unlock_irqrestore(&card->chans[D].lock, flags); + + hfc_outb(card, hfc_FIFO_EN, card->regs.fifo_en); +} + +static inline void hfc_suspend_fifo(struct hfc_card *card) +{ + card->fifo_suspended = TRUE; + + hfc_update_fifo_state(card); + + // When L1 goes down D rx receives garbage; it is nice to + // clear it to avoid a CRC error on reactivation + // udelay is needed because the FIFO deactivation happens + // in 250us + udelay(250); + hfc_clear_fifo_rx(&card->chans[D].rx); + +#ifdef DEBUG + if (debug_level >= 3) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "FIFOs suspended\n", + card->cardnum); + } +#endif +} + +static inline void hfc_resume_fifo(struct hfc_card *card) +{ + card->fifo_suspended = FALSE; + + hfc_update_fifo_state(card); + +#ifdef DEBUG + if (debug_level >= 3) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "FIFOs resumed\n", + card->cardnum); + } +#endif +} + +static void hfc_check_l1_up(struct hfc_card *card) +{ + if ((!card->nt_mode && card->l1_state != 7) || + (card->nt_mode && card->l1_state != 3)) { +// 0 because this is quite verbose when an inferface is unconnected, jaw +#if 0 + if(debug_level >= 1) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "L1 is down, bringing up L1.\n", + card->cardnum); + } +#endif + + hfc_outb(card, hfc_STATES, hfc_STATES_DO_ACTION | + hfc_STATES_ACTIVATE| + hfc_STATES_NT_G2_G3); + } +} + +/****************************************** + * /proc interface functions + ******************************************/ + +struct hfc_status_to_name_names { + enum hfc_chan_status status; + char *name; +}; + +static char *hfc_status_to_name(int status) +{ + struct hfc_status_to_name_names names[] = { + { free, "free" }, + { open_framed, "framed" }, + { open_voice, "voice" }, + { sniff_aux, "sniff aux" }, + { loopback, "loopback" }, + }; + + int i; + + for (i=0; i> 4]; + dst[i*2+1]= hexchars[src[i] & 0xf]; + } + + dst[size*2]='\0'; +} + +static int hfc_proc_read_info(char *page, char **start, + off_t off, int count, + int *eof, void *data) +{ + struct hfc_card *card = data; + int len; + + u8 chip_id; + chip_id = hfc_inb(card, hfc_CHIP_ID); + + len = snprintf(page, PAGE_SIZE, + "Driver : %s\n" + "Cardnum : %d\n" + "IRQ : %d\n" + "PCI Mem : %#08lx (0x%p)\n" + "FIFO Mem : %#08lx (0x%p)\n" + "Mode : %s\n" + "CHIP_ID : %#02x\n" + "L1 State : %c%d\n" + "Sync Lost : %s\n" + "Late IRQs : %d\n" + "FIFO susp : %s\n" + "\nChannel %12s %12s %12s %12s %4s %4s %4s %4s %4s\n" + "D : %12llu %12llu %12llu %12llu %4llu %4llu %4llu %4llu %4llu %c%c %s\n" + "B1 : %12llu %12llu %12llu %12llu %4llu %4llu %4llu %4llu %4llu %c%c %s\n" + "B2 : %12llu %12llu %12llu %12llu %4llu %4llu %4llu %4llu %4llu %c%c %s\n" + ,hfc_DRIVER_STRING + ,card->cardnum + ,card->pcidev->irq + ,card->io_bus_mem, card->io_mem + ,(ulong)card->fifo_bus_mem, card->fifo_mem + ,card->nt_mode?"NT":"TE" + ,chip_id + ,card->nt_mode?'G':'F' + ,card->l1_state + ,card->sync_loss_reported?"YES":"NO" + ,card->late_irqs + ,card->fifo_suspended?"YES":"NO" + + ,"RX Frames","TX Frames","RX Bytes","TX Bytes","RXFF","TXFF","RXU","TXU","CRC" + ,card->chans[D].rx.frames + ,card->chans[D].tx.frames + ,card->chans[D].rx.bytes + ,card->chans[D].tx.bytes + ,card->chans[D].rx.fifo_full + ,card->chans[D].tx.fifo_full + ,card->chans[D].rx.fifo_underrun + ,card->chans[D].tx.fifo_underrun + ,card->chans[D].rx.crc + ,card->chans[D].open_by_netdev ? 'N' : ' ' + ,card->chans[D].open_by_zaptel ? 'Z' : ' ' + ,hfc_status_to_name(card->chans[D].status) + + ,card->chans[B1].rx.frames + ,card->chans[B1].tx.frames + ,card->chans[B1].rx.bytes + ,card->chans[B1].tx.bytes + ,card->chans[B1].rx.fifo_full + ,card->chans[B1].tx.fifo_full + ,card->chans[B1].rx.fifo_underrun + ,card->chans[B1].tx.fifo_underrun + ,card->chans[B1].rx.crc + ,card->chans[B1].open_by_netdev ? 'N' : ' ' + ,card->chans[B1].open_by_zaptel ? 'Z' : ' ' + ,hfc_status_to_name(card->chans[B1].status) + + ,card->chans[B2].rx.frames + ,card->chans[B2].tx.frames + ,card->chans[B2].rx.bytes + ,card->chans[B2].tx.bytes + ,card->chans[B2].rx.fifo_full + ,card->chans[B2].tx.fifo_full + ,card->chans[B2].rx.fifo_underrun + ,card->chans[B2].tx.fifo_underrun + ,card->chans[B2].rx.crc + ,card->chans[B2].open_by_netdev ? 'N' : ' ' + ,card->chans[B2].open_by_zaptel ? 'Z' : ' ' + ,hfc_status_to_name(card->chans[B2].status) + ); + + return len; +} + +static int hfc_proc_read_fifos(char *page, char **start, + off_t off, int count, + int *eof, void *data) +{ + struct hfc_card *card = data; + + int len; + len = snprintf(page, PAGE_SIZE, + " Receive Transmit\n" + "Channel F1 F2 Z1 Z2 Used F1 F2 Z1 Z2 Used\n" + "D : %02x %02x %04x %04x %4d %02x %02x %04x %04x %4d\n" + "B1 : %02x %02x %04x %04x %4d %02x %02x %04x %04x %4d\n" + "B2 : %02x %02x %04x %04x %4d %02x %02x %04x %04x %4d\n" + ,*card->chans[D].rx.f1 + ,*card->chans[D].rx.f2 + ,*Z1_F2(&card->chans[D].rx) + ,*Z2_F2(&card->chans[D].rx) + ,hfc_fifo_used_rx(&card->chans[D].rx) + ,*card->chans[D].tx.f1 + ,*card->chans[D].tx.f2 + ,*Z1_F1(&card->chans[D].tx) + ,*Z2_F1(&card->chans[D].tx) + ,hfc_fifo_used_tx(&card->chans[D].tx) + + ,*card->chans[B1].rx.f1 + ,*card->chans[B1].rx.f2 + ,*Z1_F2(&card->chans[B1].rx) + ,*Z2_F2(&card->chans[B1].rx) + ,hfc_fifo_used_rx(&card->chans[B1].rx) + ,*card->chans[B1].tx.f1 + ,*card->chans[B1].tx.f2 + ,*Z1_F1(&card->chans[B1].tx) + ,*Z2_F1(&card->chans[B1].tx) + ,hfc_fifo_used_tx(&card->chans[B1].tx) + + ,*card->chans[B2].rx.f1 + ,*card->chans[B2].rx.f2 + ,*Z1_F2(&card->chans[B2].rx) + ,*Z2_F2(&card->chans[B2].rx) + ,hfc_fifo_used_rx(&card->chans[B2].rx) + ,*card->chans[B2].tx.f1 + ,*card->chans[B2].tx.f2 + ,*Z1_F1(&card->chans[B2].tx) + ,*Z2_F1(&card->chans[B2].tx) + ,hfc_fifo_used_tx(&card->chans[B2].tx) + ); + + return len; +} + +static int hfc_proc_read_bufs(char *page, char **start, + off_t off, int count, + int *eof, void *data) +{ + struct hfc_card *card = data; + int len; + + char hex_rchunk[3][ZT_CHUNKSIZE * 2 + 1]; + char hex_wchunk[3][ZT_CHUNKSIZE * 2 + 1]; + + hfc_hexdump(card->chans[D].rx.zaptel_buffer, hex_rchunk[0], ZT_CHUNKSIZE); + hfc_hexdump(card->chans[D].tx.zaptel_buffer, hex_wchunk[0], ZT_CHUNKSIZE); + hfc_hexdump(card->chans[B1].rx.zaptel_buffer, hex_rchunk[1], ZT_CHUNKSIZE); + hfc_hexdump(card->chans[B1].tx.zaptel_buffer, hex_wchunk[1], ZT_CHUNKSIZE); + hfc_hexdump(card->chans[B2].rx.zaptel_buffer, hex_rchunk[2], ZT_CHUNKSIZE); + hfc_hexdump(card->chans[B2].tx.zaptel_buffer, hex_wchunk[2], ZT_CHUNKSIZE); + + len = snprintf(page, PAGE_SIZE, + "Channel Read Chunk Write Chunk\n" + "D : %16s %16s\n" + "B1 : %16s %16s\n" + "B2 : %16s %16s\n" + ,hex_rchunk[0],hex_wchunk[0] + ,hex_rchunk[1],hex_wchunk[1] + ,hex_rchunk[2],hex_wchunk[2] + ); + + return len; +} + +/****************************************** + * net_device interface functions + ******************************************/ + +static int hfc_open(struct net_device *netdev) +{ + struct hfc_chan_duplex *chan = netdev->priv; + struct hfc_card *card = chan->card; + + spin_lock(&chan->lock); + + if (chan->status != free && + (chan->number != D || chan->status != open_framed)) { + spin_unlock(&chan->lock); + return -EBUSY; + } + + chan->status = open_framed; + chan->open_by_netdev = TRUE; + + spin_unlock(&chan->lock); + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "chan %s opened.\n", + card->cardnum, + chan->name); + + return 0; +} + +static int hfc_close(struct net_device *netdev) +{ + struct hfc_chan_duplex *chan = netdev->priv; + struct hfc_card *card = chan->card; + + spin_lock(&chan->lock); + + if (chan->status != open_framed) { + spin_unlock(&chan->lock); + return -EINVAL; + } + + chan->status = free; + chan->open_by_netdev = FALSE; + + spin_unlock(&chan->lock); + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "chan %s closed.\n", + card->cardnum, + chan->name); + + return 0; +} + +static int hfc_xmit_frame(struct sk_buff *skb, struct net_device *netdev) +{ + struct hfc_chan_duplex *chan = netdev->priv; + struct hfc_card *card = chan->card; + + netdev->trans_start = jiffies; + + hfc_check_l1_up(card); + + hfc_fifo_put_frame(&chan->tx, skb->data, skb->len); + + // We're not called from IRQ handler, otherwise we'd need + // dev_kfree_skb + dev_kfree_skb(skb); + + return 0; +} + +static struct net_device_stats *hfc_get_stats(struct net_device *netdev) +{ + struct hfc_chan_duplex *chan = netdev->priv; +// struct hfc_card *card = chan->card; + + return &chan->net_device_stats; +} + +static void hfc_set_multicast_list(struct net_device *netdev) +{ + struct hfc_chan_duplex *chan = netdev->priv; + struct hfc_card *card = chan->card; + + spin_lock(&card->lock); + + if(netdev->flags & IFF_PROMISC && !card->echo_enabled) { + if (card->nt_mode) { + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d " + "is in NT mode, not going promiscuous\n", + card->cardnum); + + spin_unlock(&card->lock); + return; + } + + if (card->chans[B2].status != free) { + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "chan %s: is busy, not going promiscuous\n", + card->cardnum, + card->chans[B2].name); + + spin_unlock(&card->lock); + return; + } + + card->regs.trm |= hfc_TRM_ECHO; + card->regs.m1 |= hfc_INTS_B2REC; + card->regs.cirm &= ~hfc_CIRM_B2_REV; + card->regs.sctrl &= ~hfc_SCTRL_B2_ENA; + card->regs.sctrl_r &= ~hfc_SCTRL_R_B2_ENA; + card->regs.connect |= hfc_CONNECT_B2_ST_from_GCI; + card->regs.ctmt &= ~hfc_CTMT_TRANSB2; + + card->chans[B2].status = sniff_aux; + + card->echo_enabled = TRUE; + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "chan %s entered echo mode on channel %s\n", + card->cardnum, + chan->name, + card->chans[B2].name); + + } else if(!(netdev->flags & IFF_PROMISC) && card->echo_enabled) { + if (!card->echo_enabled) { + spin_unlock(&card->lock); + return; + } + + card->regs.trm &= ~hfc_TRM_ECHO; + card->regs.m1 &= ~hfc_INTS_B2REC; + card->regs.cirm |= hfc_CIRM_B2_REV; + card->regs.sctrl &= ~hfc_SCTRL_B2_ENA; + card->regs.sctrl_r &= ~hfc_SCTRL_R_B2_ENA; + card->regs.connect = + hfc_CONNECT_B1_HFC_from_ST | + hfc_CONNECT_B1_ST_from_HFC | + hfc_CONNECT_B1_GCI_from_HFC | + hfc_CONNECT_B2_HFC_from_ST | + hfc_CONNECT_B2_ST_from_HFC | + hfc_CONNECT_B2_GCI_from_HFC; + + card->chans[B2].status = free; + + card->echo_enabled = FALSE; + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "chan %s left promiscuous mode.\n", + card->cardnum, + chan->name); + } + + spin_unlock(&card->lock); + + hfc_outb(card, hfc_TRM, card->regs.trm); + hfc_outb(card, hfc_CIRM, card->regs.cirm); + hfc_outb(card, hfc_SCTRL, card->regs.sctrl); + hfc_outb(card, hfc_SCTRL_R, card->regs.sctrl_r); + hfc_outb(card, hfc_CONNECT, card->regs.connect); + hfc_outb(card, hfc_CTMT, card->regs.ctmt); + + // Enable appropriate B receive interrupt + hfc_outb(card, hfc_INT_M1, card->regs.m1); + + hfc_update_fifo_state(card); +} + +/****************************************** + * Zaptel interface (zaptel sucks) + ******************************************/ + +static int hfc_zap_open(struct zt_chan *zaptel_chan) +{ + struct hfc_chan_duplex *chan = zaptel_chan->pvt; + struct hfc_card *card = chan->card; + + spin_lock(&chan->lock); + + switch (chan->number) { + case D: + if (chan->status != free && + chan->status != open_framed) { + spin_unlock(&chan->lock); + return -EBUSY; + } + + chan->status = open_framed; + break; + + case B1: + case B2: + if (chan->status != free) { + spin_unlock(&chan->lock); + return -EBUSY; + } + + chan->status = open_voice; + break; + } + + chan->open_by_zaptel = TRUE; + +#ifndef LINUX26 + MOD_INC_USE_COUNT; +#else + // Zaptel is buggy + try_module_get(THIS_MODULE); +#endif + + spin_unlock(&chan->lock); + + switch (chan->number) { + case D: + break; + + case B1: + // B1 + card->regs.m2 |= hfc_M2_PROC_TRANS; + card->regs.ctmt |= hfc_CTMT_TRANSB1; // Enable transparent mode + card->regs.cirm |= hfc_CIRM_B1_REV; // Reversed bit order + card->regs.sctrl |= hfc_SCTRL_B1_ENA; // Enable transmission + card->regs.sctrl_r |= hfc_SCTRL_R_B1_ENA; // Enable reception + break; + + case B2: + // B2 + card->regs.m2 |= hfc_M2_PROC_TRANS; + card->regs.ctmt |= hfc_CTMT_TRANSB2; // Enable transparent mode + card->regs.cirm |= hfc_CIRM_B2_REV; // Reversed bit order + card->regs.sctrl |= hfc_SCTRL_B2_ENA; // Enable transmission + card->regs.sctrl_r |= hfc_SCTRL_R_B2_ENA; // Enable reception + break; + + } + + // If not already enabled, enable processing transition (8KHz) + // interrupt + hfc_outb(card, hfc_INT_M2, card->regs.m2); + hfc_outb(card, hfc_CTMT, card->regs.ctmt); + hfc_outb(card, hfc_CIRM, card->regs.cirm); + hfc_outb(card, hfc_SCTRL, card->regs.sctrl); + hfc_outb(card, hfc_SCTRL_R, card->regs.sctrl_r); + + hfc_update_fifo_state(card); + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "chan %s opened as %s.\n", + card->cardnum, + chan->name, + zaptel_chan->name); + + return 0; +} + +static int hfc_zap_close(struct zt_chan *zaptel_chan) +{ + struct hfc_chan_duplex *chan = zaptel_chan->pvt; + struct hfc_card *card = chan->card; + + if (!card) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "hfc_zap_close called with NULL card\n"); + return -1; + } + + spin_lock(&chan->lock); + + if (chan->status == free) { + spin_unlock(&chan->lock); + return -EINVAL; + } + + chan->status = free; + chan->open_by_zaptel = FALSE; + + spin_unlock(&chan->lock); + + switch (chan->number) { + case D: + break; + + case B1: + // B1 + card->regs.ctmt &= ~hfc_CTMT_TRANSB1; + card->regs.cirm &= ~hfc_CIRM_B1_REV; + card->regs.sctrl &= ~hfc_SCTRL_B1_ENA; + card->regs.sctrl_r &= ~hfc_SCTRL_R_B1_ENA; + break; + + case B2: + // B2 + card->regs.ctmt &= ~hfc_CTMT_TRANSB2; + card->regs.cirm &= ~hfc_CIRM_B2_REV; + card->regs.sctrl &= ~hfc_SCTRL_B2_ENA; + card->regs.sctrl_r &= ~hfc_SCTRL_R_B2_ENA; + break; + } + + if (card->chans[B1].status == free && + card->chans[B2].status == free) + card->regs.m2 &= ~hfc_M2_PROC_TRANS; + + hfc_outb(card, hfc_INT_M2, card->regs.m2); + hfc_outb(card, hfc_CTMT, card->regs.ctmt); + hfc_outb(card, hfc_CIRM, card->regs.cirm); + hfc_outb(card, hfc_SCTRL, card->regs.sctrl); + hfc_outb(card, hfc_SCTRL_R, card->regs.sctrl_r); + + hfc_update_fifo_state(card); + +#ifndef LINUX26 + MOD_DEC_USE_COUNT; +#else + // Zaptel is buggy + module_put(THIS_MODULE); +#endif + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "chan %s closed as %s.\n", + card->cardnum, + chan->name, + zaptel_chan->name); + + return 0; +} + +static int hfc_zap_rbsbits(struct zt_chan *chan, int bits) +{ + return 0; +} + +static int hfc_zap_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) +{ + switch(cmd) { + default: + return -ENOTTY; + } + + return 0; +} + +static int hfc_zap_startup(struct zt_span *span) +{ + struct hfc_card *card = span->pvt; + int alreadyrunning; + + if (!card) { + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "no card for span at startup!\n", + card->cardnum); + } + + alreadyrunning = span->flags & ZT_FLAG_RUNNING; + + if (!alreadyrunning) { + span->chans[ZT_D].flags &= ~ZT_FLAG_HDLC; + span->chans[ZT_D].flags |= ZT_FLAG_BRIDCHAN; + span->flags |= ZT_FLAG_RUNNING; + } + + return 0; +} + +static int hfc_zap_shutdown(struct zt_span *span) +{ + return 0; +} + +static int hfc_zap_maint(struct zt_span *span, int cmd) +{ + return 0; +} + +static int hfc_zap_chanconfig(struct zt_chan *chan, int sigtype) +{ + return 0; +} + +static int hfc_zap_spanconfig(struct zt_span *span, struct zt_lineconfig *lc) +{ + span->lineconfig = lc->lineconfig; + + return 0; +} + +static int hfc_zap_initialize(struct hfc_card *card) +{ + int i; + memset(&card->zt_span, 0x0, sizeof(struct zt_span)); + + snprintf(card->zt_span.name, sizeof(card->zt_span.name), + "ZTHFC%d", card->cardnum + 1); + + snprintf(card->zt_span.desc, sizeof(card->zt_span.desc), + hfc_DRIVER_DESCR " card %d [%s]", + card->cardnum, + card->nt_mode?"NT":"TE"); + + card->zt_span.spanconfig = hfc_zap_spanconfig; + card->zt_span.chanconfig = hfc_zap_chanconfig; + card->zt_span.startup = hfc_zap_startup; + card->zt_span.shutdown = hfc_zap_shutdown; + card->zt_span.maint = hfc_zap_maint; + card->zt_span.rbsbits = hfc_zap_rbsbits; + card->zt_span.open = hfc_zap_open; + card->zt_span.close = hfc_zap_close; + card->zt_span.ioctl = hfc_zap_ioctl; + + card->zt_span.chans = card->zt_chans; + card->zt_span.channels = 3; + card->zt_span.deflaw = ZT_LAW_ALAW; + card->zt_span.linecompat = ZT_CONFIG_AMI | ZT_CONFIG_CCS; // <--- this is really BS + card->zt_span.offset = 0; + init_waitqueue_head(&card->zt_span.maintq); + card->zt_span.pvt = card; + + for (i = 0; i < card->zt_span.channels; i++) { + memset(&card->zt_chans[i], 0x0, sizeof(struct zt_chan)); + + snprintf(card->zt_chans[i].name, sizeof(card->zt_chans[i].name), + "ZTHFC%d/%d/%d", + card->cardnum + 1, 0, i + 1); + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "registered %s\n", + card->cardnum, + card->zt_chans[i].name); + + card->zt_chans[i].sigcap = ZT_SIG_EM | ZT_SIG_CLEAR | + ZT_SIG_FXSLS | ZT_SIG_FXSGS | + ZT_SIG_FXSKS | ZT_SIG_FXOLS | + ZT_SIG_FXOGS | ZT_SIG_FXOKS | + ZT_SIG_CAS | ZT_SIG_SF; + + card->zt_chans[i].chanpos = i + 1; + } + + card->zt_chans[ZT_D].readchunk = card->chans[D].rx.zaptel_buffer; + card->zt_chans[ZT_D].writechunk = card->chans[D].tx.zaptel_buffer; + card->zt_chans[ZT_D].pvt = &card->chans[D]; + + card->zt_chans[ZT_B1].readchunk = card->chans[B1].rx.zaptel_buffer; + card->zt_chans[ZT_B1].writechunk = card->chans[B1].tx.zaptel_buffer; + card->zt_chans[ZT_B1].pvt = &card->chans[B1]; + + card->zt_chans[ZT_B2].readchunk = card->chans[B2].rx.zaptel_buffer; + card->zt_chans[ZT_B2].writechunk = card->chans[B2].tx.zaptel_buffer; + card->zt_chans[ZT_B2].pvt = &card->chans[B2]; + + if (zt_register(&card->zt_span,0)) { + printk(KERN_CRIT "unable to register zaptel device!\n"); + return -1; + } + + return 0; +} + +static void hfc_zap_transmit(struct hfc_chan_simplex *chan) +{ + hfc_fifo_put(chan, chan->zaptel_buffer, ZT_CHUNKSIZE); +} + +static void hfc_zap_receive(struct hfc_chan_simplex *chan) +{ + hfc_fifo_get(chan, chan->zaptel_buffer, ZT_CHUNKSIZE); +} + +/****************************************** + * Interrupt Handler + ******************************************/ + +static void hfc_handle_timer_interrupt(struct hfc_card *card); +static void hfc_handle_state_interrupt(struct hfc_card *card); +static void hfc_handle_processing_interrupt(struct hfc_card *card); +static void hfc_frame_arrived(struct hfc_chan_duplex *chan); +static void hfc_handle_voice(struct hfc_card *card); + +ZAP_IRQ_HANDLER(hfc_interrupt) +{ + struct hfc_card *card = dev_id; + unsigned long flags; + u8 status,s1,s2; + + if (!card) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "spurious interrupt (IRQ %d)\n", + irq); + return IRQ_NONE; + } + + spin_lock_irqsave(&card->lock, flags); + status = hfc_inb(card, hfc_STATUS); + if (!(status & hfc_STATUS_ANYINT)) { + // maybe we are sharing the irq + spin_unlock_irqrestore(&card->lock,flags); + return IRQ_NONE; + } + + /* We used to ingore the IRQ when the card was in processing + * state but apparently there is no restriction to access the + * card in such state: + * + * Joerg Ciesielski wrote: + * > There is no restriction for the IRQ handler to access + * > HFC-S PCI during processing phase. A IRQ latency of 375 us + * > is also no problem since there are no interrupt sources in + * > HFC-S PCI which must be handled very fast. + * > Due to its deep fifos the IRQ latency can be several ms with + * > out the risk of loosing data. Even the S/T state interrupts + * > must not be handled with a latency less than <5ms. + * > + * > The processing phase only indicates that HFC-S PCI is + * > processing the Fifos as PCI master so that data is read and + * > written in the 32k memory window. But there is no restriction + * > to access data in the memory window during this time. + * + * // if (status & hfc_STATUS_PCI_PROC) { + * // return IRQ_HANDLED; + * // } + */ + + s1 = hfc_inb(card, hfc_INT_S1); + s2 = hfc_inb(card, hfc_INT_S2); + + if (s1 != 0) { + if (s1 & hfc_INTS_TIMER) { + // timer (bit 7) + hfc_handle_timer_interrupt(card); + } + + if (s1 & hfc_INTS_L1STATE) { + // state machine (bit 6) + hfc_handle_state_interrupt(card); + } + + if (s1 & hfc_INTS_DREC) { + // D chan RX (bit 5) + hfc_frame_arrived(&card->chans[D]); + } + + if (s1 & hfc_INTS_B1REC) { + // B1 chan RX (bit 3) + hfc_frame_arrived(&card->chans[B1]); + } + + if (s1 & hfc_INTS_B2REC) { + // B2 chan RX (bit 4) + hfc_frame_arrived(&card->chans[B2]); + } + + if (s1 & hfc_INTS_DTRANS) { + // D chan TX (bit 2) + } + + if (s1 & hfc_INTS_B1TRANS) { + // B1 chan TX (bit 0) + } + + if (s1 & hfc_INTS_B2TRANS) { + // B2 chan TX (bit 1) + } + + } + + if (s2 != 0) { + if (s2 & hfc_M2_PMESEL) { + // kaboom irq (bit 7) + + /* CologneChip says: + * + * the meaning of this fatal error bit is that HFC-S PCI as PCI + * master could not access the PCI bus within 125us to finish its + * data processing. If this happens only very seldom it does not + * cause big problems but of course some B-channel or D-channel + * data will be corrupted due to this event. + * + * Unfortunately this bit is only set once after the problem occurs + * and can only be reseted by a software reset. That means it is not + * easily possible to check how often this fatal error happens. + */ + + if(!card->sync_loss_reported) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "sync lost, pci performance too low!\n", + card->cardnum); + + card->sync_loss_reported = TRUE; + } + } + + if (s2 & hfc_M2_GCI_MON_REC) { + // RxR monitor channel (bit 2) + } + + if (s2 & hfc_M2_GCI_I_CHG) { + // GCI I-change (bit 1) + } + + if (s2 & hfc_M2_PROC_TRANS){ + // processing/non-processing transition (bit 0) + hfc_handle_processing_interrupt(card); + } + + } + + spin_unlock_irqrestore(&card->lock,flags); + + return IRQ_HANDLED; +} + +static void hfc_handle_timer_interrupt(struct hfc_card *card) +{ + if(card->ignore_first_timer_interrupt) { + card->ignore_first_timer_interrupt = FALSE; + return; + } + + if ((card->nt_mode && card->l1_state == 3) || + (!card->nt_mode && card->l1_state == 7)) { + + card->regs.ctmt &= ~hfc_CTMT_TIMER_MASK; + hfc_outb(card, hfc_CTMT, card->regs.ctmt); + + hfc_resume_fifo(card); + } +} + +static void hfc_handle_state_interrupt(struct hfc_card *card) +{ + u8 new_state = hfc_inb(card,hfc_STATES) & hfc_STATES_STATE_MASK; + +#ifdef DEBUG + if (debug_level >= 1) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "layer 1 state = %c%d\n", + card->cardnum, + card->nt_mode?'G':'F', + new_state); + } +#endif + + if (card->nt_mode) { + // NT mode + + if (new_state == 3) { + // fix to G3 state (see specs) + hfc_outb(card, hfc_STATES, hfc_STATES_LOAD_STATE | 3); + } + + if (new_state == 3 && card->l1_state != 3) { + hfc_resume_fifo(card); + } + + if (new_state != 3 && card->l1_state == 3) { + hfc_suspend_fifo(card); + } + } else { + if (new_state == 3) { + // Keep L1 up... zaptel & libpri expects a always up L1... + // Enable only when using an unpatched libpri + + if (force_l1_up) { + hfc_outb(card, hfc_STATES, + hfc_STATES_DO_ACTION | + hfc_STATES_ACTIVATE| + hfc_STATES_NT_G2_G3); + } + } + + if (new_state == 7 && card->l1_state != 7) { + // TE is now active, schedule FIFO activation after + // some time, otherwise the first frames are lost + + card->regs.ctmt |= hfc_CTMT_TIMER_50 | hfc_CTMT_TIMER_CLEAR; + hfc_outb(card, hfc_CTMT, card->regs.ctmt); + + // Activating the timer firest an interrupt immediately, we + // obviously need to ignore it + card->ignore_first_timer_interrupt = TRUE; + } + + if (new_state != 7 && card->l1_state == 7) { + // TE has become inactive, disable FIFO + hfc_suspend_fifo(card); + } + } + + card->l1_state = new_state; +} + +static void hfc_handle_processing_interrupt(struct hfc_card *card) +{ + int available_bytes=0; + + // Synchronize with the first enabled channel + if(card->regs.fifo_en & hfc_FIFOEN_B1RX) + available_bytes = hfc_fifo_used_rx(&card->chans[B1].rx); + if(card->regs.fifo_en & hfc_FIFOEN_B2RX) + available_bytes = hfc_fifo_used_rx(&card->chans[B2].rx); + else + available_bytes = -1; + + if ((available_bytes == -1 && card->ticks == 8) || + available_bytes >= ZT_CHUNKSIZE + hfc_RX_FIFO_PRELOAD) { + card->ticks = 0; + + if (available_bytes > ZT_CHUNKSIZE*2 + hfc_RX_FIFO_PRELOAD) { + card->late_irqs++; + // we are out of sync, clear fifos, jaw + hfc_clear_fifo_rx(&card->chans[B1].rx); + hfc_clear_fifo_tx(&card->chans[B1].tx); + hfc_clear_fifo_rx(&card->chans[B2].rx); + hfc_clear_fifo_tx(&card->chans[B2].tx); + +#ifdef DEBUG + if (debug_level >= 4) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "late IRQ, %d bytes late\n", + card->cardnum, + available_bytes - + (ZT_CHUNKSIZE + + hfc_RX_FIFO_PRELOAD)); + } +#endif + } else { + hfc_handle_voice(card); + } + } + + card->ticks++; +} + +static void hfc_sniff_zaptel_d_channel(struct hfc_card *card) +{ + struct sk_buff *skb = + dev_alloc_skb(card->chans[D].tx.ugly_framebuf_size); + + if (!skb) { + printk(KERN_ERR hfc_DRIVER_PREFIX + "card %d: " + "cannot allocate skb: sniffed frame dropped\n", + card->cardnum); + return; + } + + skb->dev = card->chans[D].netdev; + skb->protocol = htons(card->chans[D].protocol); +#ifndef CHECKSUM_HW + skb->ip_summed = CHECKSUM_COMPLETE; +#else + skb->ip_summed = CHECKSUM_HW; +#endif + skb->pkt_type = PACKET_OUTGOING; + + memcpy(skb_put(skb, card->chans[D].tx.ugly_framebuf_size), + card->chans[D].tx.ugly_framebuf, + card->chans[D].tx.ugly_framebuf_size); + + netif_rx(skb); +} + +static void hfc_handle_voice(struct hfc_card *card) +{ + if (card->chans[B1].status != open_voice && + card->chans[B2].status != open_voice) + return; + + card->zt_chans[ZT_D].bytes2transmit = 0; + card->zt_chans[ZT_D].maxbytes2transmit = ZT_CHUNKSIZE; + + zt_transmit(&card->zt_span); + + if (card->regs.fifo_en & hfc_FIFOEN_B1TX) + hfc_zap_transmit(&card->chans[B1].tx); + if (card->regs.fifo_en & hfc_FIFOEN_B2TX) + hfc_zap_transmit(&card->chans[B2].tx); + + if (card->zt_chans[ZT_D].bytes2transmit) { + hfc_check_l1_up(card); + + memcpy(card->chans[D].tx.ugly_framebuf + + card->chans[D].tx.ugly_framebuf_size, + card->chans[D].tx.zaptel_buffer, + card->zt_chans[ZT_D].bytes2transmit); + + card->chans[D].tx.ugly_framebuf_size += + card->zt_chans[ZT_D].bytes2transmit; + + if (card->zt_chans[ZT_D].eoftx) { + hfc_fifo_put_frame(&card->chans[D].tx, + card->chans[D].tx.ugly_framebuf, + card->chans[D].tx.ugly_framebuf_size); + + if (sniff_zaptel_d_channel) + hfc_sniff_zaptel_d_channel(card); + + card->chans[D].tx.ugly_framebuf_size = 0; + card->zt_chans[ZT_D].eoftx = FALSE; + } + } + + if (card->regs.fifo_en & hfc_FIFOEN_B1RX) + hfc_zap_receive(&card->chans[B1].rx); + else + memset(&card->chans[B1].rx.zaptel_buffer, 0x7f, + sizeof(card->chans[B1].rx.zaptel_buffer)); + + if (card->regs.fifo_en & hfc_FIFOEN_B2RX) + hfc_zap_receive(&card->chans[B2].rx); + else + memset(&card->chans[B2].rx.zaptel_buffer, 0x7f, + sizeof(card->chans[B1].rx.zaptel_buffer)); + + // Echo cancellation + zt_ec_chunk(&card->zt_chans[ZT_B1], + card->chans[B1].rx.zaptel_buffer, + card->chans[B1].tx.zaptel_buffer); + zt_ec_chunk(&card->zt_chans[ZT_B2], + card->chans[B2].rx.zaptel_buffer, + card->chans[B2].tx.zaptel_buffer); + + // If there's a frame in the FIFO, read it all and make it + // available to zaptel + if (hfc_fifo_has_frames(&card->chans[D].rx)) { + hfc_frame_arrived(&card->chans[D]); + } + + // Stupid zaptel frame handling + if (!card->chans[D].rx.ugly_framebuf_size) { + // hmm....ok, let zaptel receive nothing + card->zt_chans[ZT_D].bytes2receive = 0; + card->zt_chans[ZT_D].eofrx = FALSE; + } + else if (card->chans[D].rx.ugly_framebuf_size - + card->chans[D].rx.ugly_framebuf_off > ZT_CHUNKSIZE) { + + // the frame is longer than ZT_CHUNKSIZE + memcpy(card->chans[D].rx.zaptel_buffer, + card->chans[D].rx.ugly_framebuf+ + card->chans[D].rx.ugly_framebuf_off, + ZT_CHUNKSIZE); + + card->zt_chans[ZT_D].bytes2receive = ZT_CHUNKSIZE; + card->zt_chans[ZT_D].eofrx = FALSE; + + card->chans[D].rx.ugly_framebuf_off += ZT_CHUNKSIZE; + } else { + // we can read it all + memcpy(card->chans[D].rx.zaptel_buffer, + card->chans[D].rx.ugly_framebuf+ + card->chans[D].rx.ugly_framebuf_off, + card->chans[D].rx.ugly_framebuf_size); + + card->zt_chans[ZT_D].bytes2receive = card->chans[D].rx.ugly_framebuf_size- + card->chans[D].rx.ugly_framebuf_off; + card->zt_chans[ZT_D].eofrx = TRUE; + + card->chans[D].rx.ugly_framebuf_size = 0; + card->chans[D].rx.ugly_framebuf_off = 0; + } + + if (card->zt_span.flags & ZT_FLAG_RUNNING) { + zt_receive(&card->zt_span); + } + +} + +static void hfc_frame_arrived(struct hfc_chan_duplex *chan) +{ + struct hfc_card *card = chan->card; + int antiloop = 16; + struct sk_buff *skb; + + while(hfc_fifo_has_frames(&chan->rx) && --antiloop) { + int frame_size = hfc_fifo_get_frame_size(&chan->rx); + + if (frame_size < 3) { +#ifdef DEBUG + if (debug_level>=2) + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "invalid frame received, just %d bytes\n", + card->cardnum, + chan->name, + frame_size); +#endif + + hfc_fifo_drop_frame(&chan->rx); + + chan->net_device_stats.rx_dropped++; + + continue; + } else if(frame_size == 3) { +#ifdef DEBUG + if (debug_level>=2) + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "empty frame received\n", + card->cardnum, + chan->name); +#endif + + hfc_fifo_drop_frame(&chan->rx); + + chan->net_device_stats.rx_dropped++; + + continue; + } + + if (chan->open_by_zaptel && + card->chans[D].rx.ugly_framebuf_size) { + // We have to wait for zaptel to transmit the + // frame... wait for next time + + break; + } + + skb = dev_alloc_skb(frame_size - 3); + + if (!skb) { + printk(KERN_ERR hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "cannot allocate skb: frame dropped\n", + card->cardnum, + chan->name); + + hfc_fifo_drop_frame(&chan->rx); + + chan->net_device_stats.rx_dropped++; + + continue; + } + + // Oh... this is the echo channel... redirect to D + // channel's netdev + if (card->echo_enabled && chan->number == B2) { + skb->protocol = htons(card->chans[D].protocol); + skb->dev = card->chans[D].netdev; + skb->pkt_type = PACKET_OTHERHOST; + } else { + skb->protocol = htons(chan->protocol); + skb->dev = chan->netdev; + skb->pkt_type = PACKET_HOST; + } + +#ifndef CHECKSUM_HW + skb->ip_summed = CHECKSUM_COMPLETE; +#else + skb->ip_summed = CHECKSUM_HW; +#endif + + if (chan->open_by_zaptel) { + card->chans[D].rx.ugly_framebuf_size = frame_size - 1; + + if (hfc_fifo_get_frame(&card->chans[D].rx, + card->chans[D].rx.ugly_framebuf, + frame_size - 1) == -1) { + dev_kfree_skb(skb); + continue; + } + + memcpy(skb_put(skb, frame_size - 3), + card->chans[D].rx.ugly_framebuf, + frame_size - 3); + } else { + if (hfc_fifo_get_frame(&chan->rx, + skb_put(skb, frame_size - 3), + frame_size - 3) == -1) { + dev_kfree_skb(skb); + continue; + } + } + + chan->net_device_stats.rx_packets++; + chan->net_device_stats.rx_bytes += frame_size - 1; + + netif_rx(skb); + } + + if (!antiloop) + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "Infinite loop detected\n", + card->cardnum); +} + +/****************************************** + * Module initialization and cleanup + ******************************************/ + +static void hfc_setup_lapd(struct hfc_chan_duplex *chan) +{ + chan->netdev->priv = chan; + chan->netdev->open = hfc_open; + chan->netdev->stop = hfc_close; + chan->netdev->hard_start_xmit = hfc_xmit_frame; + chan->netdev->get_stats = hfc_get_stats; + chan->netdev->set_multicast_list = hfc_set_multicast_list; + + memset(chan->netdev->dev_addr, 0x00, sizeof(chan->netdev->dev_addr)); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + SET_MODULE_OWNER(chan->netdev); +#endif +} + +static int __devinit hfc_probe(struct pci_dev *pci_dev, + const struct pci_device_id *ent) +{ + static int cardnum=0; + int err; + int i; + + struct hfc_card *card = NULL; + card = kmalloc(sizeof(struct hfc_card), GFP_KERNEL); + if (!card) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "unable to kmalloc!\n"); + err = -ENOMEM; + goto err_alloc_hfccard; + } + + memset(card, 0x00, sizeof(struct hfc_card)); + card->cardnum = cardnum; + card->pcidev = pci_dev; + spin_lock_init(&card->lock); + + pci_set_drvdata(pci_dev, card); + + if ((err = pci_enable_device(pci_dev))) { + goto err_pci_enable_device; + } + + if ((err = pci_set_dma_mask(pci_dev, PCI_DMA_32BIT))) { + printk(KERN_ERR hfc_DRIVER_PREFIX + "card %d: " + "No suitable DMA configuration available.\n", + card->cardnum); + goto err_pci_set_dma_mask; + } + + pci_write_config_word(pci_dev, PCI_COMMAND, PCI_COMMAND_MEMORY); + + if((err = pci_request_regions(pci_dev, hfc_DRIVER_NAME))) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "cannot request I/O memory region\n", + card->cardnum); + goto err_pci_request_regions; + } + + pci_set_master(pci_dev); + + if (!pci_dev->irq) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "no irq!\n", + card->cardnum); + err = -ENODEV; + goto err_noirq; + } + + card->io_bus_mem = pci_resource_start(pci_dev,1); + if (!card->io_bus_mem) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "no iomem!\n", + card->cardnum); + err = -ENODEV; + goto err_noiobase; + } + + if(!(card->io_mem = ioremap(card->io_bus_mem, hfc_PCI_MEM_SIZE))) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "cannot ioremap I/O memory\n", + card->cardnum); + err = -ENODEV; + goto err_ioremap; + } + + // pci_alloc_consistent guarantees alignment (Documentation/DMA-mapping.txt) + card->fifo_mem = pci_alloc_consistent(pci_dev, hfc_FIFO_SIZE, &card->fifo_bus_mem); + if (!card->fifo_mem) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "unable to allocate FIFO DMA memory!\n", + card->cardnum); + err = -ENOMEM; + goto err_alloc_fifo; + } + + memset(card->fifo_mem, 0x00, hfc_FIFO_SIZE); + + card->fifos = card->fifo_mem; + + pci_write_config_dword(card->pcidev, hfc_PCI_MWBA, card->fifo_bus_mem); + + if ((err = request_irq(card->pcidev->irq, &hfc_interrupt, + ZAP_IRQ_SHARED, hfc_DRIVER_NAME, card))) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "unable to register irq\n", + card->cardnum); + goto err_request_irq; + } + + card->nt_mode = FALSE; + + if (modes & (1 << card->cardnum)) + card->nt_mode = TRUE; + + for (i=0; icardnum) { + card->nt_mode=TRUE; + } + } + +//---------------------------------- D + card->chans[D].card = card; + card->chans[D].name = "D"; + card->chans[D].status = free; + card->chans[D].number = D; + card->chans[D].protocol = ETH_P_LAPD; + spin_lock_init(&card->chans[D].lock); + + card->chans[D].rx.chan = &card->chans[D]; + card->chans[D].rx.fifo_base = card->fifos + 0x4000; + card->chans[D].rx.z_base = card->fifos + 0x4000; + card->chans[D].rx.z1_base = card->fifos + 0x6080; + card->chans[D].rx.z2_base = card->fifos + 0x6082; + card->chans[D].rx.z_min = 0x0000; + card->chans[D].rx.z_max = 0x01FF; + card->chans[D].rx.f_min = 0x10; + card->chans[D].rx.f_max = 0x1F; + card->chans[D].rx.f1 = card->fifos + 0x60a0; + card->chans[D].rx.f2 = card->fifos + 0x60a1; + card->chans[D].rx.fifo_size = card->chans[D].rx.z_max - card->chans[D].rx.z_min + 1; + card->chans[D].rx.f_num = card->chans[D].rx.f_max - card->chans[D].rx.f_min + 1; + + card->chans[D].tx.chan = &card->chans[D]; + card->chans[D].tx.fifo_base = card->fifos + 0x0000; + card->chans[D].tx.z_base = card->fifos + 0x0000; + card->chans[D].tx.z1_base = card->fifos + 0x2080; + card->chans[D].tx.z2_base = card->fifos + 0x2082; + card->chans[D].tx.z_min = 0x0000; + card->chans[D].tx.z_max = 0x01FF; + card->chans[D].tx.f_min = 0x10; + card->chans[D].tx.f_max = 0x1F; + card->chans[D].tx.f1 = card->fifos + 0x20a0; + card->chans[D].tx.f2 = card->fifos + 0x20a1; + card->chans[D].tx.fifo_size = card->chans[D].tx.z_max - card->chans[D].tx.z_min + 1; + card->chans[D].tx.f_num = card->chans[D].tx.f_max - card->chans[D].tx.f_min + 1; + + if(!(card->chans[D].netdev = alloc_netdev(0, "isdn%dd", setup_lapd))) { + printk(KERN_ERR hfc_DRIVER_PREFIX + "net_device alloc failed, abort.\n"); + err = -ENOMEM; + goto err_alloc_netdev_d; + } + + hfc_setup_lapd(&card->chans[D]); + + card->chans[D].netdev->irq = card->pcidev->irq; + card->chans[D].netdev->base_addr = card->io_bus_mem; +/* card->chans[D].netdev->rmem_start = card->fifo_bus_mem + 0x4000; + card->chans[D].netdev->rmem_end = card->fifo_bus_mem + 0x4000 + + card->chans[D].rx.fifo_size - 1;*/ + card->chans[D].netdev->mem_start = card->fifo_bus_mem + 0x0000; + card->chans[D].netdev->mem_end = card->fifo_bus_mem + 0x0000 + + card->chans[D].tx.fifo_size - 1; + + if((err = register_netdev(card->chans[D].netdev))) { + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "Cannot register net device, aborting.\n", + card->cardnum); + goto err_register_netdev_d; + } + +//---------------------------------- B1 + card->chans[B1].card = card; + card->chans[B1].name = "B1"; + card->chans[B1].status = free; + card->chans[B1].number = B1; + card->chans[B1].protocol = 0; + spin_lock_init(&card->chans[B1].lock); + + card->chans[B1].rx.chan = &card->chans[B1]; + card->chans[B1].rx.fifo_base = card->fifos + 0x4200; + card->chans[B1].rx.z_base = card->fifos + 0x4000; + card->chans[B1].rx.z1_base = card->fifos + 0x6000; + card->chans[B1].rx.z2_base = card->fifos + 0x6002; + card->chans[B1].rx.z_min = 0x0200; + card->chans[B1].rx.z_max = 0x1FFF; + card->chans[B1].rx.f_min = 0x00; + card->chans[B1].rx.f_max = 0x1F; + card->chans[B1].rx.f1 = card->fifos + 0x6080; + card->chans[B1].rx.f2 = card->fifos + 0x6081; + card->chans[B1].rx.fifo_size = card->chans[B1].rx.z_max - card->chans[B1].rx.z_min + 1; + card->chans[B1].rx.f_num = card->chans[B1].rx.f_max - card->chans[B1].rx.f_min + 1; + + card->chans[B1].tx.chan = &card->chans[B1]; + card->chans[B1].tx.fifo_base = card->fifos + 0x0200; + card->chans[B1].tx.z_base = card->fifos + 0x0000; + card->chans[B1].tx.z1_base = card->fifos + 0x2000; + card->chans[B1].tx.z2_base = card->fifos + 0x2002; + card->chans[B1].tx.z_min = 0x0200; + card->chans[B1].tx.z_max = 0x1FFF; + card->chans[B1].tx.f_min = 0x00; + card->chans[B1].tx.f_max = 0x1F; + card->chans[B1].tx.f1 = card->fifos + 0x2080; + card->chans[B1].tx.f2 = card->fifos + 0x2081; + card->chans[B1].tx.fifo_size = card->chans[B1].tx.z_max - card->chans[B1].tx.z_min + 1; + card->chans[B1].tx.f_num = card->chans[B1].tx.f_max - card->chans[B1].tx.f_min + 1; + +// card->chans[B1].netdev->irq = card->pcidev->irq; +// card->chans[B1].netdev->base_addr = card->io_bus_mem; +/* card->chans[B1].netdev->rmem_start = card->fifo_bus_mem + 0x4200; + card->chans[B1].netdev->rmem_end = card->fifo_bus_mem + 0x4200 + + card->chans[B1].rx.fifo_size - 1;*/ +// card->chans[B1].netdev->mem_start = card->fifo_bus_mem + 0x0200; +// card->chans[B1].netdev->mem_end = card->fifo_bus_mem + 0x0200 + +// card->chans[B1].tx.fifo_size - 1; + +//---------------------------------- B2 + card->chans[B2].card = card; + card->chans[B2].name = "B2"; + card->chans[B2].status = free; + card->chans[B2].number = B2; + card->chans[B2].protocol = 0; + spin_lock_init(&card->chans[B2].lock); + + card->chans[B2].rx.chan = &card->chans[B2]; + card->chans[B2].rx.fifo_base = card->fifos + 0x6200, + card->chans[B2].rx.z_base = card->fifos + 0x6000; + card->chans[B2].rx.z1_base = card->fifos + 0x6100; + card->chans[B2].rx.z2_base = card->fifos + 0x6102; + card->chans[B2].rx.z_min = 0x0200; + card->chans[B2].rx.z_max = 0x1FFF; + card->chans[B2].rx.f_min = 0x00; + card->chans[B2].rx.f_max = 0x1F; + card->chans[B2].rx.f1 = card->fifos + 0x6180; + card->chans[B2].rx.f2 = card->fifos + 0x6181; + card->chans[B2].rx.fifo_size = card->chans[B2].rx.z_max - card->chans[B2].rx.z_min + 1; + card->chans[B2].rx.f_num = card->chans[B2].rx.f_max - card->chans[B2].rx.f_min + 1; + + card->chans[B2].tx.chan = &card->chans[B2]; + card->chans[B2].tx.fifo_base = card->fifos + 0x2200; + card->chans[B2].tx.z_base = card->fifos + 0x2000; + card->chans[B2].tx.z1_base = card->fifos + 0x2100; + card->chans[B2].tx.z2_base = card->fifos + 0x2102; + card->chans[B2].tx.z_min = 0x0200; + card->chans[B2].tx.z_max = 0x1FFF; + card->chans[B2].tx.f_min = 0x00; + card->chans[B2].tx.f_max = 0x1F; + card->chans[B2].tx.f1 = card->fifos + 0x2180; + card->chans[B2].tx.f2 = card->fifos + 0x2181; + card->chans[B2].tx.fifo_size = card->chans[B2].tx.z_max - card->chans[B2].tx.z_min + 1; + card->chans[B2].tx.f_num = card->chans[B2].tx.f_max - card->chans[B2].tx.f_min + 1; + +// card->chans[B2].netdev->irq = card->pcidev->irq; +// card->chans[B2].netdev->base_addr = card->io_bus_mem; +/* card->chans[B2].netdev->rmem_start = card->fifo_bus_mem + 0x6200; + card->chans[B2].netdev->rmem_end = card->fifo_bus_mem + 0x6200 + + card->chans[B2].rx.fifo_size - 1;*/ +// card->chans[B2].netdev->mem_start = card->fifo_bus_mem + 0x2200; +// card->chans[B2].netdev->mem_end = card->fifo_bus_mem + 0x2200 + +// card->chans[B2].tx.fifo_size - 1; + +// ------------------------------------------------------- + + hfc_zap_initialize(card); + + snprintf(card->proc_dir_name, + sizeof(card->proc_dir_name), + "%d", card->cardnum); + card->proc_dir = proc_mkdir(card->proc_dir_name, hfc_proc_zaphfc_dir); + card->proc_dir->owner = THIS_MODULE; + + card->proc_info = create_proc_read_entry( + "info", 0444, card->proc_dir, + hfc_proc_read_info, card); + card->proc_info->owner = THIS_MODULE; + + card->proc_fifos = create_proc_read_entry( + "fifos", 0400, card->proc_dir, + hfc_proc_read_fifos, card); + card->proc_fifos->owner = THIS_MODULE; + + card->proc_bufs = create_proc_read_entry( + "bufs", 0400, card->proc_dir, + hfc_proc_read_bufs, card); + card->proc_bufs->owner = THIS_MODULE; + + hfc_resetCard(card); + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d configured for %s mode at mem %#lx (0x%p) IRQ %u\n", + card->cardnum, + card->nt_mode?"NT":"TE", + card->io_bus_mem, + card->io_mem, + card->pcidev->irq); + + cardnum++; + + return 0; + +// unregister_netdev(card->chans[D].netdev); +err_register_netdev_d: + free_netdev(card->chans[D].netdev); +err_alloc_netdev_d: + free_irq(pci_dev->irq, card); +err_request_irq: + pci_free_consistent(pci_dev, hfc_FIFO_SIZE, + card->fifo_mem, card->fifo_bus_mem); +err_alloc_fifo: + iounmap(card->io_mem); +err_ioremap: +err_noiobase: +err_noirq: + pci_release_regions(pci_dev); +err_pci_request_regions: +err_pci_set_dma_mask: +err_pci_enable_device: + kfree(card); +err_alloc_hfccard: + return err; +} + +static void __devexit hfc_remove(struct pci_dev *pci_dev) +{ + struct hfc_card *card = pci_get_drvdata(pci_dev); + + unregister_netdev(card->chans[D].netdev); + +// unsigned long flags; +// spin_lock_irqsave(&card->lock,flags); + + printk(KERN_INFO hfc_DRIVER_PREFIX + "card %d: " + "shutting down card at %p.\n", + card->cardnum, + card->io_mem); + + hfc_softreset(card); + + zt_unregister(&card->zt_span); + + + // disable memio and bustmaster + pci_write_config_word(pci_dev, PCI_COMMAND, 0); + +// spin_unlock_irqrestore(&card->lock,flags); + + remove_proc_entry("bufs", card->proc_dir); + remove_proc_entry("fifos", card->proc_dir); + remove_proc_entry("info", card->proc_dir); + remove_proc_entry(card->proc_dir_name, hfc_proc_zaphfc_dir); + + free_irq(pci_dev->irq, card); + + pci_free_consistent(pci_dev, hfc_FIFO_SIZE, + card->fifo_mem, card->fifo_bus_mem); + + iounmap(card->io_mem); + + pci_release_regions(pci_dev); + + pci_disable_device(pci_dev); + + free_netdev(card->chans[D].netdev); + kfree(card); +} + +/****************************************** + * Module stuff + ******************************************/ + +static int __init hfc_init_module(void) +{ + int ret; + + printk(KERN_INFO hfc_DRIVER_PREFIX + hfc_DRIVER_STRING " loading\n"); + + hfc_proc_zaphfc_dir = proc_mkdir(hfc_DRIVER_NAME, proc_root_driver); + + ret = zap_pci_module(&hfc_driver); + return ret; +} + +module_init(hfc_init_module); + +static void __exit hfc_module_exit(void) +{ + pci_unregister_driver(&hfc_driver); + + remove_proc_entry(hfc_DRIVER_NAME, proc_root_driver); + + printk(KERN_INFO hfc_DRIVER_PREFIX + hfc_DRIVER_STRING " unloaded\n"); +} + +module_exit(hfc_module_exit); + +#endif + +MODULE_DESCRIPTION(hfc_DRIVER_DESCR); +MODULE_AUTHOR("Jens Wilke , Daniele (Vihai) Orlandi "); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#ifdef LINUX26 + +module_param(modes, int, 0444); + +/* + * Old 2.6 kernels had module_param_array() macro that receive the counter + * by value. + */ +int nt_modes_num_values; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) +module_param_array(nt_modes, int, nt_modes_num_values, 0444); +#else +module_param_array(nt_modes, int, &nt_modes_num_values, 0444); +#endif + +module_param(force_l1_up, int, 0444); +module_param(sniff_zaptel_d_channel, int, 0444); +#ifdef DEBUG +module_param(debug_level, int, 0444); +#endif + +#else + +MODULE_PARM(modes,"i"); +MODULE_PARM(force_l1_up,"i"); +MODULE_PARM(sniff_zaptel_d_channel,"i"); +#ifdef DEBUG +MODULE_PARM(debug_level,"i"); +#endif + +#endif // LINUX26 + +MODULE_PARM_DESC(modes, "[Deprecated] bit-mask to configure NT mode"); +MODULE_PARM_DESC(nt_modes, "Comma-separated list of card IDs to configure in NT mode"); +MODULE_PARM_DESC(force_l1_up, "Don't allow L1 to go down"); +MODULE_PARM_DESC(sniff_zaptel_d_channel, "Make frames transmitted from zaptel" + " appear as received by the board"); +#ifdef DEBUG +MODULE_PARM_DESC(debug_level, "Debug verbosity level"); +#endif --- zaptel-1.4.11~dfsg.orig/kernel/vzaphfc/README +++ zaptel-1.4.11~dfsg/kernel/vzaphfc/README @@ -0,0 +1,45 @@ +== General Info == + +This is a Zaptel driver for HFC-S PCI A based ISDN BRI cards (Cologne +Chipdesign). + +== Driver Build == + +You need the kernel source (2.6) and the Zaptel driver sources +to build the module. + +== Module Parameters == + + nt_modes: + comma seperated list + + sniff_zaptel_d_channel: + Make frames transmitted from Zaptel appear as received by the board + Set this to 1 if you want to debug D channel signaling with Etherreal + + force_l1_up: + Keep ISDN bus activated. Usually set by PtP TE interfaces. + + nt_modes: + Comma-separated list of card IDs to configure in NT mode (array of int) + +== Information in the proc file system == + + You will find valuable data about the current state of your ISDN + interface in /proc/drivers/vzaphfc/. The following files + are present for each card: + + info: + Information about the bus activation, transmit errors (CRC), and + overall transfer statistics + + bufs: + Except from the data that is currently transmitted + + fifo: + sizes and counter values from the FIFOs, mainly for debugging + purposes + + +Latest Change: 6 Jun 2006 Jens Wilke + --- zaptel-1.4.11~dfsg.orig/kernel/ztgsm/zapata.conf.duoGSM +++ zaptel-1.4.11~dfsg/kernel/ztgsm/zapata.conf.duoGSM @@ -0,0 +1,23 @@ +[channels] +txgain = -10.0 +rxgain = 0.0 + +signalling = gsm +context = from-gsm + +;group=1 + +; phone number for SIM card in slot A +;exten=016012345671 +; PIN for SIM card in slot A +;pin=1234 + +;channel => 1 + +; phone number for SIM card in slot B +;exten=016012345672 +; PIN for SIM card in slot B +;pin=1234 + +;channel => 3 + --- zaptel-1.4.11~dfsg.orig/kernel/ztgsm/zaptel.conf.unoGSM +++ zaptel-1.4.11~dfsg/kernel/ztgsm/zaptel.conf.unoGSM @@ -0,0 +1,10 @@ +loadzone=nl +defaultzone=nl + +alaw=1 + +span=1,1,3,ccs,ami + +bchan=1 +dchan=2 + --- zaptel-1.4.11~dfsg.orig/kernel/ztgsm/Makefile +++ zaptel-1.4.11~dfsg/kernel/ztgsm/Makefile @@ -0,0 +1,51 @@ +KINCLUDES = /usr/src/linux/include +BRISTUFFBASE = $(shell dirname `pwd`) + +ZAP = $(shell [ -f $(BRISTUFFBASE)/zaptel/zaptel.h ] && echo "-I$(BRISTUFFBASE)/zaptel") + +CFLAGS+=-I. $(ZAP) -O2 -g -Wall -DBUILDING_TONEZONE #-DTONEZONE_DRIVER +CFLAGS+=$(shell if uname -m | grep -q ppc; then echo "-fsigned-char"; fi) + +KFLAGS=-D__KERNEL__ -DMODULE -DEXPORT_SYMTAB -fomit-frame-pointer -O2 -Wall -I$(KINCLUDES) $(ZAP) +KFLAGS+=$(shell [ -f $(KINCLUDES)/linux/modversions.h ] && echo "-DMODVERSIONS -include $(KINCLUDES)/linux/modversions.h") +KFLAGS+=$(shell if uname -m | grep -q ppc; then echo "-msoft-float -fsigned-char"; fi) + +OBJS=ztgsm.o + +BUILDVER=$(shell if uname -r | grep -q ^2.6; then echo "linux26"; else echo "linux24"; fi) + +MODCONF=$(shell if [ -d $(INSTALL_PREFIX)/etc/modprobe.d ]; then echo "$(INSTALL_PREFIX)/etc/modprobe.d/zaptel"; elif [ -d $(INSTALL_PREFIX)/etc/modutils ]; then echo "$(INSTALL_PREFIX)/etc/modutils/zaptel"; elif [ -f $(INSTALL_PREFIX)/etc/modprobe.conf ]; then echo "$(INSTALL_PREFIX)/modprobe.conf"; elif [ -f $(INSTALL_PREFIX)/etc/modules.conf ]; then echo "$(INSTALL_PREFIX)/etc/modules.conf"; else echo $(INSTALL_PREFIX)/etc/conf.modules ; fi) + +MODULES=ztgsm + +MODULESO=$(shell for x in $(MODULES); do echo "$$x.o "; done ) +MODULESKO=$(shell for x in $(MODULES); do echo "$$x.ko "; done ) + +PWD=$(shell pwd) + +obj-m := $(MODULESO) + +all: $(BUILDVER) + +linux24: $(OBJS) + sync + +linux26: + @if ! [ -d /usr/src/linux-2.6 ]; then echo "Link /usr/src/linux-2.6 to your kernel sources first!"; exit 1 ; fi + make -C /usr/src/linux-2.6 SUBDIRS=$(PWD) ZAP=$(ZAP) modules +obj-m := $(OBJS) + +ztgsm.o: ztgsm.c ztgsm.h + $(CC) -c ztgsm.c $(KFLAGS) + +clean: + rm -f $(OBJS) *.ko *.mod.c *.mod.o .*o.cmd *~ + rm -rf .tmp_versions + +install: install$(BUILDVER) + +installlinux26: all + install -D -m 644 ztgsm.ko $(INSTALL_PREFIX)/lib/modules/`uname -r`/misc/ztgsm.ko + +installlinux24: all + install -D -m 644 ztgsm.o $(INSTALL_PREFIX)/lib/modules/`uname -r`/misc/ztgsm.o --- zaptel-1.4.11~dfsg.orig/kernel/ztgsm/ztgsm.c +++ zaptel-1.4.11~dfsg/kernel/ztgsm/ztgsm.c @@ -0,0 +1,1308 @@ +/* + * ztgsm.c - Zaptel driver for the uno/duo/quad GSM PCI cards + * + * Copyright (C) 2005, 2006 Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + */ +#include +#include +#include +#include +#include +#include +#include "ztgsm.h" + +#ifdef LINUX26 +#include +#endif + +#if CONFIG_PCI + +static int debug=0; +static int pcm_xbar=0; +static int sim = 0; +static struct ztgsm_card *ztgsm_dev_list = NULL; +static int ztgsm_dev_count = 0; +static int ztgsm_spans = 0; +static struct pci_dev *multi_gsm = NULL; +static spinlock_t registerlock = SPIN_LOCK_UNLOCKED; +static long baudrate=19200; + +void ztgsm_init_xbar(struct ztgsm_card *gsmtmp) { + int i = 0; + for (i=0; i <= 0x01FF; i++) { + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_VECTOR_CFG, 0x00000182 | i); + } +} + +void ztgsm_switch_on(struct ztgsm_card *gsmtmp, int span) { + unsigned long flags; + + printk(KERN_INFO "ztgsm: Powering up span %d ...", span); + spin_lock_irqsave(&(gsmtmp->lock),flags); + gsmtmp->gsmspan[span].led = 0xC1; + gsmtmp->dtr_on_off &= ~ (1 << span); /* on_off_n low */ + ztgsm_outdw_io(gsmtmp, ztgsm_SER_DTR_ON_OFF, gsmtmp->dtr_on_off); + spin_unlock_irqrestore(&(gsmtmp->lock),flags); + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((1000 * HZ) / 1000); + + spin_lock_irqsave(&(gsmtmp->lock),flags); + gsmtmp->dtr_on_off |= 1 << span; /* on_off_n high */ + ztgsm_outdw_io(gsmtmp, ztgsm_SER_DTR_ON_OFF, gsmtmp->dtr_on_off); + + gsmtmp->power[span] = 1; + gsmtmp->gsmspan[span].led = 0x81; + spin_unlock_irqrestore(&(gsmtmp->lock),flags); + + printk(" done.\n"); + gsmtmp->gsmspan[span].span.alarms = ZT_ALARM_NONE; + zt_alarm_notify(&gsmtmp->gsmspan[span].span); +} + +void ztgsm_switch_off(struct ztgsm_card *gsmtmp, int span) { + unsigned long flags; + gsmtmp->gsmspan[span].span.alarms = ZT_ALARM_RED; + zt_alarm_notify(&gsmtmp->gsmspan[span].span); + + spin_lock_irqsave(&(gsmtmp->lock),flags); + gsmtmp->gsmspan[span].led = 0xC0; + + gsmtmp->dtr_on_off &= ~ (1 << span); /* on_off_n low */ + printk(KERN_INFO "ztgsm: Powering down span %d (SER_DTR_ON_OFF %x)...", span, gsmtmp->dtr_on_off); + ztgsm_outdw_io(gsmtmp, ztgsm_SER_DTR_ON_OFF, gsmtmp->dtr_on_off); + spin_unlock_irqrestore(&(gsmtmp->lock),flags); + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((2500 * HZ) / 1000); + + spin_lock_irqsave(&(gsmtmp->lock),flags); + gsmtmp->dtr_on_off |= (1 << span); /* on_off_n high */ + ztgsm_outdw_io(gsmtmp, ztgsm_SER_DTR_ON_OFF, gsmtmp->dtr_on_off); + gsmtmp->power[span] = 0; + gsmtmp->gsmspan[span].led = 0x80; + spin_unlock_irqrestore(&(gsmtmp->lock),flags); + printk(" done.\n"); +} + +void ztgsm_switch_on_all(struct ztgsm_card *gsmtmp, int verbose) { + unsigned long flags; + if (verbose) + printk(KERN_INFO "ztgsm: Powering up all spans..."); + spin_lock_irqsave(&(gsmtmp->lock),flags); + gsmtmp->gsmspan[0].led = 0xC1; + gsmtmp->gsmspan[1].led = 0xC1; + gsmtmp->gsmspan[2].led = 0xC1; + gsmtmp->gsmspan[3].led = 0xC1; + + ztgsm_outdw_io(gsmtmp, ztgsm_SER_DTR_ON_OFF, 0xf0); /* set ON_OFF_N to low for 1000 ms */ + spin_unlock_irqrestore(&(gsmtmp->lock),flags); + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((1000 * HZ) / 1000); + + spin_lock_irqsave(&(gsmtmp->lock),flags); + ztgsm_outdw_io(gsmtmp, ztgsm_SER_DTR_ON_OFF, 0x0f); /* set ON_OFF_N to high, DTR to low */ + ztgsm_outdw_io(gsmtmp, ztgsm_SER_RTS_O, 0x0); + + gsmtmp->power[0] = 1; + gsmtmp->power[1] = 1; + gsmtmp->power[2] = 1; + gsmtmp->power[3] = 1; + + gsmtmp->gsmspan[0].led = 0x81; + gsmtmp->gsmspan[1].led = 0x81; + gsmtmp->gsmspan[2].led = 0x81; + gsmtmp->gsmspan[3].led = 0x81; + spin_unlock_irqrestore(&(gsmtmp->lock),flags); + + if (verbose) + printk(" done.\n"); +} + +void ztgsm_switch_off_all(struct ztgsm_card *gsmtmp, int verbose) { + unsigned long flags; + + if (gsmtmp->power[0] || gsmtmp->power[1] || gsmtmp->power[2] || gsmtmp->power[3]) { + if (verbose) + printk(KERN_INFO "ztgsm: Powering down all spans..."); + spin_lock_irqsave(&(gsmtmp->lock),flags); + gsmtmp->gsmspan[0].led = 0xC0; + gsmtmp->gsmspan[1].led = 0xC0; + gsmtmp->gsmspan[2].led = 0xC0; + gsmtmp->gsmspan[3].led = 0xC0; + + ztgsm_outdw_io(gsmtmp, ztgsm_SER_DTR_ON_OFF, 0xf0); + spin_unlock_irqrestore(&(gsmtmp->lock),flags); + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((2500 * HZ) / 1000); + + spin_lock_irqsave(&(gsmtmp->lock),flags); + ztgsm_outdw_io(gsmtmp, ztgsm_SER_DTR_ON_OFF, 0xff); + ztgsm_outdw_io(gsmtmp, ztgsm_SER_RTS_O, 0xF); + + gsmtmp->power[0] = 0; + gsmtmp->power[1] = 0; + gsmtmp->power[2] = 0; + gsmtmp->power[3] = 0; + + gsmtmp->gsmspan[0].led = 0x80; + gsmtmp->gsmspan[1].led = 0x80; + gsmtmp->gsmspan[2].led = 0x80; + gsmtmp->gsmspan[3].led = 0x80; + spin_unlock_irqrestore(&(gsmtmp->lock),flags); + if (verbose) + printk(" done.\n"); + } +} + + +void ztgsm_shutdownCard(struct ztgsm_card *gsmtmp) { + unsigned long flags; + struct ztgsm_span *gsmspan = NULL; + int i = 0; + unsigned long ioport; + unsigned long pci_io_phys; + unsigned long iomem_size; + void *pci_io; + + if (gsmtmp == NULL) { + printk(KERN_INFO "ztgsm: shutting down NULL card!\n"); + return; + } + ztgsm_switch_off_all(gsmtmp, 1); + + spin_lock_irqsave(&gsmtmp->lock,flags); + + ztgsm_outdw_io(gsmtmp, ztgsm_SER_TX_EN, 0x0); + ztgsm_outdw_io(gsmtmp, ztgsm_SER_RX_EN, 0x0); + + gsmtmp->dead = 1; + + if ((!gsmtmp->pci_io) || (gsmtmp->ioport == 0)) { + return; + } + ztgsm_outdw_io(gsmtmp, ztgsm_SER_INT_MASK, 0x0); + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_FC_TOG_BIT, 0x0); + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_SAP_EN, 0x0); + + ztgsm_outdw_io(gsmtmp, ztgsm_LED_DUAL, 0xFF00); + + ioport = gsmtmp->ioport; + pci_io = gsmtmp->pci_io; + pci_io_phys = gsmtmp->pci_io_phys; + iomem_size = gsmtmp->iomem_size; + + gsmtmp->pci_io = 0; + gsmtmp->ioport = 0; + + spin_unlock_irqrestore(&gsmtmp->lock,flags); + + + for (i=0; i < gsmtmp->gsmspans; i++) { + gsmspan = &gsmtmp->gsmspan[i]; + if(gsmspan->span.flags & ZT_FLAG_RUNNING) { + if (debug) + printk(KERN_INFO "ztgsm: shutdown card %d span %d.\n",gsmtmp->cardno,i); + } + if(gsmspan->span.flags & ZT_FLAG_REGISTERED) { + zt_unregister(&gsmspan->span); + if (debug) + printk(KERN_INFO "ztgsm: unregistered card %d span %d.\n",gsmtmp->cardno,i); + } + } + + + release_region(ioport, 0x100); + iounmap((void *) pci_io); + release_mem_region(pci_io_phys, iomem_size); + + + spin_lock_irqsave(&gsmtmp->lock,flags); + + free_irq(gsmtmp->irq,gsmtmp); + + pci_write_config_word(gsmtmp->pcidev, PCI_COMMAND, 0); + + if (gsmtmp->pcidev != NULL) { + pci_disable_device(gsmtmp->pcidev); + } + + spin_unlock_irqrestore(&gsmtmp->lock,flags); +} + +void ztgsm_register_card(struct ztgsm_card *gsmtmp) { + spin_lock(®isterlock); + if (gsmtmp != NULL) { + gsmtmp->prev = NULL; + gsmtmp->next = ztgsm_dev_list; + if (ztgsm_dev_list) { + ztgsm_dev_list->prev = gsmtmp; + } + ztgsm_dev_list = gsmtmp; + gsmtmp->cardno = ++ztgsm_dev_count; + } else { + printk(KERN_INFO "ztgsm: trying to register NULL card.\n"); + } + spin_unlock(®isterlock); +} + + +void ztgsm_resetCard(struct ztgsm_card *gsmtmp) { + unsigned long flags; + int i; + spin_lock_irqsave(&(gsmtmp->lock),flags); +// pci_write_config_word(gsmtmp->pcidev, PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + + switch (baudrate) { + case 9600: + ztgsm_outdw_io(gsmtmp, ztgsm_SER_CLK_DIV, 0xD5); + break; + case 19200: + ztgsm_outdw_io(gsmtmp, ztgsm_SER_CLK_DIV, 0x6B); + break; + case 38400: + ztgsm_outdw_io(gsmtmp, ztgsm_SER_CLK_DIV, 0x35); + break; + case 57600: + ztgsm_outdw_io(gsmtmp, ztgsm_SER_CLK_DIV, 0x24); + break; + case 115200: + ztgsm_outdw_io(gsmtmp, ztgsm_SER_CLK_DIV, 0x12); + break; + default: + ztgsm_outdw_io(gsmtmp, ztgsm_SER_CLK_DIV, 0x6B); /* 19200 */ + } + + ztgsm_outdw_io(gsmtmp, ztgsm_SER_CLK_PRE_DIV, 0x06); +// ztgsm_outdw_io(gsmtmp, ztgsm_SER_CLK_PRE_DIV, 0x03); +// ztgsm_outdw_io(gsmtmp, ztgsm_PCM_CLK_PRE_DIV, 0x06); + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_CLK_PRE_DIV, 0x0C); + ztgsm_outdw_io(gsmtmp, ztgsm_SER_INT_MASK, 0x0); + ztgsm_outdw_io(gsmtmp, ztgsm_SER_IDLE_VAL, 0x01); + + ztgsm_outdw_io(gsmtmp, ztgsm_SER_TX_EN, 0x0); + ztgsm_outdw_io(gsmtmp, ztgsm_SER_RX_EN, 0x0); + + + gsmtmp->ticks = 0; + gsmtmp->clicks = 0; + + for (i=0; i<4; i++) + gsmtmp->ton_off_toggle[i] = -1; + + spin_unlock_irqrestore(&(gsmtmp->lock),flags); +} + +void ztgsm_startCard(struct ztgsm_card *gsmtmp) { + unsigned long flags; + spin_lock_irqsave(&(gsmtmp->lock),flags); + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_FC_TOG_BIT, 0x03); + ztgsm_outdw_io(gsmtmp, ztgsm_SER_INT_MASK, 0x1FFFF); + ztgsm_outdw_io(gsmtmp, ztgsm_SER_RX_WATERMARK, 0x0); + if (sim) { + gsmtmp->sim_sel = sim; + ztgsm_outdw_io(gsmtmp, ztgsm_SIM_SEL, gsmtmp->sim_sel); + } else { + gsmtmp->sim_sel = 0; + ztgsm_outdw_io(gsmtmp, ztgsm_SIM_SEL, gsmtmp->sim_sel); + } + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_SAP_EN, 0x0); + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_MASK_LAST, 0x001F0000 | 0x1F); /* PCM32 0x100 frames */ +// ztgsm_outdw_io(gsmtmp, ztgsm_PCM_MASK_LAST, (0x001FC000 | 0x1F)); /* PCM32 0x40 frames */ + + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_SAP_EN, 0x1F); + switch (gsmtmp->gsmspans) { + case 4: + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_VECTOR_CFG, 0x01010186); /* GSM_A (alaw) -> pci slot 0x01 */ + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_VECTOR_CFG, 0x01840101); /* pci slot 0x01 -> GSM_A */ + + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_VECTOR_CFG, 0x0103018A); /* GSM_B (alaw) -> pci slot 0x03 */ + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_VECTOR_CFG, 0x01880103); /* pci slot 0x03 -> GSM_B */ + + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_VECTOR_CFG, 0x0105018E); /* GSM_C (alaw) -> pci slot 0x05 */ + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_VECTOR_CFG, 0x018C0105); /* pci slot 0x05 -> GSM_C */ + + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_VECTOR_CFG, 0x01070192); /* GSM_D (alaw) -> pci slot 0x07 */ + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_VECTOR_CFG, 0x01900107); /* pci slot 0x07 -> GSM_D */ + break; + case 2: + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_VECTOR_CFG, 0x01010186); /* GSM_A (alaw) -> pci slot 0x01 */ + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_VECTOR_CFG, 0x01840101); /* pci slot 0x01 -> GSM_A */ + + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_VECTOR_CFG, 0x0103018A); /* GSM_B (alaw) -> pci slot 0x03 */ + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_VECTOR_CFG, 0x01880103); /* pci slot 0x03 -> GSM_B */ + break; + case 1: + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_VECTOR_CFG, 0x01010186); /* GSM_A (alaw) -> pci slot 0x01 */ + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_VECTOR_CFG, 0x01840101); /* pci slot 0x01 -> GSM_A */ + break; + } + + ztgsm_outdw_io(gsmtmp, ztgsm_SER_RX_EN, 0x0F); + ztgsm_outdw_io(gsmtmp, ztgsm_SER_TX_EN, 0x0F); + if (debug) + printk(KERN_INFO "ztgsm: SER_TX_EN %d SER_RX_EN %d \n", ztgsm_indw_io(gsmtmp, ztgsm_SER_TX_EN), ztgsm_indw_io(gsmtmp, ztgsm_SER_RX_EN)); + gsmtmp->jitter_offset = 0; + spin_unlock_irqrestore(&(gsmtmp->lock),flags); +} + +static void ztgsm_xbar(struct ztgsm_card *gsmtmp, int dst, int src, int slin) { + unsigned long flags; + int format = 0; + + if (!gsmtmp) return; + + if (slin) format = 0x2000; + spin_lock_irqsave(&(gsmtmp->lock), flags); + if (debug > 1) + printk(KERN_INFO "ztgsm: ztgsm_PCM_VECTOR_CFG %4x \n", (dst << 16) | src | format); + ztgsm_outdw_io(gsmtmp, ztgsm_PCM_VECTOR_CFG, (dst << 16) | src | format); + spin_unlock_irqrestore(&(gsmtmp->lock), flags); +} + +static int ztgsm_dacs(struct zt_chan *dst, struct zt_chan *src) { + struct ztgsm_span *src_span; + struct ztgsm_span *dst_span; + + dst_span = dst->pvt; + if (dst->chanpos == 2) return -1; + + if (src) { + if (src->chanpos == 2) return -1; + src_span = src->pvt; + if (debug > 1) + printk(KERN_INFO "linking channel %d span %d to channel %d span %d\n", src->chanpos, src_span->span.offset, dst->chanpos, dst_span->span.offset); + /* ALAW */ + ztgsm_xbar(dst->span->pvt, 0x184 + (4 * dst_span->span.offset), 0x186 + (4 * src_span->span.offset), 0); + + /* SLIN */ +/* ztgsm_xbar(dst->span->pvt, 0x184 + (4 * dst_span->span.offset), 0x184 + (4 * src_span->span.offset), 1); + ztgsm_xbar(dst->span->pvt, 0x185 + (4 * dst_span->span.offset), 0x185 + (4 * src_span->span.offset), 1); */ + + } else { + if (debug > 1) + printk(KERN_INFO "unlinking channel %d span %d\n", dst->chanpos, dst_span->span.offset); + /* reassign pci source */ + ztgsm_xbar(dst->span->pvt, 0x184 + (4 * dst_span->span.offset), 0x100 | ((dst_span->span.offset * 2)+1), 0); + /* reassign pci destination */ + ztgsm_xbar(dst->span->pvt, 0x100 | ((dst_span->span.offset * 2)+1), 0x186 + (4 * dst_span->span.offset), 0); + } + + return 0; +} + +static int ztgsm_ser_rx(struct ztgsm_card *gsmtmp, int span) { + unsigned int rxcreg = 0; + unsigned int rxdreg = 0; + int rx_count = 0; + int i = 0; + unsigned char data = 0; + unsigned int count_reg = 0; + int rd_ptr = 0; + int wr_ptr = 0; + switch (span) { + case 0: rxcreg = ztgsm_SER_RX_COUNT_A; + rxdreg = ztgsm_SER_RX_DATA_A; + break; + case 1: rxcreg = ztgsm_SER_RX_COUNT_B; + rxdreg = ztgsm_SER_RX_DATA_B; + break; + case 2: rxcreg = ztgsm_SER_RX_COUNT_C; + rxdreg = ztgsm_SER_RX_DATA_C; + break; + case 3: rxcreg = ztgsm_SER_RX_COUNT_D; + rxdreg = ztgsm_SER_RX_DATA_D; + break; + } + count_reg = ztgsm_indw_io(gsmtmp, rxcreg); + rx_count = count_reg & 0x1F; + if (rx_count) { + rd_ptr = (count_reg & 0x03E0) >> 5; + wr_ptr = (count_reg & 0x7C00) >> 10; +// if (debug) +// printk(KERN_CRIT "ztgsm: SER_RX_COUNT_%d [wr_ptr %d rd_ptr %d count %d]\n", span, wr_ptr, rd_ptr, rx_count); + if ((gsmtmp->ser_rx_idx[span] + rx_count) < ztgsm_SER_BUF_SIZE) { + if (debug) printk(KERN_INFO "ztgsm: span %d SER_RX [ ", span); + for (i=0;igsmspan[span].span.flags & ZT_FLAG_RUNNING) + gsmtmp->ser_rx_buf[span][gsmtmp->ser_rx_idx[span]++] = data; + } + if (debug) printk(" ]\n"); + } else { + printk(KERN_INFO "ztgsm: RX buffer overflow on span %d\n", span); + } + } + return rx_count; +} + +static int ztgsm_zap_rx(struct ztgsm_card *gsmtmp, int span) { + int i = 0; + + if (gsmtmp->ser_rx_idx[span]) { + memcpy(gsmtmp->drxbuf[span], &gsmtmp->ser_rx_buf[span], gsmtmp->ser_rx_idx[span]); + if (debug > 1) { + printk(KERN_INFO "ztgsm: span %d RX [ ", span); + for (i=0;iser_rx_idx[span]; i++) { + if (gsmtmp->ser_rx_buf[span][i] == 0xd) { + printk("\\r"); + } else if (gsmtmp->ser_rx_buf[span][i] == 0xa) { + printk("\\n"); + } else { + printk("%c", gsmtmp->ser_rx_buf[span][i]); + } + } + printk(" ]\n"); + } + gsmtmp->gsmspan[span].chans[1].eofrx = 1; + gsmtmp->gsmspan[span].chans[1].bytes2receive = gsmtmp->ser_rx_idx[span]; + gsmtmp->ser_rx_idx[span] = 0; + } + return 0; +} + +static int ztgsm_ser_tx(struct ztgsm_card *gsmtmp, int span) { + unsigned int txcreg = 0; + unsigned int txdreg = 0; + unsigned int tx_wm_sen = 0; + int left = 0; + int i = 0; + int count = 0; + unsigned int count_reg = 0; + int rd_ptr = 0; + int wr_ptr = 0; + struct ztgsm_span *gsmspan = NULL; + + switch (span) { + case 0: txcreg = ztgsm_SER_TX_COUNT_A; + txdreg = ztgsm_SER_TX_DATA_A; + break; + case 1: txcreg = ztgsm_SER_TX_COUNT_B; + txdreg = ztgsm_SER_TX_DATA_B; + break; + case 2: txcreg = ztgsm_SER_TX_COUNT_C; + txdreg = ztgsm_SER_TX_DATA_C; + break; + case 3: txcreg = ztgsm_SER_TX_COUNT_D; + txdreg = ztgsm_SER_TX_DATA_D; + break; + } + gsmspan = &gsmtmp->gsmspan[span]; + if (gsmspan) { + count_reg = ztgsm_indw_io(gsmtmp, txcreg); + left = ztgsm_FIFO_SIZE - (count_reg & 0x1F); + if (left >= 1 ) { + rd_ptr = (count_reg & 0x03E0) >> 5; + wr_ptr = (count_reg & 0x7C00) >> 10; + if (debug) + printk(KERN_CRIT "ztgsm: SER_TX_COUNT_%d [wr_ptr %d rd_ptr %d free %d]\n", span, wr_ptr, rd_ptr, left); + if (gsmtmp->ser_tx_idx[span] < left) { + count = gsmtmp->ser_tx_idx[span]; + } else { + count = left; + } + if (debug) + printk(KERN_INFO "ztgsm: span %d SER_TX [ ", span); + for (i=0;iser_tx_buf[span][i] > 0x10)) + printk("%c", gsmtmp->ser_tx_buf[span][i]); + ztgsm_outdw_io(gsmtmp, txdreg, gsmtmp->ser_tx_buf[span][i]); + } + if (debug) + printk(" ]\n"); + gsmtmp->ser_tx_idx[span] -= count; + if (gsmtmp->ser_tx_idx[span] > 0) { + memmove(&gsmtmp->ser_tx_buf[span][0], &gsmtmp->ser_tx_buf[span][i], gsmtmp->ser_tx_idx[span]); + } + tx_wm_sen = (ztgsm_indw_io(gsmtmp, ztgsm_SER_TX_WM_SEN) & 0xF) | (1 << span); + ztgsm_outdw_io(gsmtmp, ztgsm_SER_TX_WM_SEN, tx_wm_sen); +// printk(KERN_INFO "ztgsm: span %d TX_IDX %d count %d tx_wm_send %d\n", span, gsmtmp->ser_tx_idx[span], count, tx_wm_sen); + } else { + if (debug) { + printk(KERN_INFO "ztgsm: span %d wanted to TX %d bytes but only %d bytes free fifo space\n", span, gsmtmp->ser_tx_idx[span], left); + } + } + } + return i; +} + +static int ztgsm_zap_tx(struct ztgsm_card *gsmtmp, int span) { + struct ztgsm_span *gsmspan = NULL; + int i = 0; + int poweroff_cmd = 0; + int poweron_cmd = 0; + gsmspan = &gsmtmp->gsmspan[span]; + if (!gsmspan) + return -1; + if (gsmspan->chans[1].bytes2transmit) { + if (debug > 1) { + printk(KERN_INFO "ztgsm: span %d TX [ ", span); + for (i=0;ichans[1].bytes2transmit; i++) { + if (gsmtmp->dtxbuf[span][i] > 0x10) + printk("%c", gsmtmp->dtxbuf[span][i]); + } + printk(" ]\n"); + } + if ((gsmtmp->dtxbuf[span][0] == 'A') && (gsmtmp->dtxbuf[span][1] == 'T') && (gsmtmp->dtxbuf[span][2] == '+') && (gsmtmp->dtxbuf[span][3] == 'P') && (gsmtmp->dtxbuf[span][4] == 'O') && (gsmtmp->dtxbuf[span][5] == 'W') && (gsmtmp->dtxbuf[span][6] == 'E') && (gsmtmp->dtxbuf[span][7] == 'R') && (gsmtmp->dtxbuf[span][8] == '=') && (gsmtmp->dtxbuf[span][9] == '1') && (gsmtmp->dtxbuf[span][10] == '\r')) { + if (debug) printk(KERN_INFO "ztgsm: span %d received power on AT command\n", span); + gsmtmp->dtr_on_off &= ~(1 << span); /* on_off_n low */ + ztgsm_outdw_io(gsmtmp, ztgsm_SER_DTR_ON_OFF, gsmtmp->dtr_on_off); + gsmtmp->ton_off_toggle[span] = 700; + gsmtmp->gsmspan[span].led = 0xC1; + gsmtmp->power[span] = 1; + poweron_cmd = 1; + } else if ((gsmtmp->dtxbuf[span][0] == 'A') && (gsmtmp->dtxbuf[span][1] == 'T') && (gsmtmp->dtxbuf[span][2] == '+') && (gsmtmp->dtxbuf[span][3] == 'M') && (gsmtmp->dtxbuf[span][4] == 'R') && (gsmtmp->dtxbuf[span][5] == 'S') && (gsmtmp->dtxbuf[span][6] == 'T') && (gsmtmp->dtxbuf[span][7] == '\r')) { + if (debug) printk(KERN_INFO "ztgsm: span %d received power off AT command\n", span); + gsmtmp->gsmspan[span].led = 0x80; + gsmtmp->power[span] = 0; + poweroff_cmd = 1; + } else if ((gsmtmp->dtxbuf[span][0] == 'a') && (gsmtmp->dtxbuf[span][1] == 't') && (gsmtmp->dtxbuf[span][2] == '+') && (gsmtmp->dtxbuf[span][3] == 'm') && (gsmtmp->dtxbuf[span][4] == 'r') && (gsmtmp->dtxbuf[span][5] == 's') && (gsmtmp->dtxbuf[span][6] == 't') && (gsmtmp->dtxbuf[span][7] == '\r')) { + if (debug) printk(KERN_INFO "ztgsm: span %d received power off AT command\n", span); + gsmtmp->gsmspan[span].led = 0x80; + gsmtmp->power[span] = 0; + poweroff_cmd = 1; + } else if ((gsmtmp->dtxbuf[span][0] == 'A') && (gsmtmp->dtxbuf[span][1] == 'T') && (gsmtmp->dtxbuf[span][2] == '+') && (gsmtmp->dtxbuf[span][3] == 'P') && (gsmtmp->dtxbuf[span][4] == 'O') && (gsmtmp->dtxbuf[span][5] == 'W') && (gsmtmp->dtxbuf[span][6] == 'E') && (gsmtmp->dtxbuf[span][7] == 'R') && (gsmtmp->dtxbuf[span][8] == '=') && (gsmtmp->dtxbuf[span][9] == '0') && (gsmtmp->dtxbuf[span][10] == '\r')) { + if (debug) printk(KERN_INFO "ztgsm: span %d received power off AT command\n", span); + gsmtmp->dtr_on_off &= ~(1 << span); /* on_off_n low */ + ztgsm_outdw_io(gsmtmp, ztgsm_SER_DTR_ON_OFF, gsmtmp->dtr_on_off); + gsmtmp->gsmspan[span].led = 0xC0; + gsmtmp->ton_off_toggle[span] = 2200; + gsmtmp->power[span] = 0; + } else if ((gsmtmp->dtxbuf[span][0] == 'A') && (gsmtmp->dtxbuf[span][1] == 'T') && (gsmtmp->dtxbuf[span][2] == '+') && (gsmtmp->dtxbuf[span][3] == 'S') && (gsmtmp->dtxbuf[span][4] == 'I') && (gsmtmp->dtxbuf[span][5] == 'M') && (gsmtmp->dtxbuf[span][6] == '=') && (gsmtmp->dtxbuf[span][8] == '\r')) { + if (debug) printk(KERN_INFO "ztgsm: span %d received SIM select AT command, sim = %c\n", span, gsmtmp->dtxbuf[span][7]); + if (gsmtmp->dtxbuf[span][7] == '0') { + gsmtmp->sim_sel &= ~(1 << span); + ztgsm_outdw_io(gsmtmp, ztgsm_SIM_SEL, gsmtmp->sim_sel); + if (debug) printk(KERN_INFO "ztgsm: span %d now using onboard SIM card reader. Setting sim_sel to %d.\n", span, gsmtmp->sim_sel); + } else if (gsmtmp->dtxbuf[span][7] == '1') { + gsmtmp->sim_sel |= (1 << span); + ztgsm_outdw_io(gsmtmp, ztgsm_SIM_SEL, gsmtmp->sim_sel); + if (debug) printk(KERN_INFO "ztgsm: span %d now using external SIM card reader. Setting sim_sel to %d.\n", span, gsmtmp->sim_sel); + } else { + printk(KERN_INFO "ztgsm: span %d received invalied SIM select AT command, sim = %c\n", span, gsmtmp->dtxbuf[span][7]); + } + poweron_cmd = 1; + } + if ((gsmtmp->power[span] || poweroff_cmd) && (!poweron_cmd)) { + if (gsmtmp->ser_tx_idx[span] + gsmspan->chans[1].bytes2transmit < ztgsm_SER_BUF_SIZE) { + memcpy(&gsmtmp->ser_tx_buf[span][gsmtmp->ser_tx_idx[span]], gsmtmp->dtxbuf[span], gsmspan->chans[1].bytes2transmit); + gsmtmp->ser_tx_idx[span] += gsmspan->chans[1].bytes2transmit; + ztgsm_ser_tx(gsmtmp, span); + } else { + printk(KERN_INFO "ztgsm: TX buffer overflow on span %d (TX_IDX %d BTT %d)\n", span, gsmtmp->ser_tx_idx[span] , gsmspan->chans[1].bytes2transmit); + } + } + } + + gsmspan->chans[1].bytes2receive = 0; + gsmspan->chans[1].bytes2transmit = 0; + gsmspan->chans[1].eofrx = 0; + gsmspan->chans[1].eoftx = 0; + return 0; +} + + +static int ztgsm_span_rx(struct ztgsm_card *gsmtmp, int span) { + struct ztgsm_span *gsmspan = NULL; + unsigned int addr = 0; + unsigned int framecnt = 0; + unsigned int pcmframecnt = 0; + unsigned int fraddr = 0; + unsigned int data = 0; + int start = 0; + int len = 0; + int i = 0; + unsigned int slot = 0; + + switch(span) { + case 0: + slot = 1; + break; + case 1: + slot = 3; + break; + case 2: + slot = 5; + break; + case 3: + slot = 7; + break; + } +// slot = span; + gsmspan = &gsmtmp->gsmspan[span]; + if (gsmspan) { + framecnt = gsmtmp->framecnt - gsmtmp->jitter_offset; + pcmframecnt = framecnt; + framecnt &= ztgsm_FRAMES - 1; + start = framecnt - 16; + if (start < 0) { + len = -start; + if (len > ZT_CHUNKSIZE) len = ZT_CHUNKSIZE; + fraddr = ztgsm_FRAMES + start; + } else { + len = ZT_CHUNKSIZE; + fraddr = start; + } + + if (fraddr & 3) { + if (debug > 1) + printk(KERN_INFO "ztgsm: RX span %d unaligned word address %#x (fraddr & 3 = %d)\n", span, fraddr, (fraddr & 3)); + fraddr -= fraddr & 3; /* align */ + } + if (len == ZT_CHUNKSIZE) { + addr = (slot << 8) | fraddr; +// addr = ((span+1) << 8) | fraddr; +// ztgsm_outdw_io(gsmtmp, ztgsm_LED_DUAL, addr & 0x4); + data = ztgsm_indw(gsmtmp, addr); + *((unsigned int *)&gsmtmp->rxbuf[span][0]) = data; + + +// ztgsm_outdw_io(gsmtmp, ztgsm_LED_DUAL, addr & 0x4); + addr = (slot << 8) | (fraddr + 4); +// addr = ((span+1) << 8) | (fraddr + 4); + data = ztgsm_indw(gsmtmp, addr); + *((unsigned int *)&gsmtmp->rxbuf[span][4]) = data; +// ztgsm_outdw_io(gsmtmp, ztgsm_LED_DUAL, 0xf); + } else { + if (debug > 2) + printk(KERN_INFO "ztgsm: dropped audio span %d fraddr %d addr %d\n", span, fraddr, addr); + } +if (!(gsmtmp->ticks % 1000) && (debug > 4)) { + printk(KERN_INFO "ztgsm: RX DATA:"); + for (i=0; i < ZT_CHUNKSIZE; i++) { + printk("%x", gsmtmp->rxbuf[span][i]); + } + printk("\n"); +} + + } + return 0; +} + +static int ztgsm_span_tx(struct ztgsm_card *gsmtmp, int span) { + struct ztgsm_span *gsmspan = NULL; + unsigned int addr = 0; + unsigned int framecnt = 0; + unsigned int fraddr = 0; + int start = 0; + int len = 0; + unsigned int slot = 0; + + switch(span) { + case 0: + slot = 1; + break; + case 1: + slot = 3; + break; + case 2: + slot = 5; + break; + case 3: + slot = 7; + break; + } +// slot = span; + + gsmspan = &gsmtmp->gsmspan[span]; + if (gsmspan) { + framecnt = gsmtmp->framecnt - gsmtmp->jitter_offset; + framecnt &= ztgsm_FRAMES - 1; + start = framecnt + ztgsm_MAX_FC_DIFF + ztgsm_FRAME_SIZE; + + if (start < 0x0) { + len = -start; + if (len > ZT_CHUNKSIZE) len = ZT_CHUNKSIZE; + fraddr = ztgsm_FRAMES + start; + } else { + len = ZT_CHUNKSIZE; + fraddr = start; + } + + if (fraddr & 3) { + if (debug > 2) + printk(KERN_INFO "ztgsm: span_tx unaligned word address %#x\n", fraddr); + fraddr -= fraddr & 3; /* align */ + } + if (len == ZT_CHUNKSIZE) { + addr =(slot << 8 ) | fraddr; +// addr =( (span+1) << 8 ) | fraddr; + ztgsm_outdw(gsmtmp, addr, *((unsigned int*)&(gsmtmp->txbuf[span][0]))); +// addr =( (span+1) << 8 ) | (fraddr + 4); + addr =(slot << 8 ) | (fraddr + 4); + ztgsm_outdw(gsmtmp, addr, *((unsigned int*)&(gsmtmp->txbuf[span][4]))); + } + + } + return 0; +} + +static void ztgsm_leds(struct ztgsm_card *gsmtmp, int tick) { + int i = 0; + unsigned int leds = 0; + unsigned int color = 0; + for (i=0; igsmspans; i++) { +// printk(KERN_INFO "ztgsm: led = %x\n",gsmtmp->gsmspan[i].led); + color = gsmtmp->gsmspan[i].led & 0x1; + leds |= (color << i); + if (!(gsmtmp->gsmspan[i].led & 0x80)) { + leds |= (1 << (i+8)); + } + if ((gsmtmp->gsmspan[i].led & 0x40)) { + if (tick == 300) + leds |= (1 << (i+8)); + } + + } +// printk(KERN_INFO "ztgsm: leds = %d\n",leds); + ztgsm_outdw_io(gsmtmp, ztgsm_LED_DUAL, leds); +} + +static inline void ztgsm_run(struct ztgsm_card *gsmtmp) { + int s=0; + struct ztgsm_span *gsmspan = NULL; + + + for (s=0;sgsmspans;s++) { + gsmspan = &gsmtmp->gsmspan[s]; + if (gsmspan) { + if (gsmspan->span.flags & ZT_FLAG_RUNNING) { + /* oh zaptel! tell us what to transmit... */ + zt_transmit(&gsmspan->span); + if ((debug > 1) && (gsmspan->chans[1].bytes2transmit > 0)) + printk(KERN_CRIT "ztgsm: span %d bytes2transmit = %d\n", s, gsmspan->chans[1].bytes2transmit); + ztgsm_span_tx(gsmtmp, s); + ztgsm_zap_tx(gsmtmp, s); + } + + if (gsmspan->span.flags & ZT_FLAG_RUNNING) { + ztgsm_zap_rx(gsmtmp, s); + ztgsm_span_rx(gsmtmp, s); + /* oh zaptel! thou shall receive! */ + zt_receive(&gsmspan->span); + } + } + } +} + + + +ZAP_IRQ_HANDLER(ztgsm_interrupt) { + struct ztgsm_card *gsmtmp = dev_id; + unsigned int ser_status = 0; + unsigned char mods = 0; + int s = 0; + int rx_count = 0; + int dtr_on_off = 0; + unsigned long flags; + signed int jitter = 0; + + if (!gsmtmp || gsmtmp->dead) { +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + } + + if ((!gsmtmp->pci_io) || (!gsmtmp->ioport)) { + printk(KERN_CRIT "ztgsm: no pci mem/io\n"); +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + } + + spin_lock_irqsave(&(gsmtmp->lock),flags); +// printk(KERN_INFO "gsm: irq\n"); + gsmtmp->last_framecnt = gsmtmp->framecnt; + gsmtmp->framecnt = ztgsm_indw_io(gsmtmp, ztgsm_PCM_FRAME_CNT); + ser_status = ztgsm_indw_io(gsmtmp, ztgsm_SER_STATUS); + if (ser_status) { + if (ser_status & 0x10000) { + gsmtmp->ticks++; + jitter = gsmtmp->framecnt - gsmtmp->last_framecnt - ztgsm_FRAME_SIZE; + if (abs(gsmtmp->jitter_offset + jitter) > ztgsm_MAX_FC_DIFF) { + if (debug > 1) + printk(KERN_INFO "ztgsm: missed IRQ, framecnt %d last_framecnt %d (diff %d)\n", gsmtmp->framecnt, gsmtmp->last_framecnt, (gsmtmp->framecnt - gsmtmp->last_framecnt)); + gsmtmp->jitter_offset = 0; + } else { + if (gsmtmp->jitter_offset > 0) { + gsmtmp->jitter_offset += jitter; + } + if ((debug > 1) && gsmtmp->jitter_offset) + printk(KERN_INFO "ztgsm: lagged IRQ, framecnt %d last_framecnt %d (offset %d)\n", gsmtmp->framecnt, gsmtmp->last_framecnt, gsmtmp->jitter_offset); + } + + if (gsmtmp->jitter_offset >= 0) { + ztgsm_run(gsmtmp); + } + if (!(gsmtmp->ticks % 100)) { + for (s=0; s < gsmtmp->gsmspans; s++) { + if (gsmtmp->ton_off_toggle[s] >= 0) { + gsmtmp->ton_off_toggle[s] -= 100; + if (gsmtmp->ton_off_toggle[s] <= 0) { + /* timer expired */ + gsmtmp->ton_off_toggle[s] = -1; + dtr_on_off = gsmtmp->dtr_on_off & (1 << s); + if (dtr_on_off) { + if (debug) + printk(KERN_INFO "ztgsm: setting on_off to low for span %d\n",s); + gsmtmp->dtr_on_off &= ~(1 << s); /* on_off_n low */ + ztgsm_outdw_io(gsmtmp, ztgsm_SER_DTR_ON_OFF, gsmtmp->dtr_on_off); + } else { + if (debug) + printk(KERN_INFO "ztgsm: setting on_off to high for span %d\n",s); + gsmtmp->dtr_on_off |= 1 << s; /* on_off_n high */ + ztgsm_outdw_io(gsmtmp, ztgsm_SER_DTR_ON_OFF, gsmtmp->dtr_on_off); + } + if (gsmtmp->power[s]) { + gsmtmp->gsmspan[s].led = 0x81; + } else { + gsmtmp->gsmspan[s].led = 0x80; + } + } + } + } + } + + if (!(gsmtmp->ticks % 300)) + ztgsm_leds(gsmtmp, gsmtmp->ticks); + + if (!(gsmtmp->ticks % 1000)) { + if (debug > 2) + printk(KERN_INFO "ztgsm: TICK\n"); + gsmtmp->ticks = 0; + } + } + if (ser_status & 0x0F) { + mods = (ser_status & 0x0F); + for (s=0; sgsmspans; s++) { + if (mods & (1 << s)) { + rx_count = ztgsm_ser_rx(gsmtmp, s); +// if (debug) + printk(KERN_INFO "ztgsm: TX fifo overrun on span %d\n", s); + } + } + } + if (ser_status & 0xF0) { + mods = (ser_status & 0xF0) >> 4; + for (s=0; sgsmspans; s++) { + if (mods & (1 << s)) { + rx_count = ztgsm_ser_rx(gsmtmp, s); + if (debug > 1) + printk(KERN_INFO "ztgsm: HWIRQ RX %d bytes on span %d.\n", rx_count, s); + } + } + } + if (ser_status & 0xF00) { + mods = (ser_status & 0xF00) >> 8; + for (s=0; sgsmspans; s++) { + if (mods & (1 << s)) { + rx_count = ztgsm_ser_rx(gsmtmp, s); +// if (debug) + printk(KERN_INFO "ztgsm: RX fifo overrun on span %d\n", s); + } + } + } + if (ser_status & 0xF000) { + mods = (ser_status & 0xF000) >> 12; + for (s=0; sgsmspans; s++) { + if (mods & (1 << s)) { + if (gsmtmp->ser_tx_idx[s]) { + /* sumfin left to transmit */ + ztgsm_ser_tx(gsmtmp, s); + } + if (debug) + printk(KERN_INFO "ztgsm: TX low water status %#x\n", ser_status); + } + } + } + } + spin_unlock_irqrestore(&(gsmtmp->lock),flags); +#ifdef LINUX26 + return IRQ_RETVAL(1); +#endif +} + + +static int ztgsm_open(struct zt_chan *chan) { +// printk(KERN_INFO "ztgsm: channel %d opened.\n",chan->channo); +#ifndef LINUX26 + MOD_INC_USE_COUNT; +#else + try_module_get(THIS_MODULE); +#endif + return 0; +} + +static int ztgsm_close(struct zt_chan *chan) { +// printk(KERN_INFO "ztgsm: channel %d closed.\n",chan->channo); +#ifndef LINUX26 + MOD_DEC_USE_COUNT; +#else + module_put(THIS_MODULE); +#endif + return 0; +} + +static int ztgsm_chanconfig(struct zt_chan *chan,int sigtype) { +// printk(KERN_INFO "chan_config sigtype=%d\n",sigtype); + return 0; +} + +static int ztgsm_spanconfig(struct zt_span *span,struct zt_lineconfig *lc) { +// span->lineconfig = lc->lineconfig; + return 0; +} + + +static int ztgsm_startup(struct zt_span *span) { + struct ztgsm_card *gsmtmp = span->pvt; + int running; + + if (gsmtmp == NULL) { + printk(KERN_INFO "ztgsm: no card for span at startup!\n"); + } + + running = span->flags & ZT_FLAG_RUNNING; + + if (!running) { + span->chans[1].flags &= ~ZT_FLAG_HDLC; + span->chans[1].flags |= ZT_FLAG_BRIDCHAN; + if (!gsmtmp->power[span->offset]) + ztgsm_switch_on(gsmtmp, span->offset); + span->flags |= ZT_FLAG_RUNNING; + } else { + printk(KERN_INFO "ztgsm: already running\n"); + return 0; + } + return 0; +} + +static int ztgsm_shutdown(struct zt_span *span) { + int running; + struct ztgsm_card *gsmtmp = span->pvt; + + if (gsmtmp == NULL) { + printk(KERN_INFO "ztgsm: no card for span at shutdown!\n"); + } + + running = span->flags & ZT_FLAG_RUNNING; + + if (running) { + span->flags &= ~ZT_FLAG_RUNNING; + if (gsmtmp->power[span->offset]) { + ztgsm_switch_off(gsmtmp, span->offset); + } + } + return 0; +} + +static int ztgsm_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) { + switch(cmd) { + default: + return -ENOTTY; + } + return 0; +} + + +static int ztgsm_init(struct ztgsm_span *gsmspan, struct ztgsm_card *gsmtmp, int offset) { + memset(&gsmspan->span,0,sizeof(struct zt_span)); // you never can tell... + sprintf(gsmspan->span.name,"ztgsm/%d",gsmtmp->cardno); + switch (gsmtmp->type) { + case 0xb55d: + sprintf(gsmspan->span.desc,"Junghanns.NET unoGSM PCI Card %d",gsmtmp->cardno); + break; + case 0xb55e: + sprintf(gsmspan->span.desc,"Junghanns.NET duoGSM PCI Card %d",gsmtmp->cardno); + break; + case 0xb55f: + sprintf(gsmspan->span.desc,"Junghanns.NET quadGSM PCI Card %d",gsmtmp->cardno); + break; + } + + gsmspan->span.spanconfig = ztgsm_spanconfig; + gsmspan->span.chanconfig = ztgsm_chanconfig; + gsmspan->span.startup = ztgsm_startup; + gsmspan->span.shutdown = ztgsm_shutdown; + gsmspan->span.maint = NULL; + gsmspan->span.rbsbits = NULL; + gsmspan->span.open = ztgsm_open; + gsmspan->span.close = ztgsm_close; + gsmspan->span.ioctl = ztgsm_ioctl; + + if (pcm_xbar == 1) + gsmspan->span.dacs = ztgsm_dacs; + + gsmspan->span.chans = gsmspan->chans; + gsmspan->span.channels = 2; + gsmspan->span.deflaw = ZT_LAW_ALAW; + gsmspan->span.linecompat = ZT_CONFIG_CCS | ZT_CONFIG_AMI; + init_waitqueue_head(&gsmspan->span.maintq); + gsmspan->span.pvt = gsmtmp; + gsmspan->span.offset = offset; + + memset(&(gsmspan->chans[0]),0x0,sizeof(struct zt_chan)); + sprintf(gsmspan->chans[0].name,"ztgsm/%d", 0); + gsmspan->chans[0].pvt = gsmspan; + gsmspan->chans[0].sigcap = ZT_SIG_CLEAR; + gsmspan->chans[0].chanpos = 1; + + memset(&(gsmspan->chans[1]),0x0,sizeof(struct zt_chan)); + sprintf(gsmspan->chans[1].name,"ztgsm/%d", 1); + gsmspan->chans[1].pvt = gsmspan; + gsmspan->chans[1].sigcap = ZT_SIG_CLEAR; + gsmspan->chans[1].chanpos = 2; + + + if (zt_register(&gsmspan->span,0)) { + printk(KERN_INFO "ztgm: unable to register zaptel span!\n"); + return -1; + } + + /* setup B channel buffers (8 bytes each) */ + memset(gsmtmp->rxbuf[offset],0x0,sizeof(gsmtmp->rxbuf[offset])); + gsmspan->span.chans[0].readchunk = gsmtmp->rxbuf[offset]; + memset(gsmtmp->txbuf[offset],0x0,sizeof(gsmtmp->txbuf[offset])); + gsmspan->span.chans[0].writechunk = gsmtmp->txbuf[offset]; + + /* setup D channel buffer */ + memset(gsmtmp->dtxbuf[offset],0x0,sizeof(gsmtmp->dtxbuf[offset])); + gsmspan->span.chans[1].writechunk = gsmtmp->dtxbuf[offset]; + gsmspan->span.chans[1].maxbytes2transmit = sizeof(gsmtmp->dtxbuf[offset]); + + memset(gsmtmp->drxbuf[offset],0x0,sizeof(gsmtmp->drxbuf[offset])); + gsmspan->span.chans[1].readchunk = gsmtmp->drxbuf[offset]; + + return 0; +} + + +int ztgsm_findCards(unsigned int pcidid) { + struct pci_dev *tmp; + struct ztgsm_card *gsmtmp = NULL; + struct ztgsm_span *gsmspan = NULL; + unsigned int ioport_size = 0; + int i=0; + int cid=0; + tmp = pci_get_device(PCI_VENDOR_ID_CCD,pcidid,NULL); + while (tmp != NULL) { + multi_gsm = tmp; + + if (pci_enable_device(tmp)) { + return -1; + } + + gsmtmp = kmalloc(sizeof(struct ztgsm_card),GFP_KERNEL); + if (!gsmtmp) { + printk(KERN_WARNING "ztgsm: unable to kmalloc!\n"); + pci_disable_device(tmp); + return -ENOMEM; + } + memset(gsmtmp, 0x0, sizeof(struct ztgsm_card)); + + spin_lock_init(&gsmtmp->lock); + gsmtmp->pcidev = tmp; + gsmtmp->pcibus = tmp->bus->number; + gsmtmp->pcidevfn = tmp->devfn; + + if (!tmp->irq) { + printk(KERN_WARNING "ztgsm: no irq!\n"); + } else { + gsmtmp->irq = tmp->irq; + } + + gsmtmp->pci_io_phys = tmp->resource[1].start; + if (!gsmtmp->pci_io_phys) { + printk(KERN_WARNING "ztgsm: no iomem!\n"); + pci_disable_device(tmp); + return -EIO; + } + gsmtmp->iomem_size = (tmp->resource[1].end - tmp->resource[1].start + 1); + printk(KERN_INFO "ztgsm: iomem at %lx size %ld\n", gsmtmp->pci_io_phys, gsmtmp->iomem_size); + + if (check_mem_region(gsmtmp->pci_io_phys, gsmtmp->iomem_size)) { + printk(KERN_INFO "ztgsm: iomem already in use!\n");; + pci_disable_device(tmp); + return -EBUSY; + } + + request_mem_region(gsmtmp->pci_io_phys, gsmtmp->iomem_size, "ztgsm"); + + gsmtmp->pci_io = ioremap(gsmtmp->pci_io_phys, gsmtmp->iomem_size); /* 8kb */ + + gsmtmp->ioport = tmp->resource[0].start; + if (!gsmtmp->ioport) { + printk(KERN_WARNING "ztgsm: no ioport!\n"); + pci_disable_device(tmp); + return -EIO; + } + ioport_size = (tmp->resource[0].end - tmp->resource[0].start + 1); + printk(KERN_INFO "ztgsm: ioport size %d\n", ioport_size); + + if (!request_region(gsmtmp->ioport, 0x100, "ztgsm")) { + printk(KERN_WARNING "ztgsm: couldnt request io range!\n"); + release_mem_region(gsmtmp->pci_io_phys, gsmtmp->iomem_size); + pci_disable_device(tmp); + return -EIO; + } + + if (request_irq(gsmtmp->irq, ztgsm_interrupt, ZAP_IRQ_SHARED, "ztgsm", gsmtmp)) { + printk(KERN_WARNING "ztgsm: unable to register irq\n"); + release_region(gsmtmp->ioport, 0x100); + release_mem_region(gsmtmp->pci_io_phys, gsmtmp->iomem_size); + kfree(gsmtmp); + pci_disable_device(tmp); + return -EIO; + } + + pci_write_config_word(gsmtmp->pcidev, PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + + // disable ints + + gsmtmp->type = tmp->subsystem_device; + + switch (gsmtmp->type) { + case 0xb55d: + printk(KERN_INFO + "ztgsm: Junghanns.NET unoGSM card configured at io port %x IRQ %d io mem %lx HZ %d CardID %d\n", + (u_int) gsmtmp->ioport, gsmtmp->irq, (u_long) gsmtmp->pci_io, HZ, cid); + break; + case 0xb55e: + printk(KERN_INFO + "ztgsm: Junghanns.NET duoGSM card configured at io port %x IRQ %d io mem %lx HZ %d CardID %d\n", + (u_int) gsmtmp->ioport, gsmtmp->irq, (u_long) gsmtmp->pci_io, HZ, cid); + break; + case 0xb55f: + printk(KERN_INFO + "ztgsm: Junghanns.NET quadGSM card configured at io port %x IRQ %d io mem %lx HZ %d CardID %d\n", + (u_int) gsmtmp->ioport, gsmtmp->irq, (u_long) gsmtmp->pci_io, HZ, cid); + break; + } + + ztgsm_resetCard(gsmtmp); + ztgsm_init_xbar(gsmtmp); + + switch (ztgsm_indw_io(gsmtmp, ztgsm_SER_G20_ACTIVATED)) { + case 0xf: + gsmtmp->gsmspans = 4; + break; + case 0x3: + gsmtmp->gsmspans = 2; + break; + case 0x1: + gsmtmp->gsmspans = 1; + break; + } + ztgsm_spans += gsmtmp->gsmspans; + + for (i=0; i < gsmtmp->gsmspans; i++) { + gsmspan = &gsmtmp->gsmspan[i]; + ztgsm_init(gsmspan, gsmtmp, i); + gsmspan->led = 0x80; + } + ztgsm_leds(gsmtmp, 0); + + gsmtmp->dtr_on_off = 0x0f; + + gsmtmp->version = ztgsm_indw_io(gsmtmp, ztgsm_VERS_A); + printk(KERN_INFO "ztgsm: VERSION %x\n", gsmtmp->version); + if (debug) { + printk(KERN_INFO "ztgsm: G20_ACTIVATED %d\n", ztgsm_indw_io(gsmtmp, ztgsm_SER_G20_ACTIVATED)); + printk(KERN_INFO "ztgsm: DIPS %#x\n", ztgsm_indw_io(gsmtmp, ztgsm_DIP_SWITCH)); + printk(KERN_INFO "ztgsm: tx_wm_sen %d\n", ztgsm_indw_io(gsmtmp, ztgsm_SER_TX_WM_SEN)); + } + + ztgsm_register_card(gsmtmp); + + ztgsm_startCard(gsmtmp); + + if (sim >= 0x10) { + printk(KERN_INFO "ztgsm: Invalid value for parameter sim.\n"); + sim = 0; + } + + if (sim) { + ztgsm_switch_off_all(gsmtmp, 0); + printk(KERN_INFO "ztgsm: Initializing SIM extensions..."); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((2000 * HZ) / 1000); + ztgsm_switch_on_all(gsmtmp, 0); + printk("done\n"); + } else { + ztgsm_switch_on_all(gsmtmp, 1); + } + tmp = pci_get_device(PCI_VENDOR_ID_CCD,pcidid,multi_gsm); + } + return 0; +} + + +int init_module(void) { + ztgsm_findCards(0xf001); + if (ztgsm_dev_count == 0) { + printk(KERN_INFO "ztgsm: no multiGSM cards found.\n"); + } else { + printk(KERN_INFO "ztgsm: %d multiGSM card(s) in this box, %d GSM spans total.\n",ztgsm_dev_count, ztgsm_spans); + } + return 0; +} + +void cleanup_module(void) { + struct ztgsm_card *tmpcard,*tmplist; + int i=0; + tmplist = ztgsm_dev_list; + while (tmplist != NULL) { + ztgsm_shutdownCard(tmplist); + tmplist = tmplist->next; + } + tmplist = ztgsm_dev_list; + spin_lock(®isterlock); + while (tmplist != NULL) { + tmpcard = tmplist->next; + kfree(tmplist); + i++; + tmplist = tmpcard; + } + spin_unlock(®isterlock); + printk(KERN_INFO "ztgsm: shutdown %d multiGSM cards.\n", i); +} +#endif + +#ifdef LINUX26 +module_param(debug, int, 0600); +module_param(pcm_xbar, int, 0600); +module_param(sim, int, 0600); +module_param(baudrate, long, 0600); +#else +MODULE_PARM(debug,"i"); +MODULE_PARM(pcm_xbar,"i"); +MODULE_PARM(sim,"i"); +MODULE_PARM(baudrate,"l"); +#endif + +MODULE_DESCRIPTION("uno/duao/quad GSM zaptel driver"); +MODULE_AUTHOR("Klaus-Peter Junghanns "); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif --- zaptel-1.4.11~dfsg.orig/kernel/ztgsm/zapata.conf.quadGSM +++ zaptel-1.4.11~dfsg/kernel/ztgsm/zapata.conf.quadGSM @@ -0,0 +1,36 @@ +[channels] +txgain = -10.0 +rxgain = 0.0 + +signalling = gsm +context = from-gsm + +;group=1 + +; phone number for SIM card in slot A +;exten=016012345671 +; PIN for SIM card in slot A +;pin=1234 + +;channel => 1 + +; phone number for SIM card in slot B +;exten=016012345672 +; PIN for SIM card in slot B +;pin=1234 + +;channel => 3 + +; phone number for SIM card in slot C +;exten=016012345673 +; PIN for SIM card in slot C +;pin=1234 + +;channel => 5 + +; phone number for SIM card in slot D +;exten=016012345674 +; PIN for SIM card in slot D +;pin=1234 + +;channel => 7 --- zaptel-1.4.11~dfsg.orig/kernel/ztgsm/zaptel.conf.quadGSM +++ zaptel-1.4.11~dfsg/kernel/ztgsm/zaptel.conf.quadGSM @@ -0,0 +1,13 @@ +loadzone=nl +defaultzone=nl + +alaw=1,3,5,7 + +span=1,1,3,ccs,ami +span=2,2,3,ccs,ami +span=3,3,3,ccs,ami +span=4,4,3,ccs,ami + +bchan=1,3,5,7 +dchan=2,4,6,8 + --- zaptel-1.4.11~dfsg.orig/kernel/ztgsm/zapata.conf.unoGSM +++ zaptel-1.4.11~dfsg/kernel/ztgsm/zapata.conf.unoGSM @@ -0,0 +1,15 @@ +[channels] +txgain = -10.0 +rxgain = 0.0 + +signalling = gsm +context = from-gsm + +;group=1 + +; phone number for SIM card in slot A +;exten=016012345671 +; PIN for SIM card in slot A +;pin=1234 + +;channel => 1 --- zaptel-1.4.11~dfsg.orig/kernel/ztgsm/zaptel.conf.duoGSM +++ zaptel-1.4.11~dfsg/kernel/ztgsm/zaptel.conf.duoGSM @@ -0,0 +1,11 @@ +loadzone=nl +defaultzone=nl + +alaw=1,3 + +span=1,1,3,ccs,ami +span=2,2,3,ccs,ami + +bchan=1,3 +dchan=2,4 + --- zaptel-1.4.11~dfsg.orig/kernel/ztgsm/ztgsm.h +++ zaptel-1.4.11~dfsg/kernel/ztgsm/ztgsm.h @@ -0,0 +1,139 @@ +#define ztgsm_RX_MAX 1000 +#define ztgsm_FIFO_SIZE 16 +#define ztgsm_SPANS 4 +#define ztgsm_SER_BUF_SIZE 1000 +#define ztgsm_FRAMES 0x100 +#define ztgsm_MAX_FC_DIFF 32 +#define ztgsm_FRAME_SIZE ZT_CHUNKSIZE + +typedef struct ztgsm_span { + int led; /* 0 == RED 1 == GREEN 0x80 == on 0x40 == blink */ + int sim_led; + /* zaptel resources */ + struct zt_span span; + struct zt_chan chans[2]; + + /* more zaptel stuff */ + unsigned int usecount; + int spantype; + int spanflags; +} ztgsm_span; + +typedef struct ztgsm_card { + spinlock_t lock; + unsigned char power[ztgsm_SPANS]; + unsigned int dtr_on_off; + int cardID; + int dead; + unsigned char cardno; + unsigned int irq; + unsigned int iomem; + void *pci_io; + unsigned int framecnt; + unsigned int last_framecnt; + signed int jitter_offset; + unsigned long pci_io_phys; + unsigned int version; + unsigned long iomem_size; + unsigned long ioport; + struct ztgsm_span gsmspan[ztgsm_SPANS]; + unsigned int gsmspans; + unsigned int pcibus; + unsigned int pcidevfn; + struct pci_dev *pcidev; + unsigned char gsms; + unsigned int ticks; + unsigned int clicks; + unsigned int type; + unsigned int sim_sel; + int ton_off_toggle[ztgsm_SPANS]; + unsigned char rxbuf[ztgsm_SPANS][ZT_CHUNKSIZE]; + unsigned char txbuf[ztgsm_SPANS][ZT_CHUNKSIZE]; + unsigned char drxbuf[ztgsm_SPANS][ztgsm_SER_BUF_SIZE]; + unsigned char dtxbuf[ztgsm_SPANS][ztgsm_FIFO_SIZE]; + unsigned short rxbufi[ztgsm_SPANS]; + + unsigned char ser_rx_buf[ztgsm_SPANS][ztgsm_SER_BUF_SIZE]; + unsigned char ser_tx_buf[ztgsm_SPANS][ztgsm_SER_BUF_SIZE]; + unsigned short ser_rx_idx[ztgsm_SPANS]; + unsigned short ser_tx_idx[ztgsm_SPANS]; + unsigned char tx_wm_sen; + struct ztgsm_card *next; + struct ztgsm_card *prev; +} ztgsm_card; + +#define ztgsm_outb_io(a,b,c) \ + outw((b), ((a)->ioport+4)); \ + outb((c), ((a)->ioport)); + +#define ztgsm_inb_io(a,b) ({ outw((b), (a)->ioport+4); inb((a)->ioport); }) + +#define ztgsm_outw_io(a,b,c) \ + outw((b), ((a)->ioport+4)); \ + outw((c), ((a)->ioport)); + +#define ztgsm_inw_io(a,b) ({ outw((b), (a)->ioport+4); inw((a)->ioport); }) + +#define _ztgsm_outdw_io(a,b,c) \ + outw((b), ((a)->ioport+4)); \ + outl((c), ((a)->ioport)); + +#define ztgsm_outdw_io(a,b,c) (outl((c), ((a)->ioport+b))) + +#define ztgsm_indw_io(a,b) (inl((a)->ioport+b)) + +#define ztgsm_outb(a,b,c) (writeb((c),(a)->pci_io+(b))) +#define ztgsm_inb(a,b) (readb((a)->pci_io+(b))) + +#define ztgsm_outw(a,b,c) (writew((c),(a)->pci_io+(b))) +#define ztgsm_inw(a,b) (readw((a)->pci_io+(b))) + +#define ztgsm_outdw(a,b,c) (writel((c),(a)->pci_io+(b))) +#define ztgsm_indw(a,b) (readl((a)->pci_io+(b))) + +#define ztgsm_IO_BASE 0x0 +#define ztgsm_VERS_A ztgsm_IO_BASE +#define ztgsm_SER_CLK_DIV ztgsm_IO_BASE + 4 +#define ztgsm_SER_CLK_PRE_DIV ztgsm_IO_BASE + (4 * 0x02) +#define ztgsm_PCM_CLK_PRE_DIV ztgsm_IO_BASE + (4 * 0x03) +#define ztgsm_SER_IDLE_VAL ztgsm_IO_BASE + (4 * 0x04) +#define ztgsm_SER_RTS_O ztgsm_IO_BASE + (4 * 0x05) +#define ztgsm_SER_TX_EN ztgsm_IO_BASE + (4 * 0x06) +#define ztgsm_SER_RX_EN ztgsm_IO_BASE + (4 * 0x07) +#define ztgsm_SER_DTR_ON_OFF ztgsm_IO_BASE + (4 * 0x08) +#define ztgsm_DIP_SWITCH ztgsm_IO_BASE + (4 * 0x09) +#define ztgsm_LED_DUAL ztgsm_IO_BASE + (4 * 0x0A) +#define ztgsm_SER_G20_ACTIVATED ztgsm_IO_BASE + (4 * 0x0B) +#define ztgsm_SIM_SEL ztgsm_IO_BASE + (4 * 0x0C) +#define ztgsm_PCM_DIR ztgsm_IO_BASE + (4 * 0x0D) + +#define ztgsm_SER_TX_DATA_A ztgsm_IO_BASE + (4 * 0x10) +#define ztgsm_SER_TX_COUNT_A ztgsm_IO_BASE + (4 * 0x11) +#define ztgsm_SER_TX_DATA_B ztgsm_IO_BASE + (4 * 0x12) +#define ztgsm_SER_TX_COUNT_B ztgsm_IO_BASE + (4 * 0x13) +#define ztgsm_SER_TX_DATA_C ztgsm_IO_BASE + (4 * 0x14) +#define ztgsm_SER_TX_COUNT_C ztgsm_IO_BASE + (4 * 0x15) +#define ztgsm_SER_TX_DATA_D ztgsm_IO_BASE + (4 * 0x16) +#define ztgsm_SER_TX_COUNT_D ztgsm_IO_BASE + (4 * 0x17) +#define ztgsm_SER_RX_DATA_A ztgsm_IO_BASE + (4 * 0x18) +#define ztgsm_SER_RX_COUNT_A ztgsm_IO_BASE + (4 * 0x19) +#define ztgsm_SER_RX_DATA_B ztgsm_IO_BASE + (4 * 0x1a) +#define ztgsm_SER_RX_COUNT_B ztgsm_IO_BASE + (4 * 0x1b) +#define ztgsm_SER_RX_DATA_C ztgsm_IO_BASE + (4 * 0x1c) +#define ztgsm_SER_RX_COUNT_C ztgsm_IO_BASE + (4 * 0x1d) +#define ztgsm_SER_RX_DATA_D ztgsm_IO_BASE + (4 * 0x1e) +#define ztgsm_SER_RX_COUNT_D ztgsm_IO_BASE + (4 * 0x1f) +#define ztgsm_SER_STATUS ztgsm_IO_BASE + (4 * 0x20) +#define ztgsm_SER_INT_MASK ztgsm_IO_BASE + (4 * 0x21) +#define ztgsm_SER_RX_WATERMARK ztgsm_IO_BASE + (4 * 0x22) +#define ztgsm_SER_TX_WATERMARK ztgsm_IO_BASE + (4 * 0x23) +#define ztgsm_SER_TX_WM_SEN ztgsm_IO_BASE + (4 * 0x24) + +#define ztgsm_PCM_SAP_EN ztgsm_IO_BASE + (4 * 0x30) +#define ztgsm_PCM_MASK_LAST ztgsm_IO_BASE + (4 * 0x31) +#define ztgsm_PCM_FRAME_CNT ztgsm_IO_BASE + (4 * 0x32) +#define ztgsm_PCM_FC_TOG_BIT ztgsm_IO_BASE + (4 * 0x33) +#define ztgsm_PCM_SIGNAL_CFG ztgsm_IO_BASE + (4 * 0x34) +#define ztgsm_PCM_VECTOR_CFG ztgsm_IO_BASE + (4 * 0x35) + + --- zaptel-1.4.11~dfsg.orig/kernel/wcopenpci.c +++ zaptel-1.4.11~dfsg/kernel/wcopenpci.c @@ -0,0 +1,1843 @@ +/* + * Voicetronix OpenPCI Interface Driver for Zapata Telephony interface + * + * Written by Mark Spencer + * Matthew Fredrickson + * Ben Kramer + * Ron Lee + * + * Copyright (C) 2001, Linux Support Services, Inc. + * Copyright (C) 2005 - 2007, Voicetronix + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* Conditional debug options */ +#define VERBOSE_TIMING 0 + +/* Driver constants */ +#define DRIVER_DESCRIPTION "Voicetronix OpenPCI zaptel driver" +#define DRIVER_AUTHOR "Mark Spencer "\ + "Voicetronix " + +#define NAME "wcopenpci" +#define MAX_PORTS 8 /* Maximum number of ports on each carrier */ +#define MAX_CARDS 8 /* Maximum number of carriers per host */ + +#define DEFAULT_COUNTRY "AUSTRALIA" + + +#include +#include +#include +#include + +#ifdef STANDALONE_ZAPATA + #include "zaptel.h" + #include "../version.h" + #include "proslic.h" + #include "wctdm.h" +#else + //#include + #error The wcopenpci module must be built with the full zaptel source +#endif + + + +/* Compatibility helpers */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) + #include +#else + typedef void irqreturn_t; + #define IRQ_NONE + #define IRQ_HANDLED + #define IRQ_RETVAL(x) + #define __devexit_p(x) x +#endif + +// Centos4.3 uses a modified 2.6.9 kernel, with no indication that +// it is different from the mainstream (or even Centos4.2 2.6.9) +// kernel, so we must crowbar off the dunce-hat manually here. +#if !defined CENTOS4_3 && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) + typedef int gfp_t; + static inline void *kzalloc( size_t n, gfp_t flags ){ + void *p = kmalloc(n,flags); + if (p) memset(p, 0, n); + return p; + } +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) + #define DEFINE_MUTEX(x) DECLARE_MUTEX(x) + #define mutex_init(x) init_MUTEX(x) + #define mutex_lock(x) down(x) + #define mutex_lock_interruptible(x) down_interruptible(x) + #define mutex_trylock(x) down_trylock(x) + #define mutex_unlock(x) up(x) +#else + #include +#endif + + +static struct fxo_mode { + char *name; + int ohs; + int ohs2; + int rz; + int rt; + int ilim; + int dcv; + int mini; + int acim; + int ring_osc; + int ring_x; +} fxo_modes[] = +{ + { "FCC", 0, 0, 0, 1, 0, 0x3, 0, 0, }, /* US, Canada */ + { "TBR21", 0, 0, 0, 0, 1, 0x3, 0, 0x2, 0x7e6c, 0x023a, }, + /* Austria, Belgium, Denmark, Finland, France, Germany, + Greece, Iceland, Ireland, Italy, Luxembourg, Netherlands, + Norway, Portugal, Spain, Sweden, Switzerland, and UK */ + { "ARGENTINA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "AUSTRALIA", 1, 0, 0, 0, 0, 0, 0x3, 0x3, }, + { "AUSTRIA", 0, 1, 0, 0, 1, 0x3, 0, 0x3, }, + { "BAHRAIN", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "BELGIUM", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "BRAZIL", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "BULGARIA", 0, 0, 0, 0, 1, 0x3, 0x0, 0x3, }, + { "CANADA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "CHILE", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "CHINA", 0, 0, 0, 0, 0, 0, 0x3, 0xf, }, + { "COLUMBIA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "CROATIA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "CYRPUS", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "CZECH", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "DENMARK", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "ECUADOR", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "EGYPT", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "ELSALVADOR", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "FINLAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "FRANCE", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "GERMANY", 0, 1, 0, 0, 1, 0x3, 0, 0x3, }, + { "GREECE", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "GUAM", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "HONGKONG", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "HUNGARY", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "ICELAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "INDIA", 0, 0, 0, 0, 0, 0x3, 0, 0x4, }, + { "INDONESIA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "IRELAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "ISRAEL", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "ITALY", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "JAPAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "JORDAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "KAZAKHSTAN", 0, 0, 0, 0, 0, 0x3, 0, }, + { "KUWAIT", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "LATVIA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "LEBANON", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "LUXEMBOURG", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "MACAO", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "MALAYSIA", 0, 0, 0, 0, 0, 0, 0x3, 0, }, /* Current loop >= 20ma */ + { "MALTA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "MEXICO", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "MOROCCO", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, + { "NETHERLANDS", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "NEWZEALAND", 0, 0, 0, 0, 0, 0x3, 0, 0x4, }, + { "NIGERIA", 0, 0, 0, 0, 0x1, 0x3, 0, 0x2, }, + { "NORWAY", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "OMAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "PAKISTAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "PERU", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "PHILIPPINES", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "POLAND", 0, 0, 1, 1, 0, 0x3, 0, 0, }, + { "PORTUGAL", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "ROMANIA", 0, 0, 0, 0, 0, 3, 0, 0, }, + { "RUSSIA", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "SAUDIARABIA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "SINGAPORE", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "SLOVAKIA", 0, 0, 0, 0, 0, 0x3, 0, 0x3, }, + { "SLOVENIA", 0, 0, 0, 0, 0, 0x3, 0, 0x2, }, + { "SOUTHAFRICA", 1, 0, 1, 0, 0, 0x3, 0, 0x3, }, + { "SOUTHKOREA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "SPAIN", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "SWEDEN", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "SWITZERLAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, + { "SYRIA", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "TAIWAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "THAILAND", 0, 0, 0, 0, 0, 0, 0x3, 0, }, + { "UAE", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "UK", 0, 1, 0, 0, 1, 0x3, 0, 0x5, }, + { "USA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, + { "YEMEN", 0, 0, 0, 0, 0, 0x3, 0, 0, }, +}; + +static struct ps_country_reg { + const char *country; + unsigned short value; +} ps_country_regs[] = { + {"ARGENTINA", 0x8}, + {"AUSTRALIA", 0xD}, + {"AUSTRIA", 0xD}, + {"BAHRAIN", 0xC}, + {"BELGIUM", 0xC}, + {"BRAZIL", 0x8}, + {"BULGARIA", 0xD}, + {"CANADA", 0x8}, + {"CHILE", 0x8}, + {"CHINA", 0xC}, + {"COLOMBIA", 0x8}, + {"CROATIA", 0xC}, + {"CYPRUS", 0xC}, + {"CZECH", 0xC}, + {"DENMARK", 0xC}, + {"ECUADOR", 0x8}, + {"EGYPT", 0x8}, + {"ELSALVADOR", 0x8}, + {"FINLAND", 0xC}, + {"FRANCE", 0xC}, + {"GERMANY", 0xD}, + {"GREECE", 0xC}, + {"GUAM", 0x8}, + {"HONGKONG", 0x8}, + {"HUNGARY", 0x8}, + {"ICELAND", 0xC}, + {"INDIA", 0xF}, + {"INDONESIA", 0x8}, + {"IRELAND", 0xC}, + {"ISRAEL", 0xC}, + {"ITALY", 0xC}, + {"JAPAN", 0x8}, + {"JORDAN", 0x8}, + {"KAZAKHSTAN", 0x8}, + {"KUWAIT", 0x8}, + {"LATVIA", 0xC}, + {"LEBANON", 0xC}, + {"LUXEMBOURG", 0xC}, + {"MACAO", 0x8}, + {"MALAYSIA", 0x8}, + {"MALTA", 0xC}, + {"MEXICO", 0x8}, + {"MOROCCO", 0xC}, + {"NETHERLANDS",0xC}, + {"NEWZEALAND", 0xF}, + {"NIGERIA", 0xC}, + {"NORWAY", 0xC}, + {"OMAN", 0x8}, + {"PAKISTAN", 0x8}, + {"PERU", 0x8}, + {"PHILIPPINES",0x8}, + {"POLAND", 0x8}, + {"PORTUGAL", 0xC}, + {"ROMANIA", 0x8}, + {"RUSSIA", 0x8}, + {"SAUDIARABIA",0x8}, + {"SINGAPORE", 0x8}, + {"SLOVAKIA", 0xE}, + {"SLOVENIA", 0xE}, + {"SOUTHAFRICA",0xE}, + {"SOUTHKOREA", 0x8}, + {"SPAIN", 0xC}, + {"SWEDEN", 0xC}, + {"SWITZERLAND",0xC}, + {"SYRIA", 0x8}, + {"TAIWAN", 0x8}, + {"THAILAND", 0x8}, + {"UAE", 0x8}, + {"UK", 0xC}, + {"USA", 0x8}, + {"YEMEN", 0x8} +}; + +#define INOUT 2 + +/* Allocate enough memory for two zt chunks, receive and transmit. Each sample uses + 32 bits. Allocate an extra set just for control too */ +#define VT_PCIDMA_BLOCKSIZE (ZT_MAX_CHUNKSIZE * INOUT * MAX_PORTS * 2 * 2) +#define VT_PCIDMA_MIDDLE (ZT_MAX_CHUNKSIZE * MAX_PORTS - 4) +#define VT_PCIDMA_END (ZT_MAX_CHUNKSIZE * MAX_PORTS * 2 - 4) + +#define ID_DATA_MAXSIZE 30 + +#define NUM_CAL_REGS 12 +#define NUM_FXO_REGS 60 + +#define TREG(addr) (wc->ioaddr + addr) + +#define TJ_CNTL TREG(0x00) +#define TJ_OPER TREG(0x01) +#define TJ_AUXC TREG(0x02) +#define TJ_AUXD TREG(0x03) +#define TJ_MASK0 TREG(0x04) +#define TJ_MASK1 TREG(0x05) +#define TJ_INTSTAT TREG(0x06) +#define TJ_AUXR TREG(0x07) + +#define TJ_DMAWS TREG(0x08) +#define TJ_DMAWI TREG(0x0c) +#define TJ_DMAWE TREG(0x10) +#define TJ_DMAWC TREG(0x14) +#define TJ_DMARS TREG(0x18) +#define TJ_DMARI TREG(0x1c) +#define TJ_DMARE TREG(0x20) +#define TJ_DMARC TREG(0x24) + +#define TJ_AUXINTPOL TREG(0x2A) + +#define TJ_AUXFUNC TREG(0x2b) +#define TJ_SFDELAY TREG(0x2c) +#define TJ_SERCTL TREG(0x2d) +#define TJ_SFLC TREG(0x2e) +#define TJ_FSCDELAY TREG(0x2f) + +#define TJ_REGBASE TREG(0xc0) + +#define PIB(addr) (TJ_REGBASE + addr * 4) + +#define HTXF_READY (inb(PIB(0)) & 0x10) +#define HRXF_READY (inb(PIB(0)) & 0x20) + + +#define VT_PORT_EMPTY 0 +#define VT_PORT_VDAA 1 /* Voice DAA - FXO */ +#define VT_PORT_PROSLIC 2 /* ProSLIC - FXS */ + +#define VBAT 0xC7 + +#define HKMODE_FWDACT 1 +#define HKMODE_FWDONACT 2 +#define HKMODE_RINGING 4 + +#define HOOK_ONHOOK 0 +#define HOOK_OFFHOOK 1 + +#define DSP_CODEC_RING 12 /* RING rising edge detected */ +#define DSP_CODEC_HKOFF 22 /* station port off hook */ +#define DSP_CODEC_HKON 23 /* station port on hook */ +#define DSP_RING_OFF 24 /* RING falling edge detected */ +#define DSP_DROP 25 + +#define DSP_CODEC_FLASH 26 /* station port hook flash */ + +#define DSP_LOOP_OFFHOOK 38 /* Loop Off hook from OpenPCI */ +#define DSP_LOOP_ONHOOK 39 /* Loop On hook from OpenPCI */ +#define DSP_LOOP_POLARITY 40 /* Loop Polarity from OpenPCI */ +#define DSP_LOOP_NOBATT 41 + +#define DSP_PROSLIC_SANITY 50 /* Sanity alert from a ProSLIC port */ +#define DSP_PROSLIC_PWR_ALARM 51 /* Power Alarm from a ProSLIC port */ +#define DSP_VDAA_ISO_FRAME_E 52 /* ISO-cap frame sync lost on VDAA port*/ + +#if VERBOSE_TIMING + #define REPORT_WAIT(n,x) \ + cardinfo(card->cardnum, #n " wait at %d, " #x " = %d", __LINE__, x ) +#else + #define REPORT_WAIT(n,x) +#endif + +#define BUSY_WAIT(countvar,cond,delay,iter,failret) \ + countvar=0; \ + while(cond){ \ + udelay(delay); \ + if(++countvar > iter){ \ + cardcrit(wc->boardnum, "busy wait FAILED at %d", __LINE__); \ + return failret; \ + } \ + } \ + REPORT_WAIT(busy,i) + +#define LOCKED_WAIT(countvar,cond,delay,iter,failret) \ + countvar=0; \ + while(cond){ \ + udelay(delay); \ + if(++countvar > iter){ \ + dbginfo(wc->boardnum,"busy wait failed at %d",__LINE__); \ + spin_unlock_irqrestore(&wc->lock, flags); \ + return failret; \ + } \ + } \ + REPORT_WAIT(locked,i) + +#define HTXF_WAIT() BUSY_WAIT(i,HTXF_READY,5,500,RET_FAIL) +#define HRXF_WAIT() BUSY_WAIT(i,!HRXF_READY,5,70000,RET_FAIL) +#define HTXF_WAIT_RET(failret) BUSY_WAIT(i,HTXF_READY,5,500,failret) +#define HRXF_WAIT_RET(failret) BUSY_WAIT(i,!HRXF_READY,5,1000,failret) + +#define HTXF_WAIT_LOCKED() LOCKED_WAIT(i,HTXF_READY,5,500,RET_FAIL) +#define HRXF_WAIT_LOCKED() LOCKED_WAIT(i,!HRXF_READY,5,1000,RET_FAIL) +#define HTXF_WAIT_LOCKED_RET(failret) LOCKED_WAIT(i,HTXF_READY,5,500,failret) +#define HRXF_WAIT_LOCKED_RET(failret) LOCKED_WAIT(i,!HRXF_READY,5,1000,failret) + + +struct openpci { + struct pci_dev *dev; + char *variety; + int boardnum; + int portcount; + int porttype[MAX_PORTS]; + + int firmware; + char serial[ID_DATA_MAXSIZE]; + + spinlock_t lock; + + //XXX Replace these with proper try_module_get locking in the zaptel driver. + //int usecount; //XXX + //int dead; //XXX + union { + struct { + int offhook; + } fxo; + struct { + int ohttimer; + int idletxhookstate; /* IDLE changing hook state */ + int lasttxhook; + } fxs; + } mod[MAX_PORTS]; + + unsigned long ioaddr; + dma_addr_t readdma; + dma_addr_t writedma; + volatile unsigned int *writechunk; /* Double-word aligned write memory */ + volatile unsigned int *readchunk; /* Double-word aligned read memory */ + + struct zt_chan chans[MAX_PORTS]; + struct zt_span span; +} *cards[MAX_CARDS]; + +// You must hold this lock anytime you access or modify the cards[] array. +DEFINE_MUTEX(cards_mutex); + +static unsigned char fxo_port_lookup[8] = { 0x0, 0x8, 0x4, 0xc, 0x10, 0x18, 0x14, 0x1c}; +static unsigned char fxs_port_lookup[8] = { 0x0, 0x1, 0x2, 0x3, 0x10, 0x11, 0x12, 0x13}; +static char wcopenpci[] = "Voicetronix OpenPCI"; + +static char *country = DEFAULT_COUNTRY; +static int reversepolarity; // = 0 +static int debug; // = 0 + +module_param(country, charp, 0444); +module_param(debug, int, 0600); +module_param(reversepolarity, int, 0600); +MODULE_PARM_DESC(country, "Set the default country name"); +MODULE_PARM_DESC(debug, "Enable verbose logging"); + +//#define DEBUG_LOOP_VOLTAGE 1 +#ifdef DEBUG_LOOP_VOLTAGE + // This param is a 32 bit bitfield where bit 1 << cardnum * 8 << portnum + // will enable voltage monitoring on that port (fxo only presently) + static int voltmeter; // = 0 + module_param(voltmeter, int, 0600); + MODULE_PARM_DESC(voltmeter, "Enable loop voltage metering"); +#endif + + +/* boolean return values */ +#define RET_OK 1 +#define RET_FAIL 0 + +/* Convenience macros for logging */ +#define info(format,...) printk(KERN_INFO NAME ": " format "\n" , ## __VA_ARGS__) +#define warn(format,...) printk(KERN_WARNING NAME ": " format "\n" , ## __VA_ARGS__) +#define crit(format,...) printk(KERN_CRIT NAME ": " format "\n" , ## __VA_ARGS__) +#define cardinfo(cardnum,format,...) info("[%02d] " format, cardnum , ## __VA_ARGS__) +#define cardwarn(cardnum,format,...) warn("[%02d] " format, cardnum , ## __VA_ARGS__) +#define cardcrit(cardnum,format,...) crit("[%02d] " format, cardnum , ## __VA_ARGS__) +#define dbginfo(cardnum,format,...) if(debug) info("[%02d] " format, cardnum , ## __VA_ARGS__) + + +static inline const char *porttype(struct openpci *wc, int port) +{ //{{{ + switch( wc->porttype[port] ) { + case VT_PORT_VDAA: return "VDAA"; + case VT_PORT_PROSLIC: return "ProSLIC"; + case VT_PORT_EMPTY: return "empty port"; + default: return "unknown type"; + } +} //}}} + + +static int __read_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char *value) +{ //{{{ + unsigned char portadr = fxo_port_lookup[port]; + int i; + + if (HRXF_READY) *value = inb(PIB(1)); + + outb(0x11, PIB(1)); HTXF_WAIT(); + outb(0x2, PIB(1)); HTXF_WAIT(); + outb(portadr, PIB(1)); HTXF_WAIT(); + outb(reg, PIB(1)); HTXF_WAIT(); + HRXF_WAIT(); *value = inb(PIB(1)); + + return RET_OK; +} //}}} + +static int read_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char *value) +{ //{{{ + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + if( __read_reg_fxo(wc, port, reg, value) ){ + spin_unlock_irqrestore(&wc->lock, flags); + return RET_OK; + } + spin_unlock_irqrestore(&wc->lock, flags); + cardcrit(wc->boardnum, "FXO port %d, reg %d, read failed!", port, reg); + return RET_FAIL; +} //}}} + +static int __read_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char *value) +{ //{{{ + unsigned char portadr = fxs_port_lookup[port]; + int i; + + if (HRXF_READY) *value = inb(PIB(1)); + + outb(0x13, PIB(1)); HTXF_WAIT(); + outb(0x2, PIB(1)); HTXF_WAIT(); + outb(portadr, PIB(1)); HTXF_WAIT(); + outb(reg, PIB(1)); HTXF_WAIT(); + HRXF_WAIT(); *value = inb(PIB(1)); + + return RET_OK; +} //}}} + +static int read_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char *value) +{ //{{{ + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + if( __read_reg_fxs(wc, port, reg, value) ) { + spin_unlock_irqrestore(&wc->lock, flags); + return RET_OK; + } + spin_unlock_irqrestore(&wc->lock, flags); + cardcrit(wc->boardnum, "FXS port %d, reg %d, read failed!", port, reg); + return RET_FAIL; +} //}}} + +static int __write_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char value) +{ //{{{ + unsigned char portadr = fxo_port_lookup[port]; + int i; + + outb(0x10, PIB(1) ); HTXF_WAIT(); + outb(0x3, PIB(1)); HTXF_WAIT(); + outb(portadr, PIB(1)); HTXF_WAIT(); + outb(reg, PIB(1)); HTXF_WAIT(); + outb(value, PIB(1)); HTXF_WAIT(); + + return RET_OK; +} //}}} + +static int write_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char value) +{ //{{{ + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + if( __write_reg_fxo(wc, port, reg, value) ){ + spin_unlock_irqrestore(&wc->lock, flags); + return RET_OK; + } + spin_unlock_irqrestore(&wc->lock, flags); + cardcrit(wc->boardnum, "FXO port %d, reg %d, write(%d) failed!", port, reg, value); + return RET_FAIL; +} //}}} + +static int __write_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char value) +{ //{{{ + unsigned char portadr = fxs_port_lookup[port]; + int i; + + outb(0x12, PIB(1) ); HTXF_WAIT(); + outb(0x3, PIB(1)); HTXF_WAIT(); + outb(portadr, PIB(1)); HTXF_WAIT(); + outb(reg, PIB(1)); HTXF_WAIT(); + outb(value, PIB(1)); HTXF_WAIT(); + + return RET_OK; +} //}}} + +static int write_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char value) +{ //{{{ + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + if( __write_reg_fxs(wc, port, reg, value) ){ + spin_unlock_irqrestore(&wc->lock, flags); + return RET_OK; + } + spin_unlock_irqrestore(&wc->lock, flags); + cardcrit(wc->boardnum, "FXS port %d, reg %d, write(%d) failed!", port, reg, value); + return RET_FAIL; +} //}}} + +static int __wait_indreg_fxs(struct openpci *wc, int port) +{ //{{{ + unsigned char value; + int count = 100; + + while (--count) + { + if( __read_reg_fxs(wc, port, I_STATUS, &value) ){ + if( value == 0 ) + return RET_OK; + } else { + cardcrit(wc->boardnum, + "failed to read port %d PS_IND_ADDR_ST, retrying...", + port); + } + udelay(5); + } + cardcrit(wc->boardnum, "Failed to wait for indirect reg write to port %d", port); + return RET_FAIL; +} //}}} + +static int write_indreg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned short value) +{ //{{{ + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + if( __wait_indreg_fxs(wc, port) + && __write_reg_fxs(wc, port, IDA_LO, value & 0xff) + && __write_reg_fxs(wc, port, IDA_HI, (value & 0xff00)>>8) + && __write_reg_fxs(wc, port, IAA, reg) + && __wait_indreg_fxs(wc, port) ) + { + spin_unlock_irqrestore(&wc->lock, flags); + return RET_OK; + } + spin_unlock_irqrestore(&wc->lock, flags); + cardcrit(wc->boardnum, "FXS indreg %d write failed on port %d", reg, port); + return RET_FAIL; +} //}}} + +static int read_indreg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned short *value) +{ //{{{ + unsigned long flags; + unsigned char lo, hi; + + spin_lock_irqsave(&wc->lock, flags); + if( __wait_indreg_fxs(wc, port) + && __write_reg_fxs(wc, port, IAA, reg) + && __wait_indreg_fxs(wc, port) + && __read_reg_fxs(wc, port, IDA_LO, &lo) + && __read_reg_fxs(wc, port, IDA_HI, &hi) ) + { + *value = lo | hi << 8; + spin_unlock_irqrestore(&wc->lock, flags); + return RET_OK; + } + spin_unlock_irqrestore(&wc->lock, flags); + return RET_FAIL; +} //}}} + +static void start_dma(struct openpci *wc) +{ //{{{ + outb(0x0f, TJ_CNTL); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + outb(0x01, TJ_CNTL); + outb(0x01, TJ_OPER); +} //}}} + +static void restart_dma(struct openpci *wc) +{ //{{{ + /* Reset Master and TDM */ + outb(0x01, TJ_CNTL); + outb(0x01, TJ_OPER); +} //}}} + +/* You must hold the card spinlock to call this function */ +static int __ping_arm(struct openpci *wc) +{ //{{{ + int i; + int pong=0; + + while(pong != 0x02){ + outb(0x02, PIB(1)); HTXF_WAIT(); + HRXF_WAIT(); pong = inb(PIB(1)); + dbginfo(wc->boardnum, "ping_arm returned %x", pong); + } + while(pong == 0x02){ + // Poke no-ops into the arm while it is still returning data, + // if 500 usec elapses with no further response from it then + // the message queue is should be completely cleared. + outb(0x00, PIB(1)); HTXF_WAIT(); + i = 100; + while( !HRXF_READY && --i ) udelay(5); + if( i == 0 ) break; + pong = inb(PIB(1)); + dbginfo(wc->boardnum, "ping_arm returned %x.", pong); + } + return RET_OK; +} //}}} + +static void arm_event(struct openpci *wc, char *msg) +{ //{{{ + int port = msg[0]; + + switch(msg[1]){ + case DSP_LOOP_OFFHOOK: + zt_hooksig(&wc->chans[port], ZT_RXSIG_OFFHOOK); + dbginfo(wc->boardnum, "Port %d Loop OffHook", port); + break; + + case DSP_LOOP_ONHOOK: + zt_hooksig(&wc->chans[port], ZT_RXSIG_ONHOOK); + dbginfo(wc->boardnum, "Port %d Loop OnHook", port); + break; + + case DSP_LOOP_POLARITY: + zt_qevent_lock(&wc->chans[port], ZT_EVENT_POLARITY); + dbginfo(wc->boardnum, "Port %d Loop Polarity", port); + break; + + case DSP_CODEC_RING: + zt_hooksig(&wc->chans[port], ZT_RXSIG_RING); + dbginfo(wc->boardnum, "Port %d Ring On", port); + break; + + case DSP_RING_OFF: + zt_hooksig(&wc->chans[port], ZT_RXSIG_OFFHOOK); + dbginfo(wc->boardnum, "Port %d Ring Off", port); + break; + + case DSP_CODEC_HKOFF: + zt_hooksig(&wc->chans[port], ZT_RXSIG_OFFHOOK); + dbginfo(wc->boardnum, "Port %d Station OffHook", port); + if (reversepolarity) + wc->mod[port].fxs.idletxhookstate = 5; + else + wc->mod[port].fxs.idletxhookstate = 1; + break; + + case DSP_CODEC_HKON: + zt_hooksig(&wc->chans[port], ZT_RXSIG_ONHOOK); + dbginfo(wc->boardnum, "Port %d Station OnHook", port); + if (reversepolarity) + wc->mod[port].fxs.idletxhookstate = 6; + else + wc->mod[port].fxs.idletxhookstate = 2; + break; + + case DSP_CODEC_FLASH: + zt_qevent_lock(&wc->chans[port], ZT_EVENT_WINKFLASH); + dbginfo(wc->boardnum, "Port %d Station Flash", port); + break; + + case DSP_DROP: + case DSP_LOOP_NOBATT: + break; + + //XXX What to do to recover from these? + case DSP_PROSLIC_SANITY: + dbginfo(wc->boardnum, "Port %d ProSlic has gone insane!", port); + break; + + case DSP_PROSLIC_PWR_ALARM: + { + char errbuf[32] = " Unknown", *p = errbuf; + int i = 49; + + msg[2] >>= 2; + for(; i < 55; ++i, msg[2] >>= 1 ) + if(msg[2] & 1){ *(++p)='Q'; *(++p)=i; *(++p)=','; } + if( p != errbuf ) *p = '\0'; + cardcrit(wc->boardnum,"%d: ProSlic power ALARM:%s",msg[0],errbuf); + //write_reg_fxs(wc, port, 64, wc->mod[port].fxs.lasttxhook ); + return; + } + + case DSP_VDAA_ISO_FRAME_E: + dbginfo(wc->boardnum, "Port %d VDAA has lost ISO-Cap frame lock", port); + break; + + default: + cardwarn(wc->boardnum, "Unknown message from Arm[%d] for port %d", + msg[1], port); + break; + } +} //}}} + +/* You must hold the card spinlock to call this function */ +static inline int __read_arm_byte( struct openpci *wc, unsigned char *msg ) +{ //{{{ + int i; + + HRXF_WAIT(); *msg = inb(PIB(1)); + return RET_OK; +} //}}} + +static inline int read_arm_msg( struct openpci *wc, unsigned char *msg ) +{ //{{{ + unsigned long flags; + int i, d, count; + int ret = RET_OK; + + spin_lock_irqsave(&wc->lock, flags); + outb(0x08, PIB(1)); HTXF_WAIT_LOCKED(); + //XXX Do we need to clear the interrupt flag even if this fails? + HRXF_WAIT_LOCKED(); count = inb(PIB(1)); + if( count == 0 ){ + ret = RET_FAIL; + } else if( count < 3 || count > 4 ){ + cardcrit(wc->boardnum, "BOGUS arm message size %d, flushing queue", count); + // NB: This may take a while (up to 500usec or more) to complete + // and we are in the isr at present when this is called, so + // we may miss an interrupt or two while this is done in the + // bottom half, but we are already in trouble, so... + d = debug; debug = 5; __ping_arm( wc ); debug = d; + ret = RET_FAIL; + } else while( --count ){ + if( ! __read_arm_byte(wc, msg) ){ + cardcrit(wc->boardnum, + "Failed to read arm message %d more bytes expected", + count); + ret = RET_FAIL; + break; + } + ++msg; + } + outb(0x09, PIB(1)); HTXF_WAIT_LOCKED(); + spin_unlock_irqrestore(&wc->lock, flags); + return ret; +} //}}} + +static void openpci_arm_work( void *cardptr ) +{ //{{{ + struct openpci *wc = (struct openpci*)cardptr; + unsigned char armmsg[4]; + + if( read_arm_msg(wc, armmsg) ) arm_event(wc, armmsg); +} //}}} + + +static inline void openpci_write(struct openpci *wc, unsigned char flags) +{ //{{{ + int x,y; + volatile unsigned int *writechunk; + + if (flags & 0x01) + writechunk = wc->writechunk; + else if (flags & 0x02) + writechunk = wc->writechunk + ZT_CHUNKSIZE*2; + else { + cardcrit(wc->boardnum, "bad write interrupt flags %x, at %x", + flags, inb(TJ_DMAWC) ); + return; + } + /* get data */ + zt_transmit(&wc->span); + for (y=0,x=0;xporttype[4]) + writechunk[y] |= (wc->chans[4].writechunk[x] << 24); + else + writechunk[y] |= (0x01 << 24); + if (wc->porttype[5]) + writechunk[y] |= (wc->chans[5].writechunk[x] << 16); + if (wc->porttype[6]) + writechunk[y] |= (wc->chans[6].writechunk[x] << 8); + if (wc->porttype[7]) + writechunk[y] |= (wc->chans[7].writechunk[x]); + ++y; + + /* transmit first 4 ports */ + writechunk[y]=0x01000000; + /* Make sure first port doesnt equal 0x00 */ + if (wc->porttype[0]){ + if (wc->chans[0].writechunk[x] == 0) + writechunk[y] |= (0x01 << 24); + else + writechunk[y] |= (wc->chans[0].writechunk[x] << 24); + } + //else writechunk[y] |= (0x00 << 24); + if (wc->porttype[1]) + writechunk[y] |= (wc->chans[1].writechunk[x] << 16); + if (wc->porttype[2]) + writechunk[y] |= (wc->chans[2].writechunk[x] << 8); + if (wc->porttype[3]) + writechunk[y] |= (wc->chans[3].writechunk[x]); + ++y; +#endif + } +} //}}} + +static inline void openpci_read(struct openpci *wc, unsigned char flags) +{ //{{{ + int x,y; + volatile unsigned int *readchunk; + + if (flags & 0x08) + readchunk = wc->readchunk + ZT_CHUNKSIZE*2; + else if (flags & 0x04) + readchunk = wc->readchunk; + else { + cardcrit(wc->boardnum, "bad read interrupt flags %x, at %x", + flags, inb(TJ_DMARC)); + return; + } + + for (y=0,x=0;xporttype[0]) + wc->chans[0].readchunk[x] = (readchunk[y] >> 24) & 0xff; + if (wc->porttype[1]) + wc->chans[1].readchunk[x] = (readchunk[y] >> 16) & 0xff; + if (wc->porttype[2]) + wc->chans[2].readchunk[x] = (readchunk[y] >> 8) & 0xff; + if (wc->porttype[3]) + wc->chans[3].readchunk[x] = (readchunk[y]) & 0xff; + ++y; + /* Receive second 4 ports */ + if (wc->porttype[4]) + wc->chans[4].readchunk[x] = (readchunk[y] >> 24) & 0xff; + if (wc->porttype[5]) + wc->chans[5].readchunk[x] = (readchunk[y] >> 16) & 0xff; + if (wc->porttype[6]) + wc->chans[6].readchunk[x] = (readchunk[y] >> 8) & 0xff; + if (wc->porttype[7]) + wc->chans[7].readchunk[x] = (readchunk[y]) & 0xff; + ++y; +#endif + } + /* XXX We're wasting 8 taps. We should get closer :( */ + for (x = 0; x < MAX_PORTS; x++) { + if (wc->porttype[x]) + zt_ec_chunk(&wc->chans[x], wc->chans[x].readchunk, wc->chans[x].writechunk); + } + zt_receive(&wc->span); +} //}}} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +static irqreturn_t openpci_isr(int irq, void *dev_id, struct pt_regs *regs) +#else +static irqreturn_t openpci_isr(int irq, void *dev_id) +#endif +{ //{{{ + struct openpci *wc = dev_id; + unsigned long flags; + unsigned char status; + + spin_lock_irqsave(&wc->lock, flags); + status = inb(TJ_INTSTAT); + outb(status, TJ_INTSTAT); + + if (!status) { + if(inb(TJ_AUXR) & 0x02) { + spin_unlock_irqrestore(&wc->lock, flags); + return IRQ_NONE; + } + spin_unlock_irqrestore(&wc->lock, flags); + openpci_arm_work(wc); + return IRQ_HANDLED; + } + if (status & 0x10){ + /* PCI Master abort */ + cardcrit(wc->boardnum, "PCI Master Abort."); + /* Stop DMA, wait for watchdog */ + outb(0x00, TJ_OPER); + spin_unlock_irqrestore(&wc->lock, flags); + return IRQ_HANDLED; + } + spin_unlock_irqrestore(&wc->lock, flags); + + if (status & 0x20){ + /* PCI Target abort */ + cardcrit(wc->boardnum, "PCI Target Abort."); + return IRQ_HANDLED; + } + if (status & 0x03){ + openpci_write(wc, status); + } + if (status & 0x0c){ + #ifdef DEBUG_LOOP_VOLTAGE + //{{{ + static int counter[MAX_CARDS]; + int card = wc->boardnum; + int port = ++counter[card] & 0x07; + int ignore = counter[card] & 0xf0; + + if( ! ignore && (voltmeter & ((1 << (card * 8)) << port)) ) { + unsigned char lv; + if( wc->porttype[port] == VT_PORT_VDAA && read_reg_fxo(wc, port, 29, &lv) ) + cardinfo(wc->boardnum, "Port %d loop voltage %d", + port, lv < 128 ? lv : lv - 256); + } + //}}} + #endif + openpci_read(wc, status); + } + + return IRQ_HANDLED; +} //}}} + +static int openpci_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) +{ //{{{ + struct wctdm_stats stats; + struct wctdm_regs regs; + struct wctdm_regop regop; + struct wctdm_echo_coefs echoregs; + struct openpci *wc = chan->pvt; + int port = chan->chanpos - 1; + int x; + + switch (cmd) { + case ZT_ONHOOKTRANSFER: + if (wc->porttype[port] != VT_PORT_PROSLIC) + return -EINVAL; + if (get_user(x, (int *)data)) + return -EFAULT; + wc->mod[port].fxs.ohttimer = x << 3; + if (reversepolarity) + wc->mod[port].fxs.idletxhookstate = 0x6; /* OHT mode when idle */ + else + wc->mod[port].fxs.idletxhookstate = 0x2; + switch(wc->mod[port].fxs.lasttxhook) { + case 0x1: + case 0x5: + if (reversepolarity) + wc->mod[port].fxs.lasttxhook = 0x6; + else + wc->mod[port].fxs.lasttxhook = 0x2; + if( ! write_reg_fxs(wc, port, 64, wc->mod[port].fxs.lasttxhook) ) + return -EIO; + } + break; + case ZT_SETPOLARITY: + if (get_user(x, (int *)data)) + return -EFAULT; + if (wc->porttype[port] != VT_PORT_PROSLIC) + return -EINVAL; + /* Can't change polarity while ringing or when open */ + if ((wc->mod[port].fxs.lasttxhook == 0x04) || + (wc->mod[port].fxs.lasttxhook == 0x00)) + return -EINVAL; + + if ((x && !reversepolarity) || (!x && reversepolarity)) + wc->mod[port].fxs.lasttxhook |= 0x04; + else + wc->mod[port].fxs.lasttxhook &= ~0x04; + if( ! write_reg_fxs(wc, port, 64, wc->mod[port].fxs.lasttxhook) ) + return -EIO; + break; + case WCTDM_GET_STATS: + if (wc->porttype[port] == VT_PORT_PROSLIC) { + unsigned char linevolt; + if( read_reg_fxs(wc, port, 80, &linevolt) ) + stats.tipvolt = linevolt * -376; + else + return -EIO; + if( read_reg_fxs(wc, port, 81, &linevolt) ) + stats.ringvolt = linevolt * -376; + else + return -EIO; + if( read_reg_fxs(wc, port, 82, &linevolt) ) + stats.batvolt = linevolt * -376; + else + return -EIO; + } else if (wc->porttype[port] == VT_PORT_VDAA) { + unsigned char linevolt; + if( read_reg_fxo(wc, port, 29, &linevolt) ) + stats.tipvolt = stats.ringvolt = stats.batvolt = linevolt * 1000; + else + return -EIO; + } else + return -EINVAL; + if (copy_to_user((struct wctdm_stats *)data, &stats, sizeof(stats))) + return -EFAULT; + break; + case WCTDM_GET_REGS: + if (wc->porttype[port] == VT_PORT_PROSLIC) { + for (x=0;xporttype[port] != VT_PORT_PROSLIC) + return -EINVAL; + printk("Setting indirect %d to 0x%04x on %d\n", + regop.reg, regop.val, chan->chanpos); + if( ! write_indreg_fxs(wc, port, regop.reg, regop.val) ) + return -EIO; + } else { + regop.val &= 0xff; + printk("Setting direct %d to %04x on %d\n", + regop.reg, regop.val, chan->chanpos); + if (wc->porttype[port] == VT_PORT_PROSLIC) { + if( ! write_reg_fxs(wc, port, regop.reg, regop.val) ) + return -EIO; + } else { + if( ! write_reg_fxo(wc, port, regop.reg, regop.val) ) + return -EIO; + } + } + break; + case WCTDM_SET_ECHOTUNE: + cardinfo(wc->boardnum, "Setting echo registers"); + if (copy_from_user(&echoregs, (struct wctdm_echo_coefs*)data, sizeof(echoregs))) + return -EFAULT; + + if (wc->porttype[port] == VT_PORT_VDAA) { + /* Set the ACIM and digital echo canceller registers */ + if( ! write_reg_fxo(wc, port, 30, echoregs.acim) + || ! write_reg_fxo(wc, port, 45, echoregs.coef1) + || ! write_reg_fxo(wc, port, 46, echoregs.coef2) + || ! write_reg_fxo(wc, port, 47, echoregs.coef3) + || ! write_reg_fxo(wc, port, 48, echoregs.coef4) + || ! write_reg_fxo(wc, port, 49, echoregs.coef5) + || ! write_reg_fxo(wc, port, 50, echoregs.coef6) + || ! write_reg_fxo(wc, port, 51, echoregs.coef7) + || ! write_reg_fxo(wc, port, 52, echoregs.coef8) ) + { + cardcrit(wc->boardnum, "Failed to set echo registers"); + return -EIO; + } + break; + } else { + return -EINVAL; + } + break; + default: + return -ENOTTY; + } + return 0; +} //}}} + +static int openpci_open(struct zt_chan *chan) +{ + struct openpci *wc = chan->pvt; + if( ! wc->porttype[chan->chanpos-1] ) + return -ENODEV; + + //XXX This is WRONG and can prang in a race. We must pass THIS_MODULE + // as the owner of the span that holds the pointer to this function, + // then bump the refcount in the zaptel code _BEFORE_ the potentially + // fatal call to an invalid pointer is made. + //if( wc->dead ) return -ENODEV; + //wc->usecount++; + try_module_get(THIS_MODULE); //XXX + + return 0; +} + +static int openpci_watchdog(struct zt_span *span, int event) +{ + info("TDM: Restarting DMA"); + restart_dma(span->pvt); + return 0; +} + +static int openpci_close(struct zt_chan *chan) +{ + struct openpci *wc = chan->pvt; + int port = chan->chanpos - 1; + + //XXX wc->usecount--; + //XXX This is WRONG and can prang in a race. We must pass THIS_MODULE + // as the owner of the span that holds the pointer to this function, + // then bump the refcount in the zaptel code _BEFORE_ the potentially + // fatal call to an invalid pointer is made. + module_put(THIS_MODULE); + if (wc->porttype[port] == VT_PORT_PROSLIC) { + if (reversepolarity) + wc->mod[port].fxs.idletxhookstate = 5; + else + wc->mod[port].fxs.idletxhookstate = 1; + } + /* If we're dead, release us now */ + //XXX if (!wc->usecount && wc->dead) openpci_release(wc); + + return 0; +} + +static int openpci_hooksig(struct zt_chan *chan, zt_txsig_t txsig) +{ //{{{ + struct openpci *wc = chan->pvt; + int port = chan->chanpos - 1; + int new_hk_state; + + dbginfo(wc->boardnum, "Setting %s port %d hook state %s", + wc->porttype[port] == VT_PORT_VDAA ? "FXO" : "FXS", + port, + txsig == 0 ? "ONHOOK" : + txsig == 1 ? "OFFHOOK" : + txsig == 2 ? "START" : + txsig == 3 ? "KEWL" : "UNKNOWN" ); + + switch(wc->porttype[port]) { + case VT_PORT_VDAA: + switch(txsig) { + case ZT_TXSIG_START: + case ZT_TXSIG_OFFHOOK: + if( write_reg_fxo(wc, port, 5, 0x9) + && write_reg_fxo(wc, port, 0x20, 0x0) ) + wc->mod[port].fxo.offhook = 1; + else + cardcrit(wc->boardnum, "Failed set fxo off-hook"); + break; + + case ZT_TXSIG_ONHOOK: + if( write_reg_fxo(wc, port, 5, 0x8) + && write_reg_fxo(wc, port, 0x20, 0x3) ) + wc->mod[port].fxo.offhook = 0; + else + cardcrit(wc->boardnum, "Failed set fxo on-hook"); + break; + + default: + cardcrit(wc->boardnum, + "Can't set FXO port %d tx state to %d", + port, txsig); + } + break; + + case VT_PORT_PROSLIC: + new_hk_state = wc->mod[port].fxs.lasttxhook; + switch(txsig) { + case ZT_TXSIG_ONHOOK: + switch(chan->sig) { + case ZT_SIG_EM: + case ZT_SIG_FXOKS: + case ZT_SIG_FXOLS: + new_hk_state = wc->mod[port].fxs.idletxhookstate; + break; + case ZT_SIG_FXOGS: + new_hk_state = 3; + break; + } + break; + + case ZT_TXSIG_OFFHOOK: + switch(chan->sig) { + case ZT_SIG_EM: + new_hk_state = 5; + break; + default: + new_hk_state = wc->mod[port].fxs.idletxhookstate; + break; + } + break; + + case ZT_TXSIG_START: + new_hk_state = 4; + break; + + case ZT_TXSIG_KEWL: + new_hk_state = 0; + break; + + default: + cardinfo(wc->boardnum, + "Can't set FXS port %d tx state to %d", + port, txsig); + } + dbginfo(wc->boardnum, "%s port %d hook state old %d, new %d", + wc->porttype[port] == VT_PORT_VDAA ? "FXO" : "FXS", + port, wc->mod[port].fxs.lasttxhook, new_hk_state ); + + if (new_hk_state != wc->mod[port].fxs.lasttxhook){ + if( write_reg_fxs(wc, port, 64, new_hk_state) ) + wc->mod[port].fxs.lasttxhook = new_hk_state; + else + cardcrit(wc->boardnum, + "Failed to set port %d fxs hookstate from %d to %d", + port, wc->mod[port].fxs.lasttxhook, new_hk_state); + } + break; + + default: + cardcrit(wc->boardnum, + "Unknown module type %d in openpci_hooksig", + wc->porttype[port] ); + } + return 0; +} //}}} + +static int span_initialize(struct openpci *wc) +{ //{{{ + int x; + + //XXX Set a THIS_MODULE as the owner of the span... + /* Zapata stuff */ + sprintf(wc->span.name, "WCTDM/%d", wc->boardnum); + sprintf(wc->span.desc, "%s Board %d", wc->variety, wc->boardnum + 1); + for (x = 0; x < MAX_PORTS; x++) { + sprintf(wc->chans[x].name, "WCTDM/%d/%d", wc->boardnum, x); + wc->chans[x].sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS + | ZT_SIG_SF | ZT_SIG_EM | ZT_SIG_CLEAR; + wc->chans[x].sigcap |= ZT_SIG_FXSKS | ZT_SIG_FXSLS | ZT_SIG_SF | ZT_SIG_CLEAR; + wc->chans[x].chanpos = x+1; + wc->chans[x].pvt = wc; + } + wc->span.deflaw = ZT_LAW_MULAW; + wc->span.chans = wc->chans; + wc->span.channels = MAX_PORTS; + wc->span.hooksig = openpci_hooksig; + wc->span.open = openpci_open; + wc->span.close = openpci_close; + wc->span.flags = ZT_FLAG_RBS; + wc->span.ioctl = openpci_ioctl; + wc->span.watchdog = openpci_watchdog; + init_waitqueue_head(&wc->span.maintq); + + wc->span.pvt = wc; + if (zt_register(&wc->span, 0)) { + cardcrit(wc->boardnum, "Unable to register span with zaptel"); + return RET_FAIL; + } + return RET_OK; +} //}}} + +static int get_port_type(struct openpci *wc, int port) +{ //{{{ + int i, type; + unsigned long flags; + + spin_lock_irqsave(&wc->lock, flags); + outb(0x20, PIB(1)); HTXF_WAIT_LOCKED_RET(VT_PORT_EMPTY); + outb(port, PIB(1)); HTXF_WAIT_LOCKED_RET(VT_PORT_EMPTY); + HRXF_WAIT_LOCKED_RET(VT_PORT_EMPTY); type = inb(PIB(1)); + spin_unlock_irqrestore(&wc->lock, flags); + + return type; +} //}}} + +static int check_ports(struct openpci *wc) +{ //{{{ + int i = 0; + + wc->portcount = 0; + for(; i < MAX_PORTS; ++i ){ + wc->porttype[i] = get_port_type(wc, i); + dbginfo(wc->boardnum,"%d: %s", i, porttype(wc,i)); + + switch( wc->porttype[i] ) { + case VT_PORT_PROSLIC: + /* By default, don't send on hook */ + if (reversepolarity) + wc->mod[i].fxs.idletxhookstate = 5; + else + wc->mod[i].fxs.idletxhookstate = 1; + + case VT_PORT_VDAA: + ++wc->portcount; + } + } + // we 'succeed' if any ports were discovered. + return wc->portcount ? RET_OK : RET_FAIL; +} //}}} + +static int configure_vdaa_country(struct openpci *wc, int port, char *name) +{ //{{{ + unsigned char value; + int i; + + for (i=0; i < sizeof(fxo_modes)/sizeof(struct fxo_mode); ++i){ + if(!strcmp(fxo_modes[i].name, name)){ + dbginfo(wc->boardnum, "%d: Setting country to %s", port, name); + goto part2; + } + } + i = 3; + cardinfo(wc->boardnum, "Using default country %s", fxo_modes[i].name); + + part2: + value = (fxo_modes[i].ohs << 6); + value |= (fxo_modes[i].rz << 1); + value |= (fxo_modes[i].rt << 0); + if( ! write_reg_fxo(wc, port, 16, value) ) goto hell; + + /* DC Termination Control - Register 26 */ + value = (fxo_modes[i].dcv << 6); + value |= (fxo_modes[i].mini << 4); + value |= (fxo_modes[i].ilim << 1); + if( ! write_reg_fxo(wc, port, 26, value) ) goto hell; + + /* AC Termination Control - Register 30 */ + value = (fxo_modes[i].acim << 0); + if( ! write_reg_fxo(wc, port, 30, value) ) goto hell; + + /* DAA Control 5 - Register 31 */ + msleep(1); + if( ! read_reg_fxo(wc, port, 31, &value) ) goto hell; + + value = (value & 0xf7) | (fxo_modes[i].ohs2 << 3); + value = value | 0x02; + if( ! write_reg_fxo(wc, port, 31, value) ) goto hell; + + return RET_OK; + + hell: + cardcrit(wc->boardnum, "port %d failed configure vdaa country", port); + return RET_FAIL; +} //}}} + +// Do not call this from an interrupt context, it may sleep. +static void configure_vdaa_port(struct openpci *wc, int port) +{ //{{{ + /* Set Country - default to Australia */ + if( configure_vdaa_country(wc, port, country) ) + ++wc->portcount; + else { + cardcrit(wc->boardnum, "FAILED to configure vdaa port %d. Disabled.", port); + wc->porttype[port] = VT_PORT_EMPTY; + } +} //}}} + +static int configure_proslic_country(struct openpci *wc, int port, const char *name) +{ //{{{ + int i; + + for(i=0; i < sizeof(ps_country_regs)/sizeof(struct ps_country_reg); ++i) { + if(!strcmp(ps_country_regs[i].country, name)){ + dbginfo(wc->boardnum, "%d: Setting country to %s", port, name); + goto part2; + } + } + return -EINVAL; + + part2: + + if( ! write_reg_fxs(wc, port, 10, ps_country_regs[i].value) ){ + cardcrit(wc->boardnum,"%d: failed to write PS_IMPEDANCE", port); + return -EIO; + } + return 0; +} //}}} + +// Do not call this from an interrupt context, it may sleep. +static void configure_proslic_port(struct openpci *wc, int port) +{ //{{{ + /* Set Country - default to Australia */ + switch( configure_proslic_country(wc, port, country) ){ + case 0: + break; + + case -EINVAL: + cardwarn(wc->boardnum,"%d: Country '%s' unknown, using default", port, country); + if( configure_proslic_country(wc, port, DEFAULT_COUNTRY) == 0 ) + goto hell; + + default: + goto hell; + } + + ++wc->portcount; + return; + + hell: + cardcrit(wc->boardnum, "FAILED to configure proslic port %d. Disabled.", port); + wc->porttype[port] = VT_PORT_EMPTY; +} //}}} + +// Do not call this from an interrupt context, it may (indirectly) sleep. +static int configure_ports(struct openpci *wc) +{ //{{{ + unsigned long flags; + int i; + + wc->portcount = 0; + for(i=0; i < MAX_PORTS; ++i){ + switch (wc->porttype[i]){ + case VT_PORT_VDAA: configure_vdaa_port(wc,i); break; + case VT_PORT_PROSLIC: configure_proslic_port(wc,i); break; + } + } + + spin_lock_irqsave(&wc->lock, flags); + outb(0x2c, PIB(1)); HTXF_WAIT_LOCKED(); + outb(0xff, PIB(1)); HTXF_WAIT_LOCKED(); + spin_unlock_irqrestore(&wc->lock, flags); + + // otherwise we 'succeed' if any ports were configured successfully. + return wc->portcount ? RET_OK : RET_FAIL; +} //}}} + +static int __get_arm_id(struct openpci *wc, int field, char *value) +{ //{{{ + int i; + int x=0; + int count=0; + + outb(0x01, PIB(1)); HTXF_WAIT(); + outb(field, PIB(1)); HTXF_WAIT(); + HRXF_WAIT(); count = inb(PIB(1)); + if (count > ID_DATA_MAXSIZE){ + cardcrit(wc->boardnum, "Too many bytes of id(%d) data %d/%d", + field, count, ID_DATA_MAXSIZE); + return RET_FAIL; + } + //cardinfo(wc->boardnum, "get_arm_id(%d): byte count %d",field,count); + for(; x < count; ++x){ + HRXF_WAIT(); *value = inb(PIB(1)); + //cardinfo(wc->boardnum, "get_arm_id(%d): byte %d => 0x%02x",field,x,tmp); + ++value; + } + return RET_OK; +} //}}} + +static void enable_interrupts(struct openpci *wc) +{ //{{{ + outb(0x3f, TJ_MASK0); + outb(0x02, TJ_MASK1); +} //}}} + +static void disable_interrupts(struct openpci *wc) +{ //{{{ + outb(0x00, TJ_MASK0); + outb(0x00, TJ_MASK1); +} //}}} + +// Do not call this from an interrupt context, it may sleep. +static int check_arm(struct openpci *wc) +{ //{{{ + char model[ID_DATA_MAXSIZE+1] = { 0 }; + char date[ID_DATA_MAXSIZE+1] = { 0 }; + unsigned long flags; + int i=0; + int tmp=0; + + spin_lock_irqsave(&wc->lock, flags); + while ((tmp != 0x88)&&(++i<100)){ + outb(0x88, PIB(0)); + msleep(1); + tmp = inb(PIB(1)); + } + if (i>=1000) goto limbo; + dbginfo(wc->boardnum, "Arm responded on attempt %d",i); + + // Flush out the queue if we sent several pings before a response. + if(i>1) __ping_arm(wc); + + if( ! __get_arm_id(wc, 0, model) ) goto hell; + sscanf(model, "OpenPCI8.%02d", &(wc->firmware)); + cardinfo(wc->boardnum, " model: %s", model); + + if( ! __get_arm_id(wc, 1, date) ) goto hell; + cardinfo(wc->boardnum, " date: %s", date); + + if( ! __get_arm_id(wc, 2, wc->serial) ) goto hell; + cardinfo(wc->boardnum, " serial: %s", wc->serial); + + spin_unlock_irqrestore(&wc->lock, flags); + return RET_OK; + + hell: + spin_unlock_irqrestore(&wc->lock, flags); + cardwarn(wc->boardnum, "Found ARM processor, dumb firmware."); + return RET_OK; + + limbo: + spin_unlock_irqrestore(&wc->lock, flags); + return RET_FAIL; +} //}}} + +static int arm_monitor(struct openpci *wc, int on) +{ //{{{ + int i; + outb( on ? 0x06 : 0x07, PIB(1) ); HTXF_WAIT(); + return RET_OK; +} //}}} + +static int __devinit openpci_probe_board(struct pci_dev *pdev, const struct pci_device_id *ent) +{ //{{{ + struct openpci *wc; + int boardnum = 0; + int failret = -ENOMEM; + int tmp = 0; + int i; + unsigned long flags; + + if( ent->driver_data != (kernel_ulong_t)&wcopenpci ) + { + info("Probe of non-OpenPCI card, ignoring."); + return -EINVAL; + } + wc = kzalloc(sizeof(struct openpci), GFP_KERNEL); + if (!wc){ + return -ENOMEM; + } + + mutex_lock(&cards_mutex); + for (; boardnum < MAX_CARDS && cards[boardnum]; ++boardnum); + if (boardnum >= MAX_CARDS){ + crit("Too many OpenPCI cards(%d), max is %d.", boardnum, MAX_CARDS); + mutex_unlock(&cards_mutex); + goto hell; + } + cards[boardnum] = wc; + mutex_unlock(&cards_mutex); + + spin_lock_init(&wc->lock); + pci_set_drvdata(pdev, wc); + + wc->boardnum = boardnum; + wc->dev = pdev; + wc->variety = wcopenpci; + + cardinfo(boardnum, "Initialising card"); + if (pci_enable_device(pdev)) { + failret = -EIO; + goto hell_2; + } + wc->ioaddr = pci_resource_start(pdev, 0); + if( ! request_region(wc->ioaddr, 0xff, NAME) ){ + cardcrit(boardnum, "Failed to lock IO region, another driver already using it"); + failret = -EBUSY; + goto hell_2; + } + + spin_lock_irqsave(&wc->lock, flags); + outb(0xff, TJ_AUXD); /* Set up TJ to access the ARM */ + outb(0x78, TJ_AUXC); /* Set up for Jtag */ + outb(0x00, TJ_CNTL); /* pull ERST low */ + spin_unlock_irqrestore(&wc->lock, flags); + msleep(1); /* Wait a bit */ + + dbginfo(boardnum,"Starting ARM"); + spin_lock_irqsave(&wc->lock, flags); + outb(0x01, TJ_CNTL); /* pull ERST high again */ + spin_unlock_irqrestore(&wc->lock, flags); + msleep(100); /* Give it all a chance to boot */ + + if( ! check_arm(wc) ){ + cardcrit(boardnum, "Couldnt find ARM processor"); + failret = -EIO; + goto hell_3; + } + if( wc->firmware < 11 ){ + cardcrit(boardnum, + "Firmware version %d not supported by this driver", + wc->firmware); + cardcrit(boardnum, " contact Voicetronix to have it updated"); + failret = -ENODEV; + goto hell_3; + } + if( ! check_ports(wc) ){ + cardcrit(boardnum, "Couldnt find ports!"); + failret = -EIO; + goto hell_3; + } + + wc->writechunk = pci_alloc_consistent(pdev, VT_PCIDMA_BLOCKSIZE, &wc->writedma); + if (!wc->writechunk) { + cardcrit(boardnum, "Couldnt get DMA memory."); + goto hell_3; + } + wc->readchunk = wc->writechunk + ZT_MAX_CHUNKSIZE * (MAX_PORTS*2 / sizeof(int)); + wc->readdma = wc->writedma + ZT_MAX_CHUNKSIZE * (MAX_PORTS*2); + + memset((void*)wc->writechunk,0,VT_PCIDMA_BLOCKSIZE); + + spin_lock_irqsave(&wc->lock, flags); + outb(0xc1, TJ_SERCTL); + outb(0x0, TJ_FSCDELAY); + + outl(wc->writedma, TJ_DMAWS); + outl(wc->writedma + VT_PCIDMA_MIDDLE, TJ_DMAWI); + outl(wc->writedma + VT_PCIDMA_END, TJ_DMAWE); + outl(wc->readdma, TJ_DMARS); + outl(wc->readdma + VT_PCIDMA_MIDDLE, TJ_DMARI); + outl(wc->readdma + VT_PCIDMA_END, TJ_DMARE); + + /* Clear interrupts */ + outb(0xff, TJ_INTSTAT); + spin_unlock_irqrestore(&wc->lock, flags); + + if( ! arm_monitor(wc, 1) ){ + cardcrit(boardnum, "failed to start arm monitoring"); + failret = -EIO; + goto hell_4; + } + msleep(1000); + + i = 0; + while(tmp != 0x88 && ++i < 1000) { + outb(0x88, PIB(0)); + msleep(250); + tmp = inb(PIB(1)); + } + if(i>=1000) { + cardcrit(boardnum, "FAILED to initialise board"); + goto hell_4; + } + + if( ! check_ports(wc) ) { + cardcrit(boardnum, "FAILED to initialise ports"); + failret = -EIO; + goto hell_4; + } + if( ! configure_ports(wc) ){ + cardcrit(boardnum, "Failed to configure ports."); + failret = -EIO; + goto hell_4; + } + cardinfo(wc->boardnum, "have %d configured ports", wc->portcount); + + if( ! span_initialize(wc) ) { + cardcrit(boardnum, "Failed to register with zaptel driver"); + failret = -EFAULT; + goto hell_4; + } + + /* Finalize signalling */ + for (i=0; i < MAX_PORTS; ++i) { + if (wc->porttype[i] == VT_PORT_VDAA) + wc->chans[i].sigcap = ZT_SIG_FXSKS | ZT_SIG_FXSLS + | ZT_SIG_CLEAR | ZT_SIG_SF; + else if (wc->porttype[i] == VT_PORT_PROSLIC) + wc->chans[i].sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS + | ZT_SIG_FXOGS | ZT_SIG_SF + | ZT_SIG_CLEAR | ZT_SIG_EM; + else if (wc->porttype[i]) + cardcrit(wc->boardnum, "Port %d has unknown type (%d)", + i, wc->porttype[i]); + } + + /* Enable bus mastering */ + pci_set_master(pdev); + + if (request_irq(pdev->irq, openpci_isr, ZAP_IRQ_SHARED, NAME, wc)) { + cardcrit(boardnum, "Cant get IRQ!"); + failret = -EIO; + goto hell_5; + } + cardinfo(boardnum, "Got IRQ %d", pdev->irq); + + enable_interrupts(wc); + start_dma(wc); + + cardinfo(boardnum,"Initialised card."); + return 0; + + hell_5: + zt_unregister(&wc->span); + hell_4: + if (wc->writechunk){ + pci_free_consistent(pdev, VT_PCIDMA_BLOCKSIZE, + (void*)wc->writechunk, wc->writedma); + } + hell_3: + outb(0x00, TJ_CNTL); + release_region(wc->ioaddr, 0xff); + hell_2: + cards[boardnum] = NULL; + hell: + kfree(wc); + return failret; +} //}}} + +static void __devexit openpci_remove_board(struct pci_dev *pdev) +{ //{{{ + struct openpci *wc = pci_get_drvdata(pdev); + + if(!wc) return; + + arm_monitor(wc,0); + + /* Stop DMA */ + outb(0x00, TJ_OPER); + disable_interrupts(wc); + + //XXX Replace this usecount business... + // and do this BEFORE we invalidate everything above... + // check that we wont try to write to it in the meantime. + /* Release span, possibly delayed */ + //XXX if (!wc->usecount) openpci_release(wc); else wc->dead = 1; + + zt_unregister(&wc->span); + outb(0x00, TJ_CNTL); + + pci_free_consistent(pdev, VT_PCIDMA_BLOCKSIZE, (void *)wc->writechunk, wc->writedma); + free_irq(pdev->irq, wc); + + release_region(wc->ioaddr, 0xff); + + mutex_lock(&cards_mutex); + cards[wc->boardnum] = NULL; + mutex_unlock(&cards_mutex); + + kfree(wc); + cardinfo(wc->boardnum, "Removed OpenPCI card."); +} //}}} + +static struct pci_device_id openpci_pci_tbl[] = { + { 0xe159, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (kernel_ulong_t) &wcopenpci }, + { 0 } +}; + +MODULE_DEVICE_TABLE(pci, openpci_pci_tbl); + +static struct pci_driver openpci_driver = { + name: NAME, + probe: openpci_probe_board, + remove: __devexit_p(openpci_remove_board), + suspend: NULL, + resume: NULL, + id_table: openpci_pci_tbl, +}; + +static int __init openpci_init(void) +{ + if( zap_pci_module(&openpci_driver) ) + return -ENODEV; + + info("Module loaded %s", debug ? "with debug enabled" : ""); + return 0; +} + +static void __exit openpci_cleanup(void) +{ + pci_unregister_driver(&openpci_driver); + info("Module exit"); +} + +module_init(openpci_init); +module_exit(openpci_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESCRIPTION); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_VERSION(ZAPTEL_VERSION); +MODULE_LICENSE("GPL"); + --- zaptel-1.4.11~dfsg.orig/kernel/cwain/zaptel.conf.hdlcnet.doubleE1 +++ zaptel-1.4.11~dfsg/kernel/cwain/zaptel.conf.hdlcnet.doubleE1 @@ -0,0 +1,10 @@ +loadzone=nl +defaultzone=nl +span=1,1,3,ccs,hdb3,crc4 +span=2,2,3,ccs,hdb3,crc4 + +; hdlc0 +hdlcnet = 1 + +; hdlc 1 +hdlcnet = 2 \ No newline at end of file --- zaptel-1.4.11~dfsg.orig/kernel/cwain/zaptel.conf.doubleE1 +++ zaptel-1.4.11~dfsg/kernel/cwain/zaptel.conf.doubleE1 @@ -0,0 +1,14 @@ +loadzone=nl +defaultzone=nl +span=1,1,3,ccs,hdb3,crc4 +span=2,2,3,ccs,hdb3,crc4 + +alaw=1-62 + +bchan=1-15 +dchan=16 +bchan=17-31 + +bchan=32-46 +dchan=47 +bchan=48-62 --- zaptel-1.4.11~dfsg.orig/kernel/cwain/cwain.c +++ zaptel-1.4.11~dfsg/kernel/cwain/cwain.c @@ -0,0 +1,2236 @@ +/* + * cwain.c - Zaptel driver for the Junghanns.NET E1 card + * + * c.w.a.i.n. == card without an interesting name + * + * single/double E1 board + * + * Copyright (C) 2004, 2005, 2006, 2007 Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + */ +#include +#include +#include +#include +#include +#include +#include "cwain.h" + +#ifdef LINUX26 +#include +#endif + +#if CONFIG_PCI + +static int ports=-1; /* autodetect */ +static int debug=0; +static int hw_hdlc=1; +static int hdlcnet=0; +static int pwm0 = 0x50; /* TX level */ +static int pwm1 = 0x50; /* RX level */ +static int dacs = 1; /* 0 = no dacs, 1 = oncard dacs */ +static int require_ext_clock = 0; +static struct zt_cwain *cwain_span_list = NULL; +static int cwain_span_count = 0; +static struct zt_cwain_card *cwain_card_list = NULL; +static int cwain_card_count = 0; +static struct pci_dev *multi_cwain = NULL; +static spinlock_t cwain_span_registerlock = SPIN_LOCK_UNLOCKED; +static spinlock_t cwain_card_registerlock = SPIN_LOCK_UNLOCKED; + +static int ztcwain_shutdown(struct zt_span *span); + +int cwain_waitbusy(struct zt_cwain *cwaintmp) { + int x=1000; + while (x-- && (cwain_inb(cwaintmp,cwain_R_STATUS) & 1)); + if (x < 0) { + printk(KERN_CRIT "cwain: really busy waiting!\n"); + return -1; + } else { + if ((x < 990) && (cwaintmp->ticks > 500)) { + printk(KERN_CRIT "cwain: waited %d\n", 1000 - x); + } + return 0; + } +} + +void cwain_unregister_zap_span(struct zt_cwain *cwainspan) { + if (!cwainspan) { + printk(KERN_INFO "cwain: shutting down NULL span!\n"); + return; + } + if(cwainspan->span.flags & ZT_FLAG_RUNNING) { + ztcwain_shutdown(&cwainspan->span); + if (debug) + printk(KERN_INFO "cwain: shutdown span %d.\n",cwainspan->cardno); + } + if(cwainspan->span.flags & ZT_FLAG_REGISTERED) { + zt_unregister(&cwainspan->span); + if (debug) + printk(KERN_INFO "cwain: unregistered span %d.\n",cwainspan->cardno); + } +} + +void cwain_shutdown_span(struct zt_cwain *cwainspan) { + + if (!cwainspan) { + printk(KERN_INFO "cwain: shutting down NULL span!\n"); + return; + } + + if (cwainspan->pci_io == NULL) { + return; + } + + if (debug) + printk(KERN_INFO "cwain: shutting down span %d (cardID %d) at %p.\n",cwainspan->cardno,cwainspan->cardID,cwainspan->pci_io); + + // turn off irqs + + // softreset + cwain_outb(cwainspan,cwain_R_CIRM,0x8); + cwain_outb(cwainspan,cwain_R_CIRM,0x0); + cwain_waitbusy(cwainspan); + + cwain_outb(cwainspan,cwain_R_IRQMSK_MISC, 0); + cwain_outb(cwainspan,cwain_R_IRQ_CTRL, 0); + + pci_write_config_word(cwainspan->pcidev, PCI_COMMAND, 0); // disable memio + + if (cwainspan->pcidev != NULL) { + pci_disable_device(cwainspan->pcidev); + cwainspan->pcidev = NULL; + } + +} + +void cwain_shutdown_card(struct zt_cwain_card *cwaintmp) { + unsigned long flags = 0; + int i = 0; + + if (!cwaintmp) { + printk(KERN_INFO "cwain: shutting down NULL card!\n"); + return; + } + + spin_lock_irqsave(&cwaintmp->lock,flags); + + // turn off irqs + cwain_outb(cwaintmp->span[0],cwain_R_IRQ_CTRL, 0); + cwain_outb(cwaintmp->span[0],cwain_R_IRQMSK_MISC, 0); + + spin_unlock_irqrestore(&cwaintmp->lock,flags); + + for (i=0;ispans;i++) { + cwain_unregister_zap_span(cwaintmp->span[i]); + } + + + for (i=0;ispans;i++) { + release_region(cwaintmp->span[i]->ioport, 8); + cwaintmp->span[i]->ioport = 0; + iounmap((void *) cwaintmp->span[i]->pci_io); + cwaintmp->span[i]->pci_io = NULL; + release_mem_region((unsigned long)cwaintmp->span[i]->pci_io_phys, 256); + } + + if (cwaintmp->spans == 2) { + free_irq(cwaintmp->span[1]->irq,cwaintmp); + } + free_irq(cwaintmp->irq,cwaintmp); +} + +void cwain_doLEDs(struct zt_cwain *cwaintmp) { + /* + O1 O3 (red) + O2 O4 (green) + */ + if (!(cwaintmp->span.flags & ZT_FLAG_RUNNING)) { + return; + } + if ((cwaintmp->type == 0xb553) || (cwaintmp->type == 0xb554) || (cwaintmp->type == 0xb555)) { + /* sync status */ + if (((cwaintmp->sync_sta & 0x07) == 0x07) && cwaintmp->sync) { + cwaintmp->leds[0] = 1; + cwaintmp->leds[1] = 0; + } else { + cwaintmp->leds[0] = 0; + cwaintmp->leds[1] = 1; + } + /* multiframe alignment */ + if ((cwaintmp->sync_sta & 0x20) == 0x20) { + cwaintmp->leds[2] = 1; + cwaintmp->leds[3] = 0; + } else { + if ((cwaintmp->span.lineconfig & ZT_CONFIG_CRC4) && cwaintmp->sync) { + /* CRC4 requested */ + cwaintmp->leds[2] = 0; + cwaintmp->leds[3] = 1; + } else { + /* no CRC4, disable 3 and 4 */ + cwaintmp->leds[2] = 1; + cwaintmp->leds[3] = 1; + } + } + cwain_outb(cwaintmp,cwain_R_GPIO_OUT1,(cwaintmp->leds[0] | (cwaintmp->leds[1] << 1) | (cwaintmp->leds[2] << 2) | (cwaintmp->leds[3] << 3))); + } +} + +void cwain_reset_pcm(struct zt_cwain *cwaintmp, int master, int int_clock) { + /* PCM reset */ + cwain_outb(cwaintmp,cwain_R_CIRM,0x20); + cwain_outb(cwaintmp,cwain_R_CIRM,0x0); + cwain_waitbusy(cwaintmp); + + if (master) { + cwain_outb(cwaintmp,cwain_R_PCM_MD0, 0x91); + cwain_outb(cwaintmp,cwain_R_PCM_MD1, 0x20); + if (int_clock) { + cwain_outb(cwaintmp,cwain_R_PCM_MD0, 0xA1); + cwain_outb(cwaintmp,cwain_R_PCM_MD2, 0x04); + } else { + cwain_outb(cwaintmp,cwain_R_PCM_MD0, 0xA1); + cwain_outb(cwaintmp,cwain_R_PCM_MD2, 0x00); + } + } else { + cwain_outb(cwaintmp,cwain_R_PCM_MD0, 0x90); + cwain_outb(cwaintmp,cwain_R_PCM_MD1, 0x20); + } +} + +void cwain_reset_span(struct zt_cwain *cwaintmp) { + + pci_write_config_word(cwaintmp->pcidev, PCI_COMMAND, PCI_COMMAND_MEMORY | PCI_COMMAND_IO); // enable memio + + cwain_reset_pcm(cwaintmp,0,0); + + /* E1 reset */ + cwain_outb(cwaintmp,cwain_R_CIRM,0x40); + cwain_outb(cwaintmp,cwain_R_CIRM,0x0); + cwain_waitbusy(cwaintmp); + + /* soft reset */ + cwain_outb(cwaintmp,cwain_R_CIRM,0x10); + cwain_outb(cwaintmp,cwain_R_CIRM,0x0); + cwain_waitbusy(cwaintmp); + + /* 128 byte B chans, 4096 byte D chans */ + if (hdlcnet) { + cwain_outb(cwaintmp,cwain_R_FIFO_MD,0x3E); + } else { + cwain_outb(cwaintmp,cwain_R_FIFO_MD,0x36); + } + + + cwain_outb(cwaintmp,cwain_R_BRG_PCM_CFG,0x0); + cwain_outb(cwaintmp,cwain_R_CTRL,0x0); + + /* no blinky blink */ + if (cwaintmp->type == 0xb555) { + cwain_outb(cwaintmp,cwain_R_GPIO_SEL,0x20 | 0x10 | 0x02); + cwain_outb(cwaintmp,cwain_R_GPIO_EN1,0x0f); + cwain_outb(cwaintmp,cwain_R_GPIO_EN0,0x08 | 0x04); + } else { + cwain_outb(cwaintmp,cwain_R_GPIO_SEL,0x20 | 0x10); + cwain_outb(cwaintmp,cwain_R_GPIO_EN1,0x0f); + } + cwain_outb(cwaintmp,cwain_R_GPIO_OUT1,0x0f); + + /* IRQs off */ + cwain_outb(cwaintmp,cwain_R_IRQMSK_MISC,0x0); + cwain_outb(cwaintmp,cwain_R_IRQ_CTRL,0x0); + + cwaintmp->leds[0] = 1; + cwaintmp->leds[1] = 1; + cwaintmp->leds[2] = 1; + cwaintmp->leds[3] = 1; + + cwaintmp->ticks = 0; + cwaintmp->clicks = 0; +} + +struct zt_cwain_card *cwain_get_card(unsigned int pcibus) { + struct zt_cwain_card *cwaintmp = cwain_card_list; + spin_lock(&cwain_card_registerlock); + while (cwaintmp) { + if (cwaintmp->pcibus == pcibus) { + break; + } + cwaintmp = cwaintmp->next; + } + spin_unlock(&cwain_card_registerlock); + return cwaintmp; +} + + +void cwain_register_card(struct zt_cwain_card *cwaincard) { + spin_lock(&cwain_card_registerlock); + if (cwaincard != NULL) { + cwaincard->prev = NULL; + cwaincard->next = cwain_card_list; + if (cwain_card_list) { + cwain_card_list->prev = cwaincard; + } + cwain_card_list = cwaincard; + cwain_card_count++; + } else { + printk(KERN_INFO "cwain: trying to register NULL card.\n"); + } + spin_unlock(&cwain_card_registerlock); +} + +int cwain_register_span(struct zt_cwain *cwainspan) { + struct zt_cwain_card *cwaintmp; + spin_lock(&cwain_span_registerlock); + if (cwainspan != NULL) { + cwainspan->prev = NULL; + cwainspan->next = cwain_span_list; + if (cwain_span_list) { + cwain_span_list->prev = cwainspan; + } + cwain_span_list = cwainspan; + cwainspan->cardno = ++cwain_span_count; + } else { + printk(KERN_INFO "cwain: trying to register NULL span.\n"); + } + spin_unlock(&cwain_span_registerlock); + + if (cwainspan->type == 0xb553) { + cwaintmp = kmalloc(sizeof(struct zt_cwain_card),GFP_KERNEL); + if (!cwaintmp) { + printk(KERN_WARNING "cwain: unable to kmalloc!\n"); + return -1; + } + memset(cwaintmp, 0x0, sizeof(struct zt_cwain_card)); + + spin_lock_init(&cwaintmp->lock); + cwaintmp->pcibus = cwainspan->pcibus; + cwaintmp->span[0] = cwainspan; + cwaintmp->syncs[0] = -1; + cwaintmp->spans = 1; + cwaintmp->cardID = cwainspan->cardID; + cwain_register_card(cwaintmp); + printk(KERN_INFO + "cwain: Junghanns.NET singleE1 PCI ISDN card configured at mem %lx IRQ %d HZ %d CardID %d\n", + (unsigned long) cwainspan->pci_io, + cwaintmp->span[0]->irq, HZ, cwainspan->cardID); + } else { + cwaintmp = cwain_get_card(cwainspan->pcibus); + if (!cwaintmp) { + cwaintmp = kmalloc(sizeof(struct zt_cwain_card),GFP_KERNEL); + if (!cwaintmp) { + printk(KERN_WARNING "cwain: unable to kmalloc!\n"); + return -1; + } + memset(cwaintmp, 0x0, sizeof(struct zt_cwain_card)); + + spin_lock_init(&cwaintmp->lock); + cwaintmp->pcibus = cwainspan->pcibus; +// cwaintmp->spans = cwainspan->type - 46419; + cwaintmp->spans = (cwainspan->type & (2321 * 20)) - 46419; + cwaintmp->span[0] = cwainspan; + cwaintmp->cardID = cwainspan->cardID; + cwaintmp->syncs[0] = -1; + cwain_register_card(cwaintmp); + } else { + cwaintmp->spans = (cwainspan->type & (2321 * 20)) - 46418; + if (cwainspan->cardID < cwaintmp->cardID) { + cwaintmp->cardID = cwainspan->cardID; + cwaintmp->span[1] = cwaintmp->span[0]; + cwaintmp->syncs[1] = cwaintmp->syncs[0]; + cwaintmp->span[0] = cwainspan; + } else { + cwaintmp->span[1] = cwainspan; + cwaintmp->syncs[1] = -1; + } + printk(KERN_INFO + "cwain: Junghanns.NET doubleE1 PCI ISDN %d.0 card configured at mem (%lx / %lx) IRQ %d HZ %d CardID (%d / %d) bus %#x\n", + (cwainspan->type - 0xb553), (unsigned long) cwaintmp->span[0]->pci_io, (unsigned long) cwaintmp->span[1]->pci_io, + cwaintmp->span[0]->irq, HZ, cwaintmp->span[0]->cardID, cwaintmp->span[1]->cardID, cwaintmp->pcibus); + } + } + return 0; +} + +static void cwain_dfifo_check(struct zt_cwain *cwaintmp) { + unsigned short z1=1,z2=1; + unsigned short oz1=0,oz2=0; + unsigned char f1=1,f2=1; + unsigned char of1=0,of2=0; + int space = 0; + int len; + int chan; + int fifo = 0x1f; + unsigned long flags; + int frames = 0; + + if (hdlcnet) { + chan = 0; + } else { + chan = 15; + } + + + /* calculate how much data we can allow zaptel to transmit */ + spin_lock_irqsave(&cwaintmp->lock,flags); + /* select tx fifo */ + + cwain_outb(cwaintmp,cwain_R_FIFO, fifo << 1); + cwain_waitbusy(cwaintmp); + + while ((oz1 != z1) && (oz2 != z2)) { + oz1 = z1; + oz2 = z2; + z1 = cwain_inw(cwaintmp,cwain_A_Z1) & 0xfff; + z2 = cwain_inw(cwaintmp,cwain_A_Z2) & 0xfff; + } + + len = z1-(z2 & 0xfff); + if (len < 0) { + len += cwain_DFIFO_SIZE; + } + space = cwain_DFIFO_SIZE - len; + + if (((debug > 2) && (space < cwain_DFIFO_SIZE)) || (space == 0)) { + printk(KERN_INFO "cwain: card %d TX fifo %d space now %d\n", cwaintmp->cardno, fifo, space); + } + cwaintmp->chans[chan].maxbytes2transmit = space; + + + /* calculate how many frames are in the receive fifo */ + + /* select rx fifo */ + cwain_outb(cwaintmp,cwain_R_FIFO,(fifo << 1) | 1); + cwain_waitbusy(cwaintmp); + + while ((of1 != f1) && (of2 != f2)) { + of1 = f1; + of2 = f2; + f1 = cwain_inb(cwaintmp,cwain_A_F1) & 0xf; + f2 = cwain_inb(cwaintmp,cwain_A_F2) & 0xf; + } + + frames = f1 - f2; + if (frames < 0) { + frames += cwain_DFIFO_FRAMES; + } + cwaintmp->drx = frames; + + spin_unlock_irqrestore(&cwaintmp->lock,flags); + + if ((frames > 0) && (debug > 3)) + printk(KERN_INFO "\ncwain: %d frames in RX fifo\n", frames); +} + +static int cwain_dfifo_tx(struct zt_cwain *cwaintmp) { + int chan; + int x=0; + char fifo = 0; + unsigned long flags = 0; + + fifo = 0x1F; + + if (hdlcnet) { + chan = 0; + } else { + chan = 15; + } + + if (cwaintmp->chans[chan].bytes2transmit < 1) { + return 0; + } else { + spin_lock_irqsave(&cwaintmp->lock,flags); + /* select fifo */ + cwain_outb(cwaintmp,cwain_R_FIFO,fifo << 1); + cwain_waitbusy(cwaintmp); + + if (((debug > 3 ) && hdlcnet) || ((!hdlcnet) && (debug > 3))) + printk(KERN_INFO "cwain: card %d TX [ ", cwaintmp->cardno); + /* copy frame to fifo */ + for (x=0;xchans[chan].bytes2transmit;x++) { + if (((debug > 3 ) && hdlcnet) || ((!hdlcnet) && (debug > 3))) + printk("%#x ",cwaintmp->dtxbuf[x]); + cwain_outb(cwaintmp,cwain_A_FIFO_DATA0,cwaintmp->dtxbuf[x]); + } + if (((debug > 3 ) && hdlcnet) || ((!hdlcnet) && (debug > 3))) + printk("]\n"); + if (((debug > 2 ) && hdlcnet) || ((!hdlcnet) && (debug > 3))) + printk(KERN_INFO "ztx %d bytes\n",cwaintmp->chans[chan].bytes2transmit); + + if (cwaintmp->chans[chan].eoftx == 1) { + /* transmit HDLC frame */ + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x1); + cwain_waitbusy(cwaintmp); + if ((debug > 3 ) && hdlcnet) + printk(KERN_INFO "cwain: TX flag\n"); + } + spin_unlock_irqrestore(&cwaintmp->lock,flags); + } + return 0; +} + +static int cwain_fifo_tx(struct zt_cwain *cwaintmp, char fifo) { + int chan,f; + unsigned long flags = 0; + + if (hw_hdlc) { + if (fifo <= 14) { + chan = fifo; + } else { + chan = fifo + 1; + } + } else { + chan = fifo; + } + + if (dacs && (cwaintmp->ts_rx[chan] > -1)) { + /* dont transmit when we receive from the pcm bus */ + return 0; + } + + spin_lock_irqsave(&cwaintmp->lock,flags); + /* select fifo */ + cwain_outb(cwaintmp,cwain_R_FIFO,0x80 | (fifo << 1)); + cwain_waitbusy(cwaintmp); + + for (f=0; f < (cwain_FRAME_SIZE/4); f++) { + cwain_outdw(cwaintmp,cwain_A_FIFO_DATA0,*((unsigned int *) &cwaintmp->ftxbuf[chan][f * 4])); + } + spin_unlock_irqrestore(&cwaintmp->lock,flags); + return 0; +} + +static int cwain_dfifo_rx(struct zt_cwain *cwaintmp) { + int chan; + unsigned char data,stat; + int len,i = 0; + unsigned short z1=1,z2=1; + unsigned short oz1=0,oz2=0; + char fifo = 0; + unsigned long flags = 0; + + fifo = 0x1F; + if (hdlcnet) { + chan = 0; + } else { + chan = 15; + } + + spin_lock_irqsave(&cwaintmp->lock,flags); + /* select fifo */ + cwain_outb(cwaintmp,cwain_R_FIFO,(fifo << 1) | 1); + cwain_waitbusy(cwaintmp); + + while ((oz1 != z1) && (oz2 != z2)) { + oz1 = z1; + oz2 = z2; + z1 = cwain_inw(cwaintmp,cwain_A_Z1) & 0xfff; + z2 = cwain_inw(cwaintmp,cwain_A_Z2) & 0xfff; + } + + len = z1-(z2 & 0xfff); + if (len < 0) { + len += cwain_DFIFO_SIZE; + } + + if (len > cwain_DFIFO_SIZE) { + printk(KERN_INFO "\ncwain: buffer overflow in D channel RX!\n"); + cwaintmp->chans[chan].bytes2receive = 0; + cwaintmp->chans[chan].eofrx = 0; + } else { + if ((((debug > 3 ) && hdlcnet) || ((!hdlcnet) && (debug > 3))) && (cwaintmp->sync)) + printk(KERN_INFO "cwain: card %d RX [ ", cwaintmp->cardno); + for (i=0; idrxbuf[i] = data; + if ((((debug > 3 ) && hdlcnet) || ((!hdlcnet) && (debug > 3))) && (cwaintmp->sync)) + printk("%#x ",data); + } + if ((((debug > 3 ) && hdlcnet) || ((!hdlcnet) && (debug > 3))) && (cwaintmp->sync)) + printk("] %d bytes\n", i); + cwaintmp->chans[chan].bytes2receive = i; + cwaintmp->chans[chan].eofrx = 1; + } + + stat = cwain_inb(cwaintmp,cwain_A_FIFO_DATA0); + if (stat != 0x0) { + // bad CRC, skip it + if (cwaintmp->sync) { + printk(KERN_INFO "cwain: BAD CRC for hdlc frame on card %d (cardID %d) stat %#x len %d\n",cwaintmp->cardno, cwaintmp->cardID, stat, i); + } + cwaintmp->chans[chan].bytes2receive = 0; + cwaintmp->chans[chan].eofrx = 0; +// zt_qevent_nolock(&cwaintmp->chans[chan], ZT_EVENT_BADFCS); + } + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x1); + cwain_waitbusy(cwaintmp); + + /* frame received */ + cwaintmp->drx--; + if (!cwaintmp->sync) { + cwaintmp->chans[chan].bytes2receive = 0; + cwaintmp->chans[chan].eofrx = 0; + stat = 0xff; + } + spin_unlock_irqrestore(&cwaintmp->lock,flags); + if (stat == 0) { + return 1; + } else { + return 0; + } +} + +static int cwain_fifo_rx(struct zt_cwain *cwaintmp, char fifo) { + int chan; + unsigned int data; + int len = 0,i,f,flen = 0; + unsigned short z1=1,z2=1; + unsigned short oz1=0,oz2=0; + int mumbojumbo=0; + unsigned long flags = 0; + int x = 1000; + + if (hw_hdlc) { + if (fifo <= 14) { + chan = fifo; + } else { + chan = fifo + 1; + } + } else { + chan = fifo; + } + + // select rx fifo + spin_lock_irqsave(&cwaintmp->lock,flags); + // no hdlc, transparent data + cwain_outb(cwaintmp,cwain_R_FIFO,0x80 | (fifo << 1) | 1); + cwain_waitbusy(cwaintmp); + + while (x-- && ((oz1 != z1) && (oz2 != z2))) { + len = z1 - z2; + if (len < 0) { + len += cwain_FIFO_SIZE; + } + flen = len; + if (len > cwain_FIFO_HW) { + mumbojumbo = len - (cwain_FIFO_HW - cwain_FRAME_SIZE); + len = cwain_FRAME_SIZE; + } + oz1 = z1; + oz2 = z2; + z1 = cwain_inw(cwaintmp,cwain_A_Z1) & 0x7f; + z2 = cwain_inw(cwaintmp,cwain_A_Z2) & 0x7f; + } + if (x < 500) { + printk(KERN_CRIT "cwain: prevented endless loop\n"); + } + + if (mumbojumbo > 0) { + for (i=0;i<(mumbojumbo/4);i++) { + data = cwain_indw(cwaintmp,cwain_A_FIFO_DATA0); + } + cwaintmp->clicks++; + } + if (len < cwain_FRAME_SIZE) { + /* dont get nervous here */ + if ((cwaintmp->clicks > 600) && (cwaintmp->span.alarms == ZT_ALARM_NONE)) { + printk(KERN_INFO "cwain: cardID %d not enough to receive (%d bytes), fifo %d\n",cwaintmp->cardID, len, fifo); + } + spin_unlock_irqrestore(&cwaintmp->lock,flags); + return 0; + } else { + for (f=0;f<(cwain_FRAME_SIZE / 4);f++) { + *((unsigned int *) &cwaintmp->frxbuf[chan][f*4]) = cwain_indw(cwaintmp,cwain_A_FIFO_DATA0); + } + } + + + /* dont get nervous here */ + if (((cwaintmp->clicks > 50) || (debug > 3)) && ((cwaintmp->span.alarms == ZT_ALARM_NONE) && (mumbojumbo > 0))) { + printk(KERN_INFO "cwain: span %d dropped audio fifo %d mj %d flen %d z1 %d z2 %d\n", cwaintmp->cardID, fifo, mumbojumbo, flen, z1, z2); + cwaintmp->clicks = 0; + } +// printk(KERN_INFO "s/t port %d, channel %d, dbufi=%d, f1=%d, f2=%d, z1=%d, z2=%d => len = %d stat=%#x, hdlc=%d\n",stport,chan,cwaintmp->st[stport].dbufi,f1,f2,z1,z2,len,stat,hdlc); + spin_unlock_irqrestore(&cwaintmp->lock,flags); + return 0; +} + +void cwain_set_master(struct zt_cwain_card *cwaintmp, int span) { + int i=0; + unsigned long flags = 0; + + if (cwaintmp->syncsrc == span) return; + + spin_lock_irqsave(&cwaintmp->lock,flags); + /* disable the old master */ + for (i=0; i < cwaintmp->spans; i++) { + if (cwaintmp->master[i]) { + /* enable PCM slave mode, PCM128 */ + cwain_reset_pcm(cwaintmp->span[i],0,0); + cwaintmp->master[i] = 0; + } + } + + /* enable the new master */ + if (cwaintmp->syncs[span] > 0) { + /* enable PCM master mode, PCM128, synced to E1 receive */ + cwain_reset_pcm(cwaintmp->span[span],1,0); + if (debug) + printk(KERN_INFO "cwain: cardID %d span %d, PCM master E1 sync\n", cwaintmp->cardID, span); + } else { + /* enable PCM master mode, PCM128, free running */ + cwain_reset_pcm(cwaintmp->span[span],1,1); + if (debug) + printk(KERN_INFO "cwain: cardID %d span %d, PCM master internal clock\n", cwaintmp->cardID, span); + } + + /* reset the slaves */ + for (i=0; i < cwaintmp->spans; i++) { + if (i != span) { + /* enable PCM slave mode, PCM128 */ + cwain_reset_pcm(cwaintmp->span[i],0,0); + cwaintmp->master[i] = 0; + } + } + + cwaintmp->master[span] = 1; + cwaintmp->syncsrc = span; + + /* restore pcm assignments !!! */ + + spin_unlock_irqrestore(&cwaintmp->lock,flags); +} + +void cwain_check_timing(struct zt_cwain_card *cwaintmp) { + int i=0; + int bestsync = 42; + int sync_ok = 0; + + for (i=0; i < cwaintmp->spans; i++) { + if (cwaintmp->span[i]->span.lineconfig & ZT_CONFIG_CRC4) { + /* CRC4 requested */ + sync_ok = 0x27; + } else { + /* doubleframing requested */ + sync_ok = 0x07; + } + if ((cwaintmp->syncs[i] > 0) && ((cwaintmp->span[i]->sync_sta & sync_ok) == sync_ok)) { + if (bestsync < cwaintmp->spans) { + if (cwaintmp->syncs[i] < cwaintmp->syncs[bestsync]) { + bestsync = i; + } + } else { + bestsync = i; + } + } + } + + if (cwaintmp->syncsrc >= 0) { + if (debug > 3) + printk(KERN_INFO "cwain: bestsync %d cwaintmp->syncsrc %d\n", bestsync, cwaintmp->syncsrc); + + if (bestsync == cwaintmp->syncsrc) { + if (debug > 3) + printk(KERN_INFO "cwain: already on best syncsrc %d\n", bestsync); + return; + } + + /* if we have a better syncsrc */ + if (bestsync < cwaintmp->spans) { + if (debug) + printk(KERN_INFO "cwain: found better syncsrc %d\n", bestsync); + cwain_set_master(cwaintmp, bestsync); + return; + } + } + + /* if reelection failed, find internal sync source, if not forbidden! */ + if ((cwaintmp->syncsrc == -1) && !require_ext_clock) { + /* no master yet */ + if (debug > 3) + printk(KERN_INFO "cwain: no clocksource found cardID %d\n", cwaintmp->cardID); + for (i=0; i < cwaintmp->spans; i++) { + /* find the first internal source */ + if (debug > 3) + printk(KERN_INFO "cwain: cwaintmp->syncs[%d] = %d\n", i, cwaintmp->syncs[i]); + if (cwaintmp->syncs[i] == 0) { + if (debug) + printk(KERN_INFO "cwain: using internal clock of span %d\n", i); + cwain_set_master(cwaintmp, i); + return; + } + } + } + + /* if we have no internal sync source the only thing we can do is to enable any of the possible sync sources*/ + if (cwaintmp->syncsrc == -1) { + /* find the first possible sync source with framing */ + for (i=0; i < cwaintmp->spans; i++) { + if (cwaintmp->syncs[i] > 0) { + if (debug) + printk(KERN_INFO "cwain: desperately using clock of span %d\n", i); + cwain_set_master(cwaintmp, i); + break; + } + } + } +} + + +static void cwain_assign(struct zt_cwain_card *cwaincard, int src_span, int src_chan, int dst_span, int dst_chan, int timeslot, int use_pcm_bus) { + int dst_fifo = dst_chan - 1; + int src_fifo = src_chan - 1; + int src_hfc_chan = src_chan; + int dst_hfc_chan = dst_chan; + struct zt_cwain *cwain_src = NULL, *cwain_dst = NULL; + unsigned long flags = 0; + /* hw_hdlc == 1 + fifo 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 >< 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 + time 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 + chan 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 + + hw_hdlc == 0 + fifo 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 + time 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 + chan 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 + */ + + if (hw_hdlc) { + if (dst_chan > 0x10) { + dst_fifo--; + } + if (src_chan > 0x10) { + src_fifo--; + } + } + + if (cwaincard) { + cwain_src = cwaincard->span[src_span]; + cwain_dst = cwaincard->span[dst_span]; + } else { + return; + } + + if (debug) + printk(KERN_INFO "cwain: assign(src_span %d (ID %d), src_chan %d, dst_span %d (ID %d), dst_chan %d, use_pcm_bus %d, timeslot %d\n", src_span, cwain_src->cardID, src_chan, dst_span, cwain_dst->cardID, dst_chan, use_pcm_bus, timeslot); + + spin_lock_irqsave(&cwaincard->lock,flags); + + /* assign the data received from the hfc-channel "src_hfc_chan" to transmit pcm slot "timeslot" on pin STIO1 */ + /* TX up */ + cwain_outb(cwain_src,cwain_R_FIFO, src_fifo << 1); + cwain_waitbusy(cwain_src); + cwain_outb(cwain_src,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwain_src); + cwain_outb(cwain_src,cwain_A_CON_HDLC,0xC2); + cwain_outb(cwain_src,cwain_A_CHANNEL,src_hfc_chan << 1); + + cwain_outb(cwain_src,cwain_R_SLOT,timeslot << 1); + if (use_pcm_bus) { + cwain_outb(cwain_src,cwain_A_SL_CFG, (src_hfc_chan << 1) | 0 | 0x80); + } else { + cwain_outb(cwain_src,cwain_A_SL_CFG, (src_hfc_chan << 1) | 0 | 0x40); + } + cwain_src->ts_tx[src_chan - 1] = timeslot; + + /* assign the data received from the receive pcm slot "timeslot" on pin STIO1 to the hfc-channel "dst_hfc_chan"*/ + /* RX down */ + cwain_outb(cwain_dst,cwain_R_FIFO, (dst_fifo << 1) | 1); + cwain_waitbusy(cwain_dst); + cwain_outb(cwain_dst,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwain_dst); + cwain_outb(cwain_dst,cwain_A_CHANNEL,(dst_hfc_chan << 1) | 1); + cwain_outb(cwain_dst,cwain_A_CON_HDLC,0xC2); + + cwain_outb(cwain_dst,cwain_R_SLOT,(timeslot << 1) | 1); + if (use_pcm_bus) { + cwain_outb(cwain_dst,cwain_A_SL_CFG, (dst_hfc_chan << 1) | 1 | 0xC0); + } else { + cwain_outb(cwain_dst,cwain_A_SL_CFG, (dst_hfc_chan << 1) | 1 | 0x40); + } + cwain_dst->ts_rx[dst_chan - 1] = timeslot; +// printk(KERN_INFO "cwain: span %d ts_tx[%d] = %d\n", src_span, src_chan - 1, timeslot); +// printk(KERN_INFO "cwain: span %d ts_rx[%d] = %d\n", dst_span, dst_chan - 1, timeslot); + + spin_unlock_irqrestore(&cwaincard->lock,flags); +} + +static void cwain_unassign(struct zt_cwain *cwaintmp, int chan, int timeslot, int lock) { + int fifo = chan - 1; + int hfc_chan = chan; + unsigned long flags = 0; + + if (hw_hdlc && (chan > 0x10)) { + fifo--; + } + + if (lock) spin_lock_irqsave(&cwaintmp->lock,flags); + + /* unassign from_where we receive and to_where we transmit */ + + + /* we were transmitting on cwaintmp->ts_tx[chan - 1] */ + cwain_outb(cwaintmp,cwain_R_SLOT, cwaintmp->ts_tx[chan - 1] << 1); + cwain_outb(cwaintmp,cwain_A_SL_CFG, 0x0); + cwaintmp->ts_tx[chan - 1] = -1; + + /* we were receiving on cwaintmp->ts_rx[chan - 1] */ + cwain_outb(cwaintmp,cwain_R_SLOT, (cwaintmp->ts_rx[chan - 1] << 1) | 1); + cwain_outb(cwaintmp,cwain_A_SL_CFG, 0x0); + cwaintmp->ts_rx[chan - 1] = -1; + + /* restore our tx fifo */ + cwain_outb(cwaintmp,cwain_R_FIFO,fifo << 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0x2); + cwain_outb(cwaintmp,cwain_A_CHANNEL,hfc_chan << 1); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x0); + + /* restore our rx fifo */ + cwain_outb(cwaintmp,cwain_R_FIFO,(fifo << 1) | 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0x2); + cwain_outb(cwaintmp,cwain_A_CHANNEL,(hfc_chan << 1) | 1); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x0); + +// printk(KERN_INFO "cwain: ts_tx[%d] = %d\n", chan - 1, -1); +// printk(KERN_INFO "cwain: ts_rx[%d] = %d\n", chan - 1, -1); + if (lock) spin_unlock_irqrestore(&cwaintmp->lock,flags); +} + + +static int ztcwain_dacs(struct zt_chan *dst, struct zt_chan *src) { + struct zt_cwain *cwaintmp = NULL; + int use_pcm_bus = 0; + int timeslot = 0; + if (hdlcnet) return -1; + if (!dacs) return -1; + + if (src) { + cwaintmp = src->pvt; + + if ((src->pvt != dst->pvt) && (src->span->pvt != dst->span->pvt)) { + if (dacs == 2) { + if (debug) + printk("cwain: Assigning %d/%d -> %d/%d, different cards!\n", src->span->offset, src->chanpos, dst->span->offset, dst->chanpos); + timeslot = src->channo; + use_pcm_bus = 1; + } else { + if (debug) + printk("cwain: Not Assigning %d/%d -> %d/%d, different cards!\n", src->span->offset, src->chanpos, dst->span->offset, dst->chanpos); + return -1; + } + } else { + if (debug) + printk("cwain: Assigning %d/%d -> %d/%d, same cwain card!\n", src->span->offset, src->chanpos, dst->span->offset, dst->chanpos); + timeslot = src->channo; +/* if (dacs == 2) { + timeslot = src->channo; + } else { + timeslot = (src->span->offset * 30) + (src->chanpos - 1); + } */ + if (src->span == dst->span) { + use_pcm_bus = 0; + } else { + use_pcm_bus = 1; + } + } + + if (hw_hdlc && ((src->chanpos == 16) || (dst->chanpos == 16))) { + if (debug) + printk("cwain: Not Assigning D-channel %d/%d -> %d/%d!\n", src->span->offset, src->chanpos, dst->span->offset, dst->chanpos); + } else { + cwain_assign(src->span->pvt, src->span->offset, src->chanpos, dst->span->offset, dst->chanpos, timeslot, use_pcm_bus); + if (debug) + printk("cwain: Assigning channel %d/%d -> %d/%d!\n", src->span->offset, src->chanpos, dst->span->offset, dst->chanpos); + } + } else { + cwaintmp = dst->pvt; + if (hw_hdlc && (dst->chanpos == 16)) { + if (debug) + printk("cwain: Not Unassigning D-channel %d/%d!\n", dst->span->offset, dst->chanpos); + } else { + timeslot = dst->channo; +/* if (dacs == 2) { + timeslot = dst->channo; + } else { + timeslot = (dst->span->offset * 30) + (dst->chanpos - 1); + } */ +// printk(KERN_INFO "cwain: unassing chan %d ts_rx %d ts_tx %d ?\n", dst->chanpos - 1, cwaintmp->ts_rx[dst->chanpos - 1], cwaintmp->ts_tx[dst->chanpos - 1]); + if ((cwaintmp->ts_rx[dst->chanpos - 1] > -1) || (cwaintmp->ts_tx[dst->chanpos - 1] > -1)) { + cwain_unassign(cwaintmp, dst->chanpos, timeslot, 1); + if (debug) + printk("cwain: Unassigning channel %d/%d!\n", dst->span->offset, dst->chanpos); + } + } + } + return 0; +} + +static inline void cwain_isr_run(struct zt_cwain *cwaintmp, int ticks) { + int dchan; + int chan = 0; + + if (cwaintmp->span.flags & ZT_FLAG_RUNNING) { + + if (hdlcnet) { + dchan = 0; + } else { + dchan = 15; + } + cwaintmp->chans[dchan].bytes2transmit = 0; + cwaintmp->chans[dchan].eoftx = 0; + if (hw_hdlc) { + cwain_dfifo_check(cwaintmp); + } + + /* oh zaptel! tell us what to transmit... */ + zt_transmit(&cwaintmp->span); + + if (hdlcnet) { + + if (cwaintmp->sync) { + cwain_dfifo_tx(cwaintmp); + } + + /* d-chan data */ + while (cwaintmp->drx > 0) { + cwaintmp->chans[dchan].bytes2receive = 0; + cwaintmp->chans[dchan].eofrx = 0; + if (debug > 3) + printk(KERN_CRIT "drx = %d\n", cwaintmp->drx); + if (cwain_dfifo_rx(cwaintmp)) { + zt_receive(&(cwaintmp->span)); // XXX + } + } + cwaintmp->chans[dchan].bytes2receive = 0; + cwaintmp->chans[dchan].eofrx = 0; + } else { + if (hw_hdlc) { + /* B chans 1-15 mapped to fifos 0-14 */ + /* B chans 17-31 mapped to fifos 15-29 */ + for (chan=0; chan < 31; chan++) { + /* copy to fbuffer */ + if ((ticks < 1) || (ticks > 8)) { + printk(KERN_INFO "cwain: whicked ticks make whicked tricks (%d)\n",cwaintmp->ticks); + } else { + if (chan != dchan) { + memcpy(&cwaintmp->ftxbuf[chan][(ticks-1)*8], cwaintmp->txbuf[chan], ZT_CHUNKSIZE); + } + } + } + + if (cwaintmp->sync) { + cwain_dfifo_tx(cwaintmp); + } + + cwaintmp->chans[dchan].bytes2receive = 0; + cwaintmp->chans[dchan].bytes2transmit = 0; + cwaintmp->chans[dchan].eofrx = 0; + cwaintmp->chans[dchan].eoftx = 0; + + for (chan=0; chan < 31; chan++) { + /* copy from fbuffer */ + if (chan != dchan) { + memcpy(cwaintmp->rxbuf[chan], &cwaintmp->frxbuf[chan][(ticks-1)*8], ZT_CHUNKSIZE); + zt_ec_chunk(&cwaintmp->span.chans[chan], cwaintmp->span.chans[chan].readchunk, cwaintmp->span.chans[chan].writechunk); + } + } + + /* d-chan data */ + if (cwaintmp->drx > 0) { + if (debug > 3) + printk(KERN_CRIT "drx = %d\n", cwaintmp->drx); + cwain_dfifo_rx(cwaintmp); + } + } else { + /* software HDLC */ + for (chan=0; chan < 31; chan++) { + /* copy to fbuffer */ + if ((ticks < 1) || (ticks > 8)) { + printk(KERN_INFO "cwain: whicked ticks make whicked tricks (%d)\n",cwaintmp->ticks); + } else { + memcpy(&cwaintmp->ftxbuf[chan][(ticks-1)*8], cwaintmp->txbuf[chan], ZT_CHUNKSIZE); + } + } + + for (chan=0; chan < 31; chan++) { + /* copy from fbuffer */ + memcpy(cwaintmp->rxbuf[chan], &cwaintmp->frxbuf[chan][(ticks-1)*8], ZT_CHUNKSIZE); + zt_ec_chunk(&cwaintmp->span.chans[chan], cwaintmp->span.chans[chan].readchunk, cwaintmp->span.chans[chan].writechunk); + } + } + } + /* oh zaptel! thou shall receive! */ + zt_receive(&(cwaintmp->span)); + } +} + +static inline void cwain_isr_err(struct zt_cwain *cwaintmp) { + unsigned long flags = 0; + unsigned short crc, vio, ebit, fas; + unsigned short rx_slip, tx_slip; + + spin_lock_irqsave(&cwaintmp->lock,flags); + crc = (cwain_inb(cwaintmp, cwain_R_CRC_ECH) << 8) | cwain_inb(cwaintmp, cwain_R_CRC_ECL); + vio = (cwain_inb(cwaintmp, cwain_R_VIO_ECH) << 8) | cwain_inb(cwaintmp, cwain_R_VIO_ECL); + ebit = (cwain_inb(cwaintmp, cwain_R_E_ECH) << 8) | cwain_inb(cwaintmp, cwain_R_E_ECL); + fas = (cwain_inb(cwaintmp, cwain_R_FAS_ECH) << 8) | cwain_inb(cwaintmp, cwain_R_FAS_ECL); + rx_slip = cwain_inb(cwaintmp, cwain_R_SLIP) & 0x01; + tx_slip = (cwain_inb(cwaintmp, cwain_R_SLIP) >> 4) & 0x01; + if (cwaintmp->sync) { + if (rx_slip) { + cwain_outb(cwaintmp,cwain_R_RX_OFFS,0x06); + if (debug) + printk(KERN_INFO "cwain: cardID %d detected RX slip\n", cwaintmp->cardID); + } + if (tx_slip) { + cwain_outb(cwaintmp,cwain_R_TX_OFFS,0x06); + if (debug) + printk(KERN_INFO "cwain: cardID %d detected TX slip\n", cwaintmp->cardID); + } + } + + cwaintmp->span.crc4count += crc; + cwaintmp->span.bpvcount += vio; + cwaintmp->span.ebitcount += ebit; + cwaintmp->span.fascount += fas; + + if ((debug > 3) && (crc || vio || ebit || fas)) + printk(KERN_INFO "cwain: CRC4 %d BPVIOL %d EBIT %d FAS %d\n", cwaintmp->span.crc4count, cwaintmp->span.bpvcount, cwaintmp->span.ebitcount, cwaintmp->span.fascount); + spin_unlock_irqrestore(&cwaintmp->lock,flags); +} + + +static inline void cwain_audio_run(struct zt_cwain *cwaintmp) { + int fifo=0; + + if (cwaintmp->wdp) { + if (cwaintmp->wdp == 0x04) { + cwaintmp->wdp = 0x08; + } else { + cwaintmp->wdp = 0x04; + } + cwain_outb(cwaintmp, cwain_R_GPIO_OUT0, cwaintmp->wdp); + } + + if (hdlcnet) return; + + if (hw_hdlc) { + for (fifo=0; fifo < 30; fifo++) { + /* B tx */ + cwain_fifo_tx(cwaintmp, fifo); + } + + for (fifo=0; fifo < 30; fifo++) { + /* B rx */ + cwain_fifo_rx(cwaintmp, fifo); + } + } else { + /* software HDLC */ + for (fifo=0; fifo < 31; fifo++) { + /* B tx */ + cwain_fifo_tx(cwaintmp, fifo); + } + + for (fifo=0; fifo < 31; fifo++) { + /* B rx */ + cwain_fifo_rx(cwaintmp, fifo); + } + } +} + +int cwain_isr_sync(struct zt_cwain *cwainspan) { + unsigned char sync_sta; + unsigned char sync_ok = 0; + unsigned char jatt_sta = 0; + unsigned long flags = 0; +// int chan = 0; + int res = 0; /* assume no l1event */ + + if (!cwainspan->span.flags & ZT_FLAG_RUNNING) { + return res; + } + + spin_lock_irqsave(&cwainspan->lock,flags); + sync_sta = cwain_inb(cwainspan, cwain_R_SYNC_STA); + + if ((!cwainspan->sync) || (sync_sta != cwainspan->sync_sta)) { + + if (debug > 2) + printk(KERN_CRIT "cwain: cardID %d R_SYNC_STA =%#x\n", cwainspan->cardID, sync_sta); + + if (cwainspan->span.lineconfig & ZT_CONFIG_CRC4) { + if ((sync_sta & 0x80) == 0x80) { + /* reset MFA detection */ + cwain_outb(cwainspan ,cwain_R_RX_SL0_CFG1,0x41); + } else if ((sync_sta & 0x27) == 0x27) { + if (((cwainspan->sync_sta & 0x27) != 0x27) && cwainspan->span.syncsrc) { + /* sync achieved, restart JATT */ +/* if (debug) + printk(KERN_INFO "cwain: %d starting jitter attenuator\n", cwainspan->cardID); */ +// cwain_outb(cwainspan, cwain_R_JATT_CFG,0x9c); + } + sync_ok = 0x27; + } else { + sync_ok = 0x00; + } + } else { + if ((sync_sta & 0x07) == 0x07) { + if (((cwainspan->sync_sta & 0x7) != 0x7) && cwainspan->span.syncsrc) { + /* sync achieved, restart JATT */ + /* if (debug) + printk(KERN_INFO "cwain: %d starting jitter attenuator\n", cwainspan->cardID); */ +// cwain_outb(cwainspan, cwain_R_JATT_CFG,0x9c); + } + sync_ok = 0x07; + } else { + sync_ok = 0x00; + } + } + + cwainspan->sync_sta = sync_sta; + + if (cwainspan->span.syncsrc) { + jatt_sta = cwain_inb(cwainspan, cwain_R_JATT_STA); + if ((jatt_sta & 0x60) != 0x60) { + if (debug > 2) + printk(KERN_INFO "cwain: %d jitter attenuator %#x\n", cwainspan->cardID, (jatt_sta & 0x60) >> 5); + sync_ok = 0x00; + } else if (!cwainspan->sync && sync_ok) { + if (debug) + printk(KERN_CRIT "cwain: %d jitter attenuator %#x ok!\n", cwainspan->cardID, (jatt_sta & 0x60) >> 5); + } + } + + if (sync_ok && (!cwainspan->sync)) { + if (debug) + printk(KERN_INFO "cwain: cardID %d link up\n", cwainspan->cardID); + + cwain_outb(cwainspan,cwain_R_RX_OFFS,0x06); + cwain_outb(cwainspan,cwain_R_TX_OFFS,0x06); + + cwainspan->span.crc4count = 0; + cwainspan->span.bpvcount = 0; + cwainspan->span.ebitcount = 0; + cwainspan->span.fascount = 0; + cwainspan->span.alarms = ZT_ALARM_NONE; + zt_alarm_notify(&cwainspan->span); + res = 1; + } + if (!sync_ok && cwainspan->sync) { + if (debug) + printk(KERN_INFO "cwain: cardID %d link down\n", cwainspan->cardID); + + cwainspan->span.crc4count = 0; + cwainspan->span.bpvcount = 0; + cwainspan->span.ebitcount = 0; + cwainspan->span.fascount = 0; + cwainspan->span.alarms = ZT_ALARM_RED; + zt_alarm_notify(&cwainspan->span); + res = 1; + } + + cwainspan->sync = sync_ok; + if (sync_ok) { + switch (cwainspan->type) { + case 0xb553: + snprintf(cwainspan->span.desc, sizeof(cwainspan->span.desc), "Junghanns.NET singleE1 PCI ISDN %d (cardID %d) SYNCED",cwainspan->cardno,cwainspan->cardID); + break; + case 0xb554: + snprintf(cwainspan->span.desc, sizeof(cwainspan->span.desc), "Junghanns.NET doubleE1 PCI ISDN %d (cardID %d) (1 E1 port) SYNCED",cwainspan->cardno,cwainspan->cardID); + break; + case 0xb555: + snprintf(cwainspan->span.desc, sizeof(cwainspan->span.desc), "Junghanns.NET doubleE1 PCI ISDN 2.0 %d (cardID %d) (1 E1 port) SYNCED",cwainspan->cardno,cwainspan->cardID); + break; + } + } else { + switch (cwainspan->type) { + case 0xb553: + snprintf(cwainspan->span.desc, sizeof(cwainspan->span.desc), "Junghanns.NET singleE1 PCI ISDN %d (cardID %d) NO SYNC (sync_sta = %#x)",cwainspan->cardno,cwainspan->cardID, sync_sta); + break; + case 0xb554: + snprintf(cwainspan->span.desc, sizeof(cwainspan->span.desc), "Junghanns.NET doubleE1 PCI ISDN %d (cardID %d) (1 E1 port) NO SYNC (sync_sta = %#x)",cwainspan->cardno,cwainspan->cardID, sync_sta); + break; + case 0xb555: + snprintf(cwainspan->span.desc, sizeof(cwainspan->span.desc), "Junghanns.NET doubleE1 PCI ISDN 2.0 %d (cardID %d) (1 E1 port) NO SYNC (sync_sta = %#x)",cwainspan->cardno,cwainspan->cardID, sync_sta); + break; + } + } + cwain_doLEDs(cwainspan); + } + spin_unlock_irqrestore(&cwainspan->lock,flags); + return res; +} + +static int ztcwain_proc_read(struct zt_span *span, char *output) { + struct zt_cwain_card *cwaincard = span->pvt; + struct zt_cwain *cwaintmp; + unsigned long flags; + unsigned char fstate; + + if (cwaincard == NULL) { + printk(KERN_CRIT "cwain: cwaincard == NULL!\n"); + return 0; + } + cwaintmp = cwaincard->span[span->offset]; + if (cwaintmp == NULL) { + printk(KERN_CRIT "cwain: cwaintmp == NULL!\n"); + return 0; + } + + spin_lock_irqsave(&cwaintmp->lock,flags); + fstate = cwain_inb(cwaintmp, cwain_R_E1_RD_STA); + spin_unlock_irqrestore(&cwaintmp->lock,flags); + return sprintf(output, "cwain: span state = F%d.", fstate & 0x7); +} + +/* called locked */ +int cwain_isr_fifo(struct zt_cwain *cwainspan, unsigned char status) { + unsigned char irq_foview,fi; + + if (status & 0x80) { + /* fifo irq */ + irq_foview = cwain_inb(cwainspan,cwain_R_IRQ_OVIEW); + if (irq_foview & 0x80) { + fi = cwain_inb(cwainspan,cwain_R_IRQ_FIFO_BL7); + if (fi & 0x80) { + if (debug > 4) + printk(KERN_CRIT "cwain: fifo 31 RX irq for D channel cardID %d\n", cwainspan->cardID); + } + if (fi & 0x40) { + if (debug > 4) + printk(KERN_CRIT "cwain: fifo 31 TX irq for D channel cardID %d\n", cwainspan->cardID); + } + } + return 1; + } + return 0; +} + +ZAP_IRQ_HANDLER(cwain_dummy_interrupt) { + struct zt_cwain_card *cwaintmp = dev_id; + if (!cwaintmp) { +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + } + if (debug > 3) + printk(KERN_INFO "cwain: dummy irq\n"); +#ifdef LINUX26 + return IRQ_RETVAL(1); +#endif +} + + +ZAP_IRQ_HANDLER(cwain_interrupt) { + struct zt_cwain_card *cwaintmp = dev_id; + unsigned char status, status2, status_tmp, irq_misc, irq_misc2 = 0; + unsigned long flags; + int i = 0; + int l1event = 0; + + if (!cwaintmp || cwaintmp->dead) { +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + } + + spin_lock_irqsave(&(cwaintmp->lock),flags); + status = cwain_inb(cwaintmp->span[0],cwain_R_STATUS); + status2 = 0; + + for (i=0;ispans;i++) { + if (i == 0) { + status_tmp = status; + } else { + status_tmp = cwain_inb(cwaintmp->span[i],cwain_R_STATUS); + status2 = status_tmp; + } + cwain_isr_fifo(cwaintmp->span[i], status_tmp); + } + spin_unlock_irqrestore(&(cwaintmp->lock),flags); + + if (!(status & 0x80) && !(status & 0x40)) { + // it's not us! +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + } + + // misc irq + if (status & 0x40) { + spin_lock_irqsave(&(cwaintmp->lock),flags); + irq_misc = cwain_inb(cwaintmp->span[0],cwain_R_IRQ_MISC); + spin_unlock_irqrestore(&(cwaintmp->lock),flags); + if (irq_misc & 0x2) { + /* cwain timer */ + cwaintmp->ticks++; + if (cwaintmp->ticks == 1) { + for (i=0;ispans;i++) { + cwain_audio_run(cwaintmp->span[i]); + } + } + for (i=0;ispans;i++) { + cwain_isr_run(cwaintmp->span[i], cwaintmp->ticks); + } + if (cwaintmp->ticks == (cwain_FRAME_SIZE / ZT_CHUNKSIZE)) { + cwaintmp->ticks = 0; + } + } + if (irq_misc & 0x1) { + /* state machine */ + if (debug > 4) + printk(KERN_INFO "cwain: state machine irq\n"); + l1event++; + } + if (irq_misc & 0x10) { + for (i=0;ispans;i++) { + cwain_isr_err(cwaintmp->span[i]); + } + if (l1event == 0) { + /* just in case we missed it */ + for (i=0;ispans;i++) { + l1event += cwain_isr_sync(cwaintmp->span[i]); + } + } + } + } + + // misc irq + if (status2 & 0x40) { + if (cwaintmp->spans == 2) { + spin_lock_irqsave(&(cwaintmp->lock),flags); + irq_misc2 = cwain_inb(cwaintmp->span[1],cwain_R_IRQ_MISC); + spin_unlock_irqrestore(&(cwaintmp->lock),flags); + } + if (irq_misc2 & 0x1) { + /* state machine 2 */ + if (debug > 4) + printk(KERN_INFO "cwain: state machine 2 irq\n"); + l1event++; + } + } + + if (l1event > 0) { +// printk(KERN_INFO "cwain: l1event %d\n", l1event); + for (i=0;ispans;i++) { + cwain_isr_sync(cwaintmp->span[i]); + } + if (cwaintmp->spans == 2) { + cwain_check_timing(cwaintmp); + } + } + +#ifdef LINUX26 + return IRQ_RETVAL(1); +#endif +} + +static int ztcwain_open(struct zt_chan *chan) { +// printk(KERN_INFO "cwain: channel %d opened.\n",chan->channo); +#ifndef LINUX26 + MOD_INC_USE_COUNT; +#else + try_module_get(THIS_MODULE); +#endif + return 0; +} + +static int ztcwain_close(struct zt_chan *chan) { +// printk(KERN_INFO "cwain: channel %d closed.\n",chan->channo); +#ifndef LINUX26 + MOD_DEC_USE_COUNT; +#else + module_put(THIS_MODULE); +#endif + return 0; +} + +static int ztcwain_rbsbits(struct zt_chan *chan, int bits) { + return 0; +} + +static int ztcwain_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) { + switch(cmd) { + default: + return -ENOTTY; + } + return 0; +} + +static int ztcwain_startup(struct zt_span *span) { + struct zt_cwain_card *cwaincard = span->pvt; + struct zt_cwain *cwaintmp; + unsigned long flags; + int alreadyrunning; + int i=0; + int idx = 0; + +// printk(KERN_INFO "cwain: startup spanno %d offset %d\n", span->spanno, span->offset); + + if (cwaincard == NULL) { + printk(KERN_CRIT "cwain: cwaincard == NULL!\n"); + return 0; + } + + cwaintmp = cwaincard->span[span->offset]; + if (cwaintmp == NULL) { + printk(KERN_CRIT "cwain: cwaintmp == NULL!\n"); + return 0; + } + + + alreadyrunning = span->flags & ZT_FLAG_RUNNING; +// printk(KERN_CRIT "already running %d flags %d\n", alreadyrunning, span->flags); + + if (!alreadyrunning) { + for (i=0; i<31 ; i++) { + cwaintmp->ts_rx[i] = -1; + cwaintmp->ts_tx[i] = -1; + } + + if (hdlcnet) { + span->chans[0].flags &= ~ZT_FLAG_HDLC; + span->chans[0].flags |= ZT_FLAG_BRIDCHAN; /* yes! */ + + memset(cwaintmp->dtxbuf,0x0,sizeof(cwaintmp->dtxbuf)); + span->chans[0].writechunk = cwaintmp->dtxbuf; + + cwaintmp->chans[0].maxbytes2transmit = 248; +// cwaintmp->chans[0].maxbytes2transmit = 64; + + memset(cwaintmp->drxbuf,0x0,sizeof(cwaintmp->drxbuf)); + span->chans[0].readchunk = cwaintmp->drxbuf; + } else { + if (hw_hdlc) { + span->chans[15].flags &= ~ZT_FLAG_HDLC; + span->chans[15].flags |= ZT_FLAG_BRIDCHAN; /* yes! */ + + /* setup B channel buffers (8 bytes each) */ + for (i=0; i<31 ; i++) { + if (i != 15) { + memset(cwaintmp->rxbuf[i],0x0,sizeof(cwaintmp->rxbuf[i])); + memset(cwaintmp->txbuf[i],0x0,sizeof(cwaintmp->txbuf[i])); + span->chans[i].readchunk = cwaintmp->rxbuf[i]; + span->chans[i].writechunk = cwaintmp->txbuf[i]; + } + } + /* setup D channel buffer */ + memset(cwaintmp->dtxbuf,0x0,sizeof(cwaintmp->dtxbuf)); + span->chans[15].writechunk = cwaintmp->dtxbuf; + cwaintmp->chans[15].maxbytes2transmit = sizeof(cwaintmp->dtxbuf) / 2; + + memset(cwaintmp->drxbuf,0x0,sizeof(cwaintmp->drxbuf)); + span->chans[15].readchunk = cwaintmp->drxbuf; + } else { + /* software HDLC */ + /* setup B channel buffers (8 bytes each) */ + for (i=0; i<31 ; i++) { + memset(cwaintmp->rxbuf[i],0x0,sizeof(cwaintmp->rxbuf[i])); + memset(cwaintmp->txbuf[i],0x0,sizeof(cwaintmp->txbuf[i])); + span->chans[i].readchunk = cwaintmp->rxbuf[i]; + span->chans[i].writechunk = cwaintmp->txbuf[i]; + } + } + } + span->flags |= ZT_FLAG_RUNNING; + } else { + printk(KERN_CRIT "already running\n"); + return 0; + } + + spin_lock_irqsave(&cwaintmp->lock,flags); + /* irqs off */ + cwain_outb(cwaintmp,cwain_R_IRQ_CTRL, 0); + + if (hdlcnet) { + /* map ts 1 to 31 to fifo 31 */ + /* TX fifo */ +// cwain_outb(cwaintmp,cwain_R_FIFO_MD,0x3E); + cwain_outb(cwaintmp,cwain_R_FIRST_FIFO,0x1F << 1); + cwain_waitbusy(cwaintmp); + + idx = 0; + for (i=1; i<=31; i++) { + cwain_outb(cwaintmp,cwain_R_FSM_IDX, idx++); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CHANNEL,i << 1); + if (i == 31) { + cwain_outb(cwaintmp,cwain_A_FIFO_SEQ, (0x1F << 1) | 1); +// if (debug) printk(KERN_INFO "cwain: cardID %d fsm_idx %d channel %d fifo_seq %#x\n", cwaintmp->cardID, idx - 1, i , (0x1f << 1) | 1); + } else { + cwain_outb(cwaintmp,cwain_A_FIFO_SEQ, 0x1F << 1); +// if (debug) printk(KERN_INFO "cwain: cardID %d fsm_idx %d channel %d fifo_seq %#x\n", cwaintmp->cardID, idx - 1, i ,0x1f << 1); + } + } + + /* map ts 1 to 31 to fifo 31 */ + /* RX fifo */ + + for (i=1; i<=31; i++) { + cwain_outb(cwaintmp,cwain_R_FSM_IDX, idx++); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CHANNEL,(i << 1) | 1); + if (i == 31) { + cwain_outb(cwaintmp,cwain_A_FIFO_SEQ, 0x40); +// if (debug) printk(KERN_INFO "cwain: cardID %d fsm_idx %d channel %d fifo_seq %#x\n", cwaintmp->cardID, idx - 1, i, 0x40); + } else { + cwain_outb(cwaintmp,cwain_A_FIFO_SEQ, (0x1F << 1) | 1); +// if (debug) printk(KERN_INFO "cwain: cardID %d fsm_idx %d channel %d fifo_seq %#x\n", cwaintmp->cardID, idx - 1, i, (0x1f << 1) | 1); + } + } + +// cwain_outb(cwaintmp,cwain_R_FIFO_MD,0x36); + + cwain_outb(cwaintmp,cwain_R_FIFO,0x1F << 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0xc); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK, 0x1); + + cwain_outb(cwaintmp,cwain_R_FIFO,(0x1F << 1) | 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0xc); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK, 0x1); + +// cwain_outb(cwaintmp,cwain_R_FIFO_MD,0x3E); + + } else { + if (hw_hdlc) { + /* setup D-FIFO TX */ + cwain_outb(cwaintmp,cwain_R_FIFO,0x1F << 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0xd); + cwain_outb(cwaintmp,cwain_A_SUBCH_CFG,0x0); + cwain_outb(cwaintmp,cwain_A_CHANNEL,0x10 << 1); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x1); + + /* setup D-FIFO RX */ + cwain_outb(cwaintmp,cwain_R_FIFO,(0x1F << 1) | 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0xd); + cwain_outb(cwaintmp,cwain_A_SUBCH_CFG,0x0); + cwain_outb(cwaintmp,cwain_A_CHANNEL,(0x10 << 1) | 1); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x1); + + /* setup B-FIFOs TX */ + /* map ts 1 to 15 to fifos 0 to 14 */ + for (i=1; i<16 ; i++) { + cwain_outb(cwaintmp,cwain_R_FIFO,(i - 1) << 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0x2); + cwain_outb(cwaintmp,cwain_A_CHANNEL,i << 1); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x0); + } + /* map ts 17 to 31 to fifos 15 to 29 */ + for (i=17; i<32 ; i++) { + cwain_outb(cwaintmp,cwain_R_FIFO,(i - 2) << 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0x2); + cwain_outb(cwaintmp,cwain_A_CHANNEL,i << 1); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x0); + } + + /* setup B-FIFOs RX */ + /* map ts 1 to 15 to fifos 0 to 14 */ + for (i=1; i<16 ; i++) { + cwain_outb(cwaintmp,cwain_R_FIFO,((i-1) << 1) | 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0x2); + cwain_outb(cwaintmp,cwain_A_CHANNEL,(i << 1) | 1); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x0); + } + /* map ts 17 to 31 to fifos 15 to 29 */ + for (i=17; i<32 ; i++) { + cwain_outb(cwaintmp,cwain_R_FIFO,((i-2) << 1) | 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0x2); + cwain_outb(cwaintmp,cwain_A_CHANNEL,(i << 1) | 1); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x0); + } + } else { + /* software HDLC */ + /* setup B-FIFOs TX */ + for (i=1; i<32 ; i++) { + cwain_outb(cwaintmp,cwain_R_FIFO,(i - 1) << 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0x2); + cwain_outb(cwaintmp,cwain_A_CHANNEL,i << 1); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x0); + } + /* setup B-FIFOs RX */ + for (i=1; i<32 ; i++) { + cwain_outb(cwaintmp,cwain_R_FIFO,((i-1) << 1) | 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_R_INC_RES_FIFO,0x2); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_CON_HDLC,0x2); + cwain_outb(cwaintmp,cwain_A_CHANNEL,(i << 1) | 1); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x0); + } + + } + } + + if (debug) + printk(KERN_INFO "cwain: starting card %d span %d/%d.\n",cwaintmp->cardno,span->spanno,span->offset); + + if (cwaincard->spans == 1) { + cwain_set_master(cwaincard, 0); + } + + /* setup E1 amplitude */ + cwain_outb(cwaintmp,cwain_R_PWM_MD,0x20); + cwain_outb(cwaintmp,cwain_R_PWM0,pwm0); + cwain_outb(cwaintmp,cwain_R_PWM1,pwm1); + + /* setup E1 transceiver */ + cwain_outb(cwaintmp,cwain_R_TX_SL0,0xf8); + cwain_outb(cwaintmp,cwain_R_TX_SL0_CFG0,0x00); /* semiautomatic mode */ + + cwain_outb(cwaintmp,cwain_R_RX_SL0_CFG0,0x6); + + if (cwaintmp->span.lineconfig & ZT_CONFIG_AMI) { + cwain_outb(cwaintmp,cwain_R_TX0,0x82); + cwain_outb(cwaintmp,cwain_R_RX0,0x02); + } else if (cwaintmp->span.lineconfig & ZT_CONFIG_HDB3) { + cwain_outb(cwaintmp,cwain_R_TX0,0x81); + cwain_outb(cwaintmp,cwain_R_RX0,0x01); + } + cwain_outb(cwaintmp,cwain_R_TX1,0x60); /* transmitter mode */ + + cwain_outb(cwaintmp,cwain_R_LOS0,0x0f); + cwain_outb(cwaintmp,cwain_R_LOS1,0x0f); + + if (cwaintmp->span.lineconfig & ZT_CONFIG_CRC4) { + /* crc4 multiframe */ + cwain_outb(cwaintmp,cwain_R_TX_SL0_CFG1,0x31); + cwain_outb(cwaintmp,cwain_R_RX_SL0_CFG1,0x03); + } else { + /* doubleframe */ + cwain_outb(cwaintmp,cwain_R_TX_SL0_CFG1,0x0); + cwain_outb(cwaintmp,cwain_R_RX_SL0_CFG1,0x0); + } + + + /* automatic JATT */ + cwain_outb(cwaintmp, cwain_R_JATT_CFG,0x9c); + + /* setup sync mode */ + if (cwaincard->syncs[span->offset] > 0) { + cwain_outb(cwaintmp,cwain_R_SYNC_CTRL,0x2); // phase offset arb. + cwain_outb(cwaintmp,cwain_R_SYNC_OUT,0x00); // sync from e1 tx + /* layer 1, here we go! */ + cwain_outb(cwaintmp,cwain_R_E1_WR_STA,0x00); + } else { + cwain_outb(cwaintmp,cwain_R_SYNC_CTRL,0x5); // pcm_f0IO + cwain_outb(cwaintmp,cwain_R_SYNC_OUT,0x00); // sync from e1 tx + /* layer 1, up! */ + cwain_outb(cwaintmp,cwain_R_E1_WR_STA,0x11); + } + + /* elastic buffer offsets */ + cwain_outb(cwaintmp,cwain_R_RX_OFFS,0x06); + cwain_outb(cwaintmp,cwain_R_TX_OFFS,0x06); + + cwaintmp->sync = 0; + cwaintmp->sync_sta = 0; + + /* enable irqs */ + if (!span->offset) { + cwain_outb(cwaintmp,cwain_R_IRQ_CTRL, 8 | 1); + } else { + cwain_outb(cwaintmp,cwain_R_IRQ_CTRL, 0x1); + } + spin_unlock_irqrestore(&cwaintmp->lock,flags); + return 0; +} + +static int ztcwain_shutdown(struct zt_span *span) { + struct zt_cwain_card *cwaincard = span->pvt; + struct zt_cwain *cwaintmp; + unsigned long flags; + int alreadyrunning; + int i=0; + + if (cwaincard == NULL) { + printk(KERN_CRIT "cwain: cwaincard == NULL!\n"); + return 0; + } + + cwaintmp = cwaincard->span[span->offset]; + if (cwaintmp == NULL) { + printk(KERN_CRIT "cwain: cwaintmp == NULL!\n"); + return 0; + } + + alreadyrunning = span->flags & ZT_FLAG_RUNNING; + + if (!alreadyrunning) { + return 0; + } + spin_lock_irqsave(&cwaintmp->lock,flags); + +// printk(KERN_CRIT "cwain: stopping card %d span %d/%d.\n",cwaintmp->cardno,span->spanno,span->offset); + + // turn off irqs for all fifos + if (hdlcnet) { + cwain_outb(cwaintmp,cwain_R_FIFO,(0x1F << 1) | 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x0); + } else { + /* disable FIFO TX */ + for (i=0; i<0x20 ; i++) { + cwain_outb(cwaintmp,cwain_R_FIFO,i << 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x0); + } + + /* disable FIFO RX */ + for (i=0; i<0x20 ; i++) { + cwain_outb(cwaintmp,cwain_R_FIFO,(i << 1) | 1); + cwain_waitbusy(cwaintmp); + cwain_outb(cwaintmp,cwain_A_IRQ_MSK,0x0); + } + } + + /* Deactivate Layer 1 */ + cwain_outb(cwaintmp,cwain_R_E1_WR_STA,0x10); + + /* Disable transmit */ + if (cwaintmp->span.lineconfig & ZT_CONFIG_AMI) { + cwain_outb(cwaintmp,cwain_R_TX0,0x02); + } else if (cwaintmp->span.lineconfig & ZT_CONFIG_HDB3) { + cwain_outb(cwaintmp,cwain_R_TX0,0x01); + } + + cwain_outb(cwaintmp,cwain_R_IRQ_CTRL, 0); + cwain_inb(cwaintmp,cwain_R_STATUS); + + cwain_outb(cwaintmp,cwain_R_GPIO_EN0,0x00); + cwain_outb(cwaintmp,cwain_R_GPIO_EN1,0x00); + + + span->flags &= ~ZT_FLAG_RUNNING; + + + spin_unlock_irqrestore(&cwaintmp->lock,flags); + +// printk(KERN_CRIT "cwain: card %d span %d/%d down.\n",cwaintmp->cardno,span->spanno,span->offset); + return 0; +} + +static int ztcwain_maint(struct zt_span *span, int cmd) { + return 0; +} + +static int ztcwain_chanconfig(struct zt_chan *chan,int sigtype) { +// printk(KERN_INFO "chan_config sigtype=%d\n", sigtype); + return 0; +} + +static int ztcwain_spanconfig(struct zt_span *span,struct zt_lineconfig *lc) { + struct zt_cwain_card *cwaincard = span->pvt; + int alreadyrunning; + + alreadyrunning = span->flags & ZT_FLAG_RUNNING; + + if (!alreadyrunning) { + span->lineconfig = lc->lineconfig; + span->syncsrc = lc->sync; + + cwaincard->syncs[span->offset] = lc->sync; + cwaincard->syncsrc = -1; + } +// printk(KERN_INFO "span_config %d lineconfig=%d syncsrc=%d\n", span->spanno, lc->lineconfig, lc->sync); +// cwain_check_timing(cwaincard); + return 0; +} + +static int ztcwain_initialize(struct zt_cwain *cwaintmp, struct zt_cwain_card *cwaincard, int offset) { + int i=0; + + memset(&cwaintmp->span,0,sizeof(struct zt_span)); // you never can tell... + sprintf(cwaintmp->span.name,"cwain/%d",cwaintmp->cardno); + switch (cwaintmp->type) { + case 0xb553: + snprintf(cwaintmp->span.desc, sizeof(cwaintmp->span.desc), "Junghanns.NET singleE1 PCI ISDN %d (cardID %d)",cwaintmp->cardno,cwaintmp->cardID); + break; + case 0xb554: + snprintf(cwaintmp->span.desc, sizeof(cwaintmp->span.desc), "Junghanns.NET doubleE1 PCI ISDN %d (cardID %d) (1 E1 port)",cwaintmp->cardno,cwaintmp->cardID); + break; + case 0xb555: + snprintf(cwaintmp->span.desc, sizeof(cwaintmp->span.desc), "Junghanns.NET doubleE1 PCI ISDN 2.0 %d (cardID %d) (1 E1 port)",cwaintmp->cardno,cwaintmp->cardID); + cwaintmp->wdp = 0x04; + break; + default: + return -1; + } + cwaintmp->span.spanconfig = ztcwain_spanconfig; + cwaintmp->span.chanconfig = ztcwain_chanconfig; + cwaintmp->span.startup = ztcwain_startup; + cwaintmp->span.shutdown = ztcwain_shutdown; + cwaintmp->span.maint = ztcwain_maint; + cwaintmp->span.rbsbits = ztcwain_rbsbits; + cwaintmp->span.open = ztcwain_open; + cwaintmp->span.close = ztcwain_close; + cwaintmp->span.ioctl = ztcwain_ioctl; + cwaintmp->span.proc_read = ztcwain_proc_read; + cwaintmp->span.dacs = ztcwain_dacs; + + cwaintmp->span.chans = cwaintmp->chans; + if (hdlcnet) { + cwaintmp->span.channels = 1; + } else { + cwaintmp->span.channels = 31; + } + cwaintmp->span.deflaw = ZT_LAW_ALAW; + cwaintmp->span.linecompat = ZT_CONFIG_AMI | ZT_CONFIG_HDB3 | ZT_CONFIG_CCS; + init_waitqueue_head(&cwaintmp->span.maintq); + cwaintmp->span.pvt = cwaincard; + cwaintmp->span.offset = offset; + + for (i=0; i < cwaintmp->span.channels; i++) { + memset(&(cwaintmp->chans[i]),0x0,sizeof(struct zt_chan)); + sprintf(cwaintmp->chans[i].name,"cwain%d/%d",cwain_span_count + 1,i + 1); + cwaintmp->chans[i].pvt = cwaintmp; + cwaintmp->chans[i].sigcap = ZT_SIG_CLEAR | ZT_SIG_DACS; + cwaintmp->chans[i].chanpos = i + 1; + } + + if (zt_register(&cwaintmp->span,0)) { + printk(KERN_INFO "cwain: unable to register zaptel span!\n"); + return -1; + } +// printk(KERN_INFO "cwain: registered zaptel span %d.\n",s+1); + + return 0; +} + +int cwain_reset_card(struct zt_cwain_card *cwaintmp) { + unsigned long flags; + int i = 0; + + cwaintmp->irq = cwaintmp->span[0]->irq; + + if (cwaintmp->spans == 2) { + if (request_irq(cwaintmp->irq, cwain_interrupt, ZAP_IRQ_SHARED, "cwain2", cwaintmp)) { + printk(KERN_WARNING "cwain: unable to register irq\n"); + return -1; + } + if (request_irq(cwaintmp->span[1]->irq, cwain_dummy_interrupt, ZAP_IRQ_SHARED, "cwaindummy", cwaintmp)) { + printk(KERN_WARNING "cwain: unable to register irq\n"); + return -1; + } + } else { + if (request_irq(cwaintmp->irq, cwain_interrupt, ZAP_IRQ_SHARED, "cwain", cwaintmp)) { + printk(KERN_WARNING "cwain: unable to register irq\n"); + return -1; + } + } + + spin_lock_irqsave(&(cwaintmp->lock),flags); + + for (i=0;ispans;i++) { + cwain_reset_span(cwaintmp->span[i]); + } + + /* no master yet, force reelection */ + cwaintmp->syncsrc = -1; + +#if (ZT_CHUNKSIZE == 32) + /* set up the timer 250 Hz, zaptel timing */ + cwain_outb(cwaintmp->span[0],cwain_R_TI_WD, 0x4); +#endif +#if (ZT_CHUNKSIZE == 8) + /* set up the timer 1 khz, zaptel timing */ + cwain_outb(cwaintmp->span[0],cwain_R_TI_WD, 0x2); +#endif + + if (cwaintmp->spans == 2) { +// cwain_outb(cwaintmp->span[1],cwain_R_IRQMSK_MISC, 0x1); + cwain_outb(cwaintmp->span[1],cwain_R_IRQMSK_MISC, 0x0); + } + /* enable timer interrupts */ + cwain_outb(cwaintmp->span[0],cwain_R_IRQMSK_MISC, 0x13); + + /* Finally enable IRQ output */ +// cwain_outb(cwaintmp->span[0],cwain_R_IRQ_CTRL, 0x8 | 0x1); + + spin_unlock_irqrestore(&(cwaintmp->lock),flags); + return 0; +} + +int cwain_find_spans(unsigned int pcidid) { + struct pci_dev *tmp; + struct zt_cwain *cwaintmp = NULL; + int i=0; + unsigned char dips=0; + int cid=0; + int modes=0; + tmp = pci_get_device(PCI_VENDOR_ID_CCD,pcidid,multi_cwain); + while (tmp != NULL) { + multi_cwain = tmp; // skip this next time. + + if (pci_enable_device(tmp)) { + multi_cwain = NULL; + return -1; + } + + cwaintmp = kmalloc(sizeof(struct zt_cwain),GFP_KERNEL); + if (!cwaintmp) { + printk(KERN_WARNING "cwain: unable to kmalloc!\n"); + pci_disable_device(tmp); + multi_cwain = NULL; + return -ENOMEM; + } + memset(cwaintmp, 0x0, sizeof(struct zt_cwain)); + + spin_lock_init(&cwaintmp->lock); + cwaintmp->pcidev = tmp; + cwaintmp->pcibus = tmp->bus->number; + cwaintmp->pcidevfn = tmp->devfn; + + + cwaintmp->pci_io_phys = (char *) tmp->resource[1].start; + if (!cwaintmp->pci_io_phys) { + printk(KERN_WARNING "cwain: no iomem!\n"); + pci_disable_device(tmp); + multi_cwain = NULL; + return -EIO; + } + + if (!tmp->irq) { + printk(KERN_WARNING "cwain: PCI device has no irq!\n"); + pci_disable_device(tmp); + multi_cwain = NULL; + return -EIO; + } + + cwaintmp->ioport = tmp->resource[0].start; + if (!cwaintmp->ioport) { + printk(KERN_WARNING "cwain: no ioport!\n"); + pci_disable_device(tmp); + multi_cwain = NULL; + return -EIO; + } + if (!request_region(cwaintmp->ioport, 8, "cwain")) { + printk(KERN_WARNING "cwain: couldnt request io range!\n"); + pci_disable_device(tmp); + multi_cwain = NULL; + return -EIO; + } + + if (!request_mem_region((unsigned long) cwaintmp->pci_io_phys, 256, "cwain")) { + printk(KERN_WARNING "cwain: couldnt request io mem range!\n"); + release_region(cwaintmp->ioport, 8); + pci_disable_device(tmp); + multi_cwain = NULL; + return -EIO; + } + + cwaintmp->irq = tmp->irq; + + cwaintmp->pci_io = ioremap((ulong) cwaintmp->pci_io_phys, 256); + + /* enable memio */ + pci_write_config_word(cwaintmp->pcidev, PCI_COMMAND, PCI_COMMAND_MEMORY | PCI_COMMAND_IO); + + /* disable interrupts */ + cwain_outb(cwaintmp,cwain_R_IRQ_CTRL, 0); + + if (((tmp->subsystem_device==0xb553)|| (tmp->subsystem_device==0xb554) || (tmp->subsystem_device==0xb555))&& (pcidid == PCI_DEVICE_ID_CCD_E)) { + dips = (cwain_inb(cwaintmp,cwain_R_GPI_IN0) >> 5); + cid = 7; + for (i=0;i<3;i++) { + if ((dips & (1 << i)) != 0) { + cid -= (1 << (2-i)); + } + } +// printk(KERN_INFO "DIPS = %#x CID= %#x\n",dips,cid); + } else { + cid = 0xff; + } + + if (ports == -1) { + if ((tmp->subsystem_device==0xb520) && (pcidid == PCI_DEVICE_ID_CCD_E)) { + modes = (cwain_inb(cwaintmp,cwain_R_GPI_IN0) >> 4) & 0x01; + } else { + modes = 0; // assume TE mode + } + } else { + modes = ports >> cwain_span_count; + } + + + cwaintmp->cardID = cid; + cwaintmp->type = tmp->subsystem_device; + + if ((modes & 1) != 0) { + cwaintmp->nt_mode = 1; + } else { + cwaintmp->nt_mode = 0; + } + + cwain_register_span(cwaintmp); + + tmp = pci_get_device(PCI_VENDOR_ID_CCD,pcidid,multi_cwain); + } + return 0; +} + + +int cwain_sort_cards(void) { + int changed=0,tmpcardno; + struct zt_cwain_card *tmpcard,*tmpcard2; + spin_lock(&cwain_card_registerlock); + do { + changed = 0; + tmpcard = cwain_card_list; + while (tmpcard != NULL) { + if (tmpcard->prev) { + if (tmpcard->prev->cardID > tmpcard->cardID) { + tmpcardno = tmpcard->prev->cardno; + tmpcard->prev->cardno = tmpcard->cardno; + tmpcard->cardno = tmpcardno; + + tmpcard2 = tmpcard->prev; + if (tmpcard2->prev) { + tmpcard2->prev->next = tmpcard; + } else { + cwain_card_list = tmpcard; + } + if (tmpcard->next) { + tmpcard->next->prev = tmpcard2; + } + tmpcard2->next = tmpcard->next; + tmpcard->prev = tmpcard2->prev; + tmpcard->next = tmpcard2; + tmpcard2->prev = tmpcard; + changed = 1; + tmpcard = tmpcard2; + } + } + tmpcard = tmpcard->next; + } + } while (changed == 1); + spin_unlock(&cwain_card_registerlock); + return 0; +} + +int cwain_zap_cards(void) { + struct zt_cwain_card *tmpcard = cwain_card_list; + int i=0; + int res=0; + while (tmpcard != NULL) { + for (i=0; ispans; i++) { + ztcwain_initialize(tmpcard->span[i], tmpcard, i); + } + res = cwain_reset_card(tmpcard); + tmpcard = tmpcard->next; + } + return res; +} + + +int init_module(void) { + if (hdlcnet) { + hw_hdlc = 1; /* otherwise it makes no sense at all... */ + dacs = 0; + } + multi_cwain = NULL; + cwain_find_spans(PCI_DEVICE_ID_CCD_E); + cwain_sort_cards(); + cwain_zap_cards(); + if (cwain_card_count == 0) { + printk(KERN_INFO "cwain: no cwain cards found.\n"); + } else { + printk(KERN_INFO "cwain: %d cwain card(s) in this box, %d E1 ports total, hw_hdcl = %d, dacs =%d, require_ext_clock = %d, ZT_CHUNKSIZE = %d, timer = %d.\n", cwain_card_count, cwain_span_count, hw_hdlc, dacs, require_ext_clock, ZT_CHUNKSIZE, cwain_TIMER_INT); + } + return 0; +} + +void cleanup_module(void) { + struct zt_cwain_card *tmpcard,*tmplist; + struct zt_cwain *tmpspan,*spanlist; + int i=0; + int j=0; + + tmplist = cwain_card_list; + tmpcard = NULL; + while (tmplist) { + tmpcard = tmplist; + tmplist = tmplist->next; + + tmpcard->dead = 1; + cwain_shutdown_card(tmpcard); + kfree(tmpcard); + i++; + } + + spanlist = cwain_span_list; + tmpspan = NULL; + while (spanlist) { + tmpspan = spanlist; + spanlist = spanlist->next; + kfree(tmpspan); + j++; + } + printk(KERN_INFO "cwain: shutdown %d spans, %d cwain cards.\n", j, i); +} +#endif + +#ifdef LINUX26 +module_param(ports, int, 0600); +module_param(debug, int, 0600); +module_param(hw_hdlc, int, 0600); +module_param(hdlcnet, int, 0600); +module_param(pwm0, int, 0600); +module_param(pwm1, int, 0600); +module_param(dacs, int, 0600); +module_param(require_ext_clock, int, 0600); +#else +MODULE_PARM(ports,"i"); +MODULE_PARM(debug,"i"); +MODULE_PARM(hw_hdlc,"i"); +MODULE_PARM(hdlcnet,"i"); +MODULE_PARM(pwm0,"i"); +MODULE_PARM(pwm1,"i"); +MODULE_PARM(dacs,"i"); +MODULE_PARM(require_ext_clock,"i"); +#endif + +MODULE_DESCRIPTION("cwain zaptel driver"); +MODULE_AUTHOR("Klaus-Peter Junghanns "); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif --- zaptel-1.4.11~dfsg.orig/kernel/cwain/zapata.conf +++ zaptel-1.4.11~dfsg/kernel/cwain/zapata.conf @@ -0,0 +1,32 @@ +; +; Zapata telephony interface +; +; Configuration file + +[channels] +; +; Default language +; +;language=en +; +; Default context +; +; +switchtype = euroisdn + +signalling = pri_cpe + +pridialplan = local +prilocaldialplan = dynamic +nationalprefix = 0 +internationalprefix = 00 + +priindication = passthrough + +echocancel = yes + +context=demo + +group = 1 +channel => 1-15,17-31 + --- zaptel-1.4.11~dfsg.orig/kernel/cwain/zaptel.conf.hdlcnet +++ zaptel-1.4.11~dfsg/kernel/cwain/zaptel.conf.hdlcnet @@ -0,0 +1,8 @@ +loadzone=nl +defaultzone=nl +span=1,1,3,ccs,hdb3,crc4 + +; hdlc0 +hdlcnet=1 + + --- zaptel-1.4.11~dfsg.orig/kernel/cwain/Makefile +++ zaptel-1.4.11~dfsg/kernel/cwain/Makefile @@ -0,0 +1,88 @@ +KINCLUDES = /usr/src/linux/include +BRISTUFFBASE = $(shell dirname `pwd`) + +ZAP = $(shell [ -f $(BRISTUFFBASE)/zaptel/zaptel.h ] && echo "-I$(BRISTUFFBASE)/zaptel") + +CFLAGS+=-I. $(ZAP) -DRELAXED_LOCKING -O2 -g -Wall #-DBLINKYBLINK +CFLAGS+=$(shell if uname -m | grep -q ppc; then echo "-fsigned-char"; fi) + +KFLAGS=-D__KERNEL__ -DMODULE -DEXPORT_SYMTAB -DRELAXED_LOCKING -fomit-frame-pointer -O2 -Wall -I$(KINCLUDES) $(ZAP) +KFLAGS+=$(shell [ -f $(KINCLUDES)/linux/modversions.h ] && echo "-DMODVERSIONS -include $(KINCLUDES)/linux/modversions.h") +KFLAGS+=$(shell if uname -m | grep -q ppc; then echo "-msoft-float -fsigned-char"; fi) + +OBJS=cwain.o + +BUILDVER=$(shell if uname -r | grep -q ^2.6; then echo "linux26"; else echo "linux24"; fi) + +MODCONF=$(shell if [ -d $(INSTALL_PREFIX)/etc/modprobe.d ]; then echo "$(INSTALL_PREFIX)/etc/modprobe.d/zaptel"; elif [ -d $(INSTALL_PREFIX)/etc/modutils ]; then echo "$(INSTALL_PREFIX)/etc/modutils/zaptel"; elif [ -f $(INSTALL_PREFIX)/etc/modprobe.conf ]; then echo "$(INSTALL_PREFIX)/modprobe.conf"; elif [ -f $(INSTALL_PREFIX)/etc/modules.conf ]; then echo "$(INSTALL_PREFIX)/etc/modules.conf"; else echo $(INSTALL_PREFIX)/etc/conf.modules ; fi) + +MODULES=cwain + +MODULESO=$(shell for x in $(MODULES); do echo "$$x.o "; done ) +MODULESKO=$(shell for x in $(MODULES); do echo "$$x.ko "; done ) + +PWD=$(shell pwd) + +obj-m := $(MODULESO) + +all: $(BUILDVER) + +linux24: $(OBJS) + sync + +linux26: + @if ! [ -d /usr/src/linux-2.6 ]; then echo "Link /usr/src/linux-2.6 to your kernel sources first!"; exit 1 ; fi + make -C /usr/src/linux-2.6 SUBDIRS=$(PWD) ZAP=$(ZAP) modules +obj-m := $(OBJS) + +cwain.o: cwain.c cwain.h + $(CC) -c cwain.c $(KFLAGS) + +clean: + rm -f $(OBJS) *.ko *.mod.c *.mod.o .*o.cmd *~ + +testlinux24: all + modprobe zaptel + insmod ./cwain.o + ztcfg -v + cat /proc/interrupts + sleep 1 + cat /proc/interrupts + rmmod cwain zaptel + +testlinux26: all + modprobe zaptel + insmod ./cwain.ko + ztcfg -v + cat /proc/interrupts + sleep 5 + cat /proc/interrupts + rmmod cwain zaptel + +reload: unload load +load: load$(BUILDVER) + +test: test$(BUILDVER) + + +loadlinux24: linux24 + modprobe zaptel + insmod ./cwain.o + ztcfg -v + +loadlinux26: linux26 + sync + modprobe zaptel + insmod ./cwain.ko debug=1 + ztcfg -v + +install: install$(BUILDVER) + +installlinux26: + install -D -m 644 cwain.ko $(INSTALL_PREFIX)/lib/modules/`uname -r`/misc/cwain.ko + +installlinux24: + install -D -m 644 cwain.o $(INSTALL_PREFIX)/lib/modules/`uname -r`/misc/cwain.o + +unload: + rmmod cwain zaptel --- zaptel-1.4.11~dfsg.orig/kernel/cwain/cwain.h +++ zaptel-1.4.11~dfsg/kernel/cwain/cwain.h @@ -0,0 +1,252 @@ +#define cwain_FIFO_SIZE 128 +#define cwain_DFIFO_SIZE 4096 +#define cwain_DFIFO_FRAMES 0x10 +#define cwain_FRAME_SIZE 16 /* has to be %4==0 */ +#define cwain_FIFO_HW cwain_FRAME_SIZE * 2 + ZT_CHUNKSIZE +#define cwain_TIMER_INT (8000 / ZT_CHUNKSIZE) + + +typedef struct zt_cwain { + /* pci resources */ + unsigned int irq; + unsigned int iomem; + unsigned long ioport; + unsigned char *pci_io; + void *pci_io_phys; + unsigned int pcibus; + unsigned int pcidevfn; + struct pci_dev *pcidev; + unsigned int type; + + /* lock to protect the structure */ + spinlock_t lock; + int cardID; + unsigned char cardno; + + /* ticks and clicks, fish and chips */ + unsigned int ticks; + unsigned int clicks; + unsigned char nt_mode; + unsigned char sync_sta; + unsigned char sync; + int syncmode; + + /* blinky blink */ + unsigned char leds[4]; + unsigned int wdp; + + /* B chan buffers */ + unsigned char rxbuf[31][ZT_CHUNKSIZE]; + unsigned char txbuf[31][ZT_CHUNKSIZE]; + + signed short ts_rx[31]; + signed short ts_tx[31]; + + /* buffers */ + unsigned char frxbuf[31][cwain_FRAME_SIZE]; + unsigned char ftxbuf[31][cwain_FRAME_SIZE]; + + /* number of RXed dchan frames */ + unsigned char drx; + /* D chan buffer */ + unsigned char drxbuf[cwain_DFIFO_SIZE]; + unsigned char dtxbuf[cwain_DFIFO_SIZE]; + + + /* zaptel resources */ + struct zt_span span; + struct zt_chan chans[31]; + + /* more zaptel stuff */ + unsigned int usecount; + int spantype; + int spanflags; + + /* linked list */ + struct zt_cwain *next; + struct zt_cwain *prev; + +} zt_cwain; + +typedef struct zt_cwain_card { + /* lock to protect the structure */ + spinlock_t lock; + + unsigned int spans; + unsigned int dead; + int cardID; + unsigned char cardno; + unsigned int ticks; + + struct zt_cwain *span[2]; + int syncsrc; + int syncs[2]; + int master[2]; + + unsigned int irq; + unsigned int pcibus; + + /* linked list */ + struct zt_cwain_card *next; + struct zt_cwain_card *prev; +} zt_cwain_card; + +#define cwain_outb(a,b,c) (writeb((c),(a)->pci_io+(b))) +#define cwain_inb(a,b) (readb((a)->pci_io+(b))) + +#define cwain_outw(a,b,c) (writew((c),(a)->pci_io+(b))) +#define cwain_inw(a,b) (readw((a)->pci_io+(b))) + +#define cwain_outdw(a,b,c) (writel((c),(a)->pci_io+(b))) +#define cwain_indw(a,b) (readl((a)->pci_io+(b))) + + +/* Write only registers */ +#define cwain_A_CH_MSK 0xF4 +#define cwain_A_CHANNEL 0xFC +#define cwain_A_CON_HDLC 0xFA +#define cwain_A_CONF 0xD1 +#define cwain_A_FIFO_SEQ 0xFD +#define cwain_R_INC_RES_FIFO 0x0E +#define cwain_A_IRQ_MSK 0xFF +#define cwain_A_SL_CFG 0xD0 +#define cwain_A_SUBCH_CFG 0xFB +#define cwain_R_BERT_WD_MD 0x1B +#define cwain_R_BRG_PCM_CFG 0x02 +#define cwain_R_CIRM 0x00 +#define cwain_R_CONF_EN 0x18 +#define cwain_R_CTRL 0x01 +#define cwain_R_DTMF 0x1C +#define cwain_R_DTMF_N 0x1D +#define cwain_R_E1_WR_STA 0x20 +#define cwain_R_FIFO_MD 0x0D +#define cwain_R_FIFO 0x0F +#define cwain_R_FIRST_FIFO 0x0B +#define cwain_R_FSM_IDX 0x0F +#define cwain_R_GPIO_EN0 0x42 +#define cwain_R_GPIO_EN1 0x43 +#define cwain_R_GPIO_OUT0 0x40 +#define cwain_R_GPIO_OUT1 0x41 +#define cwain_R_GPIO_SEL 0x44 +#define cwain_R_IRQ_CTRL 0x13 +#define cwain_R_IRQMSK_MISC 0x11 +#define cwain_R_JATT_CFG 0x2F +#define cwain_R_LOS0 0x22 +#define cwain_R_LOS1 0x23 +#define cwain_R_PCM_MD0 0x14 +#define cwain_R_PCM_MD1 0x15 +#define cwain_R_PCM_MD2 0x15 +#define cwain_R_PWM_MD 0x46 +#define cwain_R_PWM0 0x38 +#define cwain_R_PWM1 0x39 +#define cwain_R_RAM_ADDR0 0x08 +#define cwain_R_RAM_ADDR1 0x09 +#define cwain_R_RAM_ADDR2 0x0A +#define cwain_R_RAM_MISC 0x0C +#define cwain_R_RX_OFFS 0x30 +#define cwain_R_RX_SL0_CFG0 0x25 +#define cwain_R_RX_SL0_CFG1 0x26 +#define cwain_R_RX0 0x24 +#define cwain_R_SH0H 0x15 +#define cwain_R_SH0L 0x15 +#define cwain_R_SH1H 0x15 +#define cwain_R_SH1L 0x15 +#define cwain_R_SL_SEL0 0x15 +#define cwain_R_SL_SEL1 0x15 +#define cwain_R_SL_SEL2 0x15 +#define cwain_R_SL_SEL3 0x15 +#define cwain_R_SL_SEL4 0x15 +#define cwain_R_SL_SEL5 0x15 +#define cwain_R_SL_SEL6 0x15 +#define cwain_R_SL_SEL7 0x15 +#define cwain_R_SLOT 0x10 +#define cwain_R_SYNC_CTRL 0x35 +#define cwain_R_SYNC_OUT 0x31 +#define cwain_R_TI_WD 0x1A +#define cwain_R_TX_OFFS 0x34 +#define cwain_R_TX_SL0_CFG0 0x2C +#define cwain_R_TX_SL0_CFG1 0x2E +#define cwain_R_TX_SL0 0x2D +#define cwain_R_TX0 0x28 +#define cwain_R_TX1 0x29 + +#define cwain_R_TX_FR0 0x2C +#define cwain_R_TX_FR1 0x2D +#define cwain_R_TX_FR2 0x2E + +#define cwain_R_RX_FR0 0x25 +#define cwain_R_RX_FR1 0x26 + +/* Read only registers */ +#define cwain_A_F1 0x0C +#define cwain_A_F12 0x0C +#define cwain_A_F2 0x0D +#define cwain_A_Z1 0x04 +#define cwain_A_Z12 0x04 +#define cwain_A_Z1H 0x05 +#define cwain_A_Z1L 0x04 +#define cwain_A_Z2 0x06 +#define cwain_A_Z2H 0x07 +#define cwain_A_Z2L 0x06 +#define cwain_R_BERT_ECH 0x1B +#define cwain_R_BERT_ECL 0x1A +#define cwain_R_BERT_STA 0x17 +#define cwain_R_CHIP_ID 0x16 +#define cwain_R_CHIP_RV 0x1F +#define cwain_R_CONF_OFLOW 0x14 +#define cwain_R_CRC_ECH 0x35 +#define cwain_R_CRC_ECL 0x34 +#define cwain_R_E_ECH 0x37 +#define cwain_R_E_ECL 0x36 +#define cwain_R_E1_RD_STA 0x20 +#define cwain_R_F0_CNTH 0x19 +#define cwain_R_F0_CNTL 0x18 +#define cwain_R_FAS_ECH 0x31 +#define cwain_R_FAS_ECL 0x30 +#define cwain_R_GPI_IN0 0x44 +#define cwain_R_GPI_IN1 0x45 +#define cwain_R_GPI_IN2 0x46 +#define cwain_R_GPI_IN3 0x47 +#define cwain_R_GPIO_IN0 0x40 +#define cwain_R_GPIO_IN1 0x41 +#define cwain_R_INT_DATA 0x88 +#define cwain_R_IRQ_FIFO_BL0 0xC8 +#define cwain_R_IRQ_FIFO_BL1 0xC9 +#define cwain_R_IRQ_FIFO_BL2 0xCA +#define cwain_R_IRQ_FIFO_BL3 0xCB +#define cwain_R_IRQ_FIFO_BL4 0xCC +#define cwain_R_IRQ_FIFO_BL5 0xCD +#define cwain_R_IRQ_FIFO_BL6 0xCE +#define cwain_R_IRQ_FIFO_BL7 0xCF +#define cwain_R_IRQ_MISC 0x11 +#define cwain_R_IRQ_OVIEW 0x10 +#define cwain_R_JATT_STA 0x2B +#define cwain_R_RAM_USE 0x15 +#define cwain_R_RX_SL0_0 0x25 +#define cwain_R_RX_SL0_1 0x26 +#define cwain_R_RX_SL0_2 0x27 +#define cwain_R_SA6_VAL13_ECH 0x39 +#define cwain_R_SA6_VAL13_ECL 0x38 +#define cwain_R_SA6_VAL23_ECH 0x3B +#define cwain_R_SA6_VAL23_ECL 0x3A +#define cwain_R_SLIP 0x2C +#define cwain_R_STATUS 0x1C +#define cwain_R_SYNC_STA 0x24 +#define cwain_R_VIO_ECH 0x33 +#define cwain_R_VIO_ECL 0x32 + + +/* Read/Write registers */ +#define cwain_A_FIFO_DATA0_NOINC 0x84 +#define cwain_A_FIFO_DATA0 0x80 +#define cwain_A_FIFO_DATA1_NOINC 0x84 +#define cwain_A_FIFO_DATA1 0x80 +#define cwain_A_FIFO_DATA2_NOINC 0x84 +#define cwain_A_FIFO_DATA2 0x80 +#define cwain_R_RAM_DATA 0xC0 + +#define PCI_DEVICE_ID_CCD_E 0x30b1 + +#define CLKDEL_TE 0xe /* CLKDEL in TE mode */ +#define CLKDEL_NT 0xc /* CLKDEL in NT mode */ + --- zaptel-1.4.11~dfsg.orig/kernel/cwain/TODO +++ zaptel-1.4.11~dfsg/kernel/cwain/TODO @@ -0,0 +1 @@ + --- zaptel-1.4.11~dfsg.orig/kernel/cwain/zapata.conf.doubleE1 +++ zaptel-1.4.11~dfsg/kernel/cwain/zapata.conf.doubleE1 @@ -0,0 +1,35 @@ +; +; Zapata telephony interface +; +; Configuration file + +[channels] +; +; Default language +; +;language=en +; +; Default context +; +; +switchtype = euroisdn + +signalling = pri_cpe + +pridialplan = local +prilocaldialplan = dynamic +nationalprefix = 0 +internationalprefix = 00 + +priindication = passthrough + +echocancel = yes + +context=demo + +group = 1 +channel => 1-15,17-31 + +group = 2 +channel => 32-46,48-62 + --- zaptel-1.4.11~dfsg.orig/kernel/cwain/zaptel.conf +++ zaptel-1.4.11~dfsg/kernel/cwain/zaptel.conf @@ -0,0 +1,11 @@ +loadzone=nl +defaultzone=nl +span=1,2,3,ccs,hdb3,crc4 + +alaw=1-31 + +bchan=1-15 +dchan=16 +bchan=17-31 + + --- zaptel-1.4.11~dfsg.orig/kernel/qozap/zaptel.conf.duoBRI +++ zaptel-1.4.11~dfsg/kernel/qozap/zaptel.conf.duoBRI @@ -0,0 +1,11 @@ +loadzone=nl +defaultzone=nl +# qozap span definitions +# most of the values should be bogus because we are not really zaptel +span=1,1,3,ccs,ami +span=2,2,3,ccs,ami + +bchan=1,2 +dchan=3 +bchan=4,5 +dchan=6 --- zaptel-1.4.11~dfsg.orig/kernel/qozap/qozap.c +++ zaptel-1.4.11~dfsg/kernel/qozap/qozap.c @@ -0,0 +1,2044 @@ +/* + * qozap.c - Zaptel driver for the quadBRI PCI ISDN card + * and the octoBRI PCI ISDN card! + * + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + */ +#include +#include +#include +#include +#include +#include +#include "qozap.h" + +#ifdef LINUX26 +#include +#endif + +#if CONFIG_PCI + +static int doubleclock=0; +static int ports=-1; /* autodetect */ +static int pcmslave=0; +static int bloop=0; +static int debug=0; +static int dacs=1; /* 0 = no dacs, 1 = oncard dacs */ +static struct qoz_card *qoz_dev_list = NULL; +static int qoz_dev_count = 0; +static int totalBRIs = 0; +static struct pci_dev *multi_qoz = NULL; +static spinlock_t registerlock = SPIN_LOCK_UNLOCKED; +static int sort=1; + +static int ztqoz_shutdown(struct zt_span *span); + +int qoz_waitbusy(struct qoz_card *qoztmp) { + int x=1000; + while (x-- && (qoz_inb(qoztmp,qoz_R_STATUS) & 1)); + if (x < 0) { + return -1; + } else { + return 0; + } +} + +void qoz_shutdownCard(struct qoz_card *qoztmp) { + int s=0; + unsigned long flags; + + int stports=0; + if (qoztmp == NULL) { + printk(KERN_INFO "qozap: shutting down NULL card!\n"); + return; + } + + if ((qoztmp->pci_io == NULL) || (qoztmp->ioport == 0)) { + return; + } + + if (debug) + printk(KERN_INFO "qozap: shutting down card %d (cardID %d) at io port %#x.\n",qoztmp->cardno,qoztmp->cardID,(u_int) qoztmp->ioport); + + + spin_lock_irqsave(&qoztmp->lock,flags); + + // turn off irqs + qoz_outb(qoztmp,qoz_R_IRQMSK_MISC, 0); + qoz_outb(qoztmp,qoz_R_SCI_MSK, 0); + qoz_outb(qoztmp,qoz_R_IRQ_CTRL, 0); + + spin_unlock_irqrestore(&qoztmp->lock,flags); + + stports = qoztmp->stports; + for (s=0; s < stports; s++) { + if(qoztmp->spans[s].flags & ZT_FLAG_REGISTERED) { + zt_unregister(&(qoztmp->spans[s])); + if (debug > 2) + printk(KERN_INFO "qozap: unregistered card %d span %d.\n",qoztmp->cardno,s+1); + } + } + + + release_region(qoztmp->ioport, 8); + iounmap((void *) qoztmp->pci_io); + release_mem_region((unsigned long) qoztmp->pci_io_phys, 256); + + spin_lock_irqsave(&qoztmp->lock,flags); + + qoztmp->pci_io = NULL; + qoztmp->ioport = 0; + + if (qoztmp->pcidev != NULL) { + pci_disable_device(qoztmp->pcidev); + } + pci_write_config_word(qoztmp->pcidev, PCI_COMMAND, 0); + + free_irq(qoztmp->irq,qoztmp); + spin_unlock_irqrestore(&qoztmp->lock,flags); + + +} + +void qoz_doLEDs(struct qoz_card *qoztmp) { + unsigned char leds = 0x0; + unsigned long ledw; + unsigned long flags; + spin_lock_irqsave(&qoztmp->lock,flags); + + if ((qoztmp->type == 0xb520) && (qoztmp->stports == 4)){ + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0x20 | 0x10); + qoz_outb(qoztmp,qoz_R_GPIO_EN1,0xf); + qoz_outb(qoztmp,qoz_R_GPIO_OUT1,(qoztmp->leds[0] | (qoztmp->leds[1] << 1) | (qoztmp->leds[2] << 2) | (qoztmp->leds[3] << 3))); + } else if (((qoztmp->type == 0xb550) || (qoztmp->type == 0xb556) || (qoztmp->type == 0xb752)) && (qoztmp->stports == 4)){ + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0x80 | 0x40 | 0x20 | 0x10); + qoz_outb(qoztmp,qoz_R_GPIO_EN1,0xff); + leds = 0x10; + if (qoztmp->leds[0] == 1) { + leds |= 0x04; + } + if (qoztmp->leds[1] == 1) { + leds |= 0x20; + } + if (qoztmp->leds[2] == 1) { + leds |= 0x80; + } + if (qoztmp->leds[3] == 1) { + leds |= 0x40; + } + + /* + 03 + 12 + 0x80 rr + rg + 0x40 rg + rr + 0x20 rr + gr + 0x04 gr + rr + */ + qoz_outb(qoztmp,qoz_R_GPIO_OUT1, leds); + + } else if (((qoztmp->type == 0xb556) || (qoztmp->type == 0xb751)) && (qoztmp->stports == 2)){ + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0x80 | 0x40 | 0x20 | 0x10); + qoz_outb(qoztmp,qoz_R_GPIO_EN1,0xff); + leds = 0x0; + if (qoztmp->leds[0] == 1) { + leds |= 0x04; + } + if (qoztmp->leds[1] == 1) { + leds |= 0x08; + } + qoz_outb(qoztmp,qoz_R_GPIO_OUT1, leds); + + } else if (qoztmp->type == 0xb558) { + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0x80 | 0x40 | 0x20); + qoz_outb(qoztmp,qoz_R_GPIO_EN1,0x80 | 0x40 | 0x20 | 0x4); + if (qoztmp->leds[0] == 1) { + leds |= 0x04; + } + if (qoztmp->leds[1] == 1) { + leds |= 0x20; + } + if (qoztmp->leds[2] == 1) { + leds |= 0x40; + } + if (qoztmp->leds[3] == 1) { + leds |= 0x80; + } + + qoz_outb(qoztmp,qoz_R_GPIO_OUT1, leds); + /* + gpio_10 0 + gpio_13 1 + gpio_15 2 + gpio_14 3 + */ + } else if (qoztmp->type == 0xb55b) { + qoz_outb(qoztmp,qoz_R_BRG_PCM_CFG,0x21); + /* 0x01 g1 st1 + 0x02 g2 st2 + 0x04 g3 st3 + 0x08 g4 st4 + 0x10 r8 st5 + 0x20 r7 st6 + 0x40 r6 st7 + 0x80 r5 st8 + */ + if (qoztmp->leds[0] == 0) { + leds |= 0x01; + } + if (qoztmp->leds[1] == 0) { + leds |= 0x02; + } + if (qoztmp->leds[2] == 0) { + leds |= 0x04; + } + if (qoztmp->leds[3] == 0) { + leds |= 0x08; + } + if (qoztmp->leds[4] == 0) { + leds |= 0x10; + } + if (qoztmp->leds[5] == 0) { + leds |= 0x20; + } + if (qoztmp->leds[6] == 0) { + leds |= 0x40; + } + if (qoztmp->leds[7] == 0) { + leds |= 0x80; + } + + ledw = leds << 24 | leds << 16 | leds << 8 | leds; + + qoz_outdw_io(qoztmp,0x4000, ledw); + + qoz_outb(qoztmp,qoz_R_BRG_PCM_CFG,0x20); + + } + spin_unlock_irqrestore(&qoztmp->lock,flags); +} + +void qoz_doWD(struct qoz_card *qoztmp) { + unsigned long flags; + if (!qoztmp->wdp) { + return; + } + spin_lock_irqsave(&qoztmp->lock,flags); + qoz_outb(qoztmp,qoz_R_BRG_PCM_CFG,0x21); + if (qoztmp->wdp == 1) { + qoz_outdw_io(qoztmp,0x4000, qoz_WD_P2); + qoztmp->wdp = 2; + } else { + qoz_outdw_io(qoztmp,0x4000, qoz_WD_P1); + qoztmp->wdp = 1; + } + qoz_inb_io(qoztmp,qoz_R_CHIP_ID); + qoz_outb(qoztmp,qoz_R_BRG_PCM_CFG,0x20); + spin_unlock_irqrestore(&qoztmp->lock,flags); +} + +void qoz_undoWD(struct qoz_card *qoztmp) { + unsigned long flags; + + if (qoztmp->wdp) { + printk(KERN_INFO "qozap: Stopping hardware watchdog.\n"); + spin_lock_irqsave(&qoztmp->lock,flags); + qoz_outb(qoztmp,qoz_R_BRG_PCM_CFG,0x21); + qoz_outdw_io(qoztmp,0x4000, qoz_WD_P0); + qoztmp->wdp = 0; + qoz_inb_io(qoztmp,qoz_R_CHIP_ID); + qoz_outb(qoztmp,qoz_R_BRG_PCM_CFG,0x20); + spin_unlock_irqrestore(&qoztmp->lock,flags); + } +} + +void qoz_reset_pcm(struct qoz_card *qoztmp) { + qoz_outb(qoztmp,qoz_R_CIRM,0x20); + qoz_outb(qoztmp,qoz_R_CIRM,0x0); + qoz_waitbusy(qoztmp); + + if (pcmslave) { + qoz_outb(qoztmp,qoz_R_PCM_MD0, 0x90); + qoz_outb(qoztmp,qoz_R_PCM_MD1, 0x20); + qoz_outb(qoztmp,qoz_R_PCM_MD0, 0xA0); + qoz_outb(qoztmp,qoz_R_PCM_MD2, 0x4 | 0x8); + } else { + qoz_outb(qoztmp,qoz_R_PCM_MD0, 0x91); + qoz_outb(qoztmp,qoz_R_PCM_MD1, 0x20); + } +} + +void qoz_resetCard(struct qoz_card *qoztmp) { + unsigned long flags; + spin_lock_irqsave(&(qoztmp->lock),flags); + pci_write_config_word(qoztmp->pcidev, PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + + // soft reset + qoz_outb(qoztmp,qoz_R_CIRM,0x8); + qoz_outb(qoztmp,qoz_R_CIRM,0x0); + qoz_waitbusy(qoztmp); + + qoz_reset_pcm(qoztmp); + + // fifo reset + qoz_outb(qoztmp,qoz_R_CIRM,0x10); + qoz_outb(qoztmp,qoz_R_CIRM,0x0); + qoz_waitbusy(qoztmp); + + // s/t reset + qoz_outb(qoztmp,qoz_R_CIRM,0x40); + qoz_outb(qoztmp,qoz_R_CIRM,0x0); + qoz_waitbusy(qoztmp); + + /* set S0 amplitude */ + qoz_outb(qoztmp,qoz_R_PWM_MD,0xa0); + if (qoztmp->type == 0xb552) { + qoz_outb(qoztmp,qoz_R_PWM0,0x19); + } else if (qoztmp->type == 0xb55b) { + qoz_outb(qoztmp,qoz_R_PWM0,0x19); + } else { + qoz_outb(qoztmp,qoz_R_PWM0,0x1E); + } + + /* set up the timer */ + qoz_outb(qoztmp,qoz_R_TI_WD, 0x2); + qoz_outb(qoztmp,qoz_R_IRQMSK_MISC, 0x2); + + + /* all state changes */ + qoz_outb(qoztmp,qoz_R_SCI_MSK, 0xff); + + if (qoztmp->type == 0xb552) { + qoz_outb(qoztmp,qoz_R_FIFO_MD,0x16); + } else if (qoztmp->type == 0xb55b) { + qoz_outb(qoztmp,qoz_R_FIFO_MD,0x16); + } else { + qoz_outb(qoztmp,qoz_R_FIFO_MD,0x26); + } + + // double clock + if (doubleclock == 1) { + // hopefully you have set CLK_MODE correctly! + qoz_outb(qoztmp,qoz_R_BRG_PCM_CFG,0x20); + } else { + if (qoztmp->type == 0x08b4) { + qoz_outb(qoztmp,qoz_R_BRG_PCM_CFG,0x0); + } else if (qoztmp->type == 0xb550) { + qoz_outb(qoztmp,qoz_R_BRG_PCM_CFG,0x20); + } else if (qoztmp->type == 0xb556) { + qoz_outb(qoztmp,qoz_R_BRG_PCM_CFG,0x20); + } else if (qoztmp->type == 0xb559) { + qoz_outb(qoztmp,qoz_R_BRG_PCM_CFG,0x20); + } else if (qoztmp->type == 0xb558) { + qoz_outb(qoztmp,qoz_R_BRG_PCM_CFG,0x20); + } else if (qoztmp->type == 0xb520) { + qoz_outb(qoztmp,qoz_R_BRG_PCM_CFG,0x20); + } else if (qoztmp->type == 0xb752) { + qoz_outb(qoztmp,qoz_R_BRG_PCM_CFG,0x20); + } else if (qoztmp->type == 0xb751) { + qoz_outb(qoztmp,qoz_R_BRG_PCM_CFG,0x20); + } else { + /* you are on your own here! */ + qoz_outb(qoztmp,qoz_R_BRG_PCM_CFG,0x20); + } + } + qoz_outb(qoztmp,qoz_R_CTRL,0x0); + + /* R0 G1 */ + qoztmp->leds[0] = 0x0; + qoztmp->leds[1] = 0x0; + qoztmp->leds[2] = 0x0; + qoztmp->leds[3] = 0x0; + qoztmp->leds[4] = 0x0; + qoztmp->leds[5] = 0x0; + qoztmp->leds[6] = 0x0; + qoztmp->leds[7] = 0x0; + + if (qoztmp->type == 0xb552) { + qoztmp->stports = 8; + } else if (qoztmp->type == 0xb55b) { + qoztmp->stports = 8; + } else if (qoztmp->type == 0xb556) { + qoztmp->stports = 2; + } else if (qoztmp->type == 0xb559) { + qoztmp->stports = 2; + } else if (qoztmp->type == 0xb751) { + qoztmp->stports = 2; + } else { + qoztmp->stports = 4; + } + qoztmp->ticks = 0; + qoztmp->clicks = 0; + if ((qoztmp->type == 0xb550) || (qoztmp->type == 0xb556)) { + printk(KERN_INFO "qozap: Starting hardware watchdog.\n"); + qoztmp->wdp = 2; + } else { + qoztmp->wdp = 0; + } + + qoz_outb(qoztmp,qoz_R_ST_SYNC,0x0); + + /* IRQs off */ + qoz_outb(qoztmp,qoz_R_IRQ_CTRL, 0); + + spin_unlock_irqrestore(&(qoztmp->lock),flags); +} + +void qoz_registerCard(struct qoz_card *qozcard) { + struct qoz_card *tmpcard = NULL; + spin_lock(®isterlock); + + if (qozcard != NULL) { + if (!sort) { + tmpcard = qoz_dev_list; + if (tmpcard) { + while (tmpcard->next) { + tmpcard = tmpcard->next; + } + tmpcard->next = qozcard; + qozcard->prev = tmpcard; + } else { + qoz_dev_list = qozcard; + } + } else { + qozcard->prev = NULL; + qozcard->next = qoz_dev_list; + if (qoz_dev_list) { + qoz_dev_list->prev = qozcard; + } + qoz_dev_list = qozcard; + } + qozcard->cardno = ++qoz_dev_count; + } else { + printk(KERN_INFO "qozap: trying to register NULL card.\n"); + } + spin_unlock(®isterlock); +} + +static int qoz_dfifo_tx(struct qoz_card *qoztmp, int stport) { + int chan = 2; + int x=0; + char fifo = 0; + char offset = 0; + unsigned long flags; + + + if (qoztmp->type == 0xb552) { + offset = 24; + } else if (qoztmp->type == 0xb55b) { + offset = 24; + } else { + offset = 28; + } + + fifo = stport + offset; + + if (qoztmp->chans[stport][chan].bytes2transmit < 1) { + return 0; + } else { + spin_lock_irqsave(&qoztmp->lock,flags); + /* select fifo */ + qoz_outb(qoztmp,qoz_R_FIFO,fifo << 1); + qoz_waitbusy(qoztmp); + + if (debug > 1) + printk(KERN_INFO "qozap: card %d stport %d TX [ ", qoztmp->cardno, stport + 1); + /* copy frame to fifo */ + for (x=0;xchans[stport][chan].bytes2transmit;x++) { + if (debug > 1) + printk("%#x ",qoztmp->dtxbuf[stport][x]); + qoz_outb(qoztmp,qoz_A_FIFO_DATA0,qoztmp->dtxbuf[stport][x]); + } + if (debug > 1) + printk("] %d bytes\n",qoztmp->chans[stport][chan].bytes2transmit); + + if (qoztmp->chans[stport][chan].eoftx == 1) { + /* transmit HDLC frame */ + qoz_outb(qoztmp,qoz_R_INC_RES_FIFO,0x1); + qoz_waitbusy(qoztmp); + } + spin_unlock_irqrestore(&qoztmp->lock,flags); + } + return 0; +} + +static int qoz_fifo_tx(struct qoz_card *qoztmp, char fifo) { + int stport = fifo / 2; + int chan = fifo % 2; + unsigned long flags; + + spin_lock_irqsave(&qoztmp->lock,flags); + /* select fifo */ + qoz_outb(qoztmp,qoz_R_FIFO,0x80 | (fifo << 1)); + qoz_waitbusy(qoztmp); + /* transmit 8 bytes of transparent data */ + qoz_outdw(qoztmp,qoz_A_FIFO_DATA0,*((unsigned int *) &qoztmp->txbuf[stport][chan][0])); + qoz_outdw(qoztmp,qoz_A_FIFO_DATA0,*((unsigned int *) &qoztmp->txbuf[stport][chan][4])); + + spin_unlock_irqrestore(&qoztmp->lock,flags); + return 0; +} + +static int qoz_dfifo_rx(struct qoz_card *qoztmp, int stport) { + unsigned char f1=1,f2=1,data,stat; + unsigned char of1=0,of2=0; + int len,i; + unsigned short z1=1,z2=1; + unsigned short oz1=0,oz2=0; + char fifo = 0; + char offset = 0; + unsigned long flags; + + if (qoztmp->type == 0xb552) { + offset = 24; + } else if (qoztmp->type == 0xb55b) { + offset = 24; + } else { + offset = 28; + } + + fifo = stport + offset; + + spin_lock_irqsave(&qoztmp->lock,flags); + // select rx fifo + qoz_outb(qoztmp,qoz_R_FIFO,(fifo << 1) | 1); + qoz_waitbusy(qoztmp); + + while ((of1 != f1) && (of2 != f2)) { + of1 = f1; + of2 = f2; + f1 = qoz_inb(qoztmp,qoz_A_F1) & 0xf; + f2 = qoz_inb(qoztmp,qoz_A_F2) & 0xf; + } + + if (f1 == f2) { + /* no frame */ + qoztmp->st[stport].drx--; + qoztmp->chans[stport][2].bytes2receive = 0; + spin_unlock_irqrestore(&qoztmp->lock,flags); + return 0; + } + + while ((oz1 != z1) && (oz2 != z2)) { + oz1 = z1; + oz2 = z2; + if ((qoztmp->type != 0xb552) && (qoztmp->type != 0xb55b)){ + z1 = qoz_inw(qoztmp,qoz_A_Z1) & 0x7ff; + z2 = qoz_inw(qoztmp,qoz_A_Z2) & 0x7ff; + } else { + z1 = qoz_inw(qoztmp,qoz_A_Z1) & 0x3ff; + z2 = qoz_inw(qoztmp,qoz_A_Z2) & 0x3ff; + } + } + + if ((qoztmp->type == 0xb552)|| (qoztmp->type == 0xb55b)) { + len = z1 - z2; + if (len < 0) { + len += qoz_DFIFO_SIZE8; + } + } else { + len = z1 - z2; + if (len < 0) { + len += qoz_DFIFO_SIZE4; + } + } + + if (len > qoz_DFIFO_SIZE4) { + printk(KERN_INFO "\nqozap: buffer overflow in D channel RX!\n"); + qoztmp->chans[stport][2].bytes2receive = 0; + qoztmp->chans[stport][2].eofrx = 0; + } else { + if (debug > 1) printk(KERN_INFO "qozap: card %d span %d RX [ ", qoztmp->cardno, stport + 1); + for (i=0; idrxbuf[stport][i] = data; + if (debug > 1) printk("%#x ",data); + } + if (debug > 1) printk("] %d bytes\n", len); + qoztmp->chans[stport][2].bytes2receive = i; + qoztmp->chans[stport][2].eofrx = 1; + } + + stat = qoz_inb(qoztmp,qoz_A_FIFO_DATA0); + if (stat != 0x0) { + // bad CRC, skip it + printk(KERN_INFO "qozap: CRC error for HDLC frame on card %d (cardID %d) S/T port %d\n",qoztmp->cardno, qoztmp->cardID, stport+1); + qoztmp->chans[stport][2].bytes2receive = 0; + qoztmp->chans[stport][2].eofrx = 0; +// zt_qevent_nolock(&qoztmp->chans[stport][2], ZT_EVENT_BADFCS); + } + qoz_outb(qoztmp,qoz_R_INC_RES_FIFO,0x1); + qoz_waitbusy(qoztmp); + + /* frame recevived */ + if (qoztmp->st[stport].drx > 0) { + qoztmp->st[stport].drx--; + } else { + printk(KERN_INFO "qozap: trying to receive too much (card %d span %d drx %d)\n", qoztmp->cardno, stport+1, qoztmp->st[stport].drx); + qoztmp->st[stport].drx = 0; + } + spin_unlock_irqrestore(&qoztmp->lock,flags); + return 0; +} + + +static int qoz_fifo_rx(struct qoz_card *qoztmp, char fifo) { + int stport = fifo / 2; + int chan = fifo % 2; + unsigned char data; + int len,i; + unsigned short z1=1,z2=1; + unsigned short oz1=0,oz2=0; + int mumbojumbo=0; + unsigned long flags; + + spin_lock_irqsave(&qoztmp->lock,flags); + + /* select rx fifo */ + qoz_outb(qoztmp,qoz_R_FIFO,0x80 | (fifo << 1) | 1); + qoz_waitbusy(qoztmp); + + while ((oz1 != z1) && (oz2 != z2)) { + oz1 = z1; + oz2 = z2; + z1 = qoz_inw(qoztmp,qoz_A_Z1) & 0x7f; + z2 = qoz_inw(qoztmp,qoz_A_Z2) & 0x7f; + } + len = z1 - z2; + if (len < 0) { + len += qoz_FIFO_SIZE; + } + if (len > 2 * ZT_CHUNKSIZE) { + mumbojumbo = len - (2 * ZT_CHUNKSIZE); + len = ZT_CHUNKSIZE; + for (i=0;iclicks++; + if (((qoztmp->clicks > 50) || (debug > 5)) && (mumbojumbo > 0)) { + printk(KERN_CRIT "qozap: dropped audio card %d cardid %d bytes %d z1 %d z2 %d fifo %d\n", qoztmp->cardno, qoztmp->cardID, mumbojumbo, z1, z2, fifo); + qoztmp->clicks = 0; + } + } + + + if (len < ZT_CHUNKSIZE) { +// printk(KERN_INFO "qozap: not enough to receive (%d bytes)\n",len); + spin_unlock_irqrestore(&qoztmp->lock,flags); + return 0; + } else { + if (bloop) { + *((unsigned int *) &qoztmp->txbuf[stport][chan][0]) = qoz_indw(qoztmp,qoz_A_FIFO_DATA0); + *((unsigned int *) &qoztmp->txbuf[stport][chan][4]) = qoz_indw(qoztmp,qoz_A_FIFO_DATA0); + } else { + *((unsigned int *) &qoztmp->rxbuf[stport][chan][0]) = qoz_indw(qoztmp,qoz_A_FIFO_DATA0); + *((unsigned int *) &qoztmp->rxbuf[stport][chan][4]) = qoz_indw(qoztmp,qoz_A_FIFO_DATA0); + } + } + + spin_unlock_irqrestore(&qoztmp->lock,flags); + if (bloop == 0) + zt_ec_chunk(&qoztmp->spans[stport].chans[chan], qoztmp->spans[stport].chans[chan].readchunk, qoztmp->spans[stport].chans[chan].writechunk); + +// printk(KERN_INFO "s/t port %d, channel %d, dbufi=%d, f1=%d, f2=%d, z1=%d, z2=%d => len = %d stat=%#x, hdlc=%d\n",stport,chan,qoztmp->st[stport].dbufi,f1,f2,z1,z2,len,stat,hdlc); + return 0; +} + +static void qoz_assign(struct qoz_card *qoztmp, int src_span, int src_chan, int dst_span, int dst_chan, int timeslot, int use_pcm_bus) { + unsigned long flags; + int dst_fifo = dst_span * 2 + (dst_chan - 1); + int src_fifo = src_span * 2 + (src_chan - 1); + int src_hfc_chan = src_span * 4 + (src_chan - 1); + int dst_hfc_chan = dst_span * 4 + (dst_chan - 1); + + spin_lock_irqsave(&qoztmp->lock, flags); + + qoz_outb(qoztmp,qoz_R_FIFO,(src_fifo << 1) | 1); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_R_INC_RES_FIFO,0x2); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_A_CHANNEL,(src_hfc_chan << 1) | 1); + qoz_outb(qoztmp,qoz_A_CON_HDLC,0xDE); // was c2 + + qoz_outb(qoztmp,qoz_R_SLOT,timeslot << 1); + qoz_outb(qoztmp,qoz_A_SL_CFG, (src_hfc_chan << 1) | 0 | 0x40); + + qoz_outb(qoztmp,qoz_R_FIFO, dst_fifo << 1); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_R_INC_RES_FIFO,0x2); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_A_CHANNEL,dst_hfc_chan << 1); + qoz_outb(qoztmp,qoz_A_CON_HDLC,0xDE); // was c2 + + qoz_outb(qoztmp,qoz_R_SLOT,(timeslot << 1) | 1); + qoz_outb(qoztmp,qoz_A_SL_CFG, (dst_hfc_chan << 1) | 1 | 0x40); + + spin_unlock_irqrestore(&qoztmp->lock, flags); +} + +static void qoz_unassign(struct qoz_card *qoztmp, int span, int chan, int timeslot) { + unsigned long flags; + +// int timeslot = span * 2 + (chan - 1); + int fifo = span * 2 + (chan - 1); + int hfc_chan = span * 4 + (chan - 1); + + spin_lock_irqsave(&qoztmp->lock, flags); + + qoz_outb(qoztmp,qoz_R_FIFO,(fifo << 1)); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_R_INC_RES_FIFO,0x2); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_A_CON_HDLC,0x02); + qoz_outb(qoztmp,qoz_A_CHANNEL,(hfc_chan << 1)); + qoz_outb(qoztmp,qoz_A_IRQ_MSK,0x1); + + qoz_outb(qoztmp,qoz_R_FIFO,(fifo << 1) | 1); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_R_INC_RES_FIFO,0x2); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_A_CON_HDLC,0x02); + qoz_outb(qoztmp,qoz_A_CHANNEL,(hfc_chan << 1) | 1); + qoz_outb(qoztmp,qoz_A_IRQ_MSK,0x1); + + qoz_outb(qoztmp,qoz_R_SLOT,(timeslot << 1) | 1); + qoz_outb(qoztmp,qoz_A_SL_CFG, 0x0); + + qoz_outb(qoztmp,qoz_R_SLOT,timeslot << 1); + qoz_outb(qoztmp,qoz_A_SL_CFG, 0x0); + + spin_unlock_irqrestore(&qoztmp->lock, flags); +} + + +static int ztqoz_dacs(struct zt_chan *dst, struct zt_chan *src) +{ + struct qoz_card *qoztmp = NULL; + int use_pcm_bus = 0; + int timeslot = 0; + if (!dacs) return -1; + + if (src) { + qoztmp = src->pvt; + + if (src->pvt != dst->pvt) { + if (dacs == 2) { + use_pcm_bus = 1; + timeslot = src->channo; + if (debug) + printk("qozap: Assigning channel %d/%d -> %d/%d, timeslot %d, different cards!\n", src->span->offset, src->chanpos, dst->span->offset, dst->chanpos, timeslot); + } else { + if (debug) + printk("qozap: Not Assigning %d/%d -> %d/%d, different cards!\n", src->span->offset, src->chanpos, dst->span->offset, dst->chanpos); + return -1; + } + } else { + use_pcm_bus = 0; +// timeslot = src->span->offset * 2 + (src->chanpos); + timeslot = src->channo; + if (debug) + printk("qozap: Assigning channel %d/%d -> %d/%d, timeslot %d, same card!\n", src->span->offset, src->chanpos, dst->span->offset, dst->chanpos, timeslot); + } + + if ((src->chanpos == 3) || (dst->chanpos == 3)) { + if (debug) + printk("qozap: Not Assigning D-channel %d/%d -> %d/%d!\n", src->span->offset, src->chanpos, dst->span->offset, dst->chanpos); + } else { + qoz_assign(qoztmp, src->span->offset, src->chanpos, dst->span->offset, dst->chanpos, timeslot, use_pcm_bus); + } + } else { + qoztmp = dst->pvt; + if (dst->chanpos == 3) { + if (debug) + printk("qozap: Not Unassigning D-channel %d/%d!\n", dst->span->offset, dst->chanpos); + } else { + timeslot = dst->channo; + qoz_unassign(qoztmp, dst->span->offset, dst->chanpos, timeslot); + if (debug) + printk("qozap: Unassigning channel %d/%d, timeslot %d!\n", dst->span->offset, dst->chanpos, timeslot); + } + } + return 0; +} + +static void qoz_resync(struct qoz_card *qoztmp) { + int i=0; + int best=9999; + int src=-1; + + if (pcmslave) return; + + for (i=0; istports; i++){ + if (qoztmp->st[i].l1up && (qoztmp->st_sync[i] > 0) && (qoztmp->st_sync[i] < best)) { + best = qoztmp->st_sync[i]; + src = i; + } + } + if (src < 0) { + for (i=0; istports; i++){ + if ((qoztmp->st_sync[i] > 0) && (qoztmp->st_sync[i] < best)) { + best = qoztmp->st_sync[i]; + src = i; + } + } + } + if (src == qoztmp->syncsrc) + return; + if (src >= 0) { + qoztmp->syncsrc = src; + qoz_outb(qoztmp, qoz_R_ST_SYNC, src | 0x08); + if (debug > 2) + printk(KERN_INFO "qozap: card %d Sync source changed to span %d\n", qoztmp->cardno, src + 1); + } +} + +static inline void qoz_run(struct qoz_card *qoztmp) { + int s=0; + unsigned long flags; + + for (s=0;sstports;s++) { + if (!bloop) { + if (qoztmp->spans[s].flags & ZT_FLAG_RUNNING) { + /* oh zaptel! tell us what to transmit... */ + zt_transmit(&qoztmp->spans[s]); + /* B1 xmit */ + qoz_fifo_tx(qoztmp, s * 2); + /* B2 xmit */ + qoz_fifo_tx(qoztmp, (s * 2) + 1); + + if ((qoztmp->st[s].layer1state != 7) && (qoztmp->chans[s][2].bytes2transmit > 0) && (qoztmp->st[s].nt_mode != 1)) { + if (qoztmp->st[s].t3 == -1) { + if (debug > 2) + printk(KERN_INFO "qozap: activating layer 1, span %d\n",s); + qoztmp->st[s].t3 = 0; + spin_lock_irqsave(&qoztmp->lock,flags); + qoz_outb(qoztmp,qoz_R_ST_SEL, s); + qoz_outb(qoztmp,qoz_A_ST_WR_STA,0x60); + spin_unlock_irqrestore(&qoztmp->lock, flags); + } else { + } + } + + /* D xmit */ + if (qoztmp->spans[s].alarms != ZT_ALARM_RED) { + qoz_dfifo_tx(qoztmp, s); + } else { + if ((qoztmp->st[s].t3 == -1) && (qoztmp->st[s].t4 == -1) && (qoztmp->st[s].layer1state == 3) && (qoztmp->st[s].nt_mode != 1)) { + /* clear alarms */ + if (debug > 2) + printk(KERN_INFO "qozap: clearing alarms on span %d\n",s); + qoztmp->spans[s].alarms = ZT_ALARM_NONE; + zt_alarm_notify_no_master_change(&qoztmp->spans[s]); + } + } + + qoztmp->chans[s][2].bytes2receive = 0; + qoztmp->chans[s][2].bytes2transmit = 0; + qoztmp->chans[s][2].eofrx = 0; + qoztmp->chans[s][2].eoftx = 0; + + } + + /* B1 receive */ + qoz_fifo_rx(qoztmp,(s*2)); + /* B2 receive */ + qoz_fifo_rx(qoztmp,(s*2)+1); + /* d-chan data */ + if (qoztmp->st[s].drx > 0) { + if (debug > 2) + printk(KERN_CRIT "qozap: card %d st[%d].drx = %d\n", qoztmp->cardno, s, qoztmp->st[s].drx); + qoz_dfifo_rx(qoztmp, s); + } + if (qoztmp->spans[s].flags & ZT_FLAG_RUNNING) { + /* oh zaptel! thou shall receive! */ + zt_receive(&(qoztmp->spans[s])); + } + } else { + // loop + /* B1 receive */ + qoz_fifo_rx(qoztmp,(s*2)); + /* B2 receive */ + qoz_fifo_rx(qoztmp,(s*2)+1); + /* d-chan data */ +/* if (qoztmp->st[s].drx > 0) { + if (debug > 2) + printk(KERN_CRIT "qozap: card %d st[%d].drx = %d\n", qoztmp->cardno, s, qoztmp->st[s].drx); + qoz_dfifo_rx(qoztmp, s); + } + if (qoztmp->spans[s].flags & ZT_FLAG_RUNNING) { + zt_receive(&(qoztmp->spans[s])); + } +*/ + if (qoztmp->spans[s].flags & ZT_FLAG_RUNNING) { + /* oh zaptel! tell us what to transmit... */ + // zt_transmit(&qoztmp->spans[s]); + /* B1 xmit */ + qoz_fifo_tx(qoztmp, s * 2); + /* B2 xmit */ + qoz_fifo_tx(qoztmp, (s * 2) + 1); + /* D xmit */ +// qoz_dfifo_tx(qoztmp, s); + + qoztmp->chans[s][2].bytes2receive = 0; + qoztmp->chans[s][2].bytes2transmit = 0; + qoztmp->chans[s][2].eofrx = 0; + qoztmp->chans[s][2].eoftx = 0; + + } + } + } +} + +ZAP_IRQ_HANDLER(qoz_interrupt) { + struct qoz_card *qoztmp = dev_id; + unsigned long flags; + unsigned char irq_misc,irq_sci,status,l1state,irq_foview,fi; + int st=0,i=0,offset=0; + int j=0; + + if (!qoztmp) { +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + } + + if ((!qoztmp->pci_io) || (!qoztmp->ioport)) { + printk(KERN_CRIT "qozap: no pci mem/io\n"); +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + } + if (qoztmp->dead) { +#ifdef LINUX26 + return IRQ_RETVAL(1); +#else + return; +#endif + } + + + spin_lock_irqsave(&(qoztmp->lock), flags); + status = qoz_inb(qoztmp,qoz_R_STATUS); + irq_sci = qoz_inb(qoztmp,qoz_R_SCI); + spin_unlock_irqrestore(&(qoztmp->lock), flags); + + if (!(status & 0x80) && !(status & 0x40) && (irq_sci == 0)) { +// printk(KERN_CRIT "qozap: status %#x\n", status); + // it's not us! +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + } + /* state machine irq */ + if (irq_sci != 0) { + if (debug > 1) { + printk(KERN_INFO "qozap: card %d R_BERT_STA = %#x\n", qoztmp->cardno, qoz_inb(qoztmp, qoz_R_BERT_STA) & 7); + } + if (qoztmp->type == 0xb552) { + offset = 24; + } else if (qoztmp->type == 0xb55b) { + offset = 24; + } else { + offset = 28; + } + spin_lock_irqsave(&(qoztmp->lock), flags); + for (st=0;ststports;st++) { + if (irq_sci & (1 << st)) { + qoz_outb(qoztmp,qoz_R_ST_SEL,st); + l1state = qoz_inb(qoztmp,qoz_A_ST_RD_STA) & 0xf; + if (debug > 1) { + printk(KERN_INFO "qozap: card %d span %d A_ST_RD_STA = %#x\n", qoztmp->cardno, st+1, qoz_inb(qoztmp, qoz_A_ST_RD_STA)); + } + qoztmp->st[st].layer1state = l1state; + if (qoztmp->st[st].nt_mode == 1) { + if (debug) + printk(KERN_INFO "card %d span %d state G%d (A_ST_RD_STA = %#x)\n",qoztmp->cardno,st+1,l1state,qoz_inb(qoztmp,qoz_A_ST_RD_STA)); + // NT state machine + if (l1state == 3) { + qoztmp->st[st].l1up = 1; + // keep layer1 up! + if (qoztmp->stports == 8) { + sprintf(qoztmp->spans[st].desc,"octoBRI PCI ISDN Card %d Span %d [NT] Layer 1 ACTIVATED (G%d)",qoztmp->cardno ,st + 1, l1state); + } else if (qoztmp->stports == 2) { + sprintf(qoztmp->spans[st].desc,"duoBRI PCI ISDN Card %d Span %d [NT] (cardID %d) Layer 1 ACTIVATED (G%d)",qoztmp->cardno ,st + 1,qoztmp->cardID, l1state); + } else{ + sprintf(qoztmp->spans[st].desc,"quadBRI PCI ISDN Card %d Span %d [NT] (cardID %d) Layer 1 ACTIVATED (G%d)",qoztmp->cardno ,st + 1,qoztmp->cardID, l1state); + } + qoz_outb(qoztmp,qoz_A_ST_WR_STA,3 | 0x10 ); + qoztmp->leds[st] = 1; + } else { + qoztmp->st[st].l1up = 0; + if (qoztmp->stports == 8) { + sprintf(qoztmp->spans[st].desc,"octoBRI PCI ISDN Card %d Span %d [NT] Layer 1 DEACTIVATED (G%d)",qoztmp->cardno ,st + 1, l1state); + } else if (qoztmp->stports == 2) { + sprintf(qoztmp->spans[st].desc,"duoBRI PCI ISDN Card %d Span %d [NT] (cardID %d) Layer 1 DEACTIVATED (G%d)",qoztmp->cardno ,st + 1,qoztmp->cardID, l1state); + } else { + sprintf(qoztmp->spans[st].desc,"quadBRI PCI ISDN Card %d Span %d [NT] (cardID %d) Layer 1 DEACTIVATED (G%d)",qoztmp->cardno ,st + 1,qoztmp->cardID, l1state); + } + qoztmp->leds[st] = 0; + } + } else { + if (debug) + printk(KERN_INFO "card %d span %d state F%d (A_ST_RD_STA = %#x)\n",qoztmp->cardno,st+1,l1state,qoz_inb(qoztmp,qoz_A_ST_RD_STA)); + // TE state machine + if (l1state == 3) { + qoztmp->st[st].l1up = 0; + if (qoztmp->st[st].t3 > -1) { + /* keep layer1 up, if the span is started. */ + if (qoztmp->spans[st].flags & ZT_FLAG_RUNNING) { + if (debug > 2) + printk("qozap: re-activating layer1 span %d\n", st); + qoz_outb(qoztmp,qoz_A_ST_WR_STA,0x60); + } + } else { + if (debug > 2) + printk("qozap: not re-activating layer1 span %d\n", st); + qoz_outb(qoztmp,qoz_A_ST_WR_STA,0x40); + /* if we tried to activate layer 1 and it failed make this an alarm */ +// qoztmp->spans[st].alarms = ZT_ALARM_RED; +// zt_alarm_notify(&qoztmp->spans[st]); + /* if the network shuts us down in idle mode dont make this an alarm */ + } + qoztmp->leds[st] = 0; + if (qoztmp->stports == 8) { + sprintf(qoztmp->spans[st].desc,"octoBRI PCI ISDN Card %d Span %d [TE] Layer 1 DEACTIVATED (F%d)",qoztmp->cardno ,st + 1, l1state); + } else if (qoztmp->stports == 2) { + sprintf(qoztmp->spans[st].desc,"duoBRI PCI ISDN Card %d Span %d [TE] (cardID %d) Layer 1 DEACTIVATED (F%d)",qoztmp->cardno ,st + 1,qoztmp->cardID, l1state); + } else { + sprintf(qoztmp->spans[st].desc,"quadBRI PCI ISDN Card %d Span %d [TE] (cardID %d) Layer 1 DEACTIVATED (F%d)",qoztmp->cardno ,st + 1,qoztmp->cardID, l1state); + } + } else if (l1state == 7) { + qoztmp->st[st].l1up = 1; + /* reset D RX fifo */ + qoz_outb(qoztmp,qoz_R_FIFO,((st + offset) << 1) | 1); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_R_INC_RES_FIFO,0x2); + qoz_waitbusy(qoztmp); + /* activation complete, stop timer t3 */ + qoztmp->st[st].t3 = -1; + qoztmp->spans[st].alarms = ZT_ALARM_NONE; + zt_alarm_notify(&qoztmp->spans[st]); + qoztmp->leds[st] = 1; + if (qoztmp->stports == 8) { + sprintf(qoztmp->spans[st].desc,"octoBRI PCI ISDN Card %d Span %d [TE] Layer 1 ACTIVATED (F%d)",qoztmp->cardno ,st + 1, l1state); + } else if (qoztmp->stports == 2) { + sprintf(qoztmp->spans[st].desc,"duoBRI PCI ISDN Card %d Span %d [TE] (cardID %d) Layer 1 ACTIVATED (F%d)",qoztmp->cardno ,st + 1,qoztmp->cardID, l1state); + } else { + sprintf(qoztmp->spans[st].desc,"quadBRI PCI ISDN Card %d Span %d [TE] (cardID %d) Layer 1 ACTIVATED (F%d)",qoztmp->cardno ,st + 1,qoztmp->cardID, l1state); + } + } else if (l1state == 8) { + /* lost framing */ + if (debug > 2) + printk(KERN_INFO "qozap: starting t4 for span %d\n", st); + qoztmp->st[st].t4 = 0; + } else { + qoztmp->st[st].l1up = 0; + qoztmp->leds[st] = 0; + if (qoztmp->stports == 8) { + sprintf(qoztmp->spans[st].desc,"octoBRI PCI ISDN Card %d Span %d [TE] Layer 1 DEACTIVATED (F%d)",qoztmp->cardno ,st + 1, l1state); + } else if (qoztmp->stports == 2) { + sprintf(qoztmp->spans[st].desc,"duoBRI PCI ISDN Card %d Span %d [TE] (cardID %d) Layer 1 DEACTIVATED (F%d)",qoztmp->cardno ,st + 1,qoztmp->cardID, l1state); + } else { + sprintf(qoztmp->spans[st].desc,"quadBRI PCI ISDN Card %d Span %d [TE] (cardID %d) Layer 1 DEACTIVATED (F%d)",qoztmp->cardno ,st + 1,qoztmp->cardID, l1state); + } + } + } + + } + } + qoz_resync(qoztmp); + spin_unlock_irqrestore(&(qoztmp->lock), flags); + } + + + // misc irq + if (status & 0x40) { + spin_lock_irqsave(&(qoztmp->lock), flags); + irq_misc = qoz_inb(qoztmp,qoz_R_IRQ_MISC); + spin_unlock_irqrestore(&(qoztmp->lock), flags); + if (irq_misc & 0x2) { + // qozap timer + qoztmp->ticks++; + qoz_run(qoztmp); + if (qoztmp->ticks % 100) { + qoz_doLEDs(qoztmp); + } + if (qoztmp->ticks % 40) { + /* you thought that 42 was the answer.... */ + qoz_doWD(qoztmp); + } + if (qoztmp->ticks > 1000) { + qoztmp->ticks = 0; + for (j=0;jstports;j++) { + /* t3 */ + if (qoztmp->st[j].t3 >= 0) { + qoztmp->st[j].t3++; + } + if (qoztmp->st[j].nt_mode != 1) { + if ((qoztmp->st[j].t3 > qoz_T3) && (qoztmp->st[j].layer1state != 7)) { + /* deactivate layer 1 */ + if (debug > 2) + printk(KERN_INFO "qozap: t3 timer expired for span %d\n", j); + spin_lock_irqsave(&(qoztmp->lock), flags); + qoz_outb(qoztmp,qoz_R_ST_SEL, j); + qoz_outb(qoztmp,qoz_A_ST_WR_STA, 0x40 ); + qoz_waitbusy(qoztmp); + qoztmp->st[j].t3 = -1; + qoztmp->spans[j].alarms = ZT_ALARM_RED; + spin_unlock_irqrestore(&(qoztmp->lock), flags); + zt_alarm_notify_no_master_change(&qoztmp->spans[j]); + } + } + /* t4 */ + if (qoztmp->st[j].t4 >= 0) { + qoztmp->st[j].t4++; + } + if (qoztmp->st[j].nt_mode != 1) { + if ((qoztmp->st[j].t4 > qoz_T4) && (qoztmp->st[j].layer1state != 7)) { + /* deactivate layer 1 */ + if (debug > 2) + printk(KERN_INFO "qozap: t4 timer expired for span %d\n", j); + spin_lock_irqsave(&(qoztmp->lock), flags); + qoz_outb(qoztmp,qoz_R_ST_SEL, j); + qoz_outb(qoztmp,qoz_A_ST_WR_STA, 0x40 ); + qoztmp->st[j].t4 = -1; + qoztmp->st[st].l1up = 0; + qoztmp->spans[j].alarms = ZT_ALARM_RED; + qoz_waitbusy(qoztmp); + spin_unlock_irqrestore(&(qoztmp->lock), flags); + zt_alarm_notify_no_master_change(&qoztmp->spans[j]); + } + } + } + } + } + if (irq_misc & 0x4) { + // printk(KERN_INFO "qozap proc/nonproc irq\n"); + } + } + + if (status & 0x80) { + /* fifo irq */ + spin_lock_irqsave(&(qoztmp->lock), flags); + irq_foview = qoz_inb(qoztmp,qoz_R_IRQ_OVIEW); + if ((qoztmp->type == 0xb552) || (qoztmp->type == 0xb55b)) { + if (irq_foview & 0x60) { + offset = 0; + fi = qoz_inb(qoztmp,qoz_R_IRQ_FIFO_BL6); + for (i=0; i < 8; i++) { + if (fi & (1 << i)) { + st = offset + (i / 2); + if (i % 2) { + if (debug > 2) + printk(KERN_CRIT "qozap: HDLC RX irq fifo %d span %d\n", i, st+1); + qoztmp->st[st].drx += 1; + } else { + if (debug > 2) + printk(KERN_CRIT "qozap: HDLC TX irq fifo %d span %d\n", i, st+1); + } + } + } + } + if (irq_foview & 0x80) { + offset = 4; + fi = qoz_inb(qoztmp,qoz_R_IRQ_FIFO_BL7); + for (i=0; i < 8; i++) { + if (fi & (1 << i)) { + st = offset + (i / 2); + if (i % 2) { + if (debug > 2) + printk(KERN_CRIT "qozap: HDLC RX irq fifo %d span %d\n", i, st+1); + qoztmp->st[st].drx += 1; + } else { + if (debug > 2) + printk(KERN_CRIT "qozap: HDLC TX irq fifo %d span %d\n", i, st+1); + } + } + } + } + } else { + if (irq_foview & 0x80) { + fi = qoz_inb(qoztmp,qoz_R_IRQ_FIFO_BL7); + for (i=0; i < 8; i++) { + if (fi & (1 << i)) { + st = i / 2; + if (i % 2) { + if (debug > 2) + printk(KERN_CRIT "qozap: HDLC RX irq fifo %d span %d\n", i, st+1); + qoztmp->st[st].drx += 1; + } else { + if (debug > 2) + printk(KERN_CRIT "qozap: HDLC TX irq fifo %d span %d\n", i, st+1); + } + } + } + } + } + spin_unlock_irqrestore(&(qoztmp->lock), flags); + } + +#ifdef LINUX26 + return IRQ_RETVAL(1); +#endif +} + +static int ztqoz_open(struct zt_chan *chan) { +// printk(KERN_INFO "qozap: channel %d opened.\n",chan->channo); +#ifndef LINUX26 + MOD_INC_USE_COUNT; +#else + try_module_get(THIS_MODULE); +#endif + return 0; +} + +static int ztqoz_close(struct zt_chan *chan) { +// printk(KERN_INFO "qozap: channel %d closed.\n",chan->channo); +#ifndef LINUX26 + MOD_DEC_USE_COUNT; +#else + module_put(THIS_MODULE); +#endif + return 0; +} + +static int ztqoz_rbsbits(struct zt_chan *chan, int bits) { + return 0; +} + +static int ztqoz_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) { + switch(cmd) { + default: + return -ENOTTY; + } + return 0; +} + +static int ztqoz_startup(struct zt_span *span) { + struct qoz_card *qoztmp = span->pvt; + unsigned long flags; + int alreadyrunning; + int i=0; + int offset = 0; + + if (qoztmp == NULL) { + printk(KERN_INFO "qozap: no card for span at startup!\n"); + } + + alreadyrunning = span->flags & ZT_FLAG_RUNNING; +// printk(KERN_CRIT "already running %d flags %d\n", alreadyrunning, span->flags); + + if (!alreadyrunning) { + span->chans[2].flags &= ~ZT_FLAG_HDLC; + span->chans[2].flags |= ZT_FLAG_BRIDCHAN; + + /* setup B channel buffers (8 bytes each) */ + for (i=0; i<2 ; i++) { + memset(qoztmp->rxbuf[span->offset][i],0x0,sizeof(qoztmp->rxbuf[span->offset][i])); + span->chans[i].readchunk = qoztmp->rxbuf[span->offset][i]; + memset(qoztmp->txbuf[span->offset][i],0x0,sizeof(qoztmp->txbuf[span->offset][i])); + span->chans[i].writechunk = qoztmp->txbuf[span->offset][i]; + } + /* setup D channel buffer */ + memset(qoztmp->dtxbuf[span->offset],0x0,sizeof(qoztmp->dtxbuf[span->offset])); + span->chans[2].writechunk = qoztmp->dtxbuf[span->offset]; + qoztmp->chans[span->offset][2].maxbytes2transmit = sizeof(qoztmp->dtxbuf[span->offset]); + + memset(qoztmp->drxbuf[span->offset],0x0,sizeof(qoztmp->drxbuf[span->offset])); + span->chans[2].readchunk = qoztmp->drxbuf[span->offset]; + + span->flags |= ZT_FLAG_RUNNING; + } else { +// printk(KERN_CRIT "already running\n"); + return 0; + } + + if (pcmslave && !span->offset) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((100 * HZ) / 1000); + } + + spin_lock_irqsave(&qoztmp->lock,flags); + if (pcmslave && !span->offset) { + if (debug) + printk(KERN_INFO "qozap: resetting PCM interface to slave mode\n"); + qoz_reset_pcm(qoztmp); + } + + // irqs off + qoz_outb(qoztmp,qoz_R_IRQ_CTRL, 0); + + if (qoztmp->type == 0xb552) { + offset = 24; + } else if (qoztmp->type == 0xb55b) { + offset = 24; + } else { + offset = 28; + } + + /* setup D-FIFO TX */ + qoz_outb(qoztmp,qoz_R_FIFO,(span->offset + offset) << 1); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_R_INC_RES_FIFO,0x2); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_A_CON_HDLC,0xD); + qoz_outb(qoztmp,qoz_A_SUBCH_CFG,0x2); + qoz_outb(qoztmp,qoz_A_CHANNEL,((span->offset * 4) + 2) << 1); + qoz_outb(qoztmp,qoz_A_IRQ_MSK,0x1); + + /* setup D-FIFO RX */ + qoz_outb(qoztmp,qoz_R_FIFO,((span->offset + offset) << 1) | 1); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_R_INC_RES_FIFO,0x2); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_A_CON_HDLC,0xD); + qoz_outb(qoztmp,qoz_A_SUBCH_CFG,0x2); + qoz_outb(qoztmp,qoz_A_CHANNEL,(((span->offset * 4) + 2) << 1) | 1); + qoz_outb(qoztmp,qoz_A_IRQ_MSK,0x1); + + /* setup B1-FIFO TX */ + qoz_outb(qoztmp,qoz_R_FIFO,(span->offset * 2) << 1); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_R_INC_RES_FIFO,0x2); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_A_CON_HDLC,0x2); + qoz_outb(qoztmp,qoz_A_CHANNEL,(span->offset * 4) << 1); + qoz_outb(qoztmp,qoz_A_IRQ_MSK,0x1); + + /* setup B1-FIFO RX */ + qoz_outb(qoztmp,qoz_R_FIFO,((span->offset * 2) << 1) | 1); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_R_INC_RES_FIFO,0x2); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_A_CON_HDLC,0x2); + qoz_outb(qoztmp,qoz_A_CHANNEL,((span->offset * 4) << 1) | 1); + qoz_outb(qoztmp,qoz_A_IRQ_MSK,0x1); + + /* setup B2-FIFO TX */ + qoz_outb(qoztmp,qoz_R_FIFO,((span->offset * 2) + 1) << 1); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_R_INC_RES_FIFO,0x2); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_A_CON_HDLC,0x2); + qoz_outb(qoztmp,qoz_A_CHANNEL,((span->offset * 4) + 1) << 1); + qoz_outb(qoztmp,qoz_A_IRQ_MSK,0x1); + + /* setup B2-FIFO RX */ + qoz_outb(qoztmp,qoz_R_FIFO,(((span->offset * 2) + 1) << 1) | 1); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_R_INC_RES_FIFO,0x2); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_A_CON_HDLC,0x2); + qoz_outb(qoztmp,qoz_A_CHANNEL,((((span->offset) * 4) + 1) << 1) | 1); + qoz_outb(qoztmp,qoz_A_IRQ_MSK,0x1); + + if (debug) + printk(KERN_INFO "qozap: starting card %d span %d/%d.\n",qoztmp->cardno,span->spanno,span->offset); + + /* activate layer 1 */ + qoz_outb(qoztmp,qoz_R_ST_SEL,span->offset); + if (qoztmp->st[span->offset].nt_mode == 1) { + // NT mode + qoz_outb(qoztmp,qoz_A_ST_CTRL0,0x7); + qoz_outb(qoztmp,qoz_A_ST_CTRL1,0x0); + qoz_outb(qoztmp,qoz_A_ST_CTRL2,0x3); + qoz_outb(qoztmp,qoz_A_ST_CLK_DLY,0x60 | CLKDEL_NT); + } else { + // TE mode + qoz_outb(qoztmp,qoz_A_ST_CTRL0,0x3); + qoz_outb(qoztmp,qoz_A_ST_CTRL1,0x0); + qoz_outb(qoztmp,qoz_A_ST_CTRL2,0x3); + if (qoztmp->type == 0xb550) { + qoz_outb(qoztmp,qoz_A_ST_CLK_DLY,CLKDEL_TE); + } else { + qoz_outb(qoztmp,qoz_A_ST_CLK_DLY,CLKDEL_TE + 1); + } + } + qoztmp->st[span->offset].t3 = 0; + qoztmp->st[span->offset].t4 = -1; + + qoz_outb(qoztmp,qoz_R_ST_SEL,span->offset); + if (qoztmp->st[span->offset].nt_mode == 1) { + qoz_outb(qoztmp,qoz_A_ST_WR_STA,0x80); + } else { + qoz_outb(qoztmp,qoz_A_ST_WR_STA,0x0); + } + + + qoz_outb(qoztmp,qoz_R_ST_SEL,span->offset); + if (qoztmp->st[span->offset].nt_mode == 1) { + qoz_outb(qoztmp,qoz_A_ST_WR_STA,0x60 | 0x80); // ACT, G2->G3 EN + } else { + qoz_outb(qoztmp,qoz_A_ST_WR_STA,0x60); // start Activation + } + + /* enable irqs */ + qoz_outb(qoztmp,qoz_R_IRQ_CTRL, 8 | 1); + + spin_unlock_irqrestore(&qoztmp->lock,flags); + return 0; +} + +static int ztqoz_shutdown(struct zt_span *span) { + unsigned long flags; + struct qoz_card *qoztmp = span->pvt; + int alreadyrunning; + int offset = 0; + + if (qoztmp == NULL) { + printk(KERN_CRIT "qozap: qoztmp == NULL!\n"); + return 0; + + } + + alreadyrunning = span->flags & ZT_FLAG_RUNNING; + + if (!alreadyrunning) { + return 0; + } + +// printk(KERN_CRIT "qozap: stopping card %d port %d.\n",qoztmp->cardno, span->offset + 1); + + spin_lock_irqsave(&qoztmp->lock,flags); + // turn off irqs for all fifos + if (qoztmp->type == 0xb552) { + offset = 24; + } else if (qoztmp->type == 0xb55b) { + offset = 24; + } else { + offset = 28; + } + + /* disable D TX fifo */ + qoz_outb(qoztmp,qoz_R_FIFO,(span->offset + offset) << 1); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_A_CON_HDLC,0x1); + qoz_outb(qoztmp,qoz_A_IRQ_MSK,0x0); + + /* disable D RX fifo */ + qoz_outb(qoztmp,qoz_R_FIFO,((span->offset + offset) << 1) | 1); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_A_CON_HDLC,0x1); + qoz_outb(qoztmp,qoz_A_IRQ_MSK,0x0); + + /* disable B1 TX fifo */ + qoz_outb(qoztmp,qoz_R_FIFO,(span->offset * 2) << 1); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_A_CON_HDLC,0x2); + qoz_outb(qoztmp,qoz_A_IRQ_MSK,0x0); + + /* disable B1 RX fifo */ + qoz_outb(qoztmp,qoz_R_FIFO,((span->offset * 2) << 1) | 1); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_A_CON_HDLC,0x2); + qoz_outb(qoztmp,qoz_A_IRQ_MSK,0x0); + + /* disable B2 TX fifo */ + qoz_outb(qoztmp,qoz_R_FIFO,(((span->offset) * 2) + 1) << 1); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_A_CON_HDLC,0x2); + qoz_outb(qoztmp,qoz_A_IRQ_MSK,0x0); + + /* disable B2 RX fifo */ + qoz_outb(qoztmp,qoz_R_FIFO,((((span->offset) * 2) + 1) << 1) | 1); + qoz_waitbusy(qoztmp); + qoz_outb(qoztmp,qoz_A_CON_HDLC,0x2); + qoz_outb(qoztmp,qoz_A_IRQ_MSK,0x0); + + span->flags &= ~ZT_FLAG_RUNNING; + + /* Deactivate Layer 1 */ + qoz_outb(qoztmp,qoz_R_ST_SEL,span->offset); + if (qoztmp->st[span->offset].nt_mode == 1) { + qoz_outb(qoztmp,qoz_A_ST_WR_STA,0x40); + } else { + qoz_outb(qoztmp,qoz_A_ST_WR_STA,0x40); + } + + + spin_unlock_irqrestore(&qoztmp->lock,flags); +// printk(KERN_CRIT "qozap: card %d span %d/%d down.\n",qoztmp->cardno,span->spanno,span->offset); + return 0; +} + +static int ztqoz_maint(struct zt_span *span, int cmd) { + return 0; +} + +static int ztqoz_chanconfig(struct zt_chan *chan,int sigtype) { +// printk(KERN_INFO "chan_config sigtype=%d\n",sigtype); + return 0; +} + +static int ztqoz_spanconfig(struct zt_span *span,struct zt_lineconfig *lc) { + struct qoz_card *qoztmp = span->pvt; + span->lineconfig = lc->lineconfig; + qoztmp->st_sync[span->offset] = lc->sync; + qoztmp->syncsrc = -1; + return 0; +} + +static int ztqoz_initialize(struct qoz_card *qoztmp) { + int i=0,s=0; + + for (s=0; s < qoztmp->stports; s++) { + memset(&qoztmp->spans[s],0,sizeof(struct zt_span)); + sprintf(qoztmp->spans[s].name,"ztqoz/%d/%d",qoztmp->cardno ,s + 1); + if (qoztmp->stports == 8) { + if (qoztmp->st[s].nt_mode == 1){ + sprintf(qoztmp->spans[s].desc,"octoBRI PCI ISDN Card %d Span %d [NT]",qoztmp->cardno,s + 1); + } else { + sprintf(qoztmp->spans[s].desc,"octoBRI PCI ISDN Card %d Span %d [TE]",qoztmp->cardno,s + 1); + } + } else if (qoztmp->stports == 2) { + if (qoztmp->st[s].nt_mode == 1){ + sprintf(qoztmp->spans[s].desc,"duoBRI PCI ISDN Card %d Span %d [NT]",qoztmp->cardno,s + 1); + } else { + sprintf(qoztmp->spans[s].desc,"duoBRI PCI ISDN Card %d Span %d [TE]",qoztmp->cardno,s + 1); + } + } else { + if (qoztmp->cardID < 0xff) { + if (qoztmp->st[s].nt_mode == 1){ + sprintf(qoztmp->spans[s].desc,"quadBRI PCI ISDN Card %d Span %d [NT] (cardID %d)",qoztmp->cardno,s + 1,qoztmp->cardID); + } else { + sprintf(qoztmp->spans[s].desc,"quadBRI PCI ISDN Card %d Span %d [TE] (cardID %d)",qoztmp->cardno,s + 1,qoztmp->cardID); + } + } else { + if (qoztmp->st[s].nt_mode == 1){ + sprintf(qoztmp->spans[s].desc,"quadBRI PCI ISDN Card %d Span %d [NT]",qoztmp->cardno,s + 1); + } else { + sprintf(qoztmp->spans[s].desc,"quadBRI PCI ISDN Card %d Span %d [TE]",qoztmp->cardno,s + 1); + } + } + } + + qoztmp->spans[s].spanconfig = ztqoz_spanconfig; + qoztmp->spans[s].chanconfig = ztqoz_chanconfig; + qoztmp->spans[s].startup = ztqoz_startup; + qoztmp->spans[s].shutdown = ztqoz_shutdown; + qoztmp->spans[s].maint = ztqoz_maint; + qoztmp->spans[s].rbsbits = ztqoz_rbsbits; + qoztmp->spans[s].open = ztqoz_open; + qoztmp->spans[s].close = ztqoz_close; + qoztmp->spans[s].ioctl = ztqoz_ioctl; + qoztmp->spans[s].dacs = ztqoz_dacs; + + qoztmp->spans[s].chans = qoztmp->chans[s]; + qoztmp->spans[s].channels = 3; + qoztmp->spans[s].deflaw = ZT_LAW_ALAW; + qoztmp->spans[s].linecompat = ZT_CONFIG_AMI | ZT_CONFIG_CCS; + init_waitqueue_head(&qoztmp->spans[s].maintq); + qoztmp->spans[s].pvt = qoztmp; + qoztmp->spans[s].offset = s; + + for (i=0; i < qoztmp->spans[s].channels; i++) { + memset(&(qoztmp->chans[s][i]),0x0,sizeof(struct zt_chan)); + sprintf(qoztmp->chans[s][i].name,"ztqoz%d/%d/%d",qoztmp->cardno,s + 1,i + 1); + qoztmp->chans[s][i].pvt = qoztmp; + qoztmp->chans[s][i].sigcap = ZT_SIG_CLEAR | ZT_SIG_DACS; + qoztmp->chans[s][i].chanpos = i + 1; + } + + if (zt_register(&qoztmp->spans[s],0)) { + printk(KERN_INFO "qozap: unable to register zaptel span %d!\n",s+1); + return -1; + } +// printk(KERN_INFO "qozap: registered zaptel span %d.\n",s+1); + } + + return 0; +} + +int qoz_findCards(unsigned int pcidid) { + struct pci_dev *tmp; + struct qoz_card *qoztmp = NULL; + int i=0; + unsigned char dips=0; + int cid=0; + int modes=0; + tmp = pci_get_device(PCI_VENDOR_ID_CCD,pcidid,multi_qoz); + while (tmp != NULL) { + multi_qoz = tmp; // skip this next time. + + if (pci_enable_device(tmp)) { + multi_qoz = NULL; + return -1; + } + + qoztmp = kmalloc(sizeof(struct qoz_card),GFP_KERNEL); + if (!qoztmp) { + printk(KERN_WARNING "qozap: unable to kmalloc!\n"); + pci_disable_device(tmp); + multi_qoz = NULL; + return -ENOMEM; + } + memset(qoztmp, 0x0, sizeof(struct qoz_card)); + + spin_lock_init(&qoztmp->lock); + qoztmp->pcidev = tmp; + qoztmp->pcibus = tmp->bus->number; + qoztmp->pcidevfn = tmp->devfn; + + if (!tmp->irq) { + printk(KERN_WARNING "qozap: no irq!\n"); + } else { + qoztmp->irq = tmp->irq; + } + + qoztmp->pci_io_phys = (char *) tmp->resource[1].start; + if (!qoztmp->pci_io_phys) { + printk(KERN_WARNING "qozap: no iomem!\n"); + pci_disable_device(tmp); + multi_qoz = NULL; + return -EIO; + } + + qoztmp->ioport = tmp->resource[0].start; + if (!qoztmp->ioport) { + printk(KERN_WARNING "qozap: no ioport!\n"); + pci_disable_device(tmp); + multi_qoz = NULL; + return -EIO; + } + if (!request_region(qoztmp->ioport, 8, "qozap")) { + printk(KERN_WARNING "qozap: couldnt request io range!\n"); + pci_disable_device(tmp); + multi_qoz = NULL; + return -EIO; + } + if (!request_mem_region((unsigned long) qoztmp->pci_io_phys, 256, "qozap")) { + printk(KERN_WARNING "qozap: couldnt request io mem range!\n"); + release_region(qoztmp->ioport, 8); + pci_disable_device(tmp); + multi_qoz = NULL; + return -EIO; + } + + if (request_irq(qoztmp->irq, qoz_interrupt, ZAP_IRQ_SHARED, "qozap", qoztmp)) { + printk(KERN_WARNING "qozap: unable to register irq\n"); + kfree(qoztmp); + pci_disable_device(tmp); + multi_qoz = NULL; + return -EIO; + } + + qoztmp->pci_io = ioremap((ulong) qoztmp->pci_io_phys, 256); + + pci_write_config_word(qoztmp->pcidev, PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + + qoz_outb(qoztmp,qoz_R_IRQ_CTRL, 0); + + qoztmp->type = tmp->subsystem_device; + qoz_resetCard(qoztmp); + + + if ((tmp->subsystem_device==0xb520) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + // printk(KERN_INFO "MODES = %#x.\n",modes); + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0x80 | 0x40); + qoz_outb(qoztmp,qoz_R_GPIO_EN1,0x00); + dips = (qoz_inb(qoztmp,qoz_R_GPIO_IN1) >> 5); + cid = 7; + for (i=0;i<3;i++) { + if ((dips & (1 << i)) != 0) { + cid -= (1 << (2-i)); + } + } + // printk(KERN_INFO "DIPS = %#x CID= %#x\n",dips,cid); + } else if ((tmp->subsystem_device==0xb550) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + /* quadBRI 2.0 */ + // printk(KERN_INFO "MODES = %#x.\n",modes); + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0xF0); + qoz_outb(qoztmp,qoz_R_GPIO_EN0,0x00); + qoz_outb(qoztmp,qoz_R_GPIO_EN1,0x00); +// dips = qoz_inb(qoztmp,qoz_R_GPIO_IN1) >> 5; + dips = ((qoz_inb(qoztmp,qoz_R_GPI_IN3) & 80) >> 1) | ((qoz_inb(qoztmp,qoz_R_GPI_IN3) & 8) << 2) | qoz_inb(qoztmp,qoz_R_GPIO_IN1); + + cid = 0; + for (i=0;i<3;i++) { + if ((dips & (1 << i)) == 0) { + cid += (1 << i); + } + } + + } else if ((tmp->subsystem_device==0xb556) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + /* duoBRI */ + + /* gpi27 1 gpi23 2 */ + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0xf0); + qoz_outb(qoztmp,qoz_R_GPIO_EN0,0x00); + qoz_outb(qoztmp,qoz_R_GPIO_EN1,0x00); + +/* dips = qoz_inb_io(qoztmp,0x4000); + dips = qoz_inb_io(qoztmp,0x4000); + dips = qoz_inb_io(qoztmp,0x4000); + dips = qoz_inb_io(qoztmp,0x4000); + + qoz_outw_io(qoztmp,0x0,0x0); */ + + + } else if ((tmp->subsystem_device==0xb558) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + /* quadBRI minipCI */ + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0xF0); + qoz_outb(qoztmp,qoz_R_GPIO_EN0,0x00); + qoz_outb(qoztmp,qoz_R_GPIO_EN1,0x00); + dips = qoz_inb(qoztmp,qoz_R_GPIO_IN1) >> 5; +/* printk(KERN_INFO "gpio_in1 %#x\n", qoz_inb(qoztmp,qoz_R_GPIO_IN1)); + + printk(KERN_INFO "gpi_in2 %#x\n", qoz_inb(qoztmp,qoz_R_GPI_IN2)); + printk(KERN_INFO "dip1 %#x\n", (qoz_inb(qoztmp,qoz_R_GPI_IN3) & 80) >> 7); + printk(KERN_INFO "gpi_in3 %#x\n", qoz_inb(qoztmp,qoz_R_GPI_IN3)); + printk(KERN_INFO "dip2 %#x\n", (qoz_inb(qoztmp,qoz_R_GPI_IN3) & 8) >> 3); >*/ + cid = 0; + for (i=0;i<3;i++) { + if ((dips & (1 << i)) != 0) { + cid += (1 << i); + } + } +// printk(KERN_INFO "DIPS = %#x CID= %#x\n",dips,cid); + + } else if ((tmp->subsystem_device==0xb55b) && (pcidid == PCI_DEVICE_ID_CCD_M)){ + qoz_outb(qoztmp,qoz_R_BRG_PCM_CFG,0x21); + + dips = ~qoz_inb_io(qoztmp,0x4000); + dips = ~qoz_inb_io(qoztmp,0x4000); + dips = ~qoz_inb_io(qoztmp,0x4000); + dips = ~qoz_inb_io(qoztmp,0x4000); + + dips &= 0x3F; + qoz_outw_io(qoztmp,0x0,0x0); + + cid = 0; + for (i=0;i<6;i++) { + if ((dips & (1 << i)) != 0) { + cid += (1 << i); + } + } + qoz_outb(qoztmp,qoz_R_BRG_PCM_CFG,0x20); + printk(KERN_CRIT "dips = %#x cid = %d\n", dips, cid); + } else if (((tmp->subsystem_device==0xb752) || (tmp->subsystem_device==0xb751)) && (pcidid == PCI_DEVICE_ID_CCD_M4)){ + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0xF0); + qoz_outb(qoztmp,qoz_R_GPIO_EN0,0x00); + qoz_outb(qoztmp,qoz_R_GPIO_EN1,0x00); +// dips = qoz_inb(qoztmp,qoz_R_GPIO_IN1) >> 5; + dips = ((qoz_inb(qoztmp,qoz_R_GPI_IN3) & 80) >> 1) | ((qoz_inb(qoztmp,qoz_R_GPI_IN3) & 8) << 2) | qoz_inb(qoztmp,qoz_R_GPIO_IN1); + + cid = 0; + for (i=0;i<3;i++) { + if ((dips & (1 << i)) == 0) { + cid += (1 << i); + } + } + printk(KERN_CRIT "dips = %#x cid = %d\n", dips, cid); + } else { + cid = 0xff; + } + + if (ports == -1) { + if ((tmp->subsystem_device==0xb520) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + modes = qoz_inb(qoztmp,qoz_R_GPI_IN3) >> 4; + } else if ((tmp->subsystem_device==0xb550) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0xf0); + qoz_outb(qoztmp,qoz_R_GPIO_EN0,0x00); + if (debug) { + printk(KERN_CRIT "gpio_in0 %#x \n", qoz_inb(qoztmp,qoz_R_GPIO_IN0)); + printk(KERN_CRIT "gpio_in1 %#x \n", qoz_inb(qoztmp,qoz_R_GPIO_IN1)); + printk(KERN_CRIT "gpi_in1 %#x \n", qoz_inb(qoztmp,qoz_R_GPI_IN1)); + printk(KERN_CRIT "gpi_in2 %#x \n", qoz_inb(qoztmp,qoz_R_GPI_IN2)); + printk(KERN_CRIT "gpi_in3 %#x \n", qoz_inb(qoztmp,qoz_R_GPI_IN3)); + } + modes = qoz_inb(qoztmp,qoz_R_GPI_IN3) >> 4; + } else if ((tmp->subsystem_device==0xb558) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0xf0); + qoz_outb(qoztmp,qoz_R_GPIO_EN0,0x00); + if (debug) { + printk(KERN_CRIT "gpio_in0 %#x \n", qoz_inb(qoztmp,qoz_R_GPIO_IN0)); + printk(KERN_CRIT "gpio_in1 %#x \n", qoz_inb(qoztmp,qoz_R_GPIO_IN1)); + printk(KERN_CRIT "gpi_in1 %#x \n", qoz_inb(qoztmp,qoz_R_GPI_IN1)); + printk(KERN_CRIT "gpi_in2 %#x \n", qoz_inb(qoztmp,qoz_R_GPI_IN2)); + printk(KERN_CRIT "gpi_in3 %#x \n", qoz_inb(qoztmp,qoz_R_GPI_IN3)); + } + modes = qoz_inb(qoztmp,qoz_R_GPI_IN3) >> 4; + } else if ((tmp->subsystem_device==0xb556) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0xf0); + qoz_outb(qoztmp,qoz_R_GPIO_EN0,0x00); + if (debug) { + printk(KERN_CRIT "gpio_in0 %#x \n", qoz_inb(qoztmp,qoz_R_GPIO_IN0)); + printk(KERN_CRIT "gpio_in1 %#x \n", qoz_inb(qoztmp,qoz_R_GPIO_IN1)); + printk(KERN_CRIT "gpi_in1 %#x \n", qoz_inb(qoztmp,qoz_R_GPI_IN1)); + printk(KERN_CRIT "gpi_in2 %#x \n", qoz_inb(qoztmp,qoz_R_GPI_IN2)); + printk(KERN_CRIT "gpi_in3 %#x \n", qoz_inb(qoztmp,qoz_R_GPI_IN3)); + } + modes = qoz_inb(qoztmp,qoz_R_GPI_IN3) >> 4; + } else if ((tmp->subsystem_device==0xb751) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0xf0); + qoz_outb(qoztmp,qoz_R_GPIO_EN0,0x00); + if (debug) { + printk(KERN_CRIT "gpio_in0 %#x \n", qoz_inb(qoztmp,qoz_R_GPIO_IN0)); + printk(KERN_CRIT "gpio_in1 %#x \n", qoz_inb(qoztmp,qoz_R_GPIO_IN1)); + printk(KERN_CRIT "gpi_in1 %#x \n", qoz_inb(qoztmp,qoz_R_GPI_IN1)); + printk(KERN_CRIT "gpi_in2 %#x \n", qoz_inb(qoztmp,qoz_R_GPI_IN2)); + printk(KERN_CRIT "gpi_in3 %#x \n", qoz_inb(qoztmp,qoz_R_GPI_IN3)); + } + modes = qoz_inb(qoztmp,qoz_R_GPI_IN3) >> 4; + } else if ((tmp->subsystem_device==0xb752) && (pcidid == PCI_DEVICE_ID_CCD_M4)) { + qoz_outb(qoztmp,qoz_R_GPIO_SEL,0xf0); + qoz_outb(qoztmp,qoz_R_GPIO_EN0,0x00); + if (debug) { + printk(KERN_CRIT "gpio_in0 %#x \n", qoz_inb(qoztmp,qoz_R_GPIO_IN0)); + printk(KERN_CRIT "gpio_in1 %#x \n", qoz_inb(qoztmp,qoz_R_GPIO_IN1)); + printk(KERN_CRIT "gpi_in1 %#x \n", qoz_inb(qoztmp,qoz_R_GPI_IN1)); + printk(KERN_CRIT "gpi_in2 %#x \n", qoz_inb(qoztmp,qoz_R_GPI_IN2)); + printk(KERN_CRIT "gpi_in3 %#x \n", qoz_inb(qoztmp,qoz_R_GPI_IN3)); + } + modes = qoz_inb(qoztmp,qoz_R_GPI_IN3) >> 4; + } else { + modes = 0; // assume TE mode + } + } else { + modes = ports >> totalBRIs; + } + + if (pcidid == PCI_DEVICE_ID_CCD_M4) { + switch (tmp->subsystem_device) { + case 0x08b4: + if (ports == -1) ports = 0; /* assume TE mode if no ports param */ + printk(KERN_INFO + "qozap: CologneChip HFC-4S evaluation board configured at io port %#x IRQ %d HZ %d\n", + (u_int) qoztmp->ioport, + qoztmp->irq, HZ); + totalBRIs += 4; + break; + case 0xb520: + printk(KERN_INFO + "qozap: Junghanns.NET quadBRI card configured at io port %#x IRQ %d HZ %d CardID %d\n", + (u_int) qoztmp->ioport, + qoztmp->irq, HZ, cid); + totalBRIs += 4; + break; + case 0xb550: + printk(KERN_INFO + "qozap: Junghanns.NET quadBRI (Version 2.0) card configured at io port %#x IRQ %d HZ %d CardID %d\n", + (u_int) qoztmp->ioport, + qoztmp->irq, HZ, cid); + totalBRIs += 4; + break; + case 0xb556: + printk(KERN_INFO + "qozap: Junghanns.NET duoBRI card configured at io port %#x IRQ %d HZ %d CardID %d\n", + (u_int) qoztmp->ioport, + qoztmp->irq, HZ, cid); + totalBRIs += 2; + break; + case 0xb559: + printk(KERN_INFO + "qozap: Junghanns.NET duoBRI miniPCI card configured at io port %#x IRQ %d HZ %d CardID %d\n", + (u_int) qoztmp->ioport, + qoztmp->irq, HZ, cid); + totalBRIs += 2; + break; + case 0xb751: + printk(KERN_INFO + "qozap: Junghanns.NET duoBRI PCIe card configured at io port %#x IRQ %d HZ %d CardID %d\n", + (u_int) qoztmp->ioport, + qoztmp->irq, HZ, cid); + totalBRIs += 2; + break; + case 0xb558: + printk(KERN_INFO + "qozap: Junghanns.NET quadBRI miniPCI card configured at io port %#x IRQ %d HZ %d CardID %d\n", + (u_int) qoztmp->ioport, + qoztmp->irq, HZ, cid); + totalBRIs += 4; + break; + case 0xb752: + printk(KERN_INFO + "qozap: Junghanns.NET quadBRI PCIe card configured at io port %#x IRQ %d HZ %d CardID %d\n", + (u_int) qoztmp->ioport, + qoztmp->irq, HZ, cid); + totalBRIs += 4; + break; + } + } else { + switch (tmp->subsystem_device) { + case 0xb552: + printk(KERN_INFO + "qozap: Junghanns.NET octoBRI card configured at io port %#x IRQ %d HZ %d\n", + (u_int) qoztmp->ioport, + qoztmp->irq, HZ); + break; + case 0xb55b: + printk(KERN_INFO + "qozap: Junghanns.NET octoBRI (Version 2.0) card configured at io port %#x IRQ %d HZ %d\n", + (u_int) qoztmp->ioport, + qoztmp->irq, HZ); + break; + default: + printk(KERN_INFO + "qozap: wtf\n"); + if (qoztmp->pcidev != NULL) { + pci_disable_device(qoztmp->pcidev); + } + pci_write_config_word(qoztmp->pcidev, PCI_COMMAND, 0); + free_irq(qoztmp->irq,qoztmp); + kfree(qoztmp); + qoztmp = NULL; + tmp = pci_get_device(PCI_VENDOR_ID_CCD,pcidid,multi_qoz); + continue; + break; + } + totalBRIs += 8; + } + + qoztmp->cardID = cid; + qoztmp->type = tmp->subsystem_device; + + printk(KERN_INFO "qozap: S/T ports: %d [",qoztmp->stports); + for (i=0;istports;i++) { + qoztmp->st_sync[i] = 0; + if ((modes & (1 << i)) != 0) { + qoztmp->st[i].nt_mode = 1; + printk(" NT"); + } else { + qoztmp->st[i].nt_mode = 0; + printk(" TE"); + } + } + printk(" ]\n"); + + qoz_registerCard(qoztmp); + tmp = pci_get_device(PCI_VENDOR_ID_CCD,pcidid,multi_qoz); + } + return 0; +} + + +int qoz_sortCards(void) { + int changed=0,tmpcardno; + struct qoz_card *tmpcard,*tmpcard2; + spin_lock(®isterlock); + do { + changed = 0; + tmpcard = qoz_dev_list; + while (tmpcard != NULL) { + if (tmpcard->prev) { + if (tmpcard->prev->cardID > tmpcard->cardID) { + tmpcardno = tmpcard->prev->cardno; + tmpcard->prev->cardno = tmpcard->cardno; + tmpcard->cardno = tmpcardno; + + tmpcard2 = tmpcard->prev; + if (tmpcard2->prev) { + tmpcard2->prev->next = tmpcard; + } else { + qoz_dev_list = tmpcard; + } + if (tmpcard->next) { + tmpcard->next->prev = tmpcard2; + } + tmpcard2->next = tmpcard->next; + tmpcard->prev = tmpcard2->prev; + tmpcard->next = tmpcard2; + tmpcard2->prev = tmpcard; + changed = 1; + tmpcard = tmpcard2; + } + } + tmpcard = tmpcard->next; + } + } while (changed == 1); + spin_unlock(®isterlock); + return 0; +} + +int qoz_zapCards(void) { + struct qoz_card *tmpcard; + tmpcard = qoz_dev_list; + while (tmpcard != NULL) { + ztqoz_initialize(tmpcard); + tmpcard = tmpcard->next; + } + return 0; +} + + +int init_module(void) { + multi_qoz = NULL; + qoz_findCards(PCI_DEVICE_ID_CCD_M4); + multi_qoz = NULL; + qoz_findCards(PCI_DEVICE_ID_CCD_M); + if (sort) { + qoz_sortCards(); + } + qoz_zapCards(); + if (qoz_dev_count == 0) { + printk(KERN_INFO "qozap: no multiBRI cards found.\n"); + } else { + printk(KERN_INFO "qozap: %d multiBRI card(s) in this box, %d BRI ports total, bloop %d, pcmslave %d, dacs %d.\n",qoz_dev_count, totalBRIs, bloop, pcmslave, dacs); + } + return 0; +} + +void cleanup_module(void) { + struct qoz_card *tmpcard,*tmplist; + int i=0; + tmplist = qoz_dev_list; + while (tmplist != NULL) { + tmplist->dead = 1; + qoz_resetCard(tmplist); + qoz_undoWD(tmplist); + qoz_shutdownCard(tmplist); + tmplist = tmplist->next; + } + tmplist = qoz_dev_list; + spin_lock(®isterlock); + while (tmplist != NULL) { + tmpcard = tmplist->next; + kfree(tmplist); + i++; + tmplist = tmpcard; + } + spin_unlock(®isterlock); + printk(KERN_INFO "qozap: shutdown %d multiBRI cards.\n", i); +} +#endif + +#ifdef LINUX26 +module_param(doubleclock, int, 0600); +module_param(ports, int, 0600); +module_param(pcmslave, int, 0600); +module_param(bloop, int, 0600); +module_param(debug, int, 0600); +module_param(dacs, int, 0600); +module_param(sort, int, 0600); +#else +MODULE_PARM(doubleclock,"i"); +MODULE_PARM(ports,"i"); +MODULE_PARM(pcmslave,"i"); +MODULE_PARM(bloop,"i"); +MODULE_PARM(debug,"i"); +MODULE_PARM(dacs,"i"); +MODULE_PARM(sort,"i"); +#endif + + +MODULE_DESCRIPTION("quad/octo BRI zaptel driver"); +MODULE_AUTHOR("Klaus-Peter Junghanns "); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif --- zaptel-1.4.11~dfsg.orig/kernel/qozap/qozap.h +++ zaptel-1.4.11~dfsg/kernel/qozap/qozap.h @@ -0,0 +1,236 @@ +#define qoz_SPANS 8 +#define qoz_FIFO_SIZE 128 +#define qoz_DFIFO_SIZE4 2048 +#define qoz_DFIFO_SIZE8 1024 + +typedef struct qoz_span { + unsigned char nt_mode; + unsigned char btx; + unsigned char bswapped; + unsigned char drx; + int t3; + int t4; + int l1up; + unsigned char layer1state; +} qoz_span; + +typedef struct qoz_regs { + unsigned char fifo_en; + unsigned char ctmt; + unsigned char int_m1; + unsigned char int_m2; + unsigned char sctrl; + unsigned char sctrl_e; + unsigned char sctrl_r; + unsigned char connect; + unsigned char trm; + unsigned char mst_mode; +} qoz_regs; + +typedef struct qoz_card { + spinlock_t lock; + int cardID; + unsigned char dead; + unsigned char leds[8]; + unsigned char cardno; + unsigned int irq; + unsigned int iomem; + unsigned char *pci_io; + void *pci_io_phys; + unsigned long ioport; + struct qoz_span st[qoz_SPANS]; + int st_sync[qoz_SPANS]; + int syncsrc; + unsigned int pcibus; + unsigned int pcidevfn; + struct pci_dev *pcidev; + struct zt_span spans[qoz_SPANS]; + struct zt_chan chans[qoz_SPANS][3]; + unsigned char rxbuf[qoz_SPANS][2][ZT_CHUNKSIZE]; + unsigned char txbuf[qoz_SPANS][2][ZT_CHUNKSIZE]; + unsigned char drxbuf[qoz_SPANS][qoz_DFIFO_SIZE4]; + unsigned char dtxbuf[qoz_SPANS][qoz_DFIFO_SIZE4]; + unsigned char stports; + unsigned int ticks; + unsigned int clicks; + unsigned int type; + unsigned int wdp; + struct qoz_card *next; + struct qoz_card *prev; +} qoz_card; + + +#define qoz_outb_io(a,b,c) \ + outw((b), ((a)->ioport+4)); \ + outb((c), ((a)->ioport)); + +#define qoz_inb_io(a,b) ({ outw((b), (a)->ioport+4); inb((a)->ioport); }) + +#define qoz_outw_io(a,b,c) \ + outw((b), ((a)->ioport+4)); \ + outw((c), ((a)->ioport)); + +#define qoz_inw_io(a,b) ({ outw((b), (a)->ioport+4); inw((a)->ioport); }) + +#define qoz_outdw_io(a,b,c) \ + outw((b), ((a)->ioport+4)); \ + outl((c), ((a)->ioport)); + +#define qoz_indw_io(a,b) ({ outw((b), (a)->ioport+4); inl((a)->ioport); }) + +#define qoz_outb(a,b,c) (writeb((c),(a)->pci_io+(b))) +#define qoz_inb(a,b) (readb((a)->pci_io+(b))) + +#define qoz_outw(a,b,c) (writew((c),(a)->pci_io+(b))) +#define qoz_inw(a,b) (readw((a)->pci_io+(b))) + +#define qoz_outdw(a,b,c) (writel((c),(a)->pci_io+(b))) +#define qoz_indw(a,b) (readl((a)->pci_io+(b))) + + +/* Write only registers */ +#define qoz_A_CH_MSK 0xF4 +#define qoz_A_CHANNEL 0xFC +#define qoz_A_CON_HDLC 0xFA +#define qoz_A_CONF 0xD1 +#define qoz_A_FIFO_SEQ 0xFD +#define qoz_R_INC_RES_FIFO 0x0E +#define qoz_A_IRQ_MSK 0xFF +#define qoz_A_SL_CFG 0xD0 +#define qoz_A_ST_B1_TX 0x3C +#define qoz_A_ST_B2_TX 0x3D +#define qoz_A_ST_CLK_DLY 0x37 +#define qoz_A_ST_CTRL0 0x31 +#define qoz_A_ST_CTRL1 0x32 +#define qoz_A_ST_CTRL2 0x33 +#define qoz_A_ST_D_TX 0x3E +#define qoz_A_ST_SQ_WR 0x34 +#define qoz_A_ST_WR_STA 0x30 +#define qoz_A_SUBCH_CFG 0xFB +#define qoz_R_BERT_WD_MD 0x1B +#define qoz_R_BRG_CTRL 0x45 +#define qoz_R_BRG_MD 0x47 +#define qoz_R_BRG_PCM_CFG 0x02 +#define qoz_R_BRG_TIM_SEL01 0x4C +#define qoz_R_BRG_TIM_SEL23 0x4D +#define qoz_R_BRG_TIM_SEL45 0x4E +#define qoz_R_BRG_TIM_SEL67 0x4F +#define qoz_R_BRG_TIM0 0x48 +#define qoz_R_BRG_TIM1 0x49 +#define qoz_R_BRG_TIM2 0x4A +#define qoz_R_BRG_TIM3 0x4B +#define qoz_R_CIRM 0x00 +#define qoz_R_CONF_EN 0x18 +#define qoz_R_CTRL 0x01 +#define qoz_R_DTMF0 0x1C +#define qoz_R_DTMF1 0x1D +#define qoz_R_FIFO_MD 0x0D +#define qoz_R_FIFO 0x0F +#define qoz_R_FIRST_FIFO 0x0B +#define qoz_R_FSM_IDX 0x0F +#define qoz_R_GPIO_EN0 0x42 +#define qoz_R_GPIO_EN1 0x43 +#define qoz_R_GPIO_OUT0 0x40 +#define qoz_R_GPIO_OUT1 0x41 +#define qoz_R_GPIO_SEL 0x44 +#define qoz_R_IRQ_CTRL 0x13 +#define qoz_R_IRQMSK_MISC 0x11 +#define qoz_R_PCM_MD0 0x14 +#define qoz_R_PCM_MD1 0x15 +#define qoz_R_PCM_MD2 0x15 +#define qoz_R_PWM_MD 0x46 +#define qoz_R_PWM0 0x38 +#define qoz_R_PWM1 0x39 +#define qoz_R_RAM_ADDR0 0x08 +#define qoz_R_RAM_ADDR1 0x09 +#define qoz_R_RAM_ADDR2 0x0A +#define qoz_R_RAM_MISC 0x0C +#define qoz_R_SCI_MSK 0x12 +#define qoz_R_SH0H 0x15 +#define qoz_R_SH0L 0x15 +#define qoz_R_SH1H 0x15 +#define qoz_R_SH1L 0x15 +#define qoz_R_SL_SEL0 0x15 +#define qoz_R_SL_SEL1 0x15 +#define qoz_R_SL_SEL2 0x15 +#define qoz_R_SL_SEL3 0x15 +#define qoz_R_SL_SEL4 0x15 +#define qoz_R_SL_SEL5 0x15 +#define qoz_R_SL_SEL6 0x15 +#define qoz_R_SL_SEL7 0x15 +#define qoz_R_SLOT 0x10 +#define qoz_R_ST_SEL 0x16 +#define qoz_R_ST_SYNC 0x17 +#define qoz_R_TI_WD 0x1A + +/* Read only registers */ +#define qoz_A_F1 0x0C +#define qoz_A_F12 0x0C +#define qoz_A_F2 0x0D +#define qoz_A_ST_B1_RX 0x3C +#define qoz_A_ST_B2_TX 0x3D +#define qoz_A_ST_D_RX 0x3E +#define qoz_A_ST_E_RX 0x3F +#define qoz_A_ST_RD_STA 0x30 +#define qoz_A_ST_SQ_RD 0x34 +#define qoz_A_Z1 0x04 +#define qoz_A_Z12 0x04 +#define qoz_A_Z1H 0x05 +#define qoz_A_Z1L 0x04 +#define qoz_A_Z2 0x06 +#define qoz_A_Z2H 0x07 +#define qoz_A_Z2L 0x06 +#define qoz_R_BERT_ECH 0x1B +#define qoz_R_BERT_ECL 0x1A +#define qoz_R_BERT_STA 0x17 +#define qoz_R_CHIP_ID 0x16 +#define qoz_R_CHIP_RV 0x1F +#define qoz_R_CONF_OFLOW 0x14 +#define qoz_R_F0_CNTH 0x19 +#define qoz_R_F0_CNTL 0x18 +#define qoz_R_GPI_IN0 0x44 +#define qoz_R_GPI_IN1 0x45 +#define qoz_R_GPI_IN2 0x46 +#define qoz_R_GPI_IN3 0x47 +#define qoz_R_GPIO_IN0 0x40 +#define qoz_R_GPIO_IN1 0x41 +#define qoz_R_INT_DATA 0x88 +#define qoz_R_IRQ_FIFO_BL0 0xC8 +#define qoz_R_IRQ_FIFO_BL1 0xC9 +#define qoz_R_IRQ_FIFO_BL2 0xCA +#define qoz_R_IRQ_FIFO_BL3 0xCB +#define qoz_R_IRQ_FIFO_BL4 0xCC +#define qoz_R_IRQ_FIFO_BL5 0xCD +#define qoz_R_IRQ_FIFO_BL6 0xCE +#define qoz_R_IRQ_FIFO_BL7 0xCF +#define qoz_R_IRQ_MISC 0x11 +#define qoz_R_IRQ_OVIEW 0x10 +#define qoz_R_RAM_USE 0x15 +#define qoz_R_SCI 0x12 +#define qoz_R_STATUS 0x1C + +/* Read/Write registers */ +#define qoz_A_FIFO_DATA0_NOINC 0x84 +#define qoz_A_FIFO_DATA0 0x80 +#define qoz_A_FIFO_DATA1_NOINC 0x84 +#define qoz_A_FIFO_DATA1 0x80 +#define qoz_A_FIFO_DATA2_NOINC 0x84 +#define qoz_A_FIFO_DATA2 0x80 +#define qoz_R_RAM_DATA 0xC0 + +#define PCI_DEVICE_ID_CCD_M 0x16b8 +#define PCI_DEVICE_ID_CCD_M4 0x08b4 +#define CLKDEL_TE 0xe /* CLKDEL in TE mode */ +#define CLKDEL_NT 0xc /* CLKDEL in NT mode */ + +#define HFC8S_CHIP_ID 0x80 +#define HFC4S_CHIP_ID 0xC0 + +#define qoz_WD_P0 0x000000 +#define qoz_WD_P1 0x808080 +#define qoz_WD_P2 0x404040 + +#define qoz_T3 3 +#define qoz_T4 1 + + --- zaptel-1.4.11~dfsg.orig/kernel/qozap/zapata.conf.octoBRI +++ zaptel-1.4.11~dfsg/kernel/qozap/zapata.conf.octoBRI @@ -0,0 +1,67 @@ +; +; Zapata telephony interface +; +; Configuration file + +[channels] +; +; Default language +; +;language=en +; +; Default context +; +; +switchtype = euroisdn + +; p2mp TE mode (for connecting ISDN lines in point-to-multipoint mode) +signalling = bri_cpe_ptmp +; p2p TE mode (for connecting ISDN lines in point-to-point mode) +;signalling = bri_cpe +; p2mp NT mode (for connecting ISDN phones in point-to-multipoint mode) +;signalling = bri_net_ptmp +; p2p NT mode (for connecting an ISDN pbx in point-to-point mode) +;signalling = bri_net + +pridialplan = local +prilocaldialplan = dynamic +nationalprefix = 0 +internationalprefix = 00 + +priindication = passthrough + +echocancel = yes + +context=demo +group = 1 +; S/T port 1 +channel => 1-2 + +group = 2 +; S/T port 2 +channel => 4-5 + +group = 3 +; S/T port 3 +channel => 7-8 + +group = 4 +; S/T port 4 +channel => 10-11 + +group = 5 +; S/T port 5 +channel => 13-14 + +group = 6 +; S/T port 6 +channel => 16-17 + +group = 7 +; S/T port 7 +channel => 19-20 + +group = 8 +; S/T port 8 +channel => 22-23 + --- zaptel-1.4.11~dfsg.orig/kernel/qozap/zapata.conf +++ zaptel-1.4.11~dfsg/kernel/qozap/zapata.conf @@ -0,0 +1,51 @@ +; +; Zapata telephony interface +; +; Configuration file + +[channels] +; +; Default language +; +;language=en +; +; Default context +; +; +switchtype = euroisdn + +; p2mp TE mode (for connecting ISDN lines in point-to-multipoint mode) +signalling = bri_cpe_ptmp +; p2p TE mode (for connecting ISDN lines in point-to-point mode) +;signalling = bri_cpe +; p2mp NT mode (for connecting ISDN phones in point-to-multipoint mode) +;signalling = bri_net_ptmp +; p2p NT mode (for connecting an ISDN pbx in point-to-point mode) +;signalling = bri_net + +pridialplan = local +prilocaldialplan = dynamic +nationalprefix = 0 +internationalprefix = 00 + +priindication = passthrough + +echocancel = yes + +context=demo +group = 1 +; S/T port 1 +channel => 1-2 + +group = 2 +; S/T port 2 +channel => 4-5 + +group = 3 +; S/T port 3 +channel => 7-8 + +group = 4 +; S/T port 4 +channel => 10-11 + --- zaptel-1.4.11~dfsg.orig/kernel/qozap/zaptel.conf.octoBRI +++ zaptel-1.4.11~dfsg/kernel/qozap/zaptel.conf.octoBRI @@ -0,0 +1,30 @@ +loadzone=nl +defaultzone=nl +# qozap span definitions +# most of the values should be bogus because we are not really zaptel +span=1,1,3,ccs,ami +span=2,2,3,ccs,ami +span=3,3,3,ccs,ami +span=4,4,3,ccs,ami +span=5,5,3,ccs,ami +span=6,6,3,ccs,ami +span=7,7,3,ccs,ami +span=8,8,3,ccs,ami + +bchan=1,2 +dchan=3 +bchan=4,5 +dchan=6 +bchan=7,8 +dchan=9 +bchan=10,11 +dchan=12 +bchan=13,14 +dchan=15 +bchan=16,17 +dchan=18 +bchan=19,20 +dchan=21 +bchan=22,23 +dchan=24 + --- zaptel-1.4.11~dfsg.orig/kernel/qozap/Makefile +++ zaptel-1.4.11~dfsg/kernel/qozap/Makefile @@ -0,0 +1,89 @@ +KINCLUDES = /usr/src/linux/include +BRISTUFFBASE = $(shell dirname `pwd`) + +ZAP = $(shell [ -f $(BRISTUFFBASE)/zaptel/zaptel.h ] && echo "-I$(BRISTUFFBASE)/zaptel") + +CFLAGS+=-I. $(ZAP) -DRELAXED_LOCKING -O2 -g -Wall -DBUILDING_TONEZONE #-DTONEZONE_DRIVER +CFLAGS+=$(shell if uname -m | grep -q ppc; then echo "-fsigned-char"; fi) + +KFLAGS=-D__KERNEL__ -DMODULE -DEXPORT_SYMTAB -DRELAXED_LOCKING -fomit-frame-pointer -O2 -Wall -I$(KINCLUDES) $(ZAP) +KFLAGS+=$(shell [ -f $(KINCLUDES)/linux/modversions.h ] && echo "-DMODVERSIONS -include $(KINCLUDES)/linux/modversions.h") +KFLAGS+=$(shell if uname -m | grep -q ppc; then echo "-msoft-float -fsigned-char"; fi) + +OBJS=qozap.o + +BUILDVER=$(shell if uname -r | grep -q ^2.6; then echo "linux26"; else echo "linux24"; fi) + +MODCONF=$(shell if [ -d $(INSTALL_PREFIX)/etc/modprobe.d ]; then echo "$(INSTALL_PREFIX)/etc/modprobe.d/zaptel"; elif [ -d $(INSTALL_PREFIX)/etc/modutils ]; then echo "$(INSTALL_PREFIX)/etc/modutils/zaptel"; elif [ -f $(INSTALL_PREFIX)/etc/modprobe.conf ]; then echo "$(INSTALL_PREFIX)/modprobe.conf"; elif [ -f $(INSTALL_PREFIX)/etc/modules.conf ]; then echo "$(INSTALL_PREFIX)/etc/modules.conf"; else echo $(INSTALL_PREFIX)/etc/conf.modules ; fi) + +MODULES=qozap + +MODULESO=$(shell for x in $(MODULES); do echo "$$x.o "; done ) +MODULESKO=$(shell for x in $(MODULES); do echo "$$x.ko "; done ) + +PWD=$(shell pwd) + +obj-m := $(MODULESO) + +all: $(BUILDVER) + +linux24: $(OBJS) + sync + +linux26: + @if ! [ -d /usr/src/linux-2.6 ]; then echo "Link /usr/src/linux-2.6 to your kernel sources first!"; exit 1 ; fi + make -C /usr/src/linux-2.6 SUBDIRS=$(PWD) ZAP=$(ZAP) modules +obj-m := $(OBJS) + +qozap.o: qozap.c qozap.h + $(CC) -c qozap.c $(KFLAGS) + +clean: + rm -f $(OBJS) *.ko *.mod.c *.mod.o .*o.cmd *~ + rm -rf .tmp_versions + +testlinux24: all + modprobe zaptel + insmod ./qozap.o + ztcfg -v + cat /proc/interrupts + sleep 1 + cat /proc/interrupts + rmmod qozap zaptel + +testlinux26: all + modprobe zaptel + insmod ./qozap.ko + ztcfg -v + cat /proc/interrupts + sleep 1 + cat /proc/interrupts + rmmod qozap zaptel + +reload: unload load +load: load$(BUILDVER) + +test: test$(BUILDVER) + + +loadlinux24: linux24 + modprobe zaptel + insmod ./qozap.o + ztcfg -v + +loadlinux26: linux26 + sync + modprobe zaptel + insmod ./qozap.ko + ztcfg -v + +unload: + rmmod qozap zaptel + +install: install$(BUILDVER) + +installlinux26: + install -D -m 644 qozap.ko $(INSTALL_PREFIX)/lib/modules/`uname -r`/misc/qozap.ko + +installlinux24: + install -D -m 644 qozap.o $(INSTALL_PREFIX)/lib/modules/`uname -r`/misc/qozap.o --- zaptel-1.4.11~dfsg.orig/kernel/qozap/TODO +++ zaptel-1.4.11~dfsg/kernel/qozap/TODO @@ -0,0 +1,8 @@ +- onchip dtmf +- E channel support for full debug + + +t3 (5ms max) + +t4 (500ms) layer 1 down/up + --- zaptel-1.4.11~dfsg.orig/kernel/qozap/zaptel.conf +++ zaptel-1.4.11~dfsg/kernel/qozap/zaptel.conf @@ -0,0 +1,18 @@ +loadzone=nl +defaultzone=nl +# qozap span definitions +# most of the values should be bogus because we are not really zaptel +span=1,1,3,ccs,ami +span=2,2,3,ccs,ami +span=3,3,3,ccs,ami +span=4,4,3,ccs,ami + +bchan=1,2 +dchan=3 +bchan=4,5 +dchan=6 +bchan=7,8 +dchan=9 +bchan=10,11 +dchan=12 + --- zaptel-1.4.11~dfsg.orig/kernel/qozap/zapata.conf.duoBRI +++ zaptel-1.4.11~dfsg/kernel/qozap/zapata.conf.duoBRI @@ -0,0 +1,43 @@ +; +; Zapata telephony interface +; +; Configuration file + +[channels] +; +; Default language +; +;language=en +; +; Default context +; +; +switchtype = euroisdn + +; p2mp TE mode (for connecting ISDN lines in point-to-multipoint mode) +signalling = bri_cpe_ptmp +; p2p TE mode (for connecting ISDN lines in point-to-point mode) +;signalling = bri_cpe +; p2mp NT mode (for connecting ISDN phones in point-to-multipoint mode) +;signalling = bri_net_ptmp +; p2p NT mode (for connecting an ISDN pbx in point-to-point mode) +;signalling = bri_net + +pridialplan = local +prilocaldialplan = dynamic +nationalprefix = 0 +internationalprefix = 00 + +priindication = passthrough + +echocancel = yes + +context=demo +group = 1 +; S/T port 1 +channel => 1-2 + +group = 2 +; S/T port 2 +channel => 4-5 + --- zaptel-1.4.11~dfsg.orig/kernel/zaphfc/zaphfc.h +++ zaptel-1.4.11~dfsg/kernel/zaphfc/zaphfc.h @@ -0,0 +1,289 @@ +/* + * zaphfc.h - Zaptel driver for HFC-S PCI A based ISDN BRI cards + * + * kernel module based on HFC PCI ISDN4Linux and Zaptel drivers + * + * Copyright (C) 2002, 2003, 2004, 2005 Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + */ + +/* HFC register addresses - accessed using memory mapped I/O */ +/* For a list, see datasheet section 3.2.1 at page 21 */ + +#define hfc_outb(a,b,c) (writeb((c),(a)->pci_io+(b))) +#define hfc_inb(a,b) (readb((a)->pci_io+(b))) + +/* GCI/IOM bus monitor registers */ + +#define hfc_C_I 0x08 +#define hfc_TRxR 0x0C +#define hfc_MON1_D 0x28 +#define hfc_MON2_D 0x2C + + +/* GCI/IOM bus timeslot registers */ + +#define hfc_B1_SSL 0x80 +#define hfc_B2_SSL 0x84 +#define hfc_AUX1_SSL 0x88 +#define hfc_AUX2_SSL 0x8C +#define hfc_B1_RSL 0x90 +#define hfc_B2_RSL 0x94 +#define hfc_AUX1_RSL 0x98 +#define hfc_AUX2_RSL 0x9C + +/* GCI/IOM bus data registers */ + +#define hfc_B1_D 0xA0 +#define hfc_B2_D 0xA4 +#define hfc_AUX1_D 0xA8 +#define hfc_AUX2_D 0xAC + +/* GCI/IOM bus configuration registers */ + +#define hfc_MST_EMOD 0xB4 +#define hfc_MST_MODE 0xB8 +#define hfc_CONNECT 0xBC + + +/* Interrupt and status registers */ + +#define hfc_FIFO_EN 0x44 +#define hfc_TRM 0x48 +#define hfc_B_MODE 0x4C +#define hfc_CHIP_ID 0x58 +#define hfc_CIRM 0x60 +#define hfc_CTMT 0x64 +#define hfc_INT_M1 0x68 +#define hfc_INT_M2 0x6C +#define hfc_INT_S1 0x78 +#define hfc_INT_S2 0x7C +#define hfc_STATUS 0x70 + +/* S/T section registers */ + +#define hfc_STATES 0xC0 +#define hfc_SCTRL 0xC4 +#define hfc_SCTRL_E 0xC8 +#define hfc_SCTRL_R 0xCC +#define hfc_SQ 0xD0 +#define hfc_CLKDEL 0xDC +#define hfc_B1_REC 0xF0 +#define hfc_B1_SEND 0xF0 +#define hfc_B2_REC 0xF4 +#define hfc_B2_SEND 0xF4 +#define hfc_D_REC 0xF8 +#define hfc_D_SEND 0xF8 +#define hfc_E_REC 0xFC + +/* Bits and values in various HFC PCI registers */ + +/* bits in status register (READ) */ +#define hfc_STATUS_PCI_PROC 0x02 +#define hfc_STATUS_NBUSY 0x04 +#define hfc_STATUS_TIMER_ELAP 0x10 +#define hfc_STATUS_STATINT 0x20 +#define hfc_STATUS_FRAMEINT 0x40 +#define hfc_STATUS_ANYINT 0x80 + +/* bits in CTMT (Write) */ +#define hfc_CTMT_CLTIMER 0x80 +#define hfc_CTMT_TIM3_125 0x04 +#define hfc_CTMT_TIM25 0x10 +#define hfc_CTMT_TIM50 0x14 +#define hfc_CTMT_TIM400 0x18 +#define hfc_CTMT_TIM800 0x1C +#define hfc_CTMT_AUTO_TIMER 0x20 +#define hfc_CTMT_TRANSB2 0x02 +#define hfc_CTMT_TRANSB1 0x01 + +/* bits in CIRM (Write) */ +#define hfc_CIRM_AUX_MSK 0x07 +#define hfc_CIRM_RESET 0x08 +#define hfc_CIRM_B1_REV 0x40 +#define hfc_CIRM_B2_REV 0x80 + +/* bits in INT_M1 and INT_S1 */ +#define hfc_INTS_B1TRANS 0x01 +#define hfc_INTS_B2TRANS 0x02 +#define hfc_INTS_DTRANS 0x04 +#define hfc_INTS_B1REC 0x08 +#define hfc_INTS_B2REC 0x10 +#define hfc_INTS_DREC 0x20 +#define hfc_INTS_L1STATE 0x40 +#define hfc_INTS_TIMER 0x80 + +/* bits in INT_M2 */ +#define hfc_M2_PROC_TRANS 0x01 +#define hfc_M2_GCI_I_CHG 0x02 +#define hfc_M2_GCI_MON_REC 0x04 +#define hfc_M2_IRQ_ENABLE 0x08 +#define hfc_M2_PMESEL 0x80 + +/* bits in STATES */ +#define hfc_STATES_STATE_MASK 0x0F +#define hfc_STATES_LOAD_STATE 0x10 +#define hfc_STATES_ACTIVATE 0x20 +#define hfc_STATES_DO_ACTION 0x40 +#define hfc_STATES_NT_G2_G3 0x80 + +/* bits in HFCD_MST_MODE */ +#define hfc_MST_MODE_MASTER 0x01 +#define hfc_MST_MODE_SLAVE 0x00 +/* remaining bits are for codecs control */ + +/* bits in HFCD_SCTRL */ +#define hfc_SCTRL_B1_ENA 0x01 +#define hfc_SCTRL_B2_ENA 0x02 +#define hfc_SCTRL_MODE_TE 0x00 +#define hfc_SCTRL_MODE_NT 0x04 +#define hfc_SCTRL_LOW_PRIO 0x08 +#define hfc_SCTRL_SQ_ENA 0x10 +#define hfc_SCTRL_TEST 0x20 +#define hfc_SCTRL_NONE_CAP 0x40 +#define hfc_SCTRL_PWR_DOWN 0x80 + +/* bits in SCTRL_E */ +#define hfc_SCTRL_E_AUTO_AWAKE 0x01 +#define hfc_SCTRL_E_DBIT_1 0x04 +#define hfc_SCTRL_E_IGNORE_COL 0x08 +#define hfc_SCTRL_E_CHG_B1_B2 0x80 + +/* bits in FIFO_EN register */ +#define hfc_FIFOEN_B1TX 0x01 +#define hfc_FIFOEN_B1RX 0x02 +#define hfc_FIFOEN_B2TX 0x04 +#define hfc_FIFOEN_B2RX 0x08 +#define hfc_FIFOEN_DTX 0x10 +#define hfc_FIFOEN_DRX 0x20 + +#define hfc_FIFOEN_B1 (hfc_FIFOEN_B1TX|hfc_FIFOEN_B1RX) +#define hfc_FIFOEN_B2 (hfc_FIFOEN_B2TX|hfc_FIFOEN_B2RX) +#define hfc_FIFOEN_D (hfc_FIFOEN_DTX|hfc_FIFOEN_DRX) + +/* bits in the CONNECT register */ +#define hfc_CONNECT_B1_shift 0 +#define hfc_CONNECT_B2_shift 3 + +#define hfc_CONNECT_HFC_from_ST 0x0 +#define hfc_CONNECT_HFC_from_GCI 0x1 +#define hfc_CONNECT_ST_from_HFC 0x0 +#define hfc_CONNECT_ST_from_GCI 0x2 +#define hfc_CONNECT_GCI_from_HFC 0x0 +#define hfc_CONNECT_GCI_from_ST 0x4 + +/* bits in the __SSL and __RSL registers */ +#define hfc_SRSL_STIO 0x40 +#define hfc_SRSL_ENABLE 0x80 +#define hfc_SRCL_SLOT_MASK 0x1f + +/* FIFO memory definitions */ + +#define hfc_FMASK 0x000f +#define hfc_ZMASK 0x01ff +#define hfc_ZMASKB 0x1fff + +#define hfc_D_FIFO_SIZE 0x0200 +#define hfc_B_SUB_VAL 0x0200 +#define hfc_B_FIFO_SIZE 0x1E00 +#define hfc_MAX_DFRAMES 0x000f + +#define hfc_FIFO_DTX_Z1 0x2080 +#define hfc_FIFO_DTX_Z2 0x2082 +#define hfc_FIFO_DTX_F1 0x20a0 +#define hfc_FIFO_DTX_F2 0x20a1 +#define hfc_FIFO_DTX 0x0000 +#define hfc_FIFO_DTX_ZOFF 0x000 + +#define hfc_FIFO_DRX_Z1 0x6080 +#define hfc_FIFO_DRX_Z2 0x6082 +#define hfc_FIFO_DRX_F1 0x60a0 +#define hfc_FIFO_DRX_F2 0x60a1 +#define hfc_FIFO_DRX 0x4000 +#define hfc_FIFO_DRX_ZOFF 0x4000 + +#define hfc_FIFO_B1TX_Z1 0x2000 +#define hfc_FIFO_B1TX_Z2 0x2002 +#define hfc_FIFO_B1RX_Z1 0x6000 +#define hfc_FIFO_B1RX_Z2 0x6002 + +#define hfc_FIFO_B1TX_F1 0x2080 +#define hfc_FIFO_B1TX_F2 0x2081 +#define hfc_FIFO_B1RX_F1 0x6080 +#define hfc_FIFO_B1RX_F2 0x6081 + +#define hfc_FIFO_B1RX_ZOFF 0x4000 +#define hfc_FIFO_B1TX_ZOFF 0x0000 + +#define hfc_FIFO_B2TX_Z1 0x2100 +#define hfc_FIFO_B2TX_Z2 0x2102 +#define hfc_FIFO_B2RX_Z1 0x6100 +#define hfc_FIFO_B2RX_Z2 0x6102 + +#define hfc_FIFO_B2TX_F1 0x2180 +#define hfc_FIFO_B2TX_F2 0x2181 +#define hfc_FIFO_B2RX_F1 0x6180 +#define hfc_FIFO_B2RX_F2 0x6181 + +#define hfc_FIFO_B2RX_ZOFF 0x6000 +#define hfc_FIFO_B2TX_ZOFF 0x2000 + +#define hfc_BTRANS_THRESHOLD 128 +#define hfc_BTRANS_THRESMASK 0x00 + +/* Structures */ + +typedef struct hfc_regs { + unsigned char fifo_en; + unsigned char ctmt; + unsigned char int_m1; + unsigned char int_m2; + unsigned char sctrl; + unsigned char sctrl_e; + unsigned char sctrl_r; + unsigned char connect; + unsigned char trm; + unsigned char mst_mode; + unsigned char bswapped; + unsigned char nt_mode; + unsigned char int_drec; +} hfc_regs; + +typedef struct hfc_card { + spinlock_t lock; + unsigned int irq; + unsigned int iomem; + int ticks; + int clicks; + unsigned char *pci_io; + void *fifomem; // start of the shared mem + volatile void *fifos; // 32k aligned mem for the fifos + struct hfc_regs regs; + unsigned int pcibus; + unsigned int pcidevfn; + struct pci_dev *pcidev; + struct zt_hfc *ztdev; + int drecinframe; + unsigned char drecbuf[hfc_D_FIFO_SIZE]; + unsigned char dtransbuf[hfc_D_FIFO_SIZE]; + unsigned char brecbuf[2][ZT_CHUNKSIZE]; + unsigned char btransbuf[2][ZT_CHUNKSIZE]; + unsigned char cardno; + struct hfc_card *next; +} hfc_card; + +typedef struct zt_hfc { + unsigned int usecount; + struct zt_span span; + struct zt_chan chans[3]; + struct hfc_card *card; +} zt_hfc; + +/* tune this */ +#define hfc_BCHAN_BUFFER 8 +#define hfc_MAX_CARDS 8 --- zaptel-1.4.11~dfsg.orig/kernel/zaphfc/zapata.conf +++ zaptel-1.4.11~dfsg/kernel/zaphfc/zapata.conf @@ -0,0 +1,38 @@ +; +; Zapata telephony interface +; +; Configuration file + +[channels] +; +; Default language +; +;language=en +; +; Default context +; +; +switchtype = euroisdn +; p2mp TE mode +signalling = bri_cpe_ptmp + +; p2p TE mode +;signalling = bri_cpe +; p2mp NT mode +;signalling = bri_net_ptmp +; p2p NT mode +;signalling = bri_net + +pridialplan = dynamic +prilocaldialplan = local +nationalprefix = 0 +internationalprefix = 00 + +echocancel=yes +echotraining = 100 +echocancelwhenbridged=yes + +immediate=yes +group = 1 +context=demo +channel => 1-2 --- zaptel-1.4.11~dfsg.orig/kernel/zaphfc/Makefile +++ zaptel-1.4.11~dfsg/kernel/zaphfc/Makefile @@ -0,0 +1,116 @@ +KINCLUDES = /usr/src/linux/include +BRISTUFFBASE = $(shell dirname `pwd`) + +ZAP = $(shell [ -f $(BRISTUFFBASE)/zaptel/zaptel.h ] && echo "-I$(BRISTUFFBASE)/zaptel") +RTAI = $(shell [ -f /usr/realtime/include/rtai.h ] && echo "-DRTAITIMING -I/usr/realtime/include") + +CFLAGS+=-I. $(ZAP) $(RTAI) -O2 -g -Wall -DBUILDING_TONEZONE +CFLAGS+=$(shell if uname -m | grep -q ppc; then echo "-fsigned-char"; fi) + +KFLAGS=-D__KERNEL__ -DMODULE -DEXPORT_SYMTAB -fomit-frame-pointer -O2 -Wall -I$(KINCLUDES) $(ZAP) $(RTAI) -Wall +KFLAGS+=$(shell [ -f $(KINCLUDES)/linux/modversions.h ] && echo "-DMODVERSIONS -include $(KINCLUDES)/linux/modversions.h") +KFLAGS+=$(shell if uname -m | grep -q ppc; then echo "-msoft-float -fsigned-char"; fi) + + +BUILDVER=$(shell if uname -r | grep -q ^2.6; then echo "linux26"; else echo "linux24"; fi) + +MODCONF=$(shell if [ -d $(INSTALL_PREFIX)/etc/modprobe.d ]; then echo "$(INSTALL_PREFIX)/etc/modprobe.d/zaptel"; elif [ -d $(INSTALL_PREFIX)/etc/modutils ]; then echo "$(INSTALL_PREFIX)/etc/modutils/zaptel"; elif [ -f $(INSTALL_PREFIX)/etc/modprobe.conf ]; then echo "$(INSTALL_PREFIX)/modprobe.conf"; elif [ -f $(INSTALL_PREFIX)/etc/modules.conf ]; then echo "$(INSTALL_PREFIX)/etc/modules.conf"; else echo $(INSTALL_PREFIX)/etc/conf.modules ; fi) + +OBJS=zaphfc.o + +MODULES=zaphfc + +MODULESO=$(shell for x in $(MODULES); do echo "$$x.o "; done ) +MODULESKO=$(shell for x in $(MODULES); do echo "$$x.ko "; done ) + +PWD=$(shell pwd) + +obj-m := $(MODULESO) + +all: $(BUILDVER) + +linux24: $(OBJS) + sync + + +zaphfc.o: zaphfc.c zaphfc.h + $(CC) -c zaphfc.c $(KFLAGS) + +clean: + rm -f $(OBJS) *.ko *.mod.c *.mod.o .*o.cmd *~ + rm -rf .tmp_versions + +test: all + modprobe zaptel + insmod ./zaphfc.o + cat /proc/interrupts + sleep 1 + cat /proc/interrupts + rmmod zaphfc + rmmod zaptel + +load: load$(BUILDVER) + +loadNT: load$(BUILDVER)NT + +load-debug: load$(BUILDVER)-debug + +loadNT-debug: load$(BUILDVER)NT-debug + +loadlinux24: all + modprobe zaptel + insmod ./zaphfc.o + ztcfg -v + +loadlinux24-debug: all + modprobe zaptel + insmod ./zaphfc.o debug=1 + ztcfg -v + +loadlinux26: linux26 + modprobe zaptel + insmod ./zaphfc.ko + ztcfg -v + +loadlinux26-debug: linux26 + modprobe zaptel + insmod ./zaphfc.ko debug=1 + ztcfg -v + +loadlinux24NT: all + modprobe zaptel + insmod ./zaphfc.o modes=1 + ztcfg -v + +loadlinux24NT-debug: all + modprobe zaptel + insmod ./zaphfc.o modes=1 debug=1 + ztcfg -v + +loadlinux26NT: linux26 + modprobe zaptel + insmod ./zaphfc.ko modes=1 + ztcfg -v + +loadlinux26NT-debug: linux26 + modprobe zaptel + insmod ./zaphfc.ko modes=1 debug=1 + ztcfg -v + +unload: + -rmmod zaphfc zaptel + +zaphfc.ko: zaphfc.c zaphfc.h + +linux26: + @if ! [ -d /usr/src/linux-2.6 ]; then echo "Link /usr/src/linux-2.6 to your kernel sources first!"; exit 1 ; fi + make -C /usr/src/linux-2.6 SUBDIRS=$(PWD) ZAP=$(ZAP) modules + +install: install$(BUILDVER) + +installlinux26: + install -D -m 644 zaphfc.ko $(INSTALL_PREFIX)/lib/modules/`uname -r`/misc/zaphfc.ko + +installlinux24: + install -D -m 644 zaphfc.o $(INSTALL_PREFIX)/lib/modules/`uname -r`/misc/zaphfc.o + --- zaptel-1.4.11~dfsg.orig/kernel/zaphfc/zaptel.conf +++ zaptel-1.4.11~dfsg/kernel/zaphfc/zaptel.conf @@ -0,0 +1,8 @@ +# hfc-s pci a span definition +# most of the values should be bogus because we are not really zaptel +loadzone=nl +defaultzone=nl + +span=1,1,3,ccs,ami +bchan=1-2 +dchan=3 --- zaptel-1.4.11~dfsg.orig/kernel/zaphfc/zaphfc.c +++ zaptel-1.4.11~dfsg/kernel/zaphfc/zaphfc.c @@ -0,0 +1,1155 @@ +/* + * zaphfc.c - Zaptel driver for HFC-S PCI A based ISDN BRI cards + * + * kernel module inspired by HFC PCI ISDN4Linux and Zaptel drivers + * + * Copyright (C) 2002, 2003, 2004, 2005 Junghanns.NET GmbH + * + * Klaus-Peter Junghanns + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + */ + +#include +#include +#ifdef RTAITIMING +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include "zaphfc.h" + +#ifdef LINUX26 +#include +#endif + +#if CONFIG_PCI + +#define CLKDEL_TE 0x0f /* CLKDEL in TE mode */ +#define CLKDEL_NT 0x6c /* CLKDEL in NT mode */ + +typedef struct { + int vendor_id; + int device_id; + char *vendor_name; + char *card_name; +} PCI_ENTRY; + +static const PCI_ENTRY id_list[] = +{ + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_2BD0, "CCD/Billion/Asuscom", "2BD0"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B000, "Billion", "B000"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B006, "Billion", "B006"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B007, "Billion", "B007"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B008, "Billion", "B008"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B009, "Billion", "B009"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00A, "Billion", "B00A"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00B, "Billion", "B00B"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00C, "Billion", "B00C"}, + {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B100, "Seyeon", "B100"}, + {PCI_VENDOR_ID_ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1, "Abocom/Magitek", "2BD1"}, + {PCI_VENDOR_ID_ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675, "Asuscom/Askey", "675"}, + {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT, "German telekom", "T-Concept"}, + {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_A1T, "German telekom", "A1T"}, + {PCI_VENDOR_ID_ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575, "Motorola MC145575", "MC145575"}, + {PCI_VENDOR_ID_ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0, "Zoltrix", "2BD0"}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E,"Digi International", "Digi DataFire Micro V IOM2 (Europe)"}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_E,"Digi International", "Digi DataFire Micro V (Europe)"}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A,"Digi International", "Digi DataFire Micro V IOM2 (North America)"}, + {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_A,"Digi International", "Digi DataFire Micro V (North America)"}, + {0x182d, 0x3069,"Sitecom","Isdn 128 PCI"}, + {0, 0, NULL, NULL}, +}; + +static struct hfc_card *hfc_dev_list = NULL; +static int hfc_dev_count = 0; +static int modes = 0; // all TE +static int debug = 0; +static struct pci_dev *multi_hfc = NULL; +static spinlock_t registerlock = SPIN_LOCK_UNLOCKED; + +void hfc_shutdownCard(struct hfc_card *hfctmp) { + unsigned long flags; + + if (hfctmp == NULL) { + return; + } + + if (hfctmp->pci_io == NULL) { + return; + } + + spin_lock_irqsave(&hfctmp->lock,flags); + + printk(KERN_INFO "zaphfc: shutting down card at %p.\n",hfctmp->pci_io); + + /* Clear interrupt mask */ + hfctmp->regs.int_m2 = 0; + hfc_outb(hfctmp, hfc_INT_M2, hfctmp->regs.int_m2); + + /* Reset pending interrupts */ + hfc_inb(hfctmp, hfc_INT_S1); + + /* Wait for interrupts that might still be pending */ + spin_unlock_irqrestore(&hfctmp->lock, flags); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((30 * HZ) / 1000); // wait 30 ms + spin_lock_irqsave(&hfctmp->lock,flags); + + /* Remove interrupt handler */ + if (hfctmp->irq) { + free_irq(hfctmp->irq, hfctmp); + } + + /* Soft-reset the card */ + hfc_outb(hfctmp, hfc_CIRM, hfc_CIRM_RESET); // softreset on + + spin_unlock_irqrestore(&hfctmp->lock, flags); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((30 * HZ) / 1000); // wait 30 ms + spin_lock_irqsave(&hfctmp->lock,flags); + + hfc_outb(hfctmp,hfc_CIRM,0); // softreset off + + pci_write_config_word(hfctmp->pcidev, PCI_COMMAND, 0); // disable memio and bustmaster + + if (hfctmp->fifomem != NULL) { + kfree(hfctmp->fifomem); + } + iounmap((void *) hfctmp->pci_io); + hfctmp->pci_io = NULL; + if (hfctmp->pcidev != NULL) { + pci_disable_device(hfctmp->pcidev); + } + spin_unlock_irqrestore(&hfctmp->lock,flags); + if (hfctmp->ztdev != NULL) { + zt_unregister(&hfctmp->ztdev->span); + kfree(hfctmp->ztdev); + printk(KERN_INFO "unregistered from zaptel.\n"); + } +} + +void hfc_resetCard(struct hfc_card *hfctmp) { + unsigned long flags; + + spin_lock_irqsave(&hfctmp->lock,flags); + pci_write_config_word(hfctmp->pcidev, PCI_COMMAND, PCI_COMMAND_MEMORY); // enable memio + hfctmp->regs.int_m2 = 0; + hfc_outb(hfctmp, hfc_INT_M2, hfctmp->regs.int_m2); + +// printk(KERN_INFO "zaphfc: resetting card.\n"); + pci_set_master(hfctmp->pcidev); + hfc_outb(hfctmp, hfc_CIRM, hfc_CIRM_RESET); // softreset on + spin_unlock_irqrestore(&hfctmp->lock, flags); + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((30 * HZ) / 1000); // wait 30 ms + hfc_outb(hfctmp, hfc_CIRM, 0); // softreset off + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((20 * HZ) / 1000); // wait 20 ms + if (hfc_inb(hfctmp,hfc_STATUS) & hfc_STATUS_PCI_PROC) { + printk(KERN_WARNING "zaphfc: hfc busy.\n"); + } + +// hfctmp->regs.fifo_en = hfc_FIFOEN_D | hfc_FIFOEN_B1 | hfc_FIFOEN_B2; +// hfctmp->regs.fifo_en = hfc_FIFOEN_D; /* only D fifos enabled */ + hfctmp->regs.fifo_en = 0; /* no fifos enabled */ + hfc_outb(hfctmp, hfc_FIFO_EN, hfctmp->regs.fifo_en); + + hfctmp->regs.trm = 2; + hfc_outb(hfctmp, hfc_TRM, hfctmp->regs.trm); + + if (hfctmp->regs.nt_mode == 1) { + hfc_outb(hfctmp, hfc_CLKDEL, CLKDEL_NT); /* ST-Bit delay for NT-Mode */ + } else { + hfc_outb(hfctmp, hfc_CLKDEL, CLKDEL_TE); /* ST-Bit delay for TE-Mode */ + } + hfctmp->regs.sctrl_e = hfc_SCTRL_E_AUTO_AWAKE; + hfc_outb(hfctmp, hfc_SCTRL_E, hfctmp->regs.sctrl_e); /* S/T Auto awake */ + hfctmp->regs.bswapped = 0; /* no exchange */ + + hfctmp->regs.ctmt = hfc_CTMT_TRANSB1 | hfc_CTMT_TRANSB2; // all bchans are transparent , no freaking hdlc + hfc_outb(hfctmp, hfc_CTMT, hfctmp->regs.ctmt); + + hfctmp->regs.int_m1 = 0; + hfc_outb(hfctmp, hfc_INT_M1, hfctmp->regs.int_m1); + +#ifdef RTAITIMING + hfctmp->regs.int_m2 = 0; +#else + hfctmp->regs.int_m2 = hfc_M2_PROC_TRANS; +#endif + hfc_outb(hfctmp, hfc_INT_M2, hfctmp->regs.int_m2); + + /* Clear already pending ints */ + hfc_inb(hfctmp, hfc_INT_S1); + + if (hfctmp->regs.nt_mode == 1) { + hfctmp->regs.sctrl = 3 | hfc_SCTRL_NONE_CAP | hfc_SCTRL_MODE_NT; /* set tx_lo mode, error in datasheet ! */ + } else { + hfctmp->regs.sctrl = 3 | hfc_SCTRL_NONE_CAP | hfc_SCTRL_MODE_TE; /* set tx_lo mode, error in datasheet ! */ + } + + hfctmp->regs.mst_mode = hfc_MST_MODE_MASTER; /* HFC Master Mode */ + hfc_outb(hfctmp, hfc_MST_MODE, hfctmp->regs.mst_mode); + + hfc_outb(hfctmp, hfc_SCTRL, hfctmp->regs.sctrl); + hfctmp->regs.sctrl_r = 3; + hfc_outb(hfctmp, hfc_SCTRL_R, hfctmp->regs.sctrl_r); + + hfctmp->regs.connect = 0; + hfc_outb(hfctmp, hfc_CONNECT, hfctmp->regs.connect); + + hfc_outb(hfctmp, hfc_CIRM, 0x80 | 0x40); // bit order + + /* Finally enable IRQ output */ +#ifndef RTAITIMING + hfctmp->regs.int_m2 |= hfc_M2_IRQ_ENABLE; + hfc_outb(hfctmp, hfc_INT_M2, hfctmp->regs.int_m2); +#endif + + /* clear pending ints */ + hfc_inb(hfctmp, hfc_INT_S1); + hfc_inb(hfctmp, hfc_INT_S2); +} + +void hfc_registerCard(struct hfc_card *hfccard) { + spin_lock(®isterlock); + if (hfccard != NULL) { + hfccard->cardno = hfc_dev_count++; + hfccard->next = hfc_dev_list; + hfc_dev_list = hfccard; + } + spin_unlock(®isterlock); +} + +static void hfc_btrans(struct hfc_card *hfctmp, char whichB) { + // we are called with irqs disabled from the irq handler + int count, maxlen, total; + unsigned char *f1, *f2; + unsigned short *z1, *z2, newz1; + int freebytes; + + if (whichB == 1) { + f1 = (char *)(hfctmp->fifos + hfc_FIFO_B1TX_F1); + f2 = (char *)(hfctmp->fifos + hfc_FIFO_B1TX_F2); + z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B1TX_Z1 + (*f1 * 4)); + z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B1TX_Z2 + (*f1 * 4)); + } else { + f1 = (char *)(hfctmp->fifos + hfc_FIFO_B2TX_F1); + f2 = (char *)(hfctmp->fifos + hfc_FIFO_B2TX_F2); + z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B2TX_Z1 + (*f1 * 4)); + z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B2TX_Z2 + (*f1 * 4)); + } + + freebytes = *z2 - *z1; + if (freebytes <= 0) { + freebytes += hfc_B_FIFO_SIZE; + } + count = ZT_CHUNKSIZE; + + total = count; + if (freebytes < count) { + hfctmp->clicks++; + /* only spit out this warning once per second to not make things worse! */ + if (hfctmp->clicks > 100) { + printk(KERN_CRIT "zaphfc: bchan tx fifo full, dropping audio! (z1=%d, z2=%d)\n",*z1,*z2); + hfctmp->clicks = 0; + } + return; + } + + maxlen = (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL) - *z1; + if (maxlen > count) { + maxlen = count; + } + newz1 = *z1 + total; + if (newz1 >= (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL)) { newz1 -= hfc_B_FIFO_SIZE; } + + if (whichB == 1) { + memcpy((char *)(hfctmp->fifos + hfc_FIFO_B1TX_ZOFF + *z1),hfctmp->ztdev->chans[0].writechunk, maxlen); + } else { + memcpy((char *)(hfctmp->fifos + hfc_FIFO_B2TX_ZOFF + *z1),hfctmp->ztdev->chans[1].writechunk, maxlen); + } + + count -= maxlen; + if (count > 0) { + // Buffer wrap + if (whichB == 1) { + memcpy((char *)(hfctmp->fifos + hfc_FIFO_B1TX_ZOFF + hfc_B_SUB_VAL),hfctmp->ztdev->chans[0].writechunk+maxlen, count); + } else { + memcpy((char *)(hfctmp->fifos + hfc_FIFO_B2TX_ZOFF + hfc_B_SUB_VAL),hfctmp->ztdev->chans[1].writechunk+maxlen, count); + } + } + + *z1 = newz1; /* send it now */ + +// if (count > 0) printk(KERN_CRIT "zaphfc: bchan tx fifo (f1=%d, f2=%d, z1=%d, z2=%d)\n",(*f1) & hfc_FMASK,(*f2) & hfc_FMASK, *z1, *z2); + return; +} + +static void hfc_brec(struct hfc_card *hfctmp, char whichB) { + // we are called with irqs disabled from the irq handler + int count, maxlen, drop; + volatile unsigned char *f1, *f2; + volatile unsigned short *z1, *z2, newz2; + int bytes = 0; + + if (whichB == 1) { + f1 = (char *)(hfctmp->fifos + hfc_FIFO_B1RX_F1); + f2 = (char *)(hfctmp->fifos + hfc_FIFO_B1RX_F2); + z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B1RX_Z1 + (*f1 * 4)); + z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B1RX_Z2 + (*f1 * 4)); + } else { + f1 = (char *)(hfctmp->fifos + hfc_FIFO_B2RX_F1); + f2 = (char *)(hfctmp->fifos + hfc_FIFO_B2RX_F2); + z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B2RX_Z1 + (*f1 * 4)); + z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B2RX_Z2 + (*f1 * 4)); + } + + bytes = *z1 - *z2; + if (bytes < 0) { + bytes += hfc_B_FIFO_SIZE; + } + count = ZT_CHUNKSIZE; + + if (bytes < ZT_CHUNKSIZE) { +#ifndef RTAITIMING + printk(KERN_CRIT "zaphfc: bchan rx fifo not enough bytes to receive! (z1=%d, z2=%d, wanted %d got %d), probably a buffer overrun.\n",*z1,*z2,ZT_CHUNKSIZE,bytes); +#endif + return; + } + + /* allowing the buffering of hfc_BCHAN_BUFFER bytes of audio data works around irq jitter */ + if (bytes > hfc_BCHAN_BUFFER + ZT_CHUNKSIZE) { + /* if the system is too slow to handle it, we will have to drop it all (except 1 zaptel chunk) */ + drop = bytes - ZT_CHUNKSIZE; + hfctmp->clicks++; + /* only spit out this warning once per second to not make things worse! */ + if (hfctmp->clicks > 100) { + printk(KERN_CRIT "zaphfc: dropped audio (z1=%d, z2=%d, wanted %d got %d, dropped %d).\n",*z1,*z2,count,bytes,drop); + hfctmp->clicks = 0; + } + /* hm, we are processing the b chan data tooooo slowly... let's drop the lost audio */ + newz2 = *z2 + drop; + if (newz2 >= (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL)) { + newz2 -= hfc_B_FIFO_SIZE; + } + *z2 = newz2; + } + + + maxlen = (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL) - *z2; + if (maxlen > count) { + maxlen = count; + } + if (whichB == 1) { + memcpy(hfctmp->ztdev->chans[0].readchunk,(char *)(hfctmp->fifos + hfc_FIFO_B1RX_ZOFF + *z2), maxlen); + } else { + memcpy(hfctmp->ztdev->chans[1].readchunk,(char *)(hfctmp->fifos + hfc_FIFO_B2RX_ZOFF + *z2), maxlen); + } + newz2 = *z2 + count; + if (newz2 >= (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL)) { + newz2 -= hfc_B_FIFO_SIZE; + } + *z2 = newz2; + + count -= maxlen; + if (count > 0) { + // Buffer wrap + if (whichB == 1) { + z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B1RX_Z2 + (*f1 * 4)); + memcpy(hfctmp->ztdev->chans[0].readchunk + maxlen,(char *)(hfctmp->fifos + hfc_FIFO_B1RX_ZOFF + hfc_B_SUB_VAL), count); + } else { + z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_B2RX_Z2 + (*f1 * 4)); + memcpy(hfctmp->ztdev->chans[1].readchunk + maxlen,(char *)(hfctmp->fifos + hfc_FIFO_B2RX_ZOFF + hfc_B_SUB_VAL), count); + } + newz2 = *z2 + count; + if (newz2 >= (hfc_B_FIFO_SIZE + hfc_B_SUB_VAL)) { + newz2 -= hfc_B_FIFO_SIZE; + } + } + + + if (whichB == 1) { + zt_ec_chunk(&hfctmp->ztdev->chans[0], hfctmp->ztdev->chans[0].readchunk, hfctmp->ztdev->chans[0].writechunk); + } else { + zt_ec_chunk(&hfctmp->ztdev->chans[1], hfctmp->ztdev->chans[1].readchunk, hfctmp->ztdev->chans[1].writechunk); + } + return; +} + + +static void hfc_dtrans(struct hfc_card *hfctmp) { + // we are called with irqs disabled from the irq handler + int x; + int count, maxlen, total; + unsigned char *f1, *f2, newf1; + unsigned short *z1, *z2, newz1; + int frames, freebytes; + + if (hfctmp->ztdev->chans[2].bytes2transmit == 0) { + return; + } + + f1 = (char *)(hfctmp->fifos + hfc_FIFO_DTX_F1); + f2 = (char *)(hfctmp->fifos + hfc_FIFO_DTX_F2); + z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DTX_Z1 + (*f1 * 4)); + z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DTX_Z2 + (*f1 * 4)); + + frames = (*f1 - *f2) & hfc_FMASK; + if (frames < 0) { + frames += hfc_MAX_DFRAMES + 1; + } + + if (frames >= hfc_MAX_DFRAMES) { + printk(KERN_CRIT "zaphfc: dchan tx fifo total number of frames exceeded!\n"); + return; + } + + freebytes = *z2 - *z1; + if (freebytes <= 0) { + freebytes += hfc_D_FIFO_SIZE; + } + count = hfctmp->ztdev->chans[2].bytes2transmit; + + total = count; + if (freebytes < count) { + printk(KERN_CRIT "zaphfc: dchan tx fifo not enough free bytes! (z1=%d, z2=%d)\n",*z1,*z2); + return; + } + + newz1 = (*z1 + count) & hfc_ZMASK; + newf1 = ((*f1 + 1) & hfc_MAX_DFRAMES) | (hfc_MAX_DFRAMES + 1); // next frame + + if (count > 0) { + if (debug) { + printk(KERN_CRIT "zaphfc: card %d TX [ ", hfctmp->cardno); + for (x=0; xdtransbuf[x]); + } + if (hfctmp->ztdev->chans[2].eoftx == 1) { + printk("] %d bytes\n", count); + } else { + printk("..] %d bytes\n", count); + } + } + maxlen = hfc_D_FIFO_SIZE - *z1; + if (maxlen > count) { + maxlen = count; + } + memcpy((char *)(hfctmp->fifos + hfc_FIFO_DTX_ZOFF + *z1),hfctmp->ztdev->chans[2].writechunk, maxlen); + count -= maxlen; + if (count > 0) { + memcpy((char *)(hfctmp->fifos + hfc_FIFO_DTX_ZOFF),(char *)(hfctmp->ztdev->chans[2].writechunk + maxlen), count); + } + } + + *z1 = newz1; + + if (hfctmp->ztdev->chans[2].eoftx == 1) { + *f1 = newf1; + z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DTX_Z1 + (*f1 * 4)); + *z1 = newz1; + hfctmp->ztdev->chans[2].eoftx = 0; + } +// printk(KERN_CRIT "zaphfc: dchan tx fifo (f1=%d, f2=%d, z1=%d, z2=%d)\n",(*f1) & hfc_FMASK,(*f2) & hfc_FMASK, *z1, *z2); + return; +} + +/* receive a complete hdlc frame, skip broken or short frames */ +static void hfc_drec(struct hfc_card *hfctmp) { + int count=0, maxlen=0, framelen=0; + unsigned char *f1, *f2, *crcstat; + unsigned short *z1, *z2, oldz2, newz2; + + hfctmp->ztdev->chans[2].bytes2receive=0; + hfctmp->ztdev->chans[2].eofrx = 0; + + /* put the received data into the zaptel buffer + we'll call zt_receive() later when the timer fires. */ + f1 = (char *)(hfctmp->fifos + hfc_FIFO_DRX_F1); + f2 = (char *)(hfctmp->fifos + hfc_FIFO_DRX_F2); + + if (*f1 == *f2) return; /* nothing received, strange eh? */ + + z1 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DRX_Z1 + (*f2 * 4)); + z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DRX_Z2 + (*f2 * 4)); + + /* calculate length of frame, including 2 bytes CRC and 1 byte STAT */ + count = *z1 - *z2; + + if (count < 0) { + count += hfc_D_FIFO_SIZE; /* ring buffer wrapped */ + } + count++; + framelen = count; + + crcstat = (char *)(hfctmp->fifos + hfc_FIFO_DRX_ZOFF + *z1); + + if ((framelen < 4) || (*crcstat != 0x0)) { + /* the frame is too short for a valid HDLC frame or the CRC is borked */ + printk(KERN_CRIT "zaphfc: empty HDLC frame or bad CRC received (framelen = %d, stat = %#x, card = %d).\n", framelen, *crcstat, hfctmp->cardno); + oldz2 = *z2; + *f2 = ((*f2 + 1) & hfc_MAX_DFRAMES) | (hfc_MAX_DFRAMES + 1); /* NEXT!!! */ + // recalculate z2, because Z2 is a function of F2 Z2(F2) and we INCed F2!!! + z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DRX_Z2 + (*f2 * 4)); + *z2 = (oldz2 + framelen) & hfc_ZMASK; + hfctmp->drecinframe = 0; + hfctmp->regs.int_drec--; + /* skip short or broken frames */ + hfctmp->ztdev->chans[2].bytes2receive = 0; + return; + } + + count -= 1; /* strip STAT */ + hfctmp->ztdev->chans[2].eofrx = 1; + + if (count + *z2 <= hfc_D_FIFO_SIZE) { + maxlen = count; + } else { + maxlen = hfc_D_FIFO_SIZE - *z2; + } + + /* copy first part */ + memcpy(hfctmp->drecbuf, (char *)(hfctmp->fifos + hfc_FIFO_DRX_ZOFF + *z2), maxlen); + hfctmp->ztdev->chans[2].bytes2receive += maxlen; + + count -= maxlen; + if (count > 0) { + /* ring buffer wrapped, copy rest from start of d fifo */ + memcpy(hfctmp->drecbuf + maxlen, (char *)(hfctmp->fifos + hfc_FIFO_DRX_ZOFF), count); + hfctmp->ztdev->chans[2].bytes2receive += count; + } + + /* frame read */ + oldz2 = *z2; + newz2 = (oldz2 + framelen) & hfc_ZMASK; + *f2 = ((*f2 + 1) & hfc_MAX_DFRAMES) | (hfc_MAX_DFRAMES + 1); /* NEXT!!! */ + /* recalculate z2, because Z2 is a function of F2 Z2(F2) and we INCed F2!!! */ + z2 = (unsigned short *)(hfctmp->fifos + hfc_FIFO_DRX_Z2 + (*f2 * 4)); + *z2 = newz2; + hfctmp->drecinframe = 0; + hfctmp->regs.int_drec--; +} + +#ifndef RTAITIMING +ZAP_IRQ_HANDLER(hfc_interrupt) { + struct hfc_card *hfctmp = dev_id; + unsigned long flags = 0; + unsigned char stat; +#else +static void hfc_service(struct hfc_card *hfctmp) { +#endif + struct zt_hfc *zthfc; + unsigned char s1, s2, l1state; + int x; + + if (!hfctmp) { +#ifndef RTAITIMING +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif +#else + /* rtai */ + return; +#endif + } + + if (!hfctmp->pci_io) { + printk(KERN_WARNING "%s: IO-mem disabled, cannot handle interrupt\n", + __FUNCTION__); +#ifndef RTAITIMING +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif +#else + /* rtai */ + return; +#endif + } + + /* we assume a few things in this irq handler: + - the hfc-pci will only generate "timer" irqs (proc/non-proc) + - we need to use every 8th IRQ (to generate 1khz timing) + OR + - if we use rtai for timing the hfc-pci will not generate ANY irq, + instead rtai will call this "fake" irq with a 1khz realtime timer. :) + - rtai will directly service the card, not like it used to by triggering + the linux irq + */ + +#ifndef RTAITIMING + spin_lock_irqsave(&hfctmp->lock, flags); + stat = hfc_inb(hfctmp, hfc_STATUS); + + if ((stat & hfc_STATUS_ANYINT) == 0) { + // maybe we are sharing the irq + spin_unlock_irqrestore(&hfctmp->lock,flags); +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + } +#endif + + s1 = hfc_inb(hfctmp, hfc_INT_S1); + s2 = hfc_inb(hfctmp, hfc_INT_S2); + if (s1 != 0) { + if (s1 & hfc_INTS_TIMER) { + // timer (bit 7) + // printk(KERN_CRIT "timer %d %d %d.\n", stat, s1, s2); + } + if (s1 & hfc_INTS_L1STATE) { + // state machine (bit 6) + // printk(KERN_CRIT "zaphfc: layer 1 state machine interrupt\n"); + zthfc = hfctmp->ztdev; + l1state = hfc_inb(hfctmp,hfc_STATES) & hfc_STATES_STATE_MASK; + if (hfctmp->regs.nt_mode == 1) { + if (debug) { + printk(KERN_CRIT "zaphfc: card %d layer 1 state = G%d\n", hfctmp->cardno, l1state); + } + switch (l1state) { + case 3: +#ifdef RTAITIMING + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT] layer 1 ACTIVATED (G%d) [realtime]", hfctmp->cardno, l1state); +#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT] layer 1 ACTIVATED (G%d)", hfctmp->cardno, l1state); +#endif + break; + default: +#ifdef RTAITIMING + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT] layer 1 DEACTIVATED (G%d) [realtime]", hfctmp->cardno, l1state); +#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT] layer 1 DEACTIVATED (G%d)", hfctmp->cardno, l1state); +#endif + } + if (l1state == 2) { + hfc_outb(hfctmp, hfc_STATES, hfc_STATES_ACTIVATE | hfc_STATES_DO_ACTION | hfc_STATES_NT_G2_G3); + } else if (l1state == 3) { + // fix to G3 state (see specs) + hfc_outb(hfctmp, hfc_STATES, hfc_STATES_LOAD_STATE | 3); + } + } else { + if (debug) { + printk(KERN_CRIT "zaphfc: card %d layer 1 state = F%d\n", hfctmp->cardno, l1state); + } + switch (l1state) { + case 7: +#ifdef RTAITIMING + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE] layer 1 ACTIVATED (F%d) [realtime]", hfctmp->cardno, l1state); +#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE] layer 1 ACTIVATED (F%d)", hfctmp->cardno, l1state); +#endif + break; + default: +#ifdef RTAITIMING + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE] layer 1 DEACTIVATED (F%d) [realtime]", hfctmp->cardno, l1state); +#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE] layer 1 DEACTIVATED (F%d)", hfctmp->cardno, l1state); +#endif + } + if (l1state == 3) { + hfc_outb(hfctmp, hfc_STATES, hfc_STATES_DO_ACTION | hfc_STATES_ACTIVATE); + } + } + + } + if (s1 & hfc_INTS_DREC) { + // D chan RX (bit 5) + hfctmp->regs.int_drec++; + // mr. zapata there is something for you! + // printk(KERN_CRIT "d chan rx\n"); + } + if (s1 & hfc_INTS_B2REC) { + // B2 chan RX (bit 4) + } + if (s1 & hfc_INTS_B1REC) { + // B1 chan RX (bit 3) + } + if (s1 & hfc_INTS_DTRANS) { + // D chan TX (bit 2) +// printk(KERN_CRIT "zaphfc: dchan frame transmitted.\n"); + } + if (s1 & hfc_INTS_B2TRANS) { + // B2 chan TX (bit 1) + } + if (s1 & hfc_INTS_B1TRANS) { + // B1 chan TX (bit 0) + } + } +#ifdef RTAITIMING + /* fake an irq */ + s2 |= hfc_M2_PROC_TRANS; +#endif + if (s2 != 0) { + if (s2 & hfc_M2_PMESEL) { + // kaboom irq (bit 7) + printk(KERN_CRIT "zaphfc: sync lost, pci performance too low. you might have some cpu throtteling enabled.\n"); + } + if (s2 & hfc_M2_GCI_MON_REC) { + // RxR monitor channel (bit 2) + } + if (s2 & hfc_M2_GCI_I_CHG) { + // GCI I-change (bit 1) + } + if (s2 & hfc_M2_PROC_TRANS) { + // processing/non-processing transition (bit 0) + hfctmp->ticks++; +#ifndef RTAITIMING + if (hfctmp->ticks > 7) { + // welcome to zaptel timing :) +#endif + hfctmp->ticks = 0; + + if (hfctmp->ztdev->span.flags & ZT_FLAG_RUNNING) { + // clear dchan buffer + hfctmp->ztdev->chans[2].bytes2transmit = 0; + hfctmp->ztdev->chans[2].maxbytes2transmit = hfc_D_FIFO_SIZE; + + zt_transmit(&(hfctmp->ztdev->span)); + + hfc_btrans(hfctmp,1); + hfc_btrans(hfctmp,2); + hfc_dtrans(hfctmp); + } + + hfc_brec(hfctmp,1); + hfc_brec(hfctmp,2); + if (hfctmp->regs.int_drec > 0) { + // dchan data to read + hfc_drec(hfctmp); + if (hfctmp->ztdev->chans[2].bytes2receive > 0) { + if (debug) { + printk(KERN_CRIT "zaphfc: card %d RX [ ", hfctmp->cardno); + if (hfctmp->ztdev->chans[2].eofrx) { + /* dont output CRC == less user confusion */ + for (x=0; x < hfctmp->ztdev->chans[2].bytes2receive - 2; x++) { + printk("%#2x ", hfctmp->drecbuf[x]); + } + printk("] %d bytes\n", hfctmp->ztdev->chans[2].bytes2receive - 2); + } else { + for (x=0; x < hfctmp->ztdev->chans[2].bytes2receive; x++) { + printk("%#2x ", hfctmp->drecbuf[x]); + } + printk("..] %d bytes\n", hfctmp->ztdev->chans[2].bytes2receive); + } + } + } + } else { + // hmm....ok, let zaptel receive nothing + hfctmp->ztdev->chans[2].bytes2receive = 0; + } + if (hfctmp->ztdev->span.flags & ZT_FLAG_RUNNING) { + zt_receive(&(hfctmp->ztdev->span)); + } + +#ifndef RTAITIMING + } +#endif + } + + } +#ifndef RTAITIMING + spin_unlock_irqrestore(&hfctmp->lock,flags); +#ifdef LINUX26 + return IRQ_RETVAL(1); +#endif +#endif +} + + +static int zthfc_open(struct zt_chan *chan) { + struct zt_hfc *zthfc = chan->pvt; + struct hfc_card *hfctmp = zthfc->card; + + if (!hfctmp) { + return 0; + } +#ifndef LINUX26 + MOD_INC_USE_COUNT; +#else + try_module_get(THIS_MODULE); +#endif + return 0; +} + +static int zthfc_close(struct zt_chan *chan) { + struct zt_hfc *zthfc = chan->pvt; + struct hfc_card *hfctmp = zthfc->card; + + if (!hfctmp) { + return 0; + } + +#ifndef LINUX26 + MOD_DEC_USE_COUNT; +#else + module_put(THIS_MODULE); +#endif + return 0; +} + +static int zthfc_rbsbits(struct zt_chan *chan, int bits) { + return 0; +} + +static int zthfc_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) { + switch(cmd) { + default: + return -ENOTTY; + } + return 0; +} + +static int zthfc_startup(struct zt_span *span) { + struct zt_hfc *zthfc = span->pvt; + struct hfc_card *hfctmp = zthfc->card; + int alreadyrunning; + + if (hfctmp == NULL) { + printk(KERN_INFO "zaphfc: no card for span at startup!\n"); + } + alreadyrunning = span->flags & ZT_FLAG_RUNNING; + + if (!alreadyrunning) { + span->chans[2].flags &= ~ZT_FLAG_HDLC; + span->chans[2].flags |= ZT_FLAG_BRIDCHAN; + + span->flags |= ZT_FLAG_RUNNING; + + hfctmp->ticks = -2; + hfctmp->clicks = 0; + hfctmp->regs.fifo_en = hfc_FIFOEN_D | hfc_FIFOEN_B1 | hfc_FIFOEN_B2; + hfc_outb(hfctmp, hfc_FIFO_EN, hfctmp->regs.fifo_en); + } else { + return 0; + } + + // drivers, start engines! + hfc_outb(hfctmp, hfc_STATES, hfc_STATES_DO_ACTION | hfc_STATES_ACTIVATE); + return 0; +} + +static int zthfc_shutdown(struct zt_span *span) { + return 0; +} + +static int zthfc_maint(struct zt_span *span, int cmd) { + return 0; +} + +static int zthfc_chanconfig(struct zt_chan *chan, int sigtype) { +// printk(KERN_CRIT "chan_config sigtype=%d\n", sigtype); + return 0; +} + +static int zthfc_spanconfig(struct zt_span *span, struct zt_lineconfig *lc) { + span->lineconfig = lc->lineconfig; + return 0; +} + +static int zthfc_initialize(struct zt_hfc *zthfc) { + struct hfc_card *hfctmp = zthfc->card; + int i; + + memset(&zthfc->span, 0x0, sizeof(struct zt_span)); // you never can tell... + + sprintf(zthfc->span.name, "ZTHFC%d", hfc_dev_count + 1); + if (hfctmp->regs.nt_mode == 1) { +#ifdef RTAITIMING + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT] [realtime]", hfc_dev_count + 1); +#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [NT]", hfc_dev_count + 1); +#endif + } else { +#ifdef RTAITIMING + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE] [realtime]", hfc_dev_count + 1); +#else + sprintf(zthfc->span.desc, "HFC-S PCI A ISDN card %d [TE]", hfc_dev_count + 1); +#endif + } + + zthfc->span.spanconfig = zthfc_spanconfig; + zthfc->span.chanconfig = zthfc_chanconfig; + zthfc->span.startup = zthfc_startup; + zthfc->span.shutdown = zthfc_shutdown; + zthfc->span.maint = zthfc_maint; + zthfc->span.rbsbits = zthfc_rbsbits; + zthfc->span.open = zthfc_open; + zthfc->span.close = zthfc_close; + zthfc->span.ioctl = zthfc_ioctl; + + zthfc->span.chans = zthfc->chans; + zthfc->span.channels = 3; + zthfc->span.deflaw = ZT_LAW_ALAW; + zthfc->span.linecompat = ZT_CONFIG_AMI | ZT_CONFIG_CCS; // <--- this is really BS + zthfc->span.offset = 0; + init_waitqueue_head(&zthfc->span.maintq); + zthfc->span.pvt = zthfc; + + for (i = 0; i < zthfc->span.channels; i++) { + memset(&(zthfc->chans[i]), 0x0, sizeof(struct zt_chan)); + sprintf(zthfc->chans[i].name, "ZTHFC%d/%d/%d", hfc_dev_count + 1,0,i + 1); + zthfc->chans[i].pvt = zthfc; + zthfc->chans[i].sigcap = ZT_SIG_EM | ZT_SIG_CLEAR | ZT_SIG_FXSLS | ZT_SIG_FXSGS | ZT_SIG_FXSKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_FXOKS | ZT_SIG_CAS | ZT_SIG_SF; + zthfc->chans[i].chanpos = i + 1; + } + + if (zt_register(&zthfc->span,0)) { + printk(KERN_CRIT "unable to register zaptel device!\n"); + return -1; + } +// printk(KERN_CRIT "zaphfc: registered zaptel device!\n"); + return 0; +} + +#ifdef RTAITIMING +#define TICK_PERIOD 1000000 +#define TICK_PERIOD2 1000000000 +#define TASK_PRIORITY 1 +#define STACK_SIZE 10000 + +static RT_TASK rt_task; +static struct hfc_card *rtai_hfc_list[hfc_MAX_CARDS]; +static unsigned char rtai_hfc_counter = 0; + +static void rtai_register_hfc(struct hfc_card *hfctmp) { + rtai_hfc_list[rtai_hfc_counter++] = hfctmp; +} + +static void rtai_loop(int t) { + int i=0; + for (;;) { + for (i=0; i < rtai_hfc_counter; i++) { + if (rtai_hfc_list[i] != NULL) + hfc_service(rtai_hfc_list[i]); + } + rt_task_wait_period(); + } +} +#endif + +int hfc_findCards(int pcivendor, int pcidevice, char *vendor_name, char *card_name) { + struct pci_dev *tmp; + struct hfc_card *hfctmp = NULL; + struct zt_hfc *zthfc = NULL; + + tmp = pci_get_device(pcivendor, pcidevice, multi_hfc); + while (tmp != NULL) { + multi_hfc = tmp; // skip this next time. + + if (pci_enable_device(tmp)) { + multi_hfc = NULL; + return -1; + } + pci_set_master(tmp); + + hfctmp = kmalloc(sizeof(struct hfc_card), GFP_KERNEL); + if (!hfctmp) { + printk(KERN_WARNING "zaphfc: unable to kmalloc!\n"); + pci_disable_device(tmp); + multi_hfc = NULL; + return -ENOMEM; + } + memset(hfctmp, 0x0, sizeof(struct hfc_card)); + spin_lock_init(&hfctmp->lock); + + hfctmp->pcidev = tmp; + hfctmp->pcibus = tmp->bus->number; + hfctmp->pcidevfn = tmp->devfn; + + if (!tmp->irq) { + printk(KERN_WARNING "zaphfc: no irq!\n"); + } else { + hfctmp->irq = tmp->irq; + } + + hfctmp->pci_io = (char *) tmp->resource[1].start; + if (!hfctmp->pci_io) { + printk(KERN_WARNING "zaphfc: no iomem!\n"); + kfree(hfctmp); + pci_disable_device(tmp); + multi_hfc = NULL; + return -1; + } + + hfctmp->fifomem = kmalloc(65536, GFP_KERNEL); + if (!hfctmp->fifomem) { + printk(KERN_WARNING "zaphfc: unable to kmalloc fifomem!\n"); + kfree(hfctmp); + pci_disable_device(tmp); + multi_hfc = NULL; + return -ENOMEM; + } else { + memset(hfctmp->fifomem, 0x0, 65536); + hfctmp->fifos = (void *)(((ulong) hfctmp->fifomem) & ~0x7FFF) + 0x8000; + pci_write_config_dword(hfctmp->pcidev, 0x80, (u_int) virt_to_bus(hfctmp->fifos)); + hfctmp->pci_io = ioremap((ulong) hfctmp->pci_io, 256); + } + +#ifdef RTAITIMING + /* we need no stinking irq */ + hfctmp->irq = 0; +#else + if (request_irq(hfctmp->irq, &hfc_interrupt, ZAP_IRQ_SHARED, "zaphfc", hfctmp)) { + printk(KERN_WARNING "zaphfc: unable to register irq\n"); + kfree(hfctmp->fifomem); + kfree(hfctmp); + iounmap((void *) hfctmp->pci_io); + pci_disable_device(tmp); + multi_hfc = NULL; + return -EIO; + } +#endif + +#ifdef RTAITIMING + rtai_register_hfc(hfctmp); +#endif + printk(KERN_INFO + "zaphfc: %s %s configured at mem %lx fifo %lx(%#x) IRQ %d HZ %d\n", + vendor_name, card_name, + (unsigned long) hfctmp->pci_io, + (unsigned long) hfctmp->fifos, + (u_int) virt_to_bus(hfctmp->fifos), + hfctmp->irq, HZ); + pci_write_config_word(hfctmp->pcidev, PCI_COMMAND, PCI_COMMAND_MEMORY); // enable memio + hfctmp->regs.int_m1 = 0; // no ints + hfctmp->regs.int_m2 = 0; // not at all + hfc_outb(hfctmp,hfc_INT_M1,hfctmp->regs.int_m1); + hfc_outb(hfctmp,hfc_INT_M2,hfctmp->regs.int_m2); + + if ((modes & (1 << hfc_dev_count)) != 0) { + printk(KERN_INFO "zaphfc: Card %d configured for NT mode\n",hfc_dev_count); + hfctmp->regs.nt_mode = 1; + } else { + printk(KERN_INFO "zaphfc: Card %d configured for TE mode\n",hfc_dev_count); + hfctmp->regs.nt_mode = 0; + } + + zthfc = kmalloc(sizeof(struct zt_hfc),GFP_KERNEL); + if (!zthfc) { + printk(KERN_CRIT "zaphfc: unable to kmalloc!\n"); + hfc_shutdownCard(hfctmp); + kfree(hfctmp); + multi_hfc = NULL; + return -ENOMEM; + } + memset(zthfc, 0x0, sizeof(struct zt_hfc)); + + zthfc->card = hfctmp; + zthfc_initialize(zthfc); + hfctmp->ztdev = zthfc; + + memset(hfctmp->drecbuf, 0x0, sizeof(hfctmp->drecbuf)); + hfctmp->ztdev->chans[2].readchunk = hfctmp->drecbuf; + + memset(hfctmp->dtransbuf, 0x0, sizeof(hfctmp->dtransbuf)); + hfctmp->ztdev->chans[2].writechunk = hfctmp->dtransbuf; + + memset(hfctmp->brecbuf[0], 0x0, sizeof(hfctmp->brecbuf[0])); + hfctmp->ztdev->chans[0].readchunk = hfctmp->brecbuf[0]; + memset(hfctmp->btransbuf[0], 0x0, sizeof(hfctmp->btransbuf[0])); + hfctmp->ztdev->chans[0].writechunk = hfctmp->btransbuf[0]; + + memset(hfctmp->brecbuf[1], 0x0, sizeof(hfctmp->brecbuf[1])); + hfctmp->ztdev->chans[1].readchunk = hfctmp->brecbuf[1]; + memset(hfctmp->btransbuf[1], 0x0, sizeof(hfctmp->btransbuf[1])); + hfctmp->ztdev->chans[1].writechunk = hfctmp->btransbuf[1]; + + + hfc_registerCard(hfctmp); + hfc_resetCard(hfctmp); + tmp = pci_get_device(pcivendor, pcidevice, multi_hfc); + } + return 0; +} + + + +int init_module(void) { + int i = 0; +#ifdef RTAITIMING + RTIME tick_period; + for (i=0; i < hfc_MAX_CARDS; i++) { + rtai_hfc_list[i] = NULL; + } + rt_set_periodic_mode(); +#endif + i = 0; + while (id_list[i].vendor_id) { + multi_hfc = NULL; + hfc_findCards(id_list[i].vendor_id, id_list[i].device_id, id_list[i].vendor_name, id_list[i].card_name); + i++; + } +#ifdef RTAITIMING + for (i=0; i < hfc_MAX_CARDS; i++) { + if (rtai_hfc_list[i]) { + printk(KERN_INFO + "zaphfc: configured %d at mem %#x fifo %#x(%#x) for realtime servicing\n", + rtai_hfc_list[i]->cardno, + (u_int) rtai_hfc_list[i]->pci_io, + (u_int) rtai_hfc_list[i]->fifos, + (u_int) virt_to_bus(rtai_hfc_list[i]->fifos)); + + } + } + rt_task_init(&rt_task, rtai_loop, 1, STACK_SIZE, TASK_PRIORITY, 0, 0); + tick_period = start_rt_timer(nano2count(TICK_PERIOD)); + rt_task_make_periodic(&rt_task, rt_get_time() + tick_period, tick_period); +#endif + printk(KERN_INFO "zaphfc: %d hfc-pci card(s) in this box.\n", hfc_dev_count); + return 0; +} + +void cleanup_module(void) { + struct hfc_card *tmpcard; +#ifdef RTAITIMING + stop_rt_timer(); + rt_task_delete(&rt_task); +#endif + printk(KERN_INFO "zaphfc: stop\n"); +// spin_lock(®isterlock); + while (hfc_dev_list != NULL) { + if (hfc_dev_list == NULL) break; + hfc_shutdownCard(hfc_dev_list); + tmpcard = hfc_dev_list; + hfc_dev_list = hfc_dev_list->next; + if (tmpcard != NULL) { + kfree(tmpcard); + tmpcard = NULL; + printk(KERN_INFO "zaphfc: freed one card.\n"); + } + } +// spin_unlock(®isterlock); +} +#endif + + +#ifdef LINUX26 +module_param(modes, int, 0600); +module_param(debug, int, 0600); +#else +MODULE_PARM(modes,"i"); +MODULE_PARM(debug,"i"); +#endif + +MODULE_DESCRIPTION("HFC-S PCI A Zaptel Driver"); +MODULE_AUTHOR("Klaus-Peter Junghanns "); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif