--- dahdi-linux-2.2.1+dfsg.orig/debian/modules +++ dahdi-linux-2.2.1+dfsg/debian/modules @@ -0,0 +1,3 @@ +dahdi +dahdi_dummy +dahdi_transcode --- dahdi-linux-2.2.1+dfsg.orig/debian/dahdi-dkms.postinst +++ dahdi-linux-2.2.1+dfsg/debian/dahdi-dkms.postinst @@ -0,0 +1,54 @@ +#!/bin/sh + +NAME=dahdi +PACKAGE_NAME=$NAME-dkms + +CVERSION=`dpkg-query -W -f='${Version}' $PACKAGE_NAME | cut -d\: -f2` + +ARCH=`dpkg --print-architecture` +case $ARCH in + amd64) + ARCH="x86_64" + ;; + lpia) + ARCH="i686" + ;; + i386) + ARCH="i686" + ;; + armel) + ;; + *) + echo "WARNING: potentially unsupported arch: $ARCH" + ARCH="$ARCH" + ;; +esac + + + +case "$1" in + configure) + for POSTINST in /usr/lib/dkms/common.postinst /usr/share/$PACKAGE_NAME/postinst; do + if [ -f $POSTINST ]; then + $POSTINST $NAME $CVERSION /usr/share/$PACKAGE_NAME $ARCH $2 + exit $? + fi + echo "WARNING: $POSTINST does not exist." + done + echo "ERROR: DKMS version is too old and $PACKAGE_NAME was not" + echo "built with legacy DKMS support." + echo "You must either rebuild $PACKAGE_NAME with legacy postinst" + echo "support or upgrade DKMS to a more current version." + exit 1 + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +#DEBHELPER# --- dahdi-linux-2.2.1+dfsg.orig/debian/dahdi-linux.install +++ dahdi-linux-2.2.1+dfsg/debian/dahdi-linux.install @@ -0,0 +1,6 @@ +drivers/dahdi/xpp/xpp.conf etc/dahdi +drivers/dahdi/xpp/xpp.rules etc/udev/rules.d +drivers/dahdi/xpp/init_card_* usr/share/dahdi/ +drivers/dahdi/xpp/XppConfig.pm usr/share/dahdi/ +debian/modules etc/dahdi +debian/system.conf etc/dahdi --- dahdi-linux-2.2.1+dfsg.orig/debian/dkms.conf.in +++ dahdi-linux-2.2.1+dfsg/debian/dkms.conf.in @@ -0,0 +1,151 @@ +PACKAGE_VERSION="#CVERSION#" + +# Items below here should not have to change with each driver version +PACKAGE_NAME="dahdi" +MAKE[0]="make modules KERNVER=$kernelver MODULES_EXTRA='opvxa1200 wcopenpci dahdi_echocan_oslec' SUBDIRS_EXTRA='../staging/echo zaphfc/' ;make;make firmware-loaders;for i in 32 64;do echo : > drivers/dahdi/vpmadt032_loader/.vpmadt032_x86_$i.o.cmd;done;make" +CLEAN="make KERNVER=$kernelver clean" +#MAKE[0]="make modules KERNVER=$kernelver DOWNLOAD=echo" +#CLEAN="make KERNVER=$kernelver clean DOWNLOAD=echo" + +BUILT_MODULE_NAME[0]="dahdi_dummy" +BUILT_MODULE_LOCATION[0]="drivers/dahdi/" +DEST_MODULE_LOCATION[0]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[1]="dahdi_dynamic_eth" +BUILT_MODULE_LOCATION[1]="drivers/dahdi/" +DEST_MODULE_LOCATION[1]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[2]="dahdi_dynamic" +BUILT_MODULE_LOCATION[2]="drivers/dahdi/" +DEST_MODULE_LOCATION[2]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[3]="dahdi_dynamic_loc" +BUILT_MODULE_LOCATION[3]="drivers/dahdi/" +DEST_MODULE_LOCATION[3]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[4]="dahdi_echocan_jpah" +BUILT_MODULE_LOCATION[4]="drivers/dahdi/" +DEST_MODULE_LOCATION[4]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[5]="dahdi_echocan_kb1" +BUILT_MODULE_LOCATION[5]="drivers/dahdi/" +DEST_MODULE_LOCATION[5]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[6]="dahdi_echocan_mg2" +BUILT_MODULE_LOCATION[6]="drivers/dahdi/" +DEST_MODULE_LOCATION[6]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[7]="dahdi_echocan_sec2" +BUILT_MODULE_LOCATION[7]="drivers/dahdi/" +DEST_MODULE_LOCATION[7]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[8]="dahdi_echocan_sec" +BUILT_MODULE_LOCATION[8]="drivers/dahdi/" +DEST_MODULE_LOCATION[8]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[9]="dahdi" +BUILT_MODULE_LOCATION[9]="drivers/dahdi/" +DEST_MODULE_LOCATION[9]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[10]="dahdi_transcode" +BUILT_MODULE_LOCATION[10]="drivers/dahdi/" +DEST_MODULE_LOCATION[10]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[11]="pciradio" +BUILT_MODULE_LOCATION[11]="drivers/dahdi/" +DEST_MODULE_LOCATION[11]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[12]="tor2" +BUILT_MODULE_LOCATION[12]="drivers/dahdi/" +DEST_MODULE_LOCATION[12]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[13]="wcb4xxp" +BUILT_MODULE_LOCATION[13]="drivers/dahdi/wcb4xxp/" +DEST_MODULE_LOCATION[13]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[14]="wcfxo" +BUILT_MODULE_LOCATION[14]="drivers/dahdi/" +DEST_MODULE_LOCATION[14]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[15]="wct1xxp" +BUILT_MODULE_LOCATION[15]="drivers/dahdi/" +DEST_MODULE_LOCATION[15]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[16]="wct4xxp" +BUILT_MODULE_LOCATION[16]="drivers/dahdi/wct4xxp/" +DEST_MODULE_LOCATION[16]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[17]="wctc4xxp" +BUILT_MODULE_LOCATION[17]="drivers/dahdi/wctc4xxp/" +DEST_MODULE_LOCATION[17]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[18]="wctdm" +BUILT_MODULE_LOCATION[18]="drivers/dahdi/" +DEST_MODULE_LOCATION[18]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[19]="wctdm24xxp" +BUILT_MODULE_LOCATION[19]="drivers/dahdi/wctdm24xxp/" +DEST_MODULE_LOCATION[19]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[20]="wcte11xp" +BUILT_MODULE_LOCATION[20]="drivers/dahdi/" +DEST_MODULE_LOCATION[20]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[21]="wcte12xp" +BUILT_MODULE_LOCATION[21]="drivers/dahdi/wcte12xp/" +DEST_MODULE_LOCATION[21]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[22]="xpd_fxo" +BUILT_MODULE_LOCATION[22]="drivers/dahdi/xpp/" +DEST_MODULE_LOCATION[22]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[23]="xpd_fxs" +BUILT_MODULE_LOCATION[23]="drivers/dahdi/xpp/" +DEST_MODULE_LOCATION[23]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[24]="xpd_pri" +BUILT_MODULE_LOCATION[24]="drivers/dahdi/xpp/" +DEST_MODULE_LOCATION[24]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[25]="xpp" +BUILT_MODULE_LOCATION[25]="drivers/dahdi/xpp/" +DEST_MODULE_LOCATION[25]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[26]="xpp_usb" +BUILT_MODULE_LOCATION[26]="drivers/dahdi/xpp/" +DEST_MODULE_LOCATION[26]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[27]="xpd_bri" +BUILT_MODULE_LOCATION[27]="drivers/dahdi/xpp/" +DEST_MODULE_LOCATION[27]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[28]="dahdi_voicebus" +BUILT_MODULE_LOCATION[28]="drivers/dahdi/voicebus/" +DEST_MODULE_LOCATION[28]="/kernel/drivers/telephony/dahdi/voicebus" + +#Added by Debian / Ubuntu +BUILT_MODULE_NAME[29]="opvxa1200" +BUILT_MODULE_LOCATION[29]="drivers/dahdi/" +DEST_MODULE_LOCATION[29]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[30]="wcopenpci" +BUILT_MODULE_LOCATION[30]="drivers/dahdi/" +DEST_MODULE_LOCATION[30]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[31]="dahdi_echocan_oslec" +BUILT_MODULE_LOCATION[31]="drivers/dahdi/" +DEST_MODULE_LOCATION[31]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[32]="echo" +BUILT_MODULE_LOCATION[32]="drivers/staging/echo/" +DEST_MODULE_LOCATION[32]="/kernel/drivers/telephony/staging/echo" + +BUILT_MODULE_NAME[33]="zaphfc" +BUILT_MODULE_LOCATION[33]="drivers/dahdi/zaphfc" +DEST_MODULE_LOCATION[33]="/kernel/drivers/telephony/dahdi/zaphfc" + +BUILT_MODULE_NAME[34]="dahdi_vpmadt032_loader" +BUILT_MODULE_LOCATION[34]="drivers/dahdi/" +DEST_MODULE_LOCATION[34]="/kernel/drivers/telephony/dahdi" + +AUTOINSTALL=yes --- dahdi-linux-2.2.1+dfsg.orig/debian/control.modules.in +++ dahdi-linux-2.2.1+dfsg/debian/control.modules.in @@ -0,0 +1,21 @@ +Source: dahdi-linux +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.8.0.0 + +Package: dahdi-modules-_KVERS_ +Architecture: any +Provides: dahdi-modules +Replaces: zaptel-modules-_KVERS_ +Conflicts: zaptel-modules-_KVERS_ +Description: DAHDI modules for Linux (kernel _KVERS_). + DAHDI (formly Zaptel) is an interface for telephony devices used by e.g. + the Asterisk PBX software. The dahdi-* packages provide the kernel + DAHDI kernel modules and their required setup environment. + . + This package contains the set of loadable kernel modules for the + zapata telephony API. + This package contains the compiled kernel modules for _KVERS_ --- dahdi-linux-2.2.1+dfsg.orig/debian/dahdi-dkms.prerm +++ dahdi-linux-2.2.1+dfsg/debian/dahdi-dkms.prerm @@ -0,0 +1,20 @@ +#!/bin/sh + +#DEBHELPER# +NAME=dahdi +PACKAGE_NAME=$NAME-dkms + + +CVERSION=`dpkg-query -W -f='${Version}' $PACKAGE_NAME | cut -d\: -f2` +case "$1" in + remove|upgrade|purge) + echo "Removing all DKMS Modules" + dkms remove -m $NAME -v $CVERSION --all > /dev/null + echo "Done." + ;; + purge) + delgroup --quiet $NAME + ;; +esac + + --- dahdi-linux-2.2.1+dfsg.orig/debian/changelog +++ dahdi-linux-2.2.1+dfsg/debian/changelog @@ -0,0 +1,1184 @@ +dahdi-linux (1:2.2.1+dfsg-1ubuntu1) lucid; urgency=low + + * Merge from debian pkg-voip + * Changes from Debian: + - debian/control: Change Maintainer + - debian/control: Removed Uploaders field. + - debian/control: Removed Debian Vcs-Svn entry and replaced with + ubuntu-voip Vcs-Bzr, to reflect divergence in packages. + - debian/control: Fix typo (right command is m-a a-i dahdi-source) + - debian/rules: Fix typo (s/rules/rules.d/) + * Import modifications by Javier Uruen Val : + - debian/system.conf + - debian/modules + * DKMS support: + - debian/control: Added Depends to dahdi-linux (dahdi-dkms | dahdi-source) + - debian/control: Add dkms package + - debian/dahdi-dkms.install + - debian/dahdi-dkms.postinst + - debian/dahdi-dkms.prerm + - debian/dahdi-linux.install + - debian/rules: add code to automatically configure dkms.conf + - debian/dkms.conf.in + + * From debian pkg-voip SVN: + [ Tzafrir Cohen ] + * New upstream release (Closes: #564739). + * Dropped patch wcfxo_reset_fix: merged upstream. + * Section name of dahdi-source: kernel + * Also provide an example xpp.conf . + * Patch uk_rotary (Closes: #546329). + * Override the bogus lintian warning for 'm-a a-i dahdi' + * Remove some remaining Zaptel left-overs. + * Replaced extra drivers and patches with huge patch dahdi_linux_extra + (Closes: #564720). + * Patch xpp_udev_rules_2632: fix XPP udev rules (Closes: #562024). + * Also copy the .version file, to make sure modules have versions. + * Move xpp udev rules under /lib. Name still kept for the sake of + simplicity (thanks, Lintian). + + -- Jean-Michel Dault Mon, 15 Feb 2010 17:18:14 -0500 + +dahdi-linux (1:2.2.0.2~dfsg-1) unstable; urgency=low + + * New upstream release + * New openvox drivers: r113 from their SVN. + * Fix copyright file for origin of OpenVox drivers. + * Use new zaphfc drivers (r5) from http://code.google.com/p/zaphfc/ . + Note that those use hardhdlc in system.conf (Closes: #532345). + * Remove obsolete bristuff drivers we don't build anyway. + Leave vzaphfc for now as this is practically upstream (Completely + closes: #548061). + * Not applying bri_dchan. Astribank BRI spans will use hardhdlc. + * Patch wcfxo_reset_fix: Closes: #546331 . + * Replace 'dh_clean -k' with dh_prep + * Standards version 3.8.3 (no change needed). + * Patch mmx_auto: relied on ARCH to tell between i386 and x86_64, which + is not a good idea on newer kernels. Use DAHDI_ARCH instead. + * Remove useless postinst and preinst. debhelper adds them anyway + (and with the -e). + * Do include examples of dahdi-linux . + + -- Tzafrir Cohen Sun, 04 Oct 2009 22:47:30 +0200 + +dahdi-linux (1:2.2.0~dfsg~rc5-1) unstable; urgency=low + + [ Mark Purcell ] + * New upstream release + + [ Tzafrir Cohen ] + * Enable MMX optimization on i386 that supports it (x86, not x86_64). + Doubles OSLEC's performance: + - Adds patches mmx_auto and mmx_fix from upstream bug report. + - Fixes patch oslec_kbuild to use it if enabled. + * modulestest: add -v to support building from debuild. + + -- Tzafrir Cohen Wed, 10 Jun 2009 15:59:34 +0300 + +dahdi-linux (1:2.2.0~dfsg~rc4-1) unstable; urgency=low + + * New upstream release + + [ Tzafrir Cohen ] + * Dropped qozap as wcb4xxp provides that functionality. + * New upstream RC. + * Actually build OpenVox drivers. + * opvxa1200.c: rev. 1.4.12.4 (battery fixes and such) + * Fix '${match}' in udev rules file (hardwire). + * no_firmware_download: Disable downloading a binary kernel module at + build time. + + [ Victor Seva ] + * fix debian/watch. + + -- Mark Purcell Wed, 20 May 2009 07:22:46 +1000 + +dahdi-linux (1:2.1.0.4~dfsg-1) experimental; urgency=low + + [ Tzafrir Cohen ] + * Zaptel renamed DAHDI and split to dahdi-linux and dahdi-tools. + * DAHDI is incompatible (ABI and API) with Zaptel. All modules need to + be adapted. + * OSLEC wrapper included upstream. Includeing a version of the OSLEC code + from the kernel staging directory. + - Patch oslec_kernelorg: Upstream kernel.org OSLEC code. + * Dropping some aparantly unused external Zaptel drivers. + * Extra drivers moved from kernel/ to drivers/dahdi/ to fit the kernel tree. + * UDEV rules are handled here as well. No need to change device names, + only set permissions. + * Patch fix_readme_astribank: Fix an asciidoc formatting issue. + * Patch oslec_kbuild: Make oslec's build unconditional. + * Fix for zaphfc with DAHDI (by Timo Teräs ) + * qozap no longer included: Use wcb4xxp instead. + * ztgsm no longer built: nobody really used it. + * cwain and vzaphfc currently included but not built. + * Don't generate static device files automatically. Provide a script + to do that. Until we know if there is an actual use for the non-udev + setup. + + [ Mark Purcell ] + * Update Description: fixes description-starts-with-package-name + * Add ${misc:Depends} - debhelper-but-no-misc-depends + * Add myself to uploaders + * debian/compat -> 7 + - Fixes:package-uses-deprecated-debhelper-compat-version + * Upload to experimental while we await NEW & debian-release + + -- Mark Purcell Sun, 29 Mar 2009 17:58:23 +1100 + +zaptel (1:1.4.11~dfsg-3) unstable; urgency=low + + * zaptel.init: exit on a different condition: Nicer handling of ztdummy. + * zaptel.init: Give a sane default to XPP_SYNC. + + -- Tzafrir Cohen Mon, 03 Nov 2008 09:35:23 +0200 + +zaptel (1:1.4.11~dfsg-2) unstable; urgency=low + + * Patch xpp_fxs_power: Fixed an issue with hook detection of the Astribank + FXS module. + * Don't fail init.d script if fxotune fails. This may happen if running it + when Asterisk is already running. + * Bump standards version to 3.8.0.0 . + * Ignore false lintian warning ("m-a a-i" has "a a"). + * Patch xpp_fxo_cid_always: do always pass PCM if that's what the user + asked. + * Patch vzaphfc_proc_root_dir: fix vzaphfc on 2.6.26. + * Patch wcte12xp_flags: Proper time for irq save flags. + * Patch headers_2627: Fix location of semaphore.h for 2.6.27 . + * Patch xpp_fxs_dtmf_leak: Don't play DTMFs to the wrong channel. + * Patch wctdm_fix_alarm: Fix sending channel alarms. + * Patch device_class_2626: Fix building 2.6.26 (Closes: #493397). + * Using dh_lintian for lintian overrides, hence requiring debhelper 6.0.7. + * Lintian: we know we have direct changes. Too bad we're half-upstream :-( + * Fix doc-base section names. + + -- Tzafrir Cohen Thu, 28 Aug 2008 22:58:23 +0300 + +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 + --- dahdi-linux-2.2.1+dfsg.orig/debian/NEWS.Debian +++ dahdi-linux-2.2.1+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 + --- dahdi-linux-2.2.1+dfsg.orig/debian/dahdi-linux.dahdi.udev +++ dahdi-linux-2.2.1+dfsg/debian/dahdi-linux.dahdi.udev @@ -0,0 +1,2 @@ +SUBSYSTEM=="dahdi", GROUP="dialout", MODE="0660" + --- dahdi-linux-2.2.1+dfsg.orig/debian/dahdi-source.install +++ dahdi-linux-2.2.1+dfsg/debian/dahdi-source.install @@ -0,0 +1 @@ +debian/lintian/dahdi-source usr/share/lintian/overrides --- dahdi-linux-2.2.1+dfsg.orig/debian/make_static_nodes +++ dahdi-linux-2.2.1+dfsg/debian/make_static_nodes @@ -0,0 +1,25 @@ +#!/bin/sh + +# Generate static device file nodes for dahdi +# Most peopel won't need this, as the device file nodes are normally +# generated by udev. Also note that if you do use udev, /dev is a +# ramdisk, and thus any changes you do to it will not be preserved +# on next boot. + +mknod_safe() { + if [ -c $1 ]; then return; fi + mknod "$@" || true +} + +mkdir -p /dev/dahdi +mknod_safe /dev/dahdi/ctl c 196 0 +mknod_safe /dev/dahdi/transcode c 196 250 +mknod_safe /dev/dahdi/timer c 196 253 +mknod_safe /dev/dahdi/channel c 196 254 +mknod_safe /dev/dahdi/pseudo c 196 255 +for N in `seq 249`; do + mknod_safe /dev/dahdi/$N c 196 $N +done +chown -R 0:dialout /dev/dahdi/ +chmod 0660 /dev/dahdi/* + --- dahdi-linux-2.2.1+dfsg.orig/debian/docs +++ dahdi-linux-2.2.1+dfsg/debian/docs @@ -0,0 +1,4 @@ +README +README.html +UPGRADE.txt +drivers/dahdi/xpp/Changelog_xpp --- dahdi-linux-2.2.1+dfsg.orig/debian/copyright +++ dahdi-linux-2.2.1+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/ +zaphfc driver was downloaded from + http://zaphfc.googlecode.com/ +OpenVox drivers were downloaded from + http://www.openvox.cn/viewvc/dahdi/trunk/opvxa1200.c?view=co + +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 + +zaphfc is derived from bristuff and vzaphfc and has been enhanced by: + * Copyright (C) 2009, Jose A. Deniz + +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/downloadsFile/a1200p-a800p_driver_with_dahdi.c + +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 + + +Patch oslec_kernelorg is based on the Linux Kernel distribution (see +README.Debian). Part of the Open Source Line Echo Canceller (OSLEC) project. + +echo.c: * Copyright (C) 2001, 2003 Steve Underwood, 2007 David Rowe +oslec.h: Copyright (C) 2007 David Rowe +oslec_wrap.c: Copyright (C) 2007 David Rowe +fir.h: * Copyright (C) 2002 Steve Underwood +bit_operations.h: * Copyright (C) 2006 Steve Underwood +echo.h: * Copyright (C) 2001 Steve Underwood and 2007 David Rowe + +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. --- dahdi-linux-2.2.1+dfsg.orig/debian/modulestest +++ dahdi-linux-2.2.1+dfsg/debian/modulestest @@ -0,0 +1,86 @@ +#!/bin/sh + +# debian/modulestest: a postbuild script to build dahdi modules +# example usage: +# +# svn-buildpackage --svn-postbuild='debian/modulestest -r' --svn-ignore-new +# +# debuild --post-dpkg-buildpackage-hook="$PWD/debian/modulestest -d %v -r" +# +# 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 +# + +# PACKAGE is set by svn-buildpackage to "dahdi-linux" . Override that: +PACKAGE=dahdi + +# 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= +DEBUILD_VERSION= +# 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:tv: 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 + ;; + v) DEBUILD_VERSION=$OPTARG;; + 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/.. +if [ "$DEBUILD_VERSION" != '' ]; then + TOP_DIR="$PWD" + TAG_VERSION=`echo $DEBUILD_VERSION | sed -e 's/.*://'` +else + TOP_DIR=`dirname $PWD` +fi +MODS_DIR=$TOP_DIR/modules +SRC_DIR=$MODS_DIR/usr/src +TAR_BALL=$SRC_DIR/$PACKAGE.tar.bz2 +DEB_VERSION=${TAG_VERSION#*:} +DEB=$TOP_DIR/$PACKAGE-source_${DEB_VERSION}_all.deb +LOG_FILE=$TOP_DIR/$PACKAGE-modules-build-$DEB_VERSION.log + +rm -f $LOG_FILE +dpkg -x $DEB $MODS_DIR + +if [ "$hdrs_sw" != '' ]; then + MOD_SRCDIR=$SRC_DIR TARBALL=$TAR_BALL $MODASS -u $TOP_DIR -t -i -f $hdrs_sw build $PACKAGE >>$LOG_FILE +fi +if [ "$srcs_sw" != '' ]; then + MOD_SRCDIR=$SRC_DIR TARBALL=$TAR_BALL $MODASS -u $TOP_DIR -t -i -f $srcs_sw build $PACKAGE >>$LOG_FILE +fi + +ls -l $TOP_DIR/$PACKAGE-modules-*_$DEB_VERSION+*.deb +echo "$me: Log file: $LOG_FILE" +pwd >&2 --- dahdi-linux-2.2.1+dfsg.orig/debian/control +++ dahdi-linux-2.2.1+dfsg/debian/control @@ -0,0 +1,51 @@ +Source: dahdi-linux +Section: comm +Priority: optional +Maintainer: Ubuntu MOTU Developers +XSBC-Original-Maintainer: Debian VoIP Team +Build-Depends: debhelper (>= 7), quilt, bzip2, asciidoc +Standards-Version: 3.8.3 +Homepage: http://www.asterisk.org/ +Vcs-Bzr: http://launchpad.net/~ubuntu-voip/ubuntu-voip/dahdi-linux/ + +Package: dahdi-linux +Section: comm +Architecture: all +# Xorcom packages depend on dahdi-firmware. Debian zaptel will probably +# just recommend it. +Depends: dahdi-dkms | dahdi-source, ${shlibs:Depends}, ${misc:Depends}, procps, fxload +Replaces: zaptel +Conflicts: zaptel +Description: DAHDI telephony interface - Linux userspace parts + DAHDI (formly Zaptel) is an interface for telephony devices used by e.g. + the Asterisk PBX software. The dahdi-* packages provide the kernel + DAHDI kernel modules and their required setup environment. + . + This package includes the minimal Linux-specific userspace parts: udev + configuration, xpp initialization scripts. + +Package: dahdi-source +Section: kernel +Architecture: all +Depends: ${misc:Depends}, debhelper (>> 4.0), module-assistant (>= 0.8.1), bzip2 +Recommends: dahdi-linux +Description: DAHDI telephony interface - source code for kernel driver + DAHDI (formly Zaptel) is an interface for telephony devices used by e.g. + the Asterisk PBX software. The dahdi-* packages provide the kernel + DAHDI kernel modules and their required setup environment, as well as + basic headers for building DAHDI modules and utilities. + . + It is normally used to build kernel modules package: m-a a-i dahdi-source + +Package: dahdi-dkms +Section: devel +Architecture: all +Depends: dkms, make, libc6-dev +Recommends: dahdi-linux +Description: DAHDI telephony interface (dkms kernel driver) + DAHDI (formly Zaptel) is an interface for telephony devices used by e.g. + the Asterisk PBX software. The dahdi-* packages provide the kernel + DAHDI kernel modules and their required setup environment. + . + The dkms package will automatically compile the driver for your current + kernel version. --- dahdi-linux-2.2.1+dfsg.orig/debian/compat +++ dahdi-linux-2.2.1+dfsg/debian/compat @@ -0,0 +1 @@ +7 --- dahdi-linux-2.2.1+dfsg.orig/debian/dahdi-source.dirs +++ dahdi-linux-2.2.1+dfsg/debian/dahdi-source.dirs @@ -0,0 +1 @@ +usr/src --- dahdi-linux-2.2.1+dfsg.orig/debian/dahdi-dkms.install.in +++ dahdi-linux-2.2.1+dfsg/debian/dahdi-dkms.install.in @@ -0,0 +1,5 @@ +Makefile usr/src/dahdi-#CVERSION# +build_tools usr/src/dahdi-#CVERSION# +drivers usr/src/dahdi-#CVERSION# +include usr/src/dahdi-#CVERSION# +debian/dkms.conf usr/src/dahdi-#CVERSION# --- dahdi-linux-2.2.1+dfsg.orig/debian/system.conf +++ dahdi-linux-2.2.1+dfsg/debian/system.conf @@ -0,0 +1,328 @@ +# +# DAHDI Configuration File +# +# This file is parsed by the DAHDI Configurator, dahdi_cfg +# +# Span Configuration +# ^^^^^^^^^^^^^^^^^^ +# First come the span definitions, in the format +# +# span=,,,,[,yellow] +# +# All T1/E1/BRI spans generate a clock signal on their transmit side. The +# parameter determines whether the clock signal from the far +# end of the T1/E1/BRI is used as the master source of clock timing. If it is, our +# own clock will synchronise to it. T1/E1/BRI connected directly or indirectly to +# a PSTN provider (telco) should generally be the first choice to sync to. The +# PSTN will never be a slave to you. You must be a slave to it. +# +# Choose 1 to make the equipment at the far end of the E1/T1/BRI link the preferred +# source of the master clock. Choose 2 to make it the second choice for the master +# clock, if the first choice port fails (the far end dies, a cable breaks, or +# whatever). Choose 3 to make a port the third choice, and so on. If you have, say, +# 2 ports connected to the PSTN, mark those as 1 and 2. The number used for each +# port should be different. +# +# If you choose 0, the port will never be used as a source of timing. This is +# appropriate when you know the far end should always be a slave to you. If +# the port is connected to a channel bank, for example, you should always be +# its master. Likewise, BRI TE ports should always be configured as a slave. +# Any number of ports can be marked as 0. +# +# Incorrect timing sync may cause clicks/noise in the audio, poor quality or failed +# faxes, unreliable modem operation, and is a general all round bad thing. +# +# The line build-out (or LBO) is an integer, from the following table: +# +# 0: 0 db (CSU) / 0-133 feet (DSX-1) +# 1: 133-266 feet (DSX-1) +# 2: 266-399 feet (DSX-1) +# 3: 399-533 feet (DSX-1) +# 4: 533-655 feet (DSX-1) +# 5: -7.5db (CSU) +# 6: -15db (CSU) +# 7: -22.5db (CSU) +# +# If the span is a BRI port the line build-out is not used and should be set +# to 0. +# +# framing:: +# one of 'd4' or 'esf' for T1 or 'cas' or 'ccs' for E1. Use 'ccs' for BRI. +# 'd4' could be referred to as 'sf' or 'superframe' +# +# coding:: +# one of 'ami' or 'b8zs' for T1 or 'ami' or 'hdb3' for E1. Use 'ami' for +# BRI. +# +# * For E1 there is the optional keyword 'crc4' to enable CRC4 checking. +# * If the keyword 'yellow' follows, yellow alarm is transmitted when no +# channels are open. +# +#span=1,0,0,esf,b8zs +#span=2,1,0,esf,b8zs +#span=3,0,0,ccs,hdb3,crc4 +# +# Dynamic Spans +# ^^^^^^^^^^^^^ +# Next come the dynamic span definitions, in the form: +# +# dynamic=,
,, +# +# Where is the name of the driver (e.g. eth),
is the +# driver specific address (like a MAC for eth), is the number +# of channels, and is a timing priority, like for a normal span. +# use "0" to not use this as a timing source, or prioritize them as +# primary, secondard, etc. Note that you MUST have a REAL DAHDI device +# if you are not using external timing. +# +# dynamic=eth,eth0/00:02:b3:35:43:9c,24,0 +# +# If a non-zero timing value is used, as above, only the last span should +# have the non-zero value. +# +# Channel Configuration +# ^^^^^^^^^^^^^^^^^^^^^ +# Next come the definitions for using the channels. The format is: +# = +# +# Valid devices are: +# +# e&m:: +# Channel(s) are signalled using E&M signalling (specific +# implementation, such as Immediate, Wink, or Feature Group D +# are handled by the userspace library). +# fxsls:: +# Channel(s) are signalled using FXS Loopstart protocol. +# fxsgs:: +# Channel(s) are signalled using FXS Groundstart protocol. +# fxsks:: +# Channel(s) are signalled using FXS Koolstart protocol. +# fxols:: +# Channel(s) are signalled using FXO Loopstart protocol. +# fxogs:: +# Channel(s) are signalled using FXO Groundstart protocol. +# fxoks:: +# Channel(s) are signalled using FXO Koolstart protocol. +# sf:: +# Channel(s) are signalled using in-band single freq tone. +# Syntax as follows: +# +# channel# => sf:,,,,, +# +# rxfreq is rx tone freq in Hz, rxbw is rx notch (and decode) +# bandwith in hz (typically 10.0), rxflag is either 'normal' or +# 'inverted', txfreq is tx tone freq in hz, txlevel is tx tone +# level in dbm, txflag is either 'normal' or 'inverted'. Set +# rxfreq or txfreq to 0.0 if that tone is not desired. +# +# unused:: +# No signalling is performed, each channel in the list remains idle +# clear:: +# Channel(s) are bundled into a single span. No conversion or +# signalling is performed, and raw data is available on the master. +# bchan:: +# Like 'clear' except all channels are treated individually and +# are not bundled. 'inclear' is an alias for this. +# rawhdlc:: +# The DAHDI driver performs HDLC encoding and decoding on the +# bundle, and the resulting data is communicated via the master +# device. +# dchan:: +# The DAHDI driver performs HDLC encoding and decoding on the +# bundle and also performs incoming and outgoing FCS insertion +# and verification. 'fcshdlc' is an alias for this. +# hardhdlc:: +# The hardware driver performs HDLC encoding and decoding on the +# bundle and also performs incoming and outgoing FCS insertion +# and verification. Is subject to limitations and support of underlying +# hardware. BRI spans serviced by the wcb4xxp driver must use hardhdlc +# channels for the signalling channels. +# nethdlc:: +# The DAHDI driver bundles the channels together into an +# hdlc network device, which in turn can be configured with +# sethdlc (available separately). In 2.6.x kernels you can also optionally +# pass the name for the network interface after the channel list. +# Syntax: +# +# nethdlc=[:interface name] +# Use original names, don't use the names which have been already registered +# in system e.g eth. +# +# dacs:: +# The DAHDI driver cross connects the channels starting at +# the channel number listed at the end, after a colon +# dacsrbs:: +# The DAHDI driver cross connects the channels starting at +# the channel number listed at the end, after a colon and +# also performs the DACSing of RBS bits +# +# The channel list is a comma-separated list of channels or ranges, for +# example: +# +# 1,3,5 (channels one, three, and five) +# 16-23, 29 (channels 16 through 23, as well as channel 29) +# +# So, some complete examples are: +# +# e&m=1-12 +# nethdlc=13-24 +# fxsls=25,26,27,28 +# fxols=29-32 +# +# An example of BRI port: +# +# span=1,1,0,ccs,ami +# bchan=1,2 +# hardhdlc=3 +# +# NOTE: When using BRI channels in asterisk, use the bri_cpe, bri_net, or +# bri_cpe_ptmp (for point to multipoint mode). libpri does not currently +# support point to multipoint when in NT mode. Otherwise, the bearer channel +# are configured identically to other DAHDI channels. +# +#fxoks=1-24 +#bchan=25-47 +#dchan=48 +#fxols=1-12 +#fxols=13-24 +#e&m=25-29 +#nethdlc=30-33 +#clear=44 +#clear=45 +#clear=46 +#clear=47 +#fcshdlc=48 +#dacs=1-24:48 +#dacsrbs=1-24:48 +# +# Tone Zone Data +# ^^^^^^^^^^^^^^ +# Finally, you can preload some tone zones, to prevent them from getting +# overwritten by other users (if you allow non-root users to open /dev/dahdi/* +# interfaces anyway. Also this means they won't have to be loaded at runtime. +# The format is "loadzone=" where the zone is a two letter country code. +# +# You may also specify a default zone with "defaultzone=" where zone +# is a two letter country code. +# +# An up-to-date list of the zones can be found in the file zonedata.c +# +loadzone = us +#loadzone = us-old +#loadzone=gr +#loadzone=it +#loadzone=fr +#loadzone=de +#loadzone=uk +#loadzone=fi +#loadzone=jp +#loadzone=sp +#loadzone=no +#loadzone=hu +#loadzone=lt +#loadzone=pl +defaultzone=us +# +# PCI Radio Interface +# ^^^^^^^^^^^^^^^^^^^ +# (see http://www.zapatatelephony.org/app_rpt.html) +# +# The PCI Radio Interface card interfaces up to 4 two-way radios (either +# a base/mobile radio or repeater system) to DAHDI channels. The driver +# may work either independent of an application, or with it, through +# the driver;s ioctl() interface. This file gives you access to specify +# load-time parameters for Radio channels, so that the driver may run +# by itself, and just act like a generic DAHDI radio interface. +# +# Unlike the rest of this file, you specify a block of parameters, and +# then the channel(s) to which they apply. CTCSS is specified as a frequency +# in tenths of hertz, for example 131.8 HZ is specified as 1318. DCS +# for receive is specified as the code directly, for example 223. DCS for +# transmit is specified as D and then the code, for example D223. +# +# The hardware supports a "community" CTCSS decoder system that has +# arbitrary transmit CTCSS or DCS codes associated with them, unlike +# traditional "community" systems that encode the same tone they decode. +# +# this example is a single tone DCS transmit and receive +# +# specify the transmit tone (in DCS mode this stays constant): +#tx=D371 +# +# specify the receive DCS code: +#dcsrx=223 +# +# this example is a "community" CTCSS (if you only want a single tone, then +# only specify 1 in the ctcss list) +# +# specify the default transmit tone (when not receiving): +#tx=1000 +# +# Specify the receive freq, the tag (use 0 if none), and the transmit code. +# The tag may be used by applications to determine classification of tones. +# The tones are to be specified in order of presedence, most important first. +# Currently, 15 tones may be specified.. +# +#ctcss=1318,1,1318 +#ctcss=1862,1,1862 +# +# The following parameters may be omitted if their default value is acceptible +# +# Set the receive debounce time in milliseconds: +#debouncetime=123 +# +# set the transmit quiet dropoff burst time in milliseconds: +#bursttime=234 +# +# set the COR level threshold (specified in tenths of millivolts) +# valid values are {3125,6250,9375,12500,15625,18750,21875,25000} +#corthresh=12500 +# +# Invert COR signal {y,n} +#invertcor=y +# Set the external tone mode; yes, no, internal {y,n,i} +#exttone=y +# +# Now apply the configuration to the specified channels: +# +# We are all done with our channel parameters, so now we specify what +# channels they apply to +#channels=1-4 +# +# Overiding PCM encoding +# ^^^^^^^^^^^^^^^^^^^^^^ +# Usually the channel driver sets the encoding of the PCM for the +# channel (mulaw / alaw. That is: g711u or g711a). However there are +# some cases where you would like to override that. 'mulaw' and 'alaw' +# set different such encoding. Use them for channels you have already +# defined with e.g. 'bchan' or 'fxoks'. +#mulaw=1-4 +#alaw=1-4 +# +# 'deflaw' is similar, but resets the encoding to the channel driver's +# default. It must be useful for something, I guess. +#mulaw=1-10 +#deflaw=5 +# +# Echo Cancellers +# ^^^^^^^^^^^^^^^ +# DAHDI uses modular echo cancellers that are configured per channel. The echo +# cancellers are compiled and installed as part of the dahdi-linux package. +# You can specify in this file the echo canceller to be used for each +# channel. The default behavior is for there to be NO echo canceller on any +# channel, so it is very important that you specify one here if you do +# not have hardware echo cancellers and need echo cancellation. +# +# Valid echo cancellers are: mg2, kb1, sec2, and sec. +# If compiled, 'hpec' is also a valid echo canceller. +# +# To configure the default echo cancellers, use the format: +# echocanceller=, +# +# Example: +# Configure channels 1 through 8 to use the mg2 echo canceller +#echocanceller=mg2,1-8 +# +# And change channel 2 to use the kb1 echo canceller. +#echocanceller=kb1,2 +# --- dahdi-linux-2.2.1+dfsg.orig/debian/README.Debian +++ dahdi-linux-2.2.1+dfsg/debian/README.Debian @@ -0,0 +1,110 @@ +Building kernel modules +----------------------- +First, install dahdi-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 dahdi + +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 dahdi + +You can also use the environment variable TARBALL to build the modules +with a custom dahdi.tar.bz2 tarball with some local modifications. + + +Device Files +------------ +DAHDI uses device files with major number 196 under /dev/dahdi/. In +Debian all of those files are 0660 and owned by the group dialout. They +should normally created by udev. + +If you can't generate device files with udev, use the script +make_static_nodes in the examples directory of dahdi-linux. Please also +let us know that generating static file is actually needed. + + + + +Removed VPMADT032 Support +------------------------- +VPMADT032 is a hardware echo canceller module. It is an optional addon +in various Digium cards. Currently in those serviced by wcte12xp and +wctdm24xxp. As of DAHDI 2.2 using it requires a binary-only object file +to be linked with DAHDI at build time. This object file is downloaded at +build time from downloads.digium.com . This is naturally not something we +can support. And thus we disabled building it. + +Unlike the Digium firmwares, this is not something that can be reverted at +run-time. + + +Bristuff +-------- +No longer needed and used. Note that all the BRI drivers now use +'hardhdlc' rather than 'dchan' in system.conf. dahdi_genconf should +generate a proper configuration for that. + +zaphfc included in this version is a newly-maintained version based on +vzaphfc. + +All of those modules should work with either bristuffed Asterisk (if it +is Asterisk >= 1.4.22) or with Asterisk 1.6.x (libpri >= 1.4.4). + + +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. + +The version of OSLEC included is currently one from the staging directory. +This is done temporarily until that driver will find its way into the +mainline tree and to help test it. + +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 dahdi-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 dahdi-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). --- dahdi-linux-2.2.1+dfsg.orig/debian/rules +++ dahdi-linux-2.2.1+dfsg/debian/rules @@ -0,0 +1,203 @@ +#!/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=0 +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:=dahdi +SKPG:=$(PREFIX)-source +PACKAGE:=$(PREFIX)-modules +PACKAGE_SRC:=$(PREFIX)-linux +# 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/') +DEB_BASE_VERSION:=$(shell echo $(DEBVERSION) | sed -e 's/^.*://' -e 's/-[0-9~.a-z]*$$//') +UPVERSION:=$(shell echo $(DEB_BASE_VERSION) | sed -e 's/[~+]dfsg\(~\|$$\)/\1/' -e 's/~\(rc\|beta\)/-\1/') + +# For DKMS. Use the full release (e.g.: 2.2.1+dfsg-1ubuntu1) +CVERSION := $(shell dpkg-parsechangelog | grep '^Version:' | cut -d' ' -f2 | cut -d\: -f2) + +UPFILENAME := $(PACKAGE_SRC)_$(UPVERSION).orig.tar.gz +FILENAME := $(PACKAGE_SRC)_$(DEB_BASE_VERSION).orig.tar.gz +URL := http://downloads.digium.com/pub/telephony/$(PACKAGE_SRC)/releases/$(PACKAGE_SRC)-$(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 := include/dahdi/version.h + +EXTRA_MODS=opvxa1200 + +ifneq (,$(filter-out powerpc m68k armeb mips,$(shell dpkg-architecture -qDEB_HOST_ARCH))) +EXTRA_MODS += wcopenpci +endif + +EXTRA_SUBDIRS += zaphfc ../staging/echo +EXTRA_MODS += dahdi_echocan_oslec + +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_prep + #cp -a $(CURDIR)/debian/generated/* . + 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 + + +build: patch build-stamp +build-stamp: + dh_testdir + + # For DKMS + sed -e "s|#CVERSION#|$(CVERSION)|" \ + debian/dkms.conf.in > debian/dkms.conf + sed -e "s|#CVERSION#|$(CVERSION)|" \ + debian/dahdi-dkms.install.in > debian/dahdi-dkms.install + + $(MAKE) docs + for mod in $(BRISTUFF_MODULES); do for i in c h; do \ + ln -s $$mod/$$mod.$$i drivers/dahdi/$$mod.$$i; \ + done; done + $(MAKE) $(GENERATED_SOURCES) + 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 -f $(GENERATED_SOURCES) + # * 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. + rm -f dahdi/include/version.h + [ ! -f Makefile ] || $(MAKE) dist-clean || true + dh_clean + +TARPARDIR=$(CURDIR)/debian/tmp +TARDIR=$(TARPARDIR)/modules/$(PREFIX) +install: build-stamp + dh_testdir + dh_testroot + dh_prep + dh_installdirs + + $(MAKE) install-include install-udev DESTDIR=$(CURDIR)/debian/$(SKPG) + dh_install + # driver source code + mkdir -p $(TARDIR)/debian/generated + cp Makefile $(TARDIR)/ + cp -p .version $(TARDIR)/ + for dir in build_tools firmware include drivers; do \ + if [ -d $$dir ]; then cp -r $$dir $(TARDIR); fi; \ + done + + # Packaging infrastructure + cp -r debian/rules debian/changelog debian/copyright\ + debian/control debian/compat \ + debian/control.modules.in \ + $(TARDIR)/debian/ + + tar cjf debian/$(SKPG)/usr/src/$(PREFIX).tar.bz2 \ + -C $(TARPARDIR) modules + dh_installudev --name=dahdi + mv $(CURDIR)/debian/dahdi-linux/etc/udev/rules.d/xpp.rules \ + $(CURDIR)/debian/dahdi-linux/lib/udev/rules.d/ + +# Build architecture-independent files here. +binary: install + dh_testdir + dh_testroot + + dh_installdocs + dh_installchangelogs -i ChangeLog + dh_installexamples -i + dh_perl + dh_link + dh_compress + dh_fixperms + dh_installdeb + dh_gencontrol + dh_md5sums + dh_builddeb + +print-version: + @@echo "Debian version: $(DEBVERSION)" + @@echo "Upstream version: $(UPVERSION)" + +# For the record: updating openvox drivers: +# wget 'http://www.openvox.cn/viewvc/dahdi/trunk/opvxa1200.c?view=co' \ +# | sed -e '\r$//' >drivers/dahdi/opvxa1200.c +# +# Updating the zaphfc drivers: +# rm -rf old new +# mkdir -p old new/drivers/dahdi +# svn export http://zaphfc.googlecode.com/svn/branches/2.2/zaphfc/ new/drivers/dahdi/zaphfc +# diff -Npur --no-timestamps old new >debian/patches/zaphfc +# rm -rf old new + +TARBALL_DIR=../tarballs/$(PACKAGE_SRC)-$(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)/$(PACKAGE_SRC)-$(UPVERSION)/drivers/dahdi/xpp/firmwares/*.hex + @@cd $(TARBALL_DIR) ; \ + tar cfz ../$(FILENAME) * + @@echo Cleaning up... + @@$(RM) -rf $(TARBALL_DIR)/ + @@$(RM) -f ../tarballs/$(UPFILENAME) + +.PHONY: build clean binary install configure patch unpatch --- dahdi-linux-2.2.1+dfsg.orig/debian/TODO.Debian +++ dahdi-linux-2.2.1+dfsg/debian/TODO.Debian @@ -0,0 +1,5 @@ +* Replace modules in drivers/dahdi with patches: + - vzaphfc: not actually used. Leave it for current release (2.2.0.2-1) + until we're sur zaphfc actually works. Then remove it (this package + is sadyl its upstream). + - Convert the other two drivers to patches. --- dahdi-linux-2.2.1+dfsg.orig/debian/dahdi-linux.examples +++ dahdi-linux-2.2.1+dfsg/debian/dahdi-linux.examples @@ -0,0 +1,2 @@ +debian/make_static_nodes +drivers/dahdi/xpp/xpp.conf --- dahdi-linux-2.2.1+dfsg.orig/debian/dahdi-linux.doc-base.readme +++ dahdi-linux-2.2.1+dfsg/debian/dahdi-linux.doc-base.readme @@ -0,0 +1,13 @@ +Document: dahdi-linux +Title: DAHDI Telephony Interface Driver +Author: Tzafrir Cohen +Abstract: Basic documentation of the DAHDI telephony interface +Section: Network/Communication + +Format: HTML +Index: /usr/share/doc/dahdi-linux/README.html +Files: /usr/share/doc/dahdi-linux/README.html + +Format: text +Files: /usr/share/doc/dahdi-linux/README.gz + --- dahdi-linux-2.2.1+dfsg.orig/debian/dkms.conf +++ dahdi-linux-2.2.1+dfsg/debian/dkms.conf @@ -0,0 +1,151 @@ +PACKAGE_VERSION="2.2.1+dfsg-1ubuntu2" + +# Items below here should not have to change with each driver version +PACKAGE_NAME="dahdi" +MAKE[0]="make modules KERNVER=$kernelver MODULES_EXTRA='opvxa1200 wcopenpci dahdi_echocan_oslec' SUBDIRS_EXTRA='../staging/echo zaphfc/' ;make;make firmware-loaders;for i in 32 64;do echo : > drivers/dahdi/vpmadt032_loader/.vpmadt032_x86_$i.o.cmd;done;make" +CLEAN="make KERNVER=$kernelver clean" +#MAKE[0]="make modules KERNVER=$kernelver DOWNLOAD=echo" +#CLEAN="make KERNVER=$kernelver clean DOWNLOAD=echo" + +BUILT_MODULE_NAME[0]="dahdi_dummy" +BUILT_MODULE_LOCATION[0]="drivers/dahdi/" +DEST_MODULE_LOCATION[0]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[1]="dahdi_dynamic_eth" +BUILT_MODULE_LOCATION[1]="drivers/dahdi/" +DEST_MODULE_LOCATION[1]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[2]="dahdi_dynamic" +BUILT_MODULE_LOCATION[2]="drivers/dahdi/" +DEST_MODULE_LOCATION[2]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[3]="dahdi_dynamic_loc" +BUILT_MODULE_LOCATION[3]="drivers/dahdi/" +DEST_MODULE_LOCATION[3]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[4]="dahdi_echocan_jpah" +BUILT_MODULE_LOCATION[4]="drivers/dahdi/" +DEST_MODULE_LOCATION[4]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[5]="dahdi_echocan_kb1" +BUILT_MODULE_LOCATION[5]="drivers/dahdi/" +DEST_MODULE_LOCATION[5]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[6]="dahdi_echocan_mg2" +BUILT_MODULE_LOCATION[6]="drivers/dahdi/" +DEST_MODULE_LOCATION[6]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[7]="dahdi_echocan_sec2" +BUILT_MODULE_LOCATION[7]="drivers/dahdi/" +DEST_MODULE_LOCATION[7]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[8]="dahdi_echocan_sec" +BUILT_MODULE_LOCATION[8]="drivers/dahdi/" +DEST_MODULE_LOCATION[8]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[9]="dahdi" +BUILT_MODULE_LOCATION[9]="drivers/dahdi/" +DEST_MODULE_LOCATION[9]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[10]="dahdi_transcode" +BUILT_MODULE_LOCATION[10]="drivers/dahdi/" +DEST_MODULE_LOCATION[10]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[11]="pciradio" +BUILT_MODULE_LOCATION[11]="drivers/dahdi/" +DEST_MODULE_LOCATION[11]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[12]="tor2" +BUILT_MODULE_LOCATION[12]="drivers/dahdi/" +DEST_MODULE_LOCATION[12]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[13]="wcb4xxp" +BUILT_MODULE_LOCATION[13]="drivers/dahdi/wcb4xxp/" +DEST_MODULE_LOCATION[13]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[14]="wcfxo" +BUILT_MODULE_LOCATION[14]="drivers/dahdi/" +DEST_MODULE_LOCATION[14]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[15]="wct1xxp" +BUILT_MODULE_LOCATION[15]="drivers/dahdi/" +DEST_MODULE_LOCATION[15]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[16]="wct4xxp" +BUILT_MODULE_LOCATION[16]="drivers/dahdi/wct4xxp/" +DEST_MODULE_LOCATION[16]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[17]="wctc4xxp" +BUILT_MODULE_LOCATION[17]="drivers/dahdi/wctc4xxp/" +DEST_MODULE_LOCATION[17]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[18]="wctdm" +BUILT_MODULE_LOCATION[18]="drivers/dahdi/" +DEST_MODULE_LOCATION[18]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[19]="wctdm24xxp" +BUILT_MODULE_LOCATION[19]="drivers/dahdi/wctdm24xxp/" +DEST_MODULE_LOCATION[19]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[20]="wcte11xp" +BUILT_MODULE_LOCATION[20]="drivers/dahdi/" +DEST_MODULE_LOCATION[20]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[21]="wcte12xp" +BUILT_MODULE_LOCATION[21]="drivers/dahdi/wcte12xp/" +DEST_MODULE_LOCATION[21]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[22]="xpd_fxo" +BUILT_MODULE_LOCATION[22]="drivers/dahdi/xpp/" +DEST_MODULE_LOCATION[22]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[23]="xpd_fxs" +BUILT_MODULE_LOCATION[23]="drivers/dahdi/xpp/" +DEST_MODULE_LOCATION[23]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[24]="xpd_pri" +BUILT_MODULE_LOCATION[24]="drivers/dahdi/xpp/" +DEST_MODULE_LOCATION[24]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[25]="xpp" +BUILT_MODULE_LOCATION[25]="drivers/dahdi/xpp/" +DEST_MODULE_LOCATION[25]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[26]="xpp_usb" +BUILT_MODULE_LOCATION[26]="drivers/dahdi/xpp/" +DEST_MODULE_LOCATION[26]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[27]="xpd_bri" +BUILT_MODULE_LOCATION[27]="drivers/dahdi/xpp/" +DEST_MODULE_LOCATION[27]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[28]="dahdi_voicebus" +BUILT_MODULE_LOCATION[28]="drivers/dahdi/voicebus/" +DEST_MODULE_LOCATION[28]="/kernel/drivers/telephony/dahdi/voicebus" + +#Added by Debian / Ubuntu +BUILT_MODULE_NAME[29]="opvxa1200" +BUILT_MODULE_LOCATION[29]="drivers/dahdi/" +DEST_MODULE_LOCATION[29]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[30]="wcopenpci" +BUILT_MODULE_LOCATION[30]="drivers/dahdi/" +DEST_MODULE_LOCATION[30]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[31]="dahdi_echocan_oslec" +BUILT_MODULE_LOCATION[31]="drivers/dahdi/" +DEST_MODULE_LOCATION[31]="/kernel/drivers/telephony/dahdi" + +BUILT_MODULE_NAME[32]="echo" +BUILT_MODULE_LOCATION[32]="drivers/staging/echo/" +DEST_MODULE_LOCATION[32]="/kernel/drivers/telephony/staging/echo" + +BUILT_MODULE_NAME[33]="zaphfc" +BUILT_MODULE_LOCATION[33]="drivers/dahdi/zaphfc" +DEST_MODULE_LOCATION[33]="/kernel/drivers/telephony/dahdi/zaphfc" + +BUILT_MODULE_NAME[34]="dahdi_vpmadt032_loader" +BUILT_MODULE_LOCATION[34]="drivers/dahdi/" +DEST_MODULE_LOCATION[34]="/kernel/drivers/telephony/dahdi" + +AUTOINSTALL=yes --- dahdi-linux-2.2.1+dfsg.orig/debian/watch +++ dahdi-linux-2.2.1+dfsg/debian/watch @@ -0,0 +1,4 @@ +version=3 +opts=dversionmangle=s/\~dfsg//;s/\~rc/-rc/; \ + http://downloads.asterisk.org/pub/telephony/dahdi-linux/releases/dahdi-linux-(.*)\.tar\.gz\ + debian svn-upgrade --- dahdi-linux-2.2.1+dfsg.orig/debian/dahdi-dkms.install +++ dahdi-linux-2.2.1+dfsg/debian/dahdi-dkms.install @@ -0,0 +1,5 @@ +Makefile usr/src/dahdi-2.2.1+dfsg-1ubuntu2 +build_tools usr/src/dahdi-2.2.1+dfsg-1ubuntu2 +drivers usr/src/dahdi-2.2.1+dfsg-1ubuntu2 +include usr/src/dahdi-2.2.1+dfsg-1ubuntu2 +debian/dkms.conf usr/src/dahdi-2.2.1+dfsg-1ubuntu2 --- dahdi-linux-2.2.1+dfsg.orig/debian/lintian/dahdi-source +++ dahdi-linux-2.2.1+dfsg/debian/lintian/dahdi-source @@ -0,0 +1,2 @@ +# Complete phrase: m-a a-i dahdi +dahdi-source: description-contains-duplicated-word a a --- dahdi-linux-2.2.1+dfsg.orig/debian/backports/lenny +++ dahdi-linux-2.2.1+dfsg/debian/backports/lenny @@ -0,0 +1,8 @@ +#!/bin/sh + +# Lenny backporting script + +# udev rules go to /etc/udev in Lenny +sed -i -e '\|/udev/rules/|d' debian/rules +sed -i -e '/xpp.rules/s/lib/etc/' debian/dahdi-linux.install + --- dahdi-linux-2.2.1+dfsg.orig/debian/backports/etch +++ dahdi-linux-2.2.1+dfsg/debian/backports/etch @@ -0,0 +1,5 @@ +#!/bin/sh + +# Etch has an older debhelper: +sed -i -e '/^Build-Depends:/s/\(debhelper\) ([^)]*),/\1,/' debian/control +echo 5 >debian/compat --- dahdi-linux-2.2.1+dfsg.orig/debian/patches/dahdi_linux_extra +++ dahdi-linux-2.2.1+dfsg/debian/patches/dahdi_linux_extra @@ -0,0 +1,9178 @@ +Subject: dahdi-extra: out-of-tree DAHDI drivers +Origin: http://git.tzafrir.org.il/?p=dahdi-extra.git +Forwarded: No +Last-Update: 2010-01-21 + +This patch includes a number of out-of-tree DAHDI drivers from the +dahdi-extra repository. They are all out-of-tree and are highly likely + not to be included in DAHDI-linux in the forseeable future. + +Git-Commit: 88d86f570f2460fb1adfb645f527699d615ef125 +--- + +diff -urN dahdi-svn-orig/drivers/dahdi/Kbuild dahdi-svn-new/drivers/dahdi/Kbuild +--- dahdi-svn-orig/drivers/dahdi/Kbuild 2009-06-13 01:30:02.000000000 +0300 ++++ dahdi-svn-new/drivers/dahdi/Kbuild 2010-01-21 13:35:37.000000000 +0200 +@@ -1,10 +1,16 @@ + obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI) += dahdi.o ++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OPVXA1200) += opvxa1200.o + obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_DUMMY) += dahdi_dummy.o ++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCOPENPCI) += wcopenpci.o + obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_DYNAMIC) += dahdi_dynamic.o ++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ZAPHFC) += zaphfc/ + obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_DYNAMIC_LOC) += dahdi_dynamic_loc.o ++obj-$(DAHDI_BUILD_ALL)$(CONFIG_ECHO) += ../staging/echo/ + obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_DYNAMIC_ETH) += dahdi_dynamic_eth.o ++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_OSLEC) += dahdi_echocan_oslec.o + obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_TRANSCODE) += dahdi_transcode.o + ++ + obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCT4XXP) += wct4xxp/ + obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTC4XXP) += wctc4xxp/ + obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTDM24XXP) += wctdm24xxp/ +diff -urN dahdi-svn-orig/drivers/dahdi/Kconfig dahdi-svn-new/drivers/dahdi/Kconfig +--- dahdi-svn-orig/drivers/dahdi/Kconfig 2009-04-29 20:48:39.000000000 +0300 ++++ dahdi-svn-new/drivers/dahdi/Kconfig 2010-01-21 13:35:37.000000000 +0200 +@@ -279,3 +279,54 @@ + If unsure, say Y. + + source "drivers/dahdi/xpp/Kconfig" ++ ++ ++config DAHDI_OPVXA1200 ++ tristate "OpenVox A1200P FXS/FXO Interface" ++ depends on DAHDI && PCI ++ default DAHDI ++ ---help--- ++ This driver provides support for the OpenVox A1200P FXS/FXO Interface. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called opvxa1200. ++ ++ If unsure, say Y. ++ ++config DAHDI_WCOPENPCI ++ tristate "Voicetronix OpenPCI Interface DAHDI driver" ++ depends on DAHDI && PCI ++ default DAHDI ++ ---help--- ++ This driver provides support for the Voicetronix OpenPCI Interface. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wcopenpci. ++ ++ If unsure, say Y. ++ ++config DAHDI_ZAPHFC ++ tristate "HFC-S DAHDI Driver" ++ depends on DAHDI && PCI ++ default DAHDI ++ ---help--- ++ This driver provides DAHDI support for various HFC-S single-port ++ ISDN (BRI) cards. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called zaphfc. ++ ++ If unsure, say Y. ++ ++config ECHO ++ tristate "Line Echo Canceller support" ++ default DAHDI ++ --help-- ++ This driver provides line echo cancelling support for mISDN and ++ DAHDI drivers. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called echo. ++ ++ If unsure, say Y. ++ +diff -urN dahdi-svn-orig/drivers/dahdi/opvxa1200.c dahdi-svn-new/drivers/dahdi/opvxa1200.c +--- dahdi-svn-orig/drivers/dahdi/opvxa1200.c 1970-01-01 02:00:00.000000000 +0200 ++++ dahdi-svn-new/drivers/dahdi/opvxa1200.c 2010-01-21 13:35:37.000000000 +0200 +@@ -0,0 +1,3018 @@ ++/* ++ * OpenVox A1200P FXS/FXO Interface Driver for DAHDI 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 ++ * Rev 0.13 patched to remove the warning during compile under kernel 2.6.22 ++ * Rev 0.14 patched to remove the bug for ZAP_IRQ_SHARED , 3/9/2007 ++ * Rev 0.15 patched to support new pci ID 0X9532 by james.zhu, 23/10/2007 ++ * Rev 0.16 support new pci id 0x9559 by Miao Lin 21/3/2008 ++ * Rev 0.17 ++ * patched a few bugs, ++ * add hwgain support. ++ * fixed A800P version check ++ * Rev 1.4.9.2 ++ * Only generate 8 channels for A800P ++ * Version number synced to zaptel distribution. ++ * Rev 1.4.9.2.a ++ * Fixed freeregion. ++ * ++ * Rev 1.4.9.2.b ++ * Add cid before first ring support. ++ * New Paremeters: ++ * cidbeforering : set to 1 will cause the card enable cidbeforering function. default 0 ++ * cidbuflen : length of cid buffer, in msec, default 3000 msec. ++ * cidtimeout : time out of a ring, default 6000msec ++ * User must set cidstart=polarity in zapata.conf to use with this feature ++ * cidsignalling = signalling format send before 1st ring. most likely dtmf. ++ * ++ * Rev 1.4.9.2.c ++ * add driver parameter cidtimeout. ++ * ++ * Rev 1.4.9.2.d ++ * add debug stuff to test fxs power alarm ++ * ++ * Rev 1.4.11 ++ * Support enhanced full scale tx/rx for FXO required by europe standard (Register 30, acim) (module parm fxofullscale) ++ * ++ * Rev 1.4.12 2008/10/17 ++ * Fixed bug cause FXS module report fake power alarm. ++ * Power alarm debug stuff removed. ++ * ++ * Rev 2.0 DAHDI 2008/10/17 ++ * ++ * Rev 2.0.1 add new pci id 0x9599 ++ * Re 2.0.2 12/01/2009 ++ add fixedtimepolarity: set time(ms) when send polarity after 1st ring happen. ++ * Sometimes the dtmf cid is sent just after first ring off, and the system do not have ++ * enough time to start detect 1st dtmf. ++ * 0 means send polarity at the end of 1st ring. ++ * x means send ploarity after x ms of 1st ring begin. ++ * ++ * Rev 2.0.3 12/01/2009 ++ * Add touch_softlockup_watchdog() in wctdm_hardware_init, to avoid cpu softlockup system message for FXS. ++ * ++ * ++ * Rev 1.4.12.4 17/04/2009 James.zhu ++ * Changed wctdm_voicedaa_check_hook() to detect FXO battery and solved the problem with dial(dahdi/go/XXXXXXXXXX) ++ * add alarm detection for FXO ++ * ++ * Rev 1.4.12.5 01/10/2009 james.zhu ++ * Add jiffies for 5 second in wctdm_hardware_init ++ * ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "proslic.h" ++ ++/* MiaoLin debug start */ ++#include ++#include /* get_fs(), set_fs(), KERNEL_DS */ ++#include /* fput() */ ++/* MiaoLin debug end */ ++ ++ ++/* ++ * 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}, ++}; ++ ++ ++#include ++#include ++ ++#include "fxo_modes.h" ++ ++#define NUM_FXO_REGS 60 ++ ++#define WC_MAX_IFACES 128 ++ ++#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 ++#define DEFAULT_RING_DEBOUNCE 64 /* Ringer Debounce (64 ms) */ ++#define POLARITY_DEBOUNCE 64 /* Polarity debounce (64 ms) */ ++#define OHT_TIMER 6000 /* How long after RING to retain OHT */ ++ ++#define FLAG_3215 (1 << 0) ++#define FLAG_A800 (1 << 7) ++ ++#define MAX_NUM_CARDS 12 ++#define NUM_CARDS 12 ++#define NUM_FLAG 4 /* number of flag channels. */ ++ ++ ++enum cid_hook_state { ++ CID_STATE_IDLE = 0, ++ CID_STATE_RING_ON, ++ CID_STATE_RING_OFF, ++ CID_STATE_WAIT_RING_FINISH ++}; ++ ++/* if you want to record the last 8 sec voice before the driver unload, uncomment it and rebuild. */ ++/* #define TEST_LOG_INCOME_VOICE */ ++#define voc_buffer_size (8000*8) ++ ++ ++#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, ++}; ++ ++enum battery_state { ++ BATTERY_UNKNOWN = 0, ++ BATTERY_PRESENT, ++ BATTERY_LOST, ++}; ++struct wctdm { ++ struct pci_dev *dev; ++ char *variety; ++ struct dahdi_span span; ++ unsigned char ios; ++ int usecount; ++ unsigned int intcount; ++ int dead; ++ int pos; ++ int flags[MAX_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 fxo { ++#ifdef AUDIO_RINGCHECK ++ unsigned int pegtimer; ++ int pegcount; ++ int peg; ++ int ring; ++#else ++ int wasringing; ++ int lastrdtx; ++#endif ++ int ringdebounce; ++ int offhook; ++ unsigned int battdebounce; ++ unsigned int battalarm; ++ enum battery_state battery; ++ int lastpol; ++ int polarity; ++ int polaritydebounce; ++ } fxo; ++ struct fxs { ++ 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[MAX_NUM_CARDS]; ++ ++ /* Receive hook state and debouncing */ ++ int modtype[MAX_NUM_CARDS]; ++ unsigned char reg0shadow[MAX_NUM_CARDS]; ++ unsigned char reg1shadow[MAX_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 dahdi_chan chans[MAX_NUM_CARDS];*/ ++ struct dahdi_chan _chans[NUM_CARDS]; ++ struct dahdi_chan *chans[NUM_CARDS]; ++ ++ ++#ifdef TEST_LOG_INCOME_VOICE ++ char * voc_buf[MAX_NUM_CARDS + NUM_FLAG]; ++ int voc_ptr[MAX_NUM_CARDS + NUM_FLAG]; ++#endif ++ int lastchan; ++ unsigned short ledstate; ++ unsigned char fwversion; ++ int max_cards; ++ char *card_name; ++ ++ char *cid_history_buf[MAX_NUM_CARDS]; ++ int cid_history_ptr[MAX_NUM_CARDS]; ++ int cid_history_clone_cnt[MAX_NUM_CARDS]; ++ enum cid_hook_state cid_state[MAX_NUM_CARDS]; ++ int cid_ring_on_time[MAX_NUM_CARDS]; ++}; ++ ++static char* A1200P_Name = "A1200P"; ++static char* A800P_Name = "A800P"; ++ ++struct wctdm_desc { ++ char *name; ++ int flags; ++}; ++ ++static struct wctdm_desc wctdme = { "OpenVox A1200P/A800P", 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 unsigned int battdebounce; ++static unsigned int battalarm; ++static unsigned int battthresh; ++static int ringdebounce = DEFAULT_RING_DEBOUNCE; ++static int fwringdetect = 0; ++static int debug = 0; ++static int robust = 0; ++static int timingonly = 0; ++static int lowpower = 0; ++static int boostringer = 0; ++static int fastringer = 0; ++static int _opermode = 0; ++static char *opermode = "FCC"; ++static int fxshonormode = 0; ++static int alawoverride = 0; ++static int fastpickup = 0; ++static int fxotxgain = 0; ++static int fxorxgain = 0; ++static int fxstxgain = 0; ++static int fxsrxgain = 0; ++/* special h/w control command */ ++static int spibyhw = 1; ++static int usememio = 1; ++static int cidbeforering = 0; ++static int cidbuflen = 3000; /* in msec, default 3000 */ ++static int cidtimeout = 6*1000; /* in msec, default 6000 */ ++static int fxofullscale = 0; /* fxo full scale tx/rx, register 30, acim */ ++static int fixedtimepolarity=0; /* time delay in ms when send polarity after rise edge of 1st ring.*/ ++ ++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=MAX_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 & /*0x01*/ 0x04) ++ /* Write is at interrupt address. Start writing from normal offset */ ++ txbuf = wc->writechunk; ++ else ++ txbuf = wc->writechunk + DAHDI_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG); ++ ++ /* Calculate Transmission */ ++ dahdi_transmit(&wc->span); ++ ++ if(wc->lastchan == -1) // not in sync. ++ return; ++ ++ chan_offset = (wc->lastchan*4 + 4 ) % (MAX_NUM_CARDS+NUM_FLAG); ++ ++ for (y=0;ymax_cards/*MAX_NUM_CARDS*/) ++ txbuf[pos] = wc->chans[x]->writechunk[y]; ++ else ++ txbuf[pos] = 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 += DAHDI_CHUNKSIZE; ++ for (x=0;xchans[card].readchunk[x], (&(wc->chans[card]))); ++ if ((sample > 10000) && (wc->mod[card].fxo.peg != 1)) { ++ if (debug > 1) printk(KERN_DEBUG "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(KERN_DEBUG "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(KERN_DEBUG "RING on %d/%d!\n", wc->span.spanno, card + 1); ++ if (!wc->mod[card].fxo.offhook) ++ dahdi_hooksig(&wc->chans[card], DAHDI_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(KERN_DEBUG "NO RING on %d/%d!\n", wc->span.spanno, card + 1); ++ dahdi_hooksig(&wc->chans[card], DAHDI_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 + DAHDI_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG); ++ ++ for(x=0; x<4; x++) ++ if( *(int*)(rxbuf+x*4) == WC_SYNCFLAG) ++ 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 ) % (MAX_NUM_CARDS+NUM_FLAG); ++ ++ for (x=0;xmax_cards/*MAX_NUM_CARDS*/;y++) { ++ if (wc->cardflag & (1 << y)) ++ wc->chans[y]->readchunk[x] = rxbuf[(MAX_NUM_CARDS+NUM_FLAG) * x + ((y + chan_offset ) & 0x0f)]; ++#ifdef TEST_LOG_INCOME_VOICE ++ wc->voc_buf[y][wc->voc_ptr[y]] = rxbuf[(MAX_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 ++ } ++ ++ if(cidbeforering) ++ { ++ for(x=0; xmax_cards; x++) ++ { ++ if (wc->modtype[wc->chans[x]->chanpos - 1] == MOD_TYPE_FXO) ++ if(wc->mod[wc->chans[x]->chanpos - 1].fxo.offhook == 0) ++ { ++ /*unsigned int *p_readchunk, *p_cid_history; ++ ++ p_readchunk = (unsigned int*)wc->chans[x].readchunk; ++ p_cid_history = (unsigned int*)(wc->cid_history_buf[x] + wc->cid_history_ptr[x]);*/ ++ ++ if(wc->cid_state[x] == CID_STATE_IDLE) /* we need copy data to the cid voice buffer */ ++ { ++ memcpy(wc->cid_history_buf[x] + wc->cid_history_ptr[x], wc->chans[x]->readchunk, DAHDI_CHUNKSIZE); ++ wc->cid_history_ptr[x] = (wc->cid_history_ptr[x] + DAHDI_CHUNKSIZE)%(cidbuflen * DAHDI_MAX_CHUNKSIZE); ++ } ++ else if (wc->cid_state[x] == CID_STATE_RING_ON) ++ wc->cid_history_clone_cnt[x] = cidbuflen; ++ else if (wc->cid_state[x] == CID_STATE_RING_OFF) ++ { ++ if(wc->cid_history_clone_cnt[x]) ++ { ++ memcpy(wc->chans[x]->readchunk, wc->cid_history_buf[x] + wc->cid_history_ptr[x], DAHDI_MAX_CHUNKSIZE); ++ wc->cid_history_clone_cnt[x]--; ++ wc->cid_history_ptr[x] = (wc->cid_history_ptr[x] + DAHDI_MAX_CHUNKSIZE)%(cidbuflen * DAHDI_MAX_CHUNKSIZE); ++ } ++ else ++ { ++ wc->cid_state[x] = CID_STATE_WAIT_RING_FINISH; ++ wc->cid_history_clone_cnt[x] = cidtimeout; /* wait 6 sec, if no ring, return to idle */ ++ } ++ } ++ else if(wc->cid_state[x] == CID_STATE_WAIT_RING_FINISH) ++ { ++ if(wc->cid_history_clone_cnt[x] > 0) ++ wc->cid_history_clone_cnt[x]--; ++ else ++ { ++ wc->cid_state[x] = CID_STATE_IDLE; ++ wc->cid_history_ptr[x] = 0; ++ wc->cid_history_clone_cnt[x] = 0; ++ } ++ } ++ } ++ } ++ } ++ ++#ifdef AUDIO_RINGCHECK ++ for (x=0;xmax_cards;x++) ++ ring_check(wc, x); ++#endif ++ /* XXX We're wasting 8 taps. We should get closer :( */ ++ for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { ++ if (wc->cardflag & (1 << x)) ++ dahdi_ec_chunk(wc->chans[x], wc->chans[x]->readchunk, wc->chans[x]->writechunk); ++ } ++ dahdi_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(KERN_NOTICE " ##### 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(KERN_NOTICE "%s", 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(KERN_NOTICE "!!!!!!! %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(KERN_DEBUG "Init Indirect Registers completed successfully.\n"); ++ } else { ++ printk(KERN_NOTICE " !!!!! 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)) // read real state from register By wx ++ res=wctdm_getreg(wc, card, 64); ++ ++ if (!res && (res != wc->mod[card].fxs.lasttxhook)) { ++ res = wctdm_getreg(wc, card, 8); ++ if (res) { ++ printk(KERN_NOTICE "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(KERN_NOTICE "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(KERN_NOTICE "Too many power alarms on card %d, NOT resetting!\n", card + 1); ++ } ++ } ++ } ++} ++static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) ++{ ++#define MS_PER_CHECK_HOOK 16 ++ ++#ifndef AUDIO_RINGCHECK ++ unsigned char res; ++#endif ++ signed char b; ++ int errors = 0; ++ struct fxo *fxo = &wc->mod[card].fxo; ++ ++ /* 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(KERN_DEBUG "Error (%02x) on card %d!\n", b, card + 1); ++ errors++; ++ } ++ b &= 0x9b; ++ if (fxo->offhook) { ++ if (b != 0x9) ++ wctdm_setreg(wc, card, 5, 0x9); ++ } else { ++ if (b != 0x8) ++ wctdm_setreg(wc, card, 5, 0x8); ++ } ++ if (errors) ++ return; ++ if (!fxo->offhook) { ++ if(fixedtimepolarity) { ++ if ( wc->cid_state[card] == CID_STATE_RING_ON && wc->cid_ring_on_time[card]>0) ++ { ++ if(wc->cid_ring_on_time[card]>=fixedtimepolarity ) ++ { ++ dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY); ++ wc->cid_ring_on_time[card] = -1; /* the polarity already sent */ ++ } ++ else ++ wc->cid_ring_on_time[card] += 16; ++ } ++} ++ if (fwringdetect) { ++ res = wc->reg0shadow[card] & 0x60; ++ if (fxo->ringdebounce) { ++ --fxo->ringdebounce; ++ if (res && (res != fxo->lastrdtx) && ++ (fxo->battery == BATTERY_PRESENT)) { ++ if (!fxo->wasringing) { ++ fxo->wasringing = 1; ++ if (debug) ++ printk(KERN_DEBUG "RING on %d/%d!\n", wc->span.spanno, card + 1); ++ if(cidbeforering) ++ { ++ if(wc->cid_state[card] == CID_STATE_IDLE) ++ { ++ wc->cid_state[card] = CID_STATE_RING_ON; ++ wc->cid_ring_on_time[card] = 16; /* check every 16ms */ ++ } ++ else ++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); ++ } ++ else ++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); ++ } ++ fxo->lastrdtx = res; ++ fxo->ringdebounce = 10; ++ } else if (!res) { ++ if ((fxo->ringdebounce == 0) && fxo->wasringing) { ++ fxo->wasringing = 0; ++ if (debug) ++ printk(KERN_DEBUG "NO RING on %d/%d!\n", wc->span.spanno, card + 1); ++ if(cidbeforering) ++ { ++ if(wc->cid_state[card] == CID_STATE_RING_ON) ++ { ++ if(fixedtimepolarity==0) ++ dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY); ++ wc->cid_state[card] = CID_STATE_RING_OFF; ++ } ++ else ++ { ++ if(wc->cid_state[card] == CID_STATE_WAIT_RING_FINISH) ++ wc->cid_history_clone_cnt[card] = cidtimeout; ++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); ++ } ++ } ++ else ++ ++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); ++ } ++ } ++ } else if (res && (fxo->battery == BATTERY_PRESENT)) { ++ fxo->lastrdtx = res; ++ fxo->ringdebounce = 10; ++ } ++ } else { ++ res = wc->reg0shadow[card]; ++ if ((res & 0x60) && (fxo->battery == BATTERY_PRESENT)) { ++ fxo->ringdebounce += (DAHDI_CHUNKSIZE * 16); ++ if (fxo->ringdebounce >= DAHDI_CHUNKSIZE * ringdebounce) { ++ if (!fxo->wasringing) { ++ fxo->wasringing = 1; ++ if(cidbeforering) ++ { ++ if(wc->cid_state[card] == CID_STATE_IDLE) ++ { ++ wc->cid_state[card] = CID_STATE_RING_ON; ++ wc->cid_ring_on_time[card] = 16; /* check every 16ms */ ++ } ++ else ++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); ++ } ++ else ++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); ++ if (debug) ++ printk(KERN_DEBUG "RING on %d/%d!\n", wc->span.spanno, card + 1); ++ } ++ fxo->ringdebounce = DAHDI_CHUNKSIZE * ringdebounce; ++ } ++ } else { ++ fxo->ringdebounce -= DAHDI_CHUNKSIZE * 4; ++ if (fxo->ringdebounce <= 0) { ++ if (fxo->wasringing) { ++ fxo->wasringing = 0; ++ if(cidbeforering) ++ { ++ if(wc->cid_state[card] == CID_STATE_RING_ON) ++ { ++ if(fixedtimepolarity==0) ++ dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY); ++ wc->cid_state[card] = CID_STATE_RING_OFF; ++ } ++ else ++ { ++ if(wc->cid_state[card] == CID_STATE_WAIT_RING_FINISH) ++ wc->cid_history_clone_cnt[card] = cidtimeout; ++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); ++ } ++ } ++ else ++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); ++ if (debug) ++ printk(KERN_DEBUG "NO RING on %d/%d!\n", wc->span.spanno, card + 1); ++ } ++ fxo->ringdebounce = 0; ++ } ++ } ++ } ++ } ++ ++ b = wc->reg1shadow[card]; ++ if (abs(b) < battthresh) { ++ /* possible existing states: ++ battery lost, no debounce timer ++ battery lost, debounce timer (going to battery present) ++ battery present or unknown, no debounce timer ++ battery present or unknown, debounce timer (going to battery lost) ++ */ ++ ++ if (fxo->battery == BATTERY_LOST) { ++ if (fxo->battdebounce) { ++ /* we were going to BATTERY_PRESENT, but battery was lost again, ++ so clear the debounce timer */ ++ fxo->battdebounce = 0; ++ } ++ } else { ++ if (fxo->battdebounce) { ++ /* going to BATTERY_LOST, see if we are there yet */ ++ if (--fxo->battdebounce == 0) { ++ fxo->battery = BATTERY_LOST; ++ if (debug) ++ printk(KERN_DEBUG "NO BATTERY on %d/%d!\n", wc->span.spanno, card + 1); ++#ifdef JAPAN ++ if (!wc->ohdebounce && wc->offhook) { ++ dahdi_hooksig(&wc->chans[card], DAHDI_RXSIG_ONHOOK); ++ if (debug) ++ printk(KERN_DEBUG "Signalled On Hook\n"); ++#ifdef ZERO_BATT_RING ++ wc->onhook++; ++#endif ++ } ++#else ++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK); ++ /* set the alarm timer, taking into account that part of its time ++ period has already passed while debouncing occurred */ ++ fxo->battalarm = (battalarm - battdebounce) / MS_PER_CHECK_HOOK; ++#endif ++ } ++ } else { ++ /* start the debounce timer to verify that battery has been lost */ ++ fxo->battdebounce = battdebounce / MS_PER_CHECK_HOOK; ++ } ++ } ++ } else { ++ /* possible existing states: ++ battery lost or unknown, no debounce timer ++ battery lost or unknown, debounce timer (going to battery present) ++ battery present, no debounce timer ++ battery present, debounce timer (going to battery lost) ++ */ ++ ++ if (fxo->battery == BATTERY_PRESENT) { ++ if (fxo->battdebounce) { ++ /* we were going to BATTERY_LOST, but battery appeared again, ++ so clear the debounce timer */ ++ fxo->battdebounce = 0; ++ } ++ } else { ++ if (fxo->battdebounce) { ++ /* going to BATTERY_PRESENT, see if we are there yet */ ++ if (--fxo->battdebounce == 0) { ++ fxo->battery = BATTERY_PRESENT; ++ if (debug) ++ printk(KERN_DEBUG "BATTERY on %d/%d (%s)!\n", wc->span.spanno, card + 1, ++ (b < 0) ? "-" : "+"); ++#ifdef ZERO_BATT_RING ++ if (wc->onhook) { ++ wc->onhook = 0; ++ dahdi_hooksig(&wc->chans[card], DAHDI_RXSIG_OFFHOOK); ++ if (debug) ++ printk(KERN_DEBUG "Signalled Off Hook\n"); ++ } ++#else ++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); ++#endif ++ /* set the alarm timer, taking into account that part of its time ++ period has already passed while debouncing occurred */ ++ fxo->battalarm = (battalarm - battdebounce) / MS_PER_CHECK_HOOK; ++ } ++ } else { ++ /* start the debounce timer to verify that battery has appeared */ ++ fxo->battdebounce = battdebounce / MS_PER_CHECK_HOOK; ++ } ++ } ++ } ++ ++ if (fxo->lastpol >= 0) { ++ if (b < 0) { ++ fxo->lastpol = -1; ++ fxo->polaritydebounce = POLARITY_DEBOUNCE / MS_PER_CHECK_HOOK; ++ } ++ } ++ if (fxo->lastpol <= 0) { ++ if (b > 0) { ++ fxo->lastpol = 1; ++ fxo->polaritydebounce = POLARITY_DEBOUNCE / MS_PER_CHECK_HOOK; ++ } ++ } ++ ++ if (fxo->battalarm) { ++ if (--fxo->battalarm == 0) { ++ /* the alarm timer has expired, so update the battery alarm state ++ for this channel */ ++ dahdi_alarm_channel(wc->chans[card], fxo->battery == BATTERY_LOST ? DAHDI_ALARM_RED : DAHDI_ALARM_NONE); ++ } ++ } ++ ++ if (fxo->polaritydebounce) { ++ if (--fxo->polaritydebounce == 0) { ++ if (fxo->lastpol != fxo->polarity) { ++ if (debug) ++ printk(KERN_DEBUG "%lu Polarity reversed (%d -> %d)\n", jiffies, ++ fxo->polarity, ++ fxo->lastpol); ++ if (fxo->polarity) ++ dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY); ++ fxo->polarity = fxo->lastpol; ++ } ++ } ++ } ++#undef MS_PER_CHECK_HOOK ++} ++ ++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(KERN_DEBUG "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 * DAHDI_CHUNKSIZE; ++#if 0 ++ printk(KERN_DEBUG "Sustaining hook %d, %d\n", hook, wc->mod[card].fxs.debounce); ++#endif ++ if (!wc->mod[card].fxs.debounce) { ++#if 0 ++ printk(KERN_DEBUG "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(KERN_DEBUG "opvxa1200: Card %d Going off hook\n", card); ++ dahdi_hooksig(wc->chans[card], DAHDI_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(KERN_DEBUG "opvxa1200: Card %d Going on hook\n", card); ++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK); ++ wc->mod[card].fxs.oldrxhook = 0; ++ } ++ } ++ } ++ wc->mod[card].fxs.lastrxhook = hook; ++} ++ ++DAHDI_IRQ_HANDLER(wctdm_interrupt) ++{ ++ struct wctdm *wc = dev_id; ++ unsigned char ints; ++ int x, y, z; ++ int mode; ++ ++ ints = inb(wc->ioaddr + WC_INTSTAT); ++ ++ if (!ints) ++ return IRQ_NONE; ++ ++ outb(ints, wc->ioaddr + WC_INTSTAT); ++ ++ if (ints & 0x10) { ++ /* Stop DMA, wait for watchdog */ ++ printk(KERN_INFO "TDM PCI Master abort\n"); ++ wctdm_stop_dma(wc); ++ return IRQ_RETVAL(1); ++ } ++ ++ if (ints & 0x20) { ++ printk(KERN_INFO "PCI Target abort\n"); ++ return IRQ_RETVAL(1); ++ } ++ ++ for (x=0;xmax_cards/*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-= DAHDI_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; ymax_cards/4/*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;xmax_cards/*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); ++ } ++ ++ return IRQ_RETVAL(1); ++ ++} ++ ++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(KERN_DEBUG "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(KERN_DEBUG "ProSLIC on module %d, product %d, version %d\n", card, (blah & 0x30) >> 4, (blah & 0xf)); ++ ++#if 0 ++ if ((blah & 0x30) >> 4) { ++ printk(KERN_DEBUG "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(KERN_NOTICE "ProSLIC 3210 version %d is too old\n", blah & 0xf); ++ return -1; ++ } ++ if (wctdm_getreg(wc, card, 1) & 0x80) ++ /* ProSLIC 3215, not a 3210 */ ++ wc->flags[card] |= FLAG_3215; ++ ++ blah = wctdm_getreg(wc, card, 8); ++ if (blah != 0x2) { ++ printk(KERN_NOTICE "ProSLIC on module %d insane (1) %d should be 2\n", card, blah); ++ return -1; ++ } else if ( insane_report) ++ printk(KERN_NOTICE "ProSLIC on module %d Reg 8 Reads %d Expected is 0x2\n",card,blah); ++ ++ blah = wctdm_getreg(wc, card, 64); ++ if (blah != 0x0) { ++ printk(KERN_NOTICE "ProSLIC on module %d insane (2)\n", card); ++ return -1; ++ } else if ( insane_report) ++ printk(KERN_NOTICE "ProSLIC on module %d Reg 64 Reads %d Expected is 0x0\n",card,blah); ++ ++ blah = wctdm_getreg(wc, card, 11); ++ if (blah != 0x33) { ++ printk(KERN_NOTICE "ProSLIC on module %d insane (3)\n", card); ++ return -1; ++ } else if ( insane_report) ++ printk(KERN_NOTICE "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(KERN_DEBUG "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(KERN_NOTICE "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(KERN_NOTICE "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(KERN_NOTICE "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(KERN_DEBUG "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(KERN_DEBUG "Loop current out of range! Setting to default 20mA!\n"); ++ } ++ else if (debug) ++ printk(KERN_DEBUG "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(KERN_DEBUG "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(KERN_DEBUG "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(KERN_NOTICE "Timeout waiting for calibration of module %d\n", card); ++ return -1; ++ } ++ } ++ ++ if (debug) { ++ /* Print calibration parameters */ ++ printk(KERN_DEBUG "Calibration Vector Regs 98 - 107: \n"); ++ for (x=98;x<108;x++) { ++ printk(KERN_DEBUG "%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); ++} ++ ++/********************************************************************* ++ * Set the hwgain on the analog modules ++ * ++ * card = the card position for this module (0-23) ++ * gain = gain in dB x10 (e.g. -3.5dB would be gain=-35) ++ * tx = (0 for rx; 1 for tx) ++ * ++ *******************************************************************/ ++static int wctdm_set_hwgain(struct wctdm *wc, int card, __s32 gain, __u32 tx) ++{ ++ if (!(wc->modtype[card] == MOD_TYPE_FXO)) { ++ printk(KERN_NOTICE "Cannot adjust gain. Unsupported module type!\n"); ++ return -1; ++ } ++ if (tx) { ++ if (debug) ++ printk(KERN_DEBUG "setting FXO tx gain for card=%d to %d\n", card, gain); ++ if (gain >= -150 && gain <= 0) { ++ wctdm_setreg(wc, card, 38, 16 + (gain/-10)); ++ wctdm_setreg(wc, card, 40, 16 + (-gain%10)); ++ } else if (gain <= 120 && gain > 0) { ++ wctdm_setreg(wc, card, 38, gain/10); ++ wctdm_setreg(wc, card, 40, (gain%10)); ++ } else { ++ printk(KERN_INFO "FXO tx gain is out of range (%d)\n", gain); ++ return -1; ++ } ++ } else { /* rx */ ++ if (debug) ++ printk(KERN_DEBUG "setting FXO rx gain for card=%d to %d\n", card, gain); ++ if (gain >= -150 && gain <= 0) { ++ wctdm_setreg(wc, card, 39, 16+ (gain/-10)); ++ wctdm_setreg(wc, card, 41, 16 + (-gain%10)); ++ } else if (gain <= 120 && gain > 0) { ++ wctdm_setreg(wc, card, 39, gain/10); ++ wctdm_setreg(wc, card, 41, (gain%10)); ++ } else { ++ printk(KERN_INFO "FXO rx gain is out of range (%d)\n", gain); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++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); ++ ++ if(fwringdetect) { ++ /* Enable ring detector full-wave rectifier mode */ ++ wctdm_setreg(wc, card, 18, 2); ++ wctdm_setreg(wc, card, 24, 0); ++ } else { ++ /* Set to the device defaults */ ++ wctdm_setreg(wc, card, 18, 0); ++ wctdm_setreg(wc, card, 24, 0x19); ++ } ++ ++ /* 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 = (fxofullscale==1) ? (fxo_modes[_opermode].acim|0x10) : (fxo_modes[_opermode].acim); ++ wctdm_setreg(wc, card, 30, reg30); ++ ++ /* Misc. DAA parameters */ ++ if (fastpickup) ++ reg31 = 0xb3; ++ else ++ 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); ++ ++ if (fastpickup) ++ wctdm_setreg(wc, card, 17, wctdm_getreg(wc, card, 17) | 0x20); ++ ++ /* 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(KERN_NOTICE "VoiceDAA did not bring up ISO link properly!\n"); ++ return -1; ++ } ++ if (debug) ++ printk(KERN_DEBUG "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); ++ ++ /* Take values for fxotxgain and fxorxgain and apply them to module */ ++ wctdm_set_hwgain(wc, card, fxotxgain, 1); ++ wctdm_set_hwgain(wc, card, fxorxgain, 0); ++ ++ /* NZ -- crank the tx gain up by 7 dB */ ++ if (!strcmp(fxo_modes[_opermode].name, "NEWZEALAND")) { ++ printk(KERN_INFO "Adjusting gain\n"); ++ wctdm_set_hwgain(wc, card, 7, 1); ++ } ++ ++ if(debug) ++ printk(KERN_DEBUG "DEBUG fxotxgain:%i.%i fxorxgain:%i.%i\n", (wctdm_getreg(wc, card, 38)/16)?-(wctdm_getreg(wc, card, 38) - 16) : wctdm_getreg(wc, card, 38), (wctdm_getreg(wc, card, 40)/16)? -(wctdm_getreg(wc, card, 40) - 16):wctdm_getreg(wc, card, 40), (wctdm_getreg(wc, card, 39)/16)? -(wctdm_getreg(wc, card, 39) - 16) : wctdm_getreg(wc, card, 39),(wctdm_getreg(wc, card, 41)/16)?-(wctdm_getreg(wc, card, 41) - 16):wctdm_getreg(wc, card, 41)); ++ ++ 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, r9; ++ int x; ++ int fxsmode=0; ++ ++ /* Sanity check the ProSLIC */ ++ if (!sane && wctdm_proslic_insane(wc, card)) ++ return -2; ++ ++ /* By default, don't send on hook */ ++ if (reversepolarity) ++ wc->mod[card].fxs.idletxhookstate = 5; ++ else ++ wc->mod[card].fxs.idletxhookstate = 1; ++ ++ 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, 0x07); /* Note, if pulse dialing has problems at high REN loads ++ change this to 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(KERN_NOTICE "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(KERN_NOTICE "ProSLIC module %d failed leakage test. Check for short circuit\n", card); ++ } ++ /* Power up again */ ++ if (wctdm_powerup_proslic(wc, card, fast)) { ++ printk(KERN_NOTICE "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(KERN_NOTICE "Proslic failed on Manual Calibration\n"); ++ if (wctdm_proslic_manual_calibrate(wc, card)) { ++ printk(KERN_NOTICE "Proslic Failed on Second Attempt to Calibrate Manually. (Try -DNO_CALIBRATION in Makefile)\n"); ++ return -1; ++ } ++ printk(KERN_NOTICE "Proslic Passed Manual Calibration on Second Attempt\n"); ++ } ++ } ++ else { ++ if(wctdm_proslic_calibrate(wc, card)) { ++ //printk(KERN_NOTICE "ProSlic died on Auto Calibration.\n"); ++ if (wctdm_proslic_calibrate(wc, card)) { ++ printk(KERN_NOTICE "Proslic Failed on Second Attempt to Auto Calibrate\n"); ++ return -1; ++ } ++ printk(KERN_NOTICE "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(KERN_NOTICE "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 ++ 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 ++ ++ if (fastringer) { ++ /* Speed up Ringer */ ++ wctdm_proslic_setreg_indirect(wc, card, 20, 0x7e6d); ++ wctdm_proslic_setreg_indirect(wc, card, 21, 0x01b9); ++ /* Beef up Ringing voltage to 89V */ ++ if (boostringer) { ++ wctdm_setreg(wc, card, 74, 0x3f); ++ if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x247)) ++ return -1; ++ printk(KERN_INFO "Boosting fast ringer on slot %d (89V peak)\n", card + 1); ++ } else if (lowpower) { ++ if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x14b)) ++ return -1; ++ printk(KERN_INFO "Reducing fast ring power on slot %d (50V peak)\n", card + 1); ++ } else ++ printk(KERN_INFO "Speeding up ringer on slot %d (25Hz)\n", card + 1); ++ } else { ++ /* Beef up Ringing voltage to 89V */ ++ if (boostringer) { ++ wctdm_setreg(wc, card, 74, 0x3f); ++ if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x1d1)) ++ return -1; ++ printk(KERN_INFO "Boosting ringer on slot %d (89V peak)\n", card + 1); ++ } else if (lowpower) { ++ if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x108)) ++ return -1; ++ printk(KERN_INFO "Reducing ring power on slot %d (50V peak)\n", card + 1); ++ } ++ } ++ ++ if(fxstxgain || fxsrxgain) { ++ r9 = wctdm_getreg(wc, card, 9); ++ switch (fxstxgain) { ++ ++ case 35: ++ r9+=8; ++ break; ++ case -35: ++ r9+=4; ++ break; ++ case 0: ++ break; ++ } ++ ++ switch (fxsrxgain) { ++ ++ case 35: ++ r9+=2; ++ break; ++ case -35: ++ r9+=1; ++ break; ++ case 0: ++ break; ++ } ++ wctdm_setreg(wc,card,9,r9); ++ } ++ ++ if(debug) ++ printk(KERN_DEBUG "DEBUG: fxstxgain:%s fxsrxgain:%s\n",((wctdm_getreg(wc, card, 9)/8) == 1)?"3.5":(((wctdm_getreg(wc,card,9)/4) == 1)?"-3.5":"0.0"),((wctdm_getreg(wc, card, 9)/2) == 1)?"3.5":((wctdm_getreg(wc,card,9)%2)?"-3.5":"0.0")); ++ ++ wctdm_setreg(wc, card, 64, 0x01); ++ return 0; ++} ++ ++ ++static int wctdm_ioctl(struct dahdi_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 dahdi_hwgain hwgain; ++ struct wctdm *wc = chan->pvt; ++ int x; ++ switch (cmd) { ++ case DAHDI_ONHOOKTRANSFER: ++ if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) ++ return -EINVAL; ++ if (get_user(x, (__user 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 || wc->mod[chan->chanpos - 1].fxs.lasttxhook == 0x5) { ++ /* 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 DAHDI_SETPOLARITY: ++ if (get_user(x, (__user 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((__user void *)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((__user void *)data, ®s, sizeof(regs))) ++ return -EFAULT; ++ break; ++ case WCTDM_SET_REG: ++ if (copy_from_user(®op, (__user void *)data, sizeof(regop))) ++ return -EFAULT; ++ if (regop.indirect) { ++ if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) ++ return -EINVAL; ++ printk(KERN_INFO "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(KERN_INFO "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(KERN_INFO "-- Setting echo registers: \n"); ++ if (copy_from_user(&echoregs, (__user void *)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, (fxofullscale==1) ? (echoregs.acim|0x10) : 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(KERN_INFO "-- Set echo registers successfully\n"); ++ ++ break; ++ } else { ++ return -EINVAL; ++ ++ } ++ break; ++ case DAHDI_SET_HWGAIN: ++ if (copy_from_user(&hwgain, (__user void *) data, sizeof(hwgain))) ++ return -EFAULT; ++ ++ wctdm_set_hwgain(wc, chan->chanpos-1, hwgain.newgain, hwgain.tx); ++ ++ if (debug) ++ printk(KERN_DEBUG "Setting hwgain on channel %d to %d for %s direction\n", ++ chan->chanpos-1, hwgain.newgain, hwgain.tx ? "tx" : "rx"); ++ break; ++ default: ++ return -ENOTTY; ++ } ++ return 0; ++ ++} ++ ++static int wctdm_open(struct dahdi_chan *chan) ++{ ++ struct wctdm *wc = chan->pvt; ++ if (!(wc->cardflag & (1 << (chan->chanpos - 1)))) ++ return -ENODEV; ++ if (wc->dead) ++ return -ENODEV; ++ wc->usecount++; ++ ++ /*MOD_INC_USE_COUNT; */ ++ try_module_get(THIS_MODULE); ++ return 0; ++} ++ ++static int wctdm_watchdog(struct dahdi_span *span, int event) ++{ ++ printk(KERN_INFO "opvxa1200: Restarting DMA\n"); ++ wctdm_restart_dma(span->pvt); ++ return 0; ++} ++ ++static int wctdm_close(struct dahdi_chan *chan) ++{ ++ struct wctdm *wc = chan->pvt; ++ wc->usecount--; ++ ++ /*MOD_DEC_USE_COUNT;*/ ++ module_put(THIS_MODULE); ++ ++ 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 dahdi_chan *chan, enum dahdi_txsig 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 DAHDI_TXSIG_START: ++ case DAHDI_TXSIG_OFFHOOK: ++ wc->mod[chan->chanpos - 1].fxo.offhook = 1; ++ wctdm_setreg(wc, chan->chanpos - 1, 5, 0x9); ++ if(cidbeforering) ++ { ++ wc->cid_state[chan->chanpos - 1] = CID_STATE_IDLE; ++ wc->cid_history_clone_cnt[chan->chanpos - 1] = 0; ++ wc->cid_history_ptr[chan->chanpos - 1] = 0; ++ memset(wc->cid_history_buf[chan->chanpos - 1], DAHDI_LIN2X(0, chan), cidbuflen * DAHDI_MAX_CHUNKSIZE); ++ } ++ break; ++ case DAHDI_TXSIG_ONHOOK: ++ wc->mod[chan->chanpos - 1].fxo.offhook = 0; ++ wctdm_setreg(wc, chan->chanpos - 1, 5, 0x8); ++ break; ++ default: ++ printk(KERN_NOTICE "wcfxo: Can't set tx state to %d\n", txsig); ++ } ++ } else { ++ switch(txsig) { ++ case DAHDI_TXSIG_ONHOOK: ++ switch(chan->sig) { ++ case DAHDI_SIG_EM: ++ case DAHDI_SIG_FXOKS: ++ case DAHDI_SIG_FXOLS: ++ wc->mod[chan->chanpos-1].fxs.lasttxhook = wc->mod[chan->chanpos-1].fxs.idletxhookstate; ++ break; ++ case DAHDI_SIG_FXOGS: ++ wc->mod[chan->chanpos-1].fxs.lasttxhook = 3; ++ break; ++ } ++ break; ++ case DAHDI_TXSIG_OFFHOOK: ++ switch(chan->sig) { ++ case DAHDI_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 DAHDI_TXSIG_START: ++ wc->mod[chan->chanpos-1].fxs.lasttxhook = 4; ++ break; ++ case DAHDI_TXSIG_KEWL: ++ wc->mod[chan->chanpos-1].fxs.lasttxhook = 0; ++ break; ++ default: ++ printk(KERN_NOTICE "opvxa1200: Can't set tx state to %d\n", txsig); ++ } ++ if (debug) ++ printk(KERN_DEBUG "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; ++ ++ /* Dahdi stuff */ ++ sprintf(wc->span.name, "OPVXA1200/%d", wc->pos); ++ snprintf(wc->span.desc, sizeof(wc->span.desc)-1, "%s Board %d", wc->variety, wc->pos + 1); ++ snprintf(wc->span.location, sizeof(wc->span.location) - 1, ++ "PCI Bus %02d Slot %02d", wc->dev->bus->number, PCI_SLOT(wc->dev->devfn) + 1); ++ wc->span.manufacturer = "OpenVox"; ++ dahdi_copy_string(wc->span.devicetype, wc->variety, sizeof(wc->span.devicetype)); ++ if (alawoverride) { ++ printk(KERN_INFO "ALAW override parameter detected. Device will be operating in ALAW\n"); ++ wc->span.deflaw = DAHDI_LAW_ALAW; ++ } else ++ wc->span.deflaw = DAHDI_LAW_MULAW; ++ ++ x = __wctdm_getcreg(wc, WC_VER); ++ wc->fwversion = x; ++ if( x & FLAG_A800) ++ { ++ wc->card_name = A800P_Name; ++ wc->max_cards = 8; ++ } ++ else ++ { ++ wc->card_name = A1200P_Name; ++ wc->max_cards = 12; ++ } ++ ++ for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { ++ sprintf(wc->chans[x]->name, "OPVXA1200/%d/%d", wc->pos, x); ++ wc->chans[x]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; ++ wc->chans[x]->sigcap |= DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR; ++ wc->chans[x]->chanpos = x+1; ++ wc->chans[x]->pvt = wc; ++ } ++ wc->span.chans = wc->chans; ++ wc->span.channels = wc->max_cards; /*MAX_NUM_CARDS;*/ ++ wc->span.hooksig = wctdm_hooksig; ++ wc->span.irq = wc->dev->irq; ++ wc->span.open = wctdm_open; ++ wc->span.close = wctdm_close; ++ wc->span.flags = DAHDI_FLAG_RBS; ++ wc->span.ioctl = wctdm_ioctl; ++ wc->span.watchdog = wctdm_watchdog; ++ init_waitqueue_head(&wc->span.maintq); ++ ++ wc->span.pvt = wc; ++ if (dahdi_register(&wc->span, 0)) { ++ printk(KERN_NOTICE "Unable to register span with Dahdi\n"); ++ return -1; ++ } ++ return 0; ++} ++ ++static void wctdm_post_initialize(struct wctdm *wc) ++{ ++ int x; ++ ++ /* Finalize signalling */ ++ for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { ++ if (wc->cardflag & (1 << x)) { ++ if (wc->modtype[x] == MOD_TYPE_FXO) ++ wc->chans[x]->sigcap = DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR; ++ else ++ wc->chans[x]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; ++ } else if (!(wc->chans[x]->sigcap & DAHDI_SIG_BROKEN)) { ++ wc->chans[x]->sigcap = 0; ++ } ++ } ++} ++ ++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 5 second */ ++ ++ origjiffies = jiffies; ++ ++ while(1) ++ { ++ if ((jiffies - origjiffies) >= (HZ*5)) ++ break;; ++ } ++ ++ /* printk(KERN_INFO "after raise reset\n");*/ ++ ++ /* Check OpenVox chip */ ++ x=inb(wc->ioaddr + WC_CNTL); ++ ver = __wctdm_getcreg(wc, WC_VER); ++ wc->fwversion = ver; ++ /*if( ver & FLAG_A800) ++ { ++ wc->card_name = A800P_Name; ++ wc->max_cards = 8; ++ } ++ else ++ { ++ wc->card_name = A1200P_Name; ++ wc->max_cards = 12; ++ }*/ ++ printk(KERN_NOTICE "OpenVox %s version: %01x.%01x\n", wc->card_name, (ver&(~FLAG_A800))>>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(KERN_INFO "%02x != %02x\n", x, y); ++ failed++; ++ } ++ } ++ ++ if (!failed) { ++ printk(KERN_INFO "OpenVox %s passed register test\n", wc->card_name); ++ } else { ++ printk(KERN_NOTICE "OpenVox %s failed register test\n", wc->card_name); ++ return -1; ++ } ++ } else { ++ printk(KERN_INFO "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 */ /* Useless in OpenVox by MiaoLin. */ ++ /* 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->ioaddr + WC_CNTL); ++ ++ /* Configure serial port for MSB->LSB operation */ ++ outb(0xc1, wc->ioaddr + WC_SERCTL); ++ ++ /* Delay FSC by 0 so it's properly aligned */ ++ outb(0x01, wc->ioaddr + WC_FSCDELAY); /* Modify to 1 by MiaoLin */ ++ ++ /* Setup DMA Addresses */ ++ outl(wc->writedma, wc->ioaddr + WC_DMAWS); /* Write start */ ++ outl(wc->writedma + DAHDI_CHUNKSIZE * 4 * 4 - 4, wc->ioaddr + WC_DMAWI); /* Middle (interrupt) */ ++ outl(wc->writedma + DAHDI_CHUNKSIZE * 8 * 4 - 4, wc->ioaddr + WC_DMAWE); /* End */ ++ ++ outl(wc->readdma, wc->ioaddr + WC_DMARS); /* Read start */ ++ outl(wc->readdma + DAHDI_CHUNKSIZE * 4 * 4 - 4, wc->ioaddr + WC_DMARI); /* Middle (interrupt) */ ++ outl(wc->readdma + DAHDI_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 < wc->max_cards/*MAX_NUM_CARDS*/; x++) { ++ int sane=0,ret=0,readi=0; ++#if 1 ++ touch_softlockup_watchdog(); // avoid showing CPU softlock message ++ /* 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(KERN_INFO "Module %d: Installed -- AUTO FXS/DPO\n",x); ++ wctdm_set_led(wc, (unsigned int)x, 1); ++ } else { ++ if(ret!=-2) { ++ sane=1; ++ ++ printk(KERN_INFO "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(KERN_INFO "Module %d: Installed -- MANUAL FXS\n",x); ++ } else { ++ printk(KERN_NOTICE "Module %d: FAILED FXS (%s)\n", x, fxshonormode ? fxo_modes[_opermode].name : "FCC"); ++ wc->chans[x]->sigcap = __DAHDI_SIG_FXO | DAHDI_SIG_BROKEN; ++ } ++ } else if (!(ret = wctdm_init_voicedaa(wc, x, 0, 0, sane))) { ++ wc->cardflag |= (1 << x); ++ printk(KERN_INFO "Module %d: Installed -- AUTO FXO (%s mode)\n",x, fxo_modes[_opermode].name); ++ wctdm_set_led(wc, (unsigned int)x, 1); ++ } else ++ printk(KERN_NOTICE "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); */ /* removed by MiaoLin */ ++ 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, 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(KERN_NOTICE "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->lastchan = -1; /* first channel offset = -1; */ ++ wc->ledstate = 0; ++ ++ ifaces[x] = wc; ++ memset(wc, 0, sizeof(struct wctdm)); ++ for (x=0; x < sizeof(wc->chans)/sizeof(wc->chans[0]); ++x) { ++ wc->chans[x] = &wc->_chans[x]; ++ } ++ ++ 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, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2, &wc->writedma); ++ if (!wc->writechunk) { ++ printk(KERN_NOTICE "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 + DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2; /* in bytes */ ++ wc->readdma = wc->writedma + DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2; /* in bytes */ ++ ++ if (wctdm_initialize(wc)) { ++ printk(KERN_NOTICE "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); ++ } ++ } ++ ++ /* 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, DAHDI_IRQ_SHARED, "opvxa1200", wc)) { ++ printk(KERN_NOTICE "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, DAHDI_MAX_CHUNKSIZE * (MAX_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 w; ++ ++ /* Set Reset Low */ ++ w=inb(wc->ioaddr + WC_CNTL); ++ outb((~0x1)&w, 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, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2, (void *)wc->writechunk, wc->writedma); ++ pci_set_drvdata(pdev, NULL); ++ dahdi_unregister(&wc->span); ++ kfree(wc); ++ return -EIO; ++ ++ } ++ ++#ifdef TEST_LOG_INCOME_VOICE ++ for(x=0; xvoc_buf[x] = kmalloc(voc_buffer_size, GFP_KERNEL); ++ wc->voc_ptr[x] = 0; ++ } ++#endif ++ ++ if(cidbeforering) ++ { ++ int len = cidbuflen * DAHDI_MAX_CHUNKSIZE; ++ if(debug) ++ printk("cidbeforering support enabled, length is %d msec\n", cidbuflen); ++ for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) ++ { ++ wc->cid_history_buf[x] = kmalloc(len, GFP_KERNEL); ++ wc->cid_history_ptr[x] = 0; ++ wc->cid_history_clone_cnt[x] = 0; ++ wc->cid_state[x] = CID_STATE_IDLE; ++ } ++ } ++ ++ wctdm_post_initialize(wc); ++ ++ /* Enable interrupts */ ++ wctdm_enable_interrupts(wc); ++ /* Initialize Write/Buffers to all blank data */ ++ memset((void *)wc->writechunk,0, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2); ++ ++ /* Start DMA */ ++ wctdm_start_dma(wc); ++ ++ for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { ++ if (wc->cardflag & (1 << x)) ++ cardcount++; ++ } ++ ++ printk(KERN_INFO "Found an OpenVox %s: Version %x.%x (%d modules)\n", wc->card_name, (wc->fwversion&(~FLAG_A800))>>4, wc->fwversion&0x0f, cardcount); ++ if(debug) ++ printk(KERN_DEBUG "OpenVox %s debug On\n", wc->card_name); ++ ++ 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 ++ ++ dahdi_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 ++ ++ if(cidbeforering) ++ { ++ int x; ++ for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) ++ kfree(wc->cid_history_buf[x]); ++ } ++ ++ kfree(wc); ++ printk(KERN_INFO "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, DAHDI_MAX_CHUNKSIZE * (MAX_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, 0x9500, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, ++ { 0xe159, 0x0001, 0x9532, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, ++ { 0xe159, 0x0001, 0x8519, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, ++ { 0xe159, 0x0001, 0x9559, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, ++ { 0xe159, 0x0001, 0x9599, 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, ++ .remove = __devexit_p(wctdm_remove_one), ++ .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(KERN_NOTICE "Invalid/unknown operating mode '%s' specified. Please choose one of:\n", opermode); ++ for (x=0;x"); ++MODULE_LICENSE("GPL v2"); ++ ++module_init(wctdm_init); ++module_exit(wctdm_cleanup); +diff -urN dahdi-svn-orig/drivers/dahdi/wcopenpci.c dahdi-svn-new/drivers/dahdi/wcopenpci.c +--- dahdi-svn-orig/drivers/dahdi/wcopenpci.c 1970-01-01 02:00:00.000000000 +0200 ++++ dahdi-svn-new/drivers/dahdi/wcopenpci.c 2010-01-21 13:35:37.000000000 +0200 +@@ -0,0 +1,1842 @@ ++/* ++ * 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 DAHDI 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 ++#include ++ ++#include ++#include ++#include "proslic.h" ++#include ++ ++ ++ ++/* 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 (DAHDI_MAX_CHUNKSIZE * INOUT * MAX_PORTS * 2 * 2) ++#define VT_PCIDMA_MIDDLE (DAHDI_MAX_CHUNKSIZE * MAX_PORTS - 4) ++#define VT_PCIDMA_END (DAHDI_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 dahdi 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 dahdi_chan _chans[MAX_PORTS]; ++ struct dahdi_chan *chans[MAX_PORTS]; ++ struct dahdi_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: ++ dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_OFFHOOK); ++ dbginfo(wc->boardnum, "Port %d Loop OffHook", port); ++ break; ++ ++ case DSP_LOOP_ONHOOK: ++ dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_ONHOOK); ++ dbginfo(wc->boardnum, "Port %d Loop OnHook", port); ++ break; ++ ++ case DSP_LOOP_POLARITY: ++ dahdi_qevent_lock(wc->chans[port], DAHDI_EVENT_POLARITY); ++ dbginfo(wc->boardnum, "Port %d Loop Polarity", port); ++ break; ++ ++ case DSP_CODEC_RING: ++ dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_RING); ++ dbginfo(wc->boardnum, "Port %d Ring On", port); ++ break; ++ ++ case DSP_RING_OFF: ++ dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_OFFHOOK); ++ dbginfo(wc->boardnum, "Port %d Ring Off", port); ++ break; ++ ++ case DSP_CODEC_HKOFF: ++ dahdi_hooksig(wc->chans[port], DAHDI_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: ++ dahdi_hooksig(wc->chans[port], DAHDI_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: ++ dahdi_qevent_lock(wc->chans[port], DAHDI_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 + DAHDI_CHUNKSIZE*2; ++ else { ++ cardcrit(wc->boardnum, "bad write interrupt flags %x, at %x", ++ flags, inb(TJ_DMAWC) ); ++ return; ++ } ++ /* get data */ ++ dahdi_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 + DAHDI_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]) ++ dahdi_ec_chunk(wc->chans[x], wc->chans[x]->readchunk, wc->chans[x]->writechunk); ++ } ++ dahdi_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 dahdi_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 DAHDI_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 DAHDI_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 dahdi_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 dahdi 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 dahdi_span *span, int event) ++{ ++ info("TDM: Restarting DMA"); ++ restart_dma(span->pvt); ++ return 0; ++} ++ ++static int openpci_close(struct dahdi_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 dahdi 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 dahdi_chan *chan, enum dahdi_txsig 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 DAHDI_TXSIG_START: ++ case DAHDI_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 DAHDI_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 DAHDI_TXSIG_ONHOOK: ++ switch(chan->sig) { ++ case DAHDI_SIG_EM: ++ case DAHDI_SIG_FXOKS: ++ case DAHDI_SIG_FXOLS: ++ new_hk_state = wc->mod[port].fxs.idletxhookstate; ++ break; ++ case DAHDI_SIG_FXOGS: ++ new_hk_state = 3; ++ break; ++ } ++ break; ++ ++ case DAHDI_TXSIG_OFFHOOK: ++ switch(chan->sig) { ++ case DAHDI_SIG_EM: ++ new_hk_state = 5; ++ break; ++ default: ++ new_hk_state = wc->mod[port].fxs.idletxhookstate; ++ break; ++ } ++ break; ++ ++ case DAHDI_TXSIG_START: ++ new_hk_state = 4; ++ break; ++ ++ case DAHDI_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++) { ++ struct dahdi_chan *chan = &wc->_chans[x]; ++ wc->chans[x] = chan; ++ sprintf(chan->name, "WCTDM/%d/%d", wc->boardnum, x); ++ chan->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS ++ | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; ++ chan->sigcap |= DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR; ++ chan->chanpos = x+1; ++ chan->pvt = wc; ++ } ++ wc->span.deflaw = DAHDI_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 = DAHDI_FLAG_RBS; ++ wc->span.ioctl = openpci_ioctl; ++ wc->span.watchdog = openpci_watchdog; ++ init_waitqueue_head(&wc->span.maintq); ++ ++ wc->span.pvt = wc; ++ if (dahdi_register(&wc->span, 0)) { ++ cardcrit(wc->boardnum, "Unable to register span with dahdi"); ++ 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 + DAHDI_MAX_CHUNKSIZE * (MAX_PORTS*2 / sizeof(int)); ++ wc->readdma = wc->writedma + DAHDI_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 dahdi 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 = DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS ++ | DAHDI_SIG_CLEAR | DAHDI_SIG_SF; ++ else if (wc->porttype[i] == VT_PORT_PROSLIC) ++ wc->chans[i]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS ++ | DAHDI_SIG_FXOGS | DAHDI_SIG_SF ++ | DAHDI_SIG_CLEAR | DAHDI_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, DAHDI_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: ++ dahdi_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; ++ ++ dahdi_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( dahdi_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(DAHDI_VERSION); ++MODULE_LICENSE("GPL"); ++ +diff -urN dahdi-svn-orig/drivers/dahdi/zaphfc/base.c dahdi-svn-new/drivers/dahdi/zaphfc/base.c +--- dahdi-svn-orig/drivers/dahdi/zaphfc/base.c 1970-01-01 02:00:00.000000000 +0200 ++++ dahdi-svn-new/drivers/dahdi/zaphfc/base.c 2010-01-21 13:35:37.000000000 +0200 +@@ -0,0 +1,1706 @@ ++/* ++ * zaphfc.c - Dahdi driver for HFC-S PCI A based ISDN BRI cards ++ * ++ * Dahdi rewrite in hardhdlc mode ++ * Jose A. Deniz ++ * ++ * Copyright (C) 2009, Jose A. Deniz ++ * 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 ++ ++#include "zaphfc.h" ++#include "fifo.h" ++ ++#if CONFIG_PCI ++ ++#define DAHDI_B1 0 ++#define DAHDI_B2 1 ++#define DAHDI_D 2 ++ ++#define D 0 ++#define B1 1 ++#define B2 2 ++ ++/* ++ * Mode Te for all ++ */ ++static int modes; ++static int nt_modes[hfc_MAX_BOARDS]; ++static int nt_modes_count; ++static int force_l1_up; ++static struct proc_dir_entry *hfc_proc_zaphfc_dir; ++ ++#ifdef DEBUG ++int debug_level; ++#endif ++ ++#ifndef FALSE ++#define FALSE 0 ++#endif ++#ifndef TRUE ++#define TRUE (!FALSE) ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) ++#define SET_PROC_DIRENTRY_OWNER(p) do { (p)->owner = THIS_MODULE; } while(0); ++#else ++#define SET_PROC_DIRENTRY_OWNER(p) do { } while(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); ++ ++static 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); ++ ++/* ++ * Softreset procedure. Put it on, wait and off again ++ */ ++ hfc_outb(card, hfc_CIRM, hfc_CIRM_RESET); ++ udelay(6); ++ hfc_outb(card, hfc_CIRM, 0); ++ ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ schedule_timeout((hfc_RESET_DELAY * HZ) / 1000); ++} ++ ++static void hfc_resetCard(struct hfc_card *card) ++{ ++ card->regs.m1 = 0; ++ hfc_outb(card, hfc_INT_M1, card->regs.m1); ++ ++ card->regs.m2 = 0; ++ hfc_outb(card, hfc_INT_M2, card->regs.m2); ++ ++ 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_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_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)) { ++ ++ hfc_outb(card, hfc_STATES, hfc_STATES_DO_ACTION | ++ hfc_STATES_ACTIVATE| ++ hfc_STATES_NT_G2_G3); ++ ++ /* ++ * 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 ++ } ++} ++ ++ ++/******************* ++ * Dahdi interface * ++ *******************/ ++ ++static int hfc_zap_open(struct dahdi_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; ++ try_module_get(THIS_MODULE); ++ spin_unlock(&chan->lock); ++ ++ switch (chan->number) { ++ case D: ++ break; ++ ++ case B1: ++ card->regs.m2 |= hfc_M2_PROC_TRANS; ++ /* ++ * Enable transparent mode ++ */ ++ card->regs.ctmt |= hfc_CTMT_TRANSB1; ++ /* ++ * Reversed bit order ++ */ ++ card->regs.cirm |= hfc_CIRM_B1_REV; ++ /* ++ * Enable transmission ++ */ ++ card->regs.sctrl |= hfc_SCTRL_B1_ENA; ++ /* ++ * Enable reception ++ */ ++ card->regs.sctrl_r |= hfc_SCTRL_R_B1_ENA; ++ break; ++ ++ case B2: ++ card->regs.m2 |= hfc_M2_PROC_TRANS; ++ 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 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 dahdi_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: ++ 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: ++ 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); ++ ++ module_put(THIS_MODULE); ++ ++ 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 dahdi_chan *chan, int bits) ++{ ++ return 0; ++} ++ ++static int hfc_zap_ioctl(struct dahdi_chan *chan, ++ unsigned int cmd, unsigned long data) ++{ ++ switch (cmd) { ++ ++ default: ++ return -ENOTTY; ++ } ++ ++ return 0; ++} ++ ++static void hfc_hdlc_hard_xmit(struct dahdi_chan *d_chan) ++{ ++ struct hfc_chan_duplex *chan = d_chan->pvt; ++ struct hfc_card *card = chan->card; ++ struct dahdi_hfc *hfccard = card->ztdev; ++ ++ atomic_inc(&hfccard->hdlc_pending); ++ ++} ++ ++static int hfc_zap_startup(struct dahdi_span *span) ++{ ++ struct dahdi_hfc *zthfc = span->pvt; ++ struct hfc_card *hfctmp = zthfc->card; ++ int alreadyrunning; ++ ++ if (!hfctmp) { ++ printk(KERN_INFO hfc_DRIVER_PREFIX ++ "card %d: " ++ "no card for span at startup!\n", ++ hfctmp->cardnum); ++ } ++ ++ alreadyrunning = span->flags & DAHDI_FLAG_RUNNING; ++ ++ if (!alreadyrunning) ++ span->flags |= DAHDI_FLAG_RUNNING; ++ ++ return 0; ++} ++ ++static int hfc_zap_shutdown(struct dahdi_span *span) ++{ ++ return 0; ++} ++ ++static int hfc_zap_maint(struct dahdi_span *span, int cmd) ++{ ++ return 0; ++} ++ ++static int hfc_zap_chanconfig(struct dahdi_chan *d_chan, int sigtype) ++{ ++ struct hfc_chan_duplex *chan = d_chan->pvt; ++ struct hfc_card *card = chan->card; ++ struct dahdi_hfc *hfccard = card->ztdev; ++ ++ if ((sigtype == DAHDI_SIG_HARDHDLC) && (hfccard->sigchan == d_chan)) { ++ hfccard->sigactive = 0; ++ atomic_set(&hfccard->hdlc_pending, 0); ++ } ++ ++ return 0; ++} ++ ++static int hfc_zap_spanconfig(struct dahdi_span *span, ++ struct dahdi_lineconfig *lc) ++{ ++ span->lineconfig = lc->lineconfig; ++ ++ return 0; ++} ++ ++static int hfc_zap_initialize(struct dahdi_hfc *hfccard) ++{ ++ struct hfc_card *hfctmp = hfccard->card; ++ int i; ++ ++ memset(&hfccard->span, 0x0, sizeof(struct dahdi_span)); ++ sprintf(hfccard->span.name, "ZTHFC%d", hfctmp->cardnum + 1); ++ sprintf(hfccard->span.desc, ++ "HFC-S PCI A ISDN card %d [%s] ", ++ hfctmp->cardnum, ++ hfctmp->nt_mode ? "NT" : "TE"); ++ hfccard->span.spantype = hfctmp->nt_mode ? "NT" : "TE"; ++ hfccard->span.manufacturer = "Cologne Chips"; ++ hfccard->span.spanconfig = hfc_zap_spanconfig; ++ hfccard->span.chanconfig = hfc_zap_chanconfig; ++ hfccard->span.startup = hfc_zap_startup; ++ hfccard->span.shutdown = hfc_zap_shutdown; ++ hfccard->span.maint = hfc_zap_maint; ++ hfccard->span.rbsbits = hfc_zap_rbsbits; ++ hfccard->span.open = hfc_zap_open; ++ hfccard->span.close = hfc_zap_close; ++ hfccard->span.ioctl = hfc_zap_ioctl; ++ hfccard->span.hdlc_hard_xmit = hfc_hdlc_hard_xmit; ++ hfccard->span.flags = 0; ++ hfccard->span.irq = hfctmp->pcidev->irq; ++ dahdi_copy_string(hfccard->span.devicetype, "HFC-S PCI-A ISDN", ++ sizeof(hfccard->span.devicetype)); ++ sprintf(hfccard->span.location, "PCI Bus %02d Slot %02d", ++ hfctmp->pcidev->bus->number, ++ PCI_SLOT(hfctmp->pcidev->devfn) + 1); ++ hfccard->span.chans = hfccard->_chans; ++ hfccard->span.channels = 3; ++ for (i = 0; i < hfccard->span.channels; i++) ++ hfccard->_chans[i] = &hfccard->chans[i]; ++ hfccard->span.deflaw = DAHDI_LAW_ALAW; ++ hfccard->span.linecompat = DAHDI_CONFIG_AMI | DAHDI_CONFIG_CCS; ++ hfccard->span.offset = 0; ++ init_waitqueue_head(&hfccard->span.maintq); ++ hfccard->span.pvt = hfccard; ++ ++ for (i = 0; i < hfccard->span.channels; i++) { ++ memset(&hfccard->chans[i], 0x0, sizeof(struct dahdi_chan)); ++ ++ sprintf(hfccard->chans[i].name, ++ "ZTHFC%d/%d/%d", ++ hfctmp->cardnum + 1, 0, i + 1); ++ ++ printk(KERN_INFO hfc_DRIVER_PREFIX ++ "card %d: " ++ "registered %s\n", ++ hfctmp->cardnum, ++ hfccard->chans[i].name); ++ ++ if (i == hfccard->span.channels - 1) { ++ hfccard->chans[i].sigcap = DAHDI_SIG_HARDHDLC; ++ hfccard->sigchan = &hfccard->chans[D]; ++ hfccard->sigactive = 0; ++ atomic_set(&hfccard->hdlc_pending, 0); ++ } else { ++ hfccard->chans[i].sigcap = ++ DAHDI_SIG_CLEAR | DAHDI_SIG_DACS; ++ } ++ ++ hfccard->chans[i].chanpos = i + 1; ++ } ++ ++ hfccard->chans[DAHDI_D].readchunk = ++ hfctmp->chans[D].rx.zaptel_buffer; ++ ++ hfccard->chans[DAHDI_D].writechunk = ++ hfctmp->chans[D].tx.zaptel_buffer; ++ ++ hfccard->chans[DAHDI_D].pvt = &hfctmp->chans[D]; ++ ++ hfccard->chans[DAHDI_B1].readchunk = ++ hfctmp->chans[B1].rx.zaptel_buffer; ++ ++ hfccard->chans[DAHDI_B1].writechunk = ++ hfctmp->chans[B1].tx.zaptel_buffer; ++ ++ hfccard->chans[DAHDI_B1].pvt = &hfctmp->chans[B1]; ++ ++ hfccard->chans[DAHDI_B2].readchunk = ++ hfctmp->chans[B2].rx.zaptel_buffer; ++ ++ hfccard->chans[DAHDI_B2].writechunk = ++ hfctmp->chans[B2].tx.zaptel_buffer; ++ ++ hfccard->chans[DAHDI_B2].pvt = &hfctmp->chans[B2]; ++ ++ if (dahdi_register(&hfccard->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, DAHDI_CHUNKSIZE); ++} ++ ++static void hfc_zap_receive(struct hfc_chan_simplex *chan) ++{ ++ hfc_fifo_get(chan, chan->zaptel_buffer, DAHDI_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); ++ ++#if (KERNEL_VERSION(2, 6, 24) < LINUX_VERSION_CODE) ++static irqreturn_t hfc_interrupt(int irq, void *dev_id) ++#else ++static irqreturn_t hfc_interrupt(int irq, void *dev_id, struct pt_regs *regs) ++#endif ++{ ++ 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 >= DAHDI_CHUNKSIZE + hfc_RX_FIFO_PRELOAD) { ++ card->ticks = 0; ++ ++ if (available_bytes > DAHDI_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 - ++ (DAHDI_CHUNKSIZE + ++ hfc_RX_FIFO_PRELOAD)); ++ } ++#endif ++ } else { ++ hfc_handle_voice(card); ++ } ++ } ++ ++ card->ticks++; ++} ++ ++ ++static void hfc_handle_voice(struct hfc_card *card) ++{ ++ struct dahdi_hfc *hfccard = card->ztdev; ++ int frame_left, res; ++ unsigned char buf[hfc_HDLC_BUF_LEN]; ++ unsigned int size = sizeof(buf) / sizeof(buf[0]); ++ ++ ++ if (card->chans[B1].status != open_voice && ++ card->chans[B2].status != open_voice) ++ return; ++ ++ dahdi_transmit(&hfccard->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); ++ ++ /* ++ * dahdi hdlc frame tx ++ */ ++ ++ if (atomic_read(&hfccard->hdlc_pending)) { ++ hfc_check_l1_up(card); ++ res = dahdi_hdlc_getbuf(hfccard->sigchan, buf, &size); ++ if (size > 0) { ++ hfccard->sigactive = 1; ++ memcpy(card->chans[D].tx.ugly_framebuf + ++ card->chans[D].tx.ugly_framebuf_size, ++ buf, size); ++ card->chans[D].tx.ugly_framebuf_size += size; ++ if (res != 0) { ++ hfc_fifo_put_frame(&card->chans[D].tx, ++ card->chans[D].tx.ugly_framebuf, ++ card->chans[D].tx.ugly_framebuf_size); ++ ++hfccard->frames_out; ++ hfccard->sigactive = 0; ++ card->chans[D].tx.ugly_framebuf_size ++ = 0; ++ atomic_dec(&hfccard->hdlc_pending); ++ } ++ } ++ } ++ /* ++ * dahdi hdlc frame tx done ++ */ ++ ++ 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 ++ */ ++ dahdi_ec_chunk(&hfccard->chans[DAHDI_B1], ++ card->chans[B1].rx.zaptel_buffer, ++ card->chans[B1].tx.zaptel_buffer); ++ dahdi_ec_chunk(&hfccard->chans[DAHDI_B2], ++ card->chans[B2].rx.zaptel_buffer, ++ card->chans[B2].tx.zaptel_buffer); ++ ++ /* ++ * dahdi hdlc frame rx ++ */ ++ if (hfc_fifo_has_frames(&card->chans[D].rx)) ++ hfc_frame_arrived(&card->chans[D]); ++ ++ if (card->chans[D].rx.ugly_framebuf_size) { ++ frame_left = card->chans[D].rx.ugly_framebuf_size - ++ card->chans[D].rx.ugly_framebuf_off ; ++ if (frame_left > hfc_HDLC_BUF_LEN) { ++ dahdi_hdlc_putbuf(hfccard->sigchan, ++ card->chans[D].rx.ugly_framebuf + ++ card->chans[D].rx.ugly_framebuf_off, ++ hfc_HDLC_BUF_LEN); ++ card->chans[D].rx.ugly_framebuf_off += ++ hfc_HDLC_BUF_LEN; ++ } else { ++ dahdi_hdlc_putbuf(hfccard->sigchan, ++ card->chans[D].rx.ugly_framebuf + ++ card->chans[D].rx.ugly_framebuf_off, ++ frame_left); ++ dahdi_hdlc_finish(hfccard->sigchan); ++ card->chans[D].rx.ugly_framebuf_size = 0; ++ card->chans[D].rx.ugly_framebuf_off = 0; ++ } ++ } ++ /* ++ * dahdi hdlc frame rx done ++ */ ++ ++ if (hfccard->span.flags & DAHDI_FLAG_RUNNING) ++ dahdi_receive(&hfccard->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); ++ ++ ++ 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); ++ ++ ++ continue; ++ } ++ ++ if (chan->open_by_zaptel && ++ card->chans[D].rx.ugly_framebuf_size) { ++ ++ /* ++ * We have to wait for Dahdi 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); ++ ++ ++ continue; ++ } ++ ++ ++ /* ++ * HFC does the checksum ++ */ ++#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; ++ } ++ } ++ } ++ ++ if (!antiloop) ++ printk(KERN_CRIT hfc_DRIVER_PREFIX ++ "card %d: " ++ "Infinite loop detected\n", ++ card->cardnum); ++} ++ ++/****************************************** ++ * Module initialization and cleanup ++ ******************************************/ ++ ++static int __devinit hfc_probe(struct pci_dev *pci_dev, ++ const struct pci_device_id *ent) ++{ ++ static int cardnum; ++ int err; ++ int i; ++ ++ struct hfc_card *card = NULL; ++ struct dahdi_hfc *zthfc = 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); ++ ++ err = pci_enable_device(pci_dev); ++ if (err) ++ goto err_pci_enable_device; ++ ++ err = pci_set_dma_mask(pci_dev, PCI_DMA_32BIT); ++ if (err) { ++ 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); ++ err = pci_request_regions(pci_dev, hfc_DRIVER_NAME); ++ if (err) { ++ 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; ++ } ++ ++ card->io_mem = ioremap(card->io_bus_mem, hfc_PCI_MEM_SIZE); ++ if (!(card->io_mem)) { ++ 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); ++ ++ err = request_irq(card->pcidev->irq, &hfc_interrupt, ++ ++#if (KERNEL_VERSION(2, 6, 23) < LINUX_VERSION_CODE) ++ IRQF_SHARED, hfc_DRIVER_NAME, card); ++#else ++ SA_SHIRQ, hfc_DRIVER_NAME, card); ++#endif ++ ++ if (err) { ++ 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; i < nt_modes_count; i++) { ++ if (nt_modes[i] == card->cardnum) ++ card->nt_mode = TRUE; ++ } ++ ++ /* ++ * D Channel ++ */ ++ card->chans[D].card = card; ++ card->chans[D].name = "D"; ++ card->chans[D].status = free; ++ card->chans[D].number = D; ++ 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; ++ ++ /* ++ * B1 Channel ++ */ ++ 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; ++ ++ /* ++ * B2 Channel ++ */ ++ 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; ++ ++ /* ++ * All done ++ */ ++ ++ zthfc = kmalloc(sizeof(struct dahdi_hfc), GFP_KERNEL); ++ if (!zthfc) { ++ printk(KERN_CRIT hfc_DRIVER_PREFIX ++ "unable to kmalloc!\n"); ++ goto err_request_irq; ++ } ++ memset(zthfc, 0x0, sizeof(struct dahdi_hfc)); ++ ++ zthfc->card = card; ++ hfc_zap_initialize(zthfc); ++ card->ztdev = zthfc; ++ ++ 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); ++ SET_PROC_DIRENTRY_OWNER(card->proc_dir); ++ ++ 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; ++ ++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); ++ ++ ++ printk(KERN_INFO hfc_DRIVER_PREFIX ++ "card %d: " ++ "shutting down card at %p.\n", ++ card->cardnum, ++ card->io_mem); ++ ++ hfc_softreset(card); ++ ++ dahdi_unregister(&card->ztdev->span); ++ ++ ++ /* ++ * disable memio and bustmaster ++ */ ++ pci_write_config_word(pci_dev, PCI_COMMAND, 0); ++ ++ 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); ++ ++ kfree(card); ++} ++ ++/****************************************** ++ * Module stuff ++ ******************************************/ ++ ++static int __init hfc_init_module(void) ++{ ++ int ret; ++ ++ printk(KERN_INFO hfc_DRIVER_PREFIX ++ hfc_DRIVER_STRING " loading\n"); ++ ++#if (KERNEL_VERSION(2, 6, 26) <= LINUX_VERSION_CODE) ++ hfc_proc_zaphfc_dir = proc_mkdir(hfc_DRIVER_NAME, NULL); ++#else ++ hfc_proc_zaphfc_dir = proc_mkdir(hfc_DRIVER_NAME, proc_root_driver); ++#endif ++ ++ ret = dahdi_pci_module(&hfc_driver); ++ return ret; ++} ++ ++module_init(hfc_init_module); ++ ++static void __exit hfc_module_exit(void) ++{ ++ pci_unregister_driver(&hfc_driver); ++ ++#if (KERNEL_VERSION(2, 6, 26) <= LINUX_VERSION_CODE) ++ remove_proc_entry(hfc_DRIVER_NAME, NULL); ++#else ++ remove_proc_entry(hfc_DRIVER_NAME, proc_root_driver); ++#endif ++ ++ 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 , " ++ "Jose A. Deniz "); ++MODULE_ALIAS("vzaphfc"); ++#ifdef MODULE_LICENSE ++MODULE_LICENSE("GPL"); ++#endif ++ ++ ++module_param(modes, int, 0444); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10) ++module_param_array(nt_modes, int, &nt_modes_count, 0444); ++#else ++module_param_array(nt_modes, int, nt_modes_count, 0444); ++#endif ++ ++module_param(force_l1_up, int, 0444); ++#ifdef DEBUG ++module_param(debug_level, int, 0444); ++#endif ++ ++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"); ++#ifdef DEBUG ++MODULE_PARM_DESC(debug_level, "Debug verbosity level"); ++#endif +diff -urN dahdi-svn-orig/drivers/dahdi/zaphfc/fifo.c dahdi-svn-new/drivers/dahdi/zaphfc/fifo.c +--- dahdi-svn-orig/drivers/dahdi/zaphfc/fifo.c 1970-01-01 02:00:00.000000000 +0200 ++++ dahdi-svn-new/drivers/dahdi/zaphfc/fifo.c 2010-01-21 13:35:37.000000000 +0200 +@@ -0,0 +1,375 @@ ++/* ++ * 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 ++ ++#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; ++} ++ ++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++; ++ ++ 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; ++ } ++ ++ 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 < size; i++) ++ printk("%02x", ((u8 *)data)[i]); ++ ++ printk("\n"); ++ } ++#endif ++ ++ available_frames = hfc_fifo_free_frames(chan); ++ ++ if (available_frames >= 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 + ++ DAHDI_CHUNKSIZE + hfc_RX_FIFO_PRELOAD]; ++ memset(empty_fifo, 0x7f, sizeof(empty_fifo)); ++ ++ hfc_fifo_put(chan, empty_fifo, sizeof(empty_fifo)); ++ } ++} ++ +diff -urN dahdi-svn-orig/drivers/dahdi/zaphfc/fifo.h dahdi-svn-new/drivers/dahdi/zaphfc/fifo.h +--- dahdi-svn-orig/drivers/dahdi/zaphfc/fifo.h 1970-01-01 02:00:00.000000000 +0200 ++++ dahdi-svn-new/drivers/dahdi/zaphfc/fifo.h 2010-01-21 13:35:37.000000000 +0200 +@@ -0,0 +1,139 @@ ++/* ++ * fifo.h - Dahdi 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 "zaphfc.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 +diff -urN dahdi-svn-orig/drivers/dahdi/zaphfc/Kbuild dahdi-svn-new/drivers/dahdi/zaphfc/Kbuild +--- dahdi-svn-orig/drivers/dahdi/zaphfc/Kbuild 1970-01-01 02:00:00.000000000 +0200 ++++ dahdi-svn-new/drivers/dahdi/zaphfc/Kbuild 2010-01-21 13:35:37.000000000 +0200 +@@ -0,0 +1,10 @@ ++obj-m += zaphfc.o ++ ++EXTRA_CFLAGS := -I$(src)/.. -Wno-undef ++ ++zaphfc-objs := base.o fifo.o ++ ++$(obj)/base.o: $(src)/zaphfc.h ++$(obj)/fifo.o: $(src)/fifo.h ++ ++ +diff -urN dahdi-svn-orig/drivers/dahdi/zaphfc/zaphfc.h dahdi-svn-new/drivers/dahdi/zaphfc/zaphfc.h +--- dahdi-svn-orig/drivers/dahdi/zaphfc/zaphfc.h 1970-01-01 02:00:00.000000000 +0200 ++++ dahdi-svn-new/drivers/dahdi/zaphfc/zaphfc.h 2010-01-21 13:35:37.000000000 +0200 +@@ -0,0 +1,414 @@ ++/* ++ * zaphfc.h - Dahdi driver for HFC-S PCI A based ISDN BRI cards ++ * ++ * Dahdi port by Jose A. Deniz ++ * ++ * Copyright (C) 2009 Jose A. Deniz ++ * 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 ++ ++#define hfc_DRIVER_NAME "vzaphfc" ++#define hfc_DRIVER_PREFIX hfc_DRIVER_NAME ": " ++#define hfc_DRIVER_DESCR "HFC-S PCI A ISDN" ++#define hfc_DRIVER_VERSION "1.42" ++#define hfc_DRIVER_STRING hfc_DRIVER_DESCR " (V" hfc_DRIVER_VERSION ")" ++ ++#define hfc_MAX_BOARDS 32 ++ ++#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 (DAHDI_CHUNKSIZE + 2) ++#define hfc_RX_FIFO_PRELOAD 4 ++ ++/* HDLC STUFF */ ++#define hfc_HDLC_BUF_LEN 32 ++/* arbitrary, just the max # of byts we will send to DAHDI per call */ ++ ++ ++/* 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[DAHDI_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; ++ ++}; ++ ++typedef struct hfc_card { ++ int cardnum; ++ struct pci_dev *pcidev; ++ struct dahdi_hfc *ztdev; ++ 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 __iomem *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; ++ ++ ++ ++ int debug_event; ++ ++ spinlock_t lock; ++ unsigned int irq; ++ unsigned int iomem; ++ int ticks; ++ int clicks; ++ unsigned char *pci_io; ++ void *fifomem; /* start of the shared mem */ ++ ++ unsigned int pcibus; ++ unsigned int pcidevfn; ++ ++ int drecinframe; ++ ++ unsigned char cardno; ++ struct hfc_card *next; ++ ++} hfc_card; ++ ++typedef struct dahdi_hfc { ++ unsigned int usecount; ++ struct dahdi_span span; ++ struct dahdi_chan chans[3]; ++ struct dahdi_chan *_chans[3]; ++ struct hfc_card *card; ++ ++ /* pointer to the signalling channel for this span */ ++ struct dahdi_chan *sigchan; ++ /* nonzero means we're in the middle of sending an HDLC frame */ ++ int sigactive; ++ /* hdlc_hard_xmit() increments, hdlc_tx_frame() decrements */ ++ atomic_t hdlc_pending; ++ int frames_out; ++ int frames_in; ++ ++} dahdi_hfc; ++ ++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 +diff -urN dahdi-svn-orig/drivers/staging/echo/echo.c dahdi-svn-new/drivers/staging/echo/echo.c +--- dahdi-svn-orig/drivers/staging/echo/echo.c 1970-01-01 02:00:00.000000000 +0200 ++++ dahdi-svn-new/drivers/staging/echo/echo.c 2010-01-21 13:35:37.000000000 +0200 +@@ -0,0 +1,662 @@ ++/* ++ * 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, 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. ++ */ ++ ++/*! \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. ++*/ ++ ++#include ++#include ++#include ++ ++#include "echo.h" ++ ++#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 */ ++ ++ ++/* adapting coeffs using the traditional stochastic descent (N)LMS algorithm */ ++ ++#ifdef __bfin__ ++static inline void lms_adapt_bg(struct oslec_state *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(struct oslec_state *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 ++ ++static inline int top_bit(unsigned int bits) ++{ ++ if (bits == 0) ++ return -1; ++ else ++ return (int)fls((int32_t)bits)-1; ++} ++ ++struct oslec_state *oslec_create(int len, int adaption_mode) ++{ ++ struct oslec_state *ec; ++ int i; ++ ++ ec = kzalloc(sizeof(*ec), GFP_KERNEL); ++ if (!ec) ++ return NULL; ++ ++ ec->taps = len; ++ ec->log2taps = top_bit(len); ++ ec->curr_pos = ec->taps - 1; ++ ++ for (i = 0; i < 2; i++) { ++ ec->fir_taps16[i] = ++ kcalloc(ec->taps, sizeof(int16_t), GFP_KERNEL); ++ if (!ec->fir_taps16[i]) ++ goto error_oom; ++ } ++ ++ 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; ++ oslec_adaption_mode(ec, adaption_mode); ++ ++ ec->snapshot = kcalloc(ec->taps, sizeof(int16_t), GFP_KERNEL); ++ if (!ec->snapshot) ++ goto error_oom; ++ ++ 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; ++ ++error_oom: ++ for (i = 0; i < 2; i++) ++ kfree(ec->fir_taps16[i]); ++ ++ kfree(ec); ++ return NULL; ++} ++EXPORT_SYMBOL_GPL(oslec_create); ++ ++void oslec_free(struct oslec_state *ec) ++{ ++ int i; ++ ++ fir16_free(&ec->fir_state); ++ fir16_free(&ec->fir_state_bg); ++ for (i = 0; i < 2; i++) ++ kfree(ec->fir_taps16[i]); ++ kfree(ec->snapshot); ++ kfree(ec); ++} ++EXPORT_SYMBOL_GPL(oslec_free); ++ ++void oslec_adaption_mode(struct oslec_state *ec, int adaption_mode) ++{ ++ ec->adaption_mode = adaption_mode; ++} ++EXPORT_SYMBOL_GPL(oslec_adaption_mode); ++ ++void oslec_flush(struct oslec_state *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; ++} ++EXPORT_SYMBOL_GPL(oslec_flush); ++ ++void oslec_snapshot(struct oslec_state *ec) ++{ ++ memcpy(ec->snapshot, ec->fir_taps16[0], ec->taps * sizeof(int16_t)); ++} ++EXPORT_SYMBOL_GPL(oslec_snapshot); ++ ++/* Dual Path Echo Canceller */ ++ ++int16_t oslec_update(struct oslec_state *ec, int16_t tx, int16_t rx) ++{ ++ int32_t echo_value; ++ int clean_bg; ++ int tmp, tmp1; ++ ++ /* ++ * 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) on 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; ++ ++ /* ++ * Make sure the gain of the HPF is 1.0. This can still ++ * saturate a little under impulse conditions, and it might ++ * roll to 32768 and need clipping on sustained peak level ++ * signals. However, the scale of such clipping is small, and ++ * the error due to any saturation should not markedly affect ++ * the downstream processing. ++ */ ++ tmp -= (tmp >> 4); ++ ++ ec->rx_1 += -(ec->rx_1 >> DC_LOG2BETA) + tmp - ec->rx_2; ++ ++ /* ++ * hard limit filter to prevent clipping. Note that at this ++ * stage rx should be limited to +/- 16383 due to right shift ++ * above ++ */ ++ tmp1 = ec->rx_1 >> 15; ++ if (tmp1 > 16383) ++ tmp1 = 16383; ++ if (tmp1 < -16383) ++ tmp1 = -16383; ++ rx = tmp1; ++ 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 << (ec->log2taps-1))) >> 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) && ++ /* (ec->Lclean_bg < 0.875*ec->Lclean) */ ++ (8 * ec->Lclean_bg < 7 * ec->Lclean) && ++ /* (ec->Lclean_bg < 0.125*ec->Ltx) */ ++ (8 * ec->Lclean_bg < 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; ++} ++EXPORT_SYMBOL_GPL(oslec_update); ++ ++/* 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. ++ ++ This is one of the classic DC removal filters, adjusted to provide ++ sufficient bass rolloff to meet the above requirement to protect hybrids ++ from things that upset them. The difference between successive samples ++ produces a lousy HPF, and then a suitably placed pole flattens things out. ++ The final result is a nicely rolled off bass end. The filtering is ++ implemented with extended fractional precision, which noise shapes things, ++ giving very clean DC removal. ++*/ ++ ++int16_t oslec_hpf_tx(struct oslec_state *ec, int16_t tx) ++{ ++ int tmp, tmp1; ++ ++ if (ec->adaption_mode & ECHO_CAN_USE_TX_HPF) { ++ tmp = tx << 15; ++ ++ /* ++ * Make sure the gain of the HPF is 1.0. The first can still ++ * saturate a little under impulse conditions, and it might ++ * roll to 32768 and need clipping on sustained peak level ++ * signals. However, the scale of such clipping is small, and ++ * the error due to any saturation should not markedly affect ++ * the downstream processing. ++ */ ++ tmp -= (tmp >> 4); ++ ++ 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; ++} ++EXPORT_SYMBOL_GPL(oslec_hpf_tx); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("David Rowe"); ++MODULE_DESCRIPTION("Open Source Line Echo Canceller"); ++MODULE_VERSION("0.3.0"); +diff -urN dahdi-svn-orig/drivers/staging/echo/echo.h dahdi-svn-new/drivers/staging/echo/echo.h +--- dahdi-svn-orig/drivers/staging/echo/echo.h 1970-01-01 02:00:00.000000000 +0200 ++++ dahdi-svn-new/drivers/staging/echo/echo.h 2010-01-21 13:35:37.000000000 +0200 +@@ -0,0 +1,175 @@ ++/* ++ * 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. ++ */ ++ ++#ifndef __ECHO_H ++#define __ECHO_H ++ ++/* ++Line echo cancellation for voice ++ ++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. ++ ++ ++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. ++ ++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" ++#include "oslec.h" ++ ++/* ++ G.168 echo canceller descriptor. This defines the working state for a line ++ echo canceller. ++*/ ++struct oslec_state { ++ 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 */ ++ struct fir16_state_t fir_state; ++ struct 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; ++}; ++ ++#endif /* __ECHO_H */ +diff -urN dahdi-svn-orig/drivers/staging/echo/echo.mod.c dahdi-svn-new/drivers/staging/echo/echo.mod.c +--- dahdi-svn-orig/drivers/staging/echo/echo.mod.c 1970-01-01 02:00:00.000000000 +0200 ++++ dahdi-svn-new/drivers/staging/echo/echo.mod.c 2010-01-21 13:35:37.000000000 +0200 +@@ -0,0 +1,17 @@ ++#include ++#include ++#include ++ ++MODULE_INFO(vermagic, VERMAGIC_STRING); ++ ++struct module __this_module ++__attribute__((section(".gnu.linkonce.this_module"))) = { ++ .name = KBUILD_MODNAME, ++ .arch = MODULE_ARCH_INIT, ++}; ++ ++static const char __module_depends[] ++__used ++__attribute__((section(".modinfo"))) = ++"depends="; ++ +diff -urN dahdi-svn-orig/drivers/staging/echo/fir.h dahdi-svn-new/drivers/staging/echo/fir.h +--- dahdi-svn-orig/drivers/staging/echo/fir.h 1970-01-01 02:00:00.000000000 +0200 ++++ dahdi-svn-new/drivers/staging/echo/fir.h 2010-01-21 13:35:37.000000000 +0200 +@@ -0,0 +1,286 @@ ++/* ++ * 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. ++ */ ++ ++#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. ++ */ ++struct fir16_state_t { ++ int taps; ++ int curr_pos; ++ const int16_t *coeffs; ++ int16_t *history; ++}; ++ ++/* ++ * 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. ++ */ ++struct fir32_state_t { ++ int taps; ++ int curr_pos; ++ const int32_t *coeffs; ++ int16_t *history; ++}; ++ ++/* ++ * Floating point FIR descriptor. This defines the working state for a single ++ * instance of an FIR filter using floating point coefficients and data. ++ */ ++struct fir_float_state_t { ++ int taps; ++ int curr_pos; ++ const float *coeffs; ++ float *history; ++}; ++ ++static inline const int16_t *fir16_create(struct 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(__bfin__) ++ fir->history = kcalloc(2 * taps, sizeof(int16_t), GFP_KERNEL); ++#else ++ fir->history = kcalloc(taps, sizeof(int16_t), GFP_KERNEL); ++#endif ++ return fir->history; ++} ++ ++static inline void fir16_flush(struct fir16_state_t *fir) ++{ ++#if defined(USE_MMX) || defined(USE_SSE2) || defined(__bfin__) ++ memset(fir->history, 0, 2 * fir->taps * sizeof(int16_t)); ++#else ++ memset(fir->history, 0, fir->taps * sizeof(int16_t)); ++#endif ++} ++ ++static inline void fir16_free(struct fir16_state_t *fir) ++{ ++ kfree(fir->history); ++} ++ ++#ifdef __bfin__ ++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 ++ ++static inline int16_t fir16(struct fir16_state_t *fir, int16_t sample) ++{ ++ int32_t y; ++#if defined(USE_MMX) ++ int i; ++ union mmx_t *mmx_coeffs; ++ union mmx_t *mmx_hist; ++ ++ fir->history[fir->curr_pos] = sample; ++ fir->history[fir->curr_pos + fir->taps] = sample; ++ ++ mmx_coeffs = (union mmx_t *) fir->coeffs; ++ mmx_hist = (union 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; ++ union xmm_t *xmm_coeffs; ++ union xmm_t *xmm_hist; ++ ++ fir->history[fir->curr_pos] = sample; ++ fir->history[fir->curr_pos + fir->taps] = sample; ++ ++ xmm_coeffs = (union xmm_t *) fir->coeffs; ++ xmm_hist = (union 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(__bfin__) ++ 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); ++} ++ ++static inline const int16_t *fir32_create(struct fir32_state_t *fir, ++ const int32_t *coeffs, int taps) ++{ ++ fir->taps = taps; ++ fir->curr_pos = taps - 1; ++ fir->coeffs = coeffs; ++ fir->history = kcalloc(taps, sizeof(int16_t), GFP_KERNEL); ++ return fir->history; ++} ++ ++static inline void fir32_flush(struct fir32_state_t *fir) ++{ ++ memset(fir->history, 0, fir->taps * sizeof(int16_t)); ++} ++ ++static inline void fir32_free(struct fir32_state_t *fir) ++{ ++ kfree(fir->history); ++} ++ ++static inline int16_t fir32(struct 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); ++} ++ ++#endif +diff -urN dahdi-svn-orig/drivers/staging/echo/Kbuild dahdi-svn-new/drivers/staging/echo/Kbuild +--- dahdi-svn-orig/drivers/staging/echo/Kbuild 1970-01-01 02:00:00.000000000 +0200 ++++ dahdi-svn-new/drivers/staging/echo/Kbuild 2010-01-21 13:35:37.000000000 +0200 +@@ -0,0 +1,6 @@ ++ifdef DAHDI_USE_MMX ++EXTRA_CFLAGS += -DUSE_MMX ++endif ++ ++# An explicit 'obj-m' , unlike the Makefile ++obj-m += echo.o +diff -urN dahdi-svn-orig/drivers/staging/echo/mmx.h dahdi-svn-new/drivers/staging/echo/mmx.h +--- dahdi-svn-orig/drivers/staging/echo/mmx.h 1970-01-01 02:00:00.000000000 +0200 ++++ dahdi-svn-new/drivers/staging/echo/mmx.h 2010-01-21 13:35:37.000000000 +0200 +@@ -0,0 +1,288 @@ ++/* ++ * 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) ++ */ ++ ++union mmx_t { ++ 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 */ ++}; /* On an 8-byte (64-bit) boundary */ ++ ++/* SSE registers */ ++union xmm_t { ++ char b[16]; ++}; ++ ++ ++#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 */ +diff -urN dahdi-svn-orig/drivers/staging/echo/oslec.h dahdi-svn-new/drivers/staging/echo/oslec.h +--- dahdi-svn-orig/drivers/staging/echo/oslec.h 1970-01-01 02:00:00.000000000 +0200 ++++ dahdi-svn-new/drivers/staging/echo/oslec.h 2010-01-21 13:35:37.000000000 +0200 +@@ -0,0 +1,94 @@ ++/* ++ * OSLEC - A line echo canceller. This code is being developed ++ * against and partially complies with G168. Using code from SpanDSP ++ * ++ * Written by Steve Underwood ++ * and David Rowe ++ * ++ * Copyright (C) 2001 Steve Underwood and 2007-2008 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_H ++#define __OSLEC_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 ++ ++/** ++ * oslec_state: G.168 echo canceller descriptor. ++ * ++ * This defines the working state for a line echo canceller. ++ */ ++struct oslec_state; ++ ++/** ++ * oslec_create - Create a voice echo canceller context. ++ * @len: The length of the canceller, in samples. ++ * @return: The new canceller context, or NULL if the canceller could not be ++ * created. ++ */ ++struct oslec_state *oslec_create(int len, int adaption_mode); ++ ++/** ++ * oslec_free - Free a voice echo canceller context. ++ * @ec: The echo canceller context. ++ */ ++void oslec_free(struct oslec_state *ec); ++ ++/** ++ * oslec_flush - Flush (reinitialise) a voice echo canceller context. ++ * @ec: The echo canceller context. ++ */ ++void oslec_flush(struct oslec_state *ec); ++ ++/** ++ * oslec_adaption_mode - set the adaption mode of a voice echo canceller context. ++ * @ec The echo canceller context. ++ * @adaption_mode: The mode. ++ */ ++void oslec_adaption_mode(struct oslec_state *ec, int adaption_mode); ++ ++void oslec_snapshot(struct oslec_state *ec); ++ ++/** ++ * oslec_update: Process a sample through a voice echo canceller. ++ * @ec: The echo canceller context. ++ * @tx: The transmitted audio sample. ++ * @rx: The received audio sample. ++ * ++ * The return value is the clean (echo cancelled) received sample. ++ */ ++int16_t oslec_update(struct oslec_state *ec, int16_t tx, int16_t rx); ++ ++/** ++ * oslec_hpf_tx: Process to high pass filter the tx signal. ++ * @ec: The echo canceller context. ++ * @tx: The transmitted auio sample. ++ * ++ * The return value is the HP filtered transmit sample, send this to your D/A. ++ */ ++int16_t oslec_hpf_tx(struct oslec_state *ec, int16_t tx); ++ ++#endif /* __OSLEC_H */ --- dahdi-linux-2.2.1+dfsg.orig/debian/patches/bri_dchan +++ dahdi-linux-2.2.1+dfsg/debian/patches/bri_dchan @@ -0,0 +1,161 @@ +# 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. + +--- a/include/dahdi/kernel.h ++++ b/include/dahdi/kernel.h +@@ -389,6 +389,13 @@ struct dahdi_chan { + int statcount; + int lastnumbufs; + #endif ++#ifdef CONFIG_DAHDI_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]; + /* Specified by DAHDI */ +@@ -693,6 +700,9 @@ enum { + DAHDI_FLAGBIT_LOOPED = 18, /*!< Loopback the receive data from the channel to the transmit */ + DAHDI_FLAGBIT_MTP2 = 19, /*!< Repeats last message in buffer and also discards repeating messages sent to us */ + DAHDI_FLAGBIT_HDLC56 = 20, /*!< Sets the given channel (if in HDLC mode) to use 56K HDLC instead of 64K */ ++#if defined(CONFIG_DAHDI_BRI_DCHANS) ++ DAHDI_FLAGBIT_BRIDCHAN = 21, /*!< hardhdlc-like handling of the D channel */ ++#endif + }; + + /* map flagbits to flag masks */ +@@ -731,6 +741,7 @@ enum { + #define DAHDI_FLAG_LOOPED DAHDI_FLAG(LOOPED) + #define DAHDI_FLAG_MTP2 DAHDI_FLAG(MTP2) + #define DAHDI_FLAG_HDLC56 DAHDI_FLAG(HDLC56) ++#define DAHDI_FLAG_BRIDCHAN DAHDI_FLAG(BRIDCHAN) + + struct dahdi_span { + spinlock_t lock; +--- a/include/dahdi/dahdi_config.h ++++ b/include/dahdi/dahdi_config.h +@@ -167,4 +167,10 @@ + */ + /* #define OPTIMIZE_CHANMUTE */ + ++/* ++ * Uncomment the following for BRI D channels ++ * ++ */ ++#define CONFIG_DAHDI_BRI_DCHANS ++ + #endif +--- a/drivers/dahdi/dahdi-base.c ++++ b/drivers/dahdi/dahdi-base.c +@@ -6014,11 +6014,40 @@ static inline void __dahdi_getbuf_chunk( + *(txb++) = fasthdlc_tx_run_nocheck(&ms->txhdlc); + } + bytes -= left; ++#ifdef CONFIG_DAHDI_BRI_DCHANS ++ } else if (test_bit(DAHDI_FLAGBIT_BRIDCHAN, &ms->flags)) { ++ /* ++ * 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_DAHDI_BRI_DCHANS) ++ ms->bytes2transmit=DAHDI_CHUNKSIZE; ++#endif + } + /* Check buffer status */ + if (ms->writeidx[ms->outwritebuf] >= ms->writen[ms->outwritebuf]) { +@@ -6077,6 +6106,17 @@ out in the later versions, and is put ba + /* Transmit a flag if this is an HDLC channel */ + if (ms->flags & DAHDI_FLAG_HDLC) + fasthdlc_tx_frame_nocheck(&ms->txhdlc); ++#if defined(CONFIG_DAHDI_BRI_DCHANS) ++ if (test_bit(DAHDI_FLAGBIT_BRIDCHAN, &ms->flags)) { ++ // 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_DAHDI_NET + if (ms->flags & DAHDI_FLAG_NETDEV) + netif_wake_queue(ztchan_to_dev(ms)); +@@ -6137,6 +6177,12 @@ out in the later versions, and is put ba + memset(txb, 0xFF, bytes); + } + bytes = 0; ++#if defined(CONFIG_DAHDI_BRI_DCHANS) ++ } else if (test_bit(DAHDI_FLAGBIT_BRIDCHAN, &ms->flags)) { ++ ms->bytes2transmit = 0; ++ ms->eoftx = 0; ++ bytes = 0; ++#endif + } else { + memset(txb, DAHDI_LIN2X(0, ms), bytes); /* Lastly we use silence on telephony channels */ + bytes = 0; +@@ -6993,6 +7039,14 @@ static inline void __putbuf_chunk(struct + int res; + int left, x; + ++#if defined(CONFIG_DAHDI_BRI_DCHANS) ++ if (test_bit(DAHDI_FLAGBIT_BRIDCHAN, &ms->flags)) { ++ bytes = ms->bytes2receive; ++ if (bytes < 1) return; ++// printk(KERN_CRIT "bytes2receive %d\n",ms->bytes2receive); ++ } ++#endif ++ + while(bytes) { + #if defined(CONFIG_DAHDI_NET) || defined(CONFIG_DAHDI_PPP) + skb = NULL; +@@ -7050,6 +7104,19 @@ static inline void __putbuf_chunk(struct + } + } + } ++#ifdef CONFIG_DAHDI_BRI_DCHANS ++ } else if (test_bit(DAHDI_FLAGBIT_BRIDCHAN, &ms->flags)) { ++ 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); --- dahdi-linux-2.2.1+dfsg.orig/debian/patches/oslec_zaptap +++ dahdi-linux-2.2.1+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;x ++ * ++ * Copyright (C) 2009, Jose A. Deniz ++ * 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 "zaphfc.h" ++#include "fifo.h" ++ ++#if CONFIG_PCI ++ ++#define DAHDI_B1 0 ++#define DAHDI_B2 1 ++#define DAHDI_D 2 ++ ++#define D 0 ++#define B1 1 ++#define B2 2 ++ ++/* ++ * Mode Te for all ++ */ ++static int modes; ++static int nt_modes[hfc_MAX_BOARDS]; ++static int nt_modes_count; ++static int force_l1_up; ++static struct proc_dir_entry *hfc_proc_zaphfc_dir; ++ ++#ifdef DEBUG ++int debug_level; ++#endif ++ ++#ifndef FALSE ++#define FALSE 0 ++#endif ++#ifndef TRUE ++#define TRUE (!FALSE) ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) ++#define SET_PROC_DIRENTRY_OWNER(p) do { (p)->owner = THIS_MODULE; } while(0); ++#else ++#define SET_PROC_DIRENTRY_OWNER(p) do { } while(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); ++ ++static 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); ++ ++/* ++ * Softreset procedure. Put it on, wait and off again ++ */ ++ hfc_outb(card, hfc_CIRM, hfc_CIRM_RESET); ++ udelay(6); ++ hfc_outb(card, hfc_CIRM, 0); ++ ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ schedule_timeout((hfc_RESET_DELAY * HZ) / 1000); ++} ++ ++static void hfc_resetCard(struct hfc_card *card) ++{ ++ card->regs.m1 = 0; ++ hfc_outb(card, hfc_INT_M1, card->regs.m1); ++ ++ card->regs.m2 = 0; ++ hfc_outb(card, hfc_INT_M2, card->regs.m2); ++ ++ 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_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_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)) { ++ ++ hfc_outb(card, hfc_STATES, hfc_STATES_DO_ACTION | ++ hfc_STATES_ACTIVATE| ++ hfc_STATES_NT_G2_G3); ++ ++ /* ++ * 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 ++ } ++} ++ ++ ++/******************* ++ * Dahdi interface * ++ *******************/ ++ ++static int hfc_zap_open(struct dahdi_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; ++ try_module_get(THIS_MODULE); ++ spin_unlock(&chan->lock); ++ ++ switch (chan->number) { ++ case D: ++ break; ++ ++ case B1: ++ card->regs.m2 |= hfc_M2_PROC_TRANS; ++ /* ++ * Enable transparent mode ++ */ ++ card->regs.ctmt |= hfc_CTMT_TRANSB1; ++ /* ++ * Reversed bit order ++ */ ++ card->regs.cirm |= hfc_CIRM_B1_REV; ++ /* ++ * Enable transmission ++ */ ++ card->regs.sctrl |= hfc_SCTRL_B1_ENA; ++ /* ++ * Enable reception ++ */ ++ card->regs.sctrl_r |= hfc_SCTRL_R_B1_ENA; ++ break; ++ ++ case B2: ++ card->regs.m2 |= hfc_M2_PROC_TRANS; ++ 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 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 dahdi_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: ++ 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: ++ 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); ++ ++ module_put(THIS_MODULE); ++ ++ 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 dahdi_chan *chan, int bits) ++{ ++ return 0; ++} ++ ++static int hfc_zap_ioctl(struct dahdi_chan *chan, ++ unsigned int cmd, unsigned long data) ++{ ++ switch (cmd) { ++ ++ default: ++ return -ENOTTY; ++ } ++ ++ return 0; ++} ++ ++static void hfc_hdlc_hard_xmit(struct dahdi_chan *d_chan) ++{ ++ struct hfc_chan_duplex *chan = d_chan->pvt; ++ struct hfc_card *card = chan->card; ++ struct dahdi_hfc *hfccard = card->ztdev; ++ ++ atomic_inc(&hfccard->hdlc_pending); ++ ++} ++ ++static int hfc_zap_startup(struct dahdi_span *span) ++{ ++ struct dahdi_hfc *zthfc = span->pvt; ++ struct hfc_card *hfctmp = zthfc->card; ++ int alreadyrunning; ++ ++ if (!hfctmp) { ++ printk(KERN_INFO hfc_DRIVER_PREFIX ++ "card %d: " ++ "no card for span at startup!\n", ++ hfctmp->cardnum); ++ } ++ ++ alreadyrunning = span->flags & DAHDI_FLAG_RUNNING; ++ ++ if (!alreadyrunning) ++ span->flags |= DAHDI_FLAG_RUNNING; ++ ++ return 0; ++} ++ ++static int hfc_zap_shutdown(struct dahdi_span *span) ++{ ++ return 0; ++} ++ ++static int hfc_zap_maint(struct dahdi_span *span, int cmd) ++{ ++ return 0; ++} ++ ++static int hfc_zap_chanconfig(struct dahdi_chan *d_chan, int sigtype) ++{ ++ struct hfc_chan_duplex *chan = d_chan->pvt; ++ struct hfc_card *card = chan->card; ++ struct dahdi_hfc *hfccard = card->ztdev; ++ ++ if ((sigtype == DAHDI_SIG_HARDHDLC) && (hfccard->sigchan == d_chan)) { ++ hfccard->sigactive = 0; ++ atomic_set(&hfccard->hdlc_pending, 0); ++ } ++ ++ return 0; ++} ++ ++static int hfc_zap_spanconfig(struct dahdi_span *span, ++ struct dahdi_lineconfig *lc) ++{ ++ span->lineconfig = lc->lineconfig; ++ ++ return 0; ++} ++ ++static int hfc_zap_initialize(struct dahdi_hfc *hfccard) ++{ ++ struct hfc_card *hfctmp = hfccard->card; ++ int i; ++ ++ memset(&hfccard->span, 0x0, sizeof(struct dahdi_span)); ++ sprintf(hfccard->span.name, "ZTHFC%d", hfctmp->cardnum + 1); ++ sprintf(hfccard->span.desc, ++ "HFC-S PCI A ISDN card %d [%s] ", ++ hfctmp->cardnum, ++ hfctmp->nt_mode ? "NT" : "TE"); ++ hfccard->span.spantype = hfctmp->nt_mode ? "NT" : "TE"; ++ hfccard->span.manufacturer = "Cologne Chips"; ++ hfccard->span.spanconfig = hfc_zap_spanconfig; ++ hfccard->span.chanconfig = hfc_zap_chanconfig; ++ hfccard->span.startup = hfc_zap_startup; ++ hfccard->span.shutdown = hfc_zap_shutdown; ++ hfccard->span.maint = hfc_zap_maint; ++ hfccard->span.rbsbits = hfc_zap_rbsbits; ++ hfccard->span.open = hfc_zap_open; ++ hfccard->span.close = hfc_zap_close; ++ hfccard->span.ioctl = hfc_zap_ioctl; ++ hfccard->span.hdlc_hard_xmit = hfc_hdlc_hard_xmit; ++ hfccard->span.flags = 0; ++ hfccard->span.irq = hfctmp->pcidev->irq; ++ dahdi_copy_string(hfccard->span.devicetype, "HFC-S PCI-A ISDN", ++ sizeof(hfccard->span.devicetype)); ++ sprintf(hfccard->span.location, "PCI Bus %02d Slot %02d", ++ hfctmp->pcidev->bus->number, ++ PCI_SLOT(hfctmp->pcidev->devfn) + 1); ++ hfccard->span.chans = hfccard->_chans; ++ hfccard->span.channels = 3; ++ for (i = 0; i < hfccard->span.channels; i++) ++ hfccard->_chans[i] = &hfccard->chans[i]; ++ hfccard->span.deflaw = DAHDI_LAW_ALAW; ++ hfccard->span.linecompat = DAHDI_CONFIG_AMI | DAHDI_CONFIG_CCS; ++ hfccard->span.offset = 0; ++ init_waitqueue_head(&hfccard->span.maintq); ++ hfccard->span.pvt = hfccard; ++ ++ for (i = 0; i < hfccard->span.channels; i++) { ++ memset(&hfccard->chans[i], 0x0, sizeof(struct dahdi_chan)); ++ ++ sprintf(hfccard->chans[i].name, ++ "ZTHFC%d/%d/%d", ++ hfctmp->cardnum + 1, 0, i + 1); ++ ++ printk(KERN_INFO hfc_DRIVER_PREFIX ++ "card %d: " ++ "registered %s\n", ++ hfctmp->cardnum, ++ hfccard->chans[i].name); ++ ++ if (i == hfccard->span.channels - 1) { ++ hfccard->chans[i].sigcap = DAHDI_SIG_HARDHDLC; ++ hfccard->sigchan = &hfccard->chans[D]; ++ hfccard->sigactive = 0; ++ atomic_set(&hfccard->hdlc_pending, 0); ++ } else { ++ hfccard->chans[i].sigcap = ++ DAHDI_SIG_CLEAR | DAHDI_SIG_DACS; ++ } ++ ++ hfccard->chans[i].chanpos = i + 1; ++ } ++ ++ hfccard->chans[DAHDI_D].readchunk = ++ hfctmp->chans[D].rx.zaptel_buffer; ++ ++ hfccard->chans[DAHDI_D].writechunk = ++ hfctmp->chans[D].tx.zaptel_buffer; ++ ++ hfccard->chans[DAHDI_D].pvt = &hfctmp->chans[D]; ++ ++ hfccard->chans[DAHDI_B1].readchunk = ++ hfctmp->chans[B1].rx.zaptel_buffer; ++ ++ hfccard->chans[DAHDI_B1].writechunk = ++ hfctmp->chans[B1].tx.zaptel_buffer; ++ ++ hfccard->chans[DAHDI_B1].pvt = &hfctmp->chans[B1]; ++ ++ hfccard->chans[DAHDI_B2].readchunk = ++ hfctmp->chans[B2].rx.zaptel_buffer; ++ ++ hfccard->chans[DAHDI_B2].writechunk = ++ hfctmp->chans[B2].tx.zaptel_buffer; ++ ++ hfccard->chans[DAHDI_B2].pvt = &hfctmp->chans[B2]; ++ ++ if (dahdi_register(&hfccard->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, DAHDI_CHUNKSIZE); ++} ++ ++static void hfc_zap_receive(struct hfc_chan_simplex *chan) ++{ ++ hfc_fifo_get(chan, chan->zaptel_buffer, DAHDI_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); ++ ++#if (KERNEL_VERSION(2, 6, 24) < LINUX_VERSION_CODE) ++static irqreturn_t hfc_interrupt(int irq, void *dev_id) ++#else ++static irqreturn_t hfc_interrupt(int irq, void *dev_id, struct pt_regs *regs) ++#endif ++{ ++ 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 >= DAHDI_CHUNKSIZE + hfc_RX_FIFO_PRELOAD) { ++ card->ticks = 0; ++ ++ if (available_bytes > DAHDI_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 - ++ (DAHDI_CHUNKSIZE + ++ hfc_RX_FIFO_PRELOAD)); ++ } ++#endif ++ } else { ++ hfc_handle_voice(card); ++ } ++ } ++ ++ card->ticks++; ++} ++ ++ ++static void hfc_handle_voice(struct hfc_card *card) ++{ ++ struct dahdi_hfc *hfccard = card->ztdev; ++ int frame_left, res; ++ unsigned char buf[hfc_HDLC_BUF_LEN]; ++ unsigned int size = sizeof(buf) / sizeof(buf[0]); ++ ++ ++ if (card->chans[B1].status != open_voice && ++ card->chans[B2].status != open_voice) ++ return; ++ ++ dahdi_transmit(&hfccard->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); ++ ++ /* ++ * dahdi hdlc frame tx ++ */ ++ ++ if (atomic_read(&hfccard->hdlc_pending)) { ++ hfc_check_l1_up(card); ++ res = dahdi_hdlc_getbuf(hfccard->sigchan, buf, &size); ++ if (size > 0) { ++ hfccard->sigactive = 1; ++ memcpy(card->chans[D].tx.ugly_framebuf + ++ card->chans[D].tx.ugly_framebuf_size, ++ buf, size); ++ card->chans[D].tx.ugly_framebuf_size += size; ++ if (res != 0) { ++ hfc_fifo_put_frame(&card->chans[D].tx, ++ card->chans[D].tx.ugly_framebuf, ++ card->chans[D].tx.ugly_framebuf_size); ++ ++hfccard->frames_out; ++ hfccard->sigactive = 0; ++ card->chans[D].tx.ugly_framebuf_size ++ = 0; ++ atomic_dec(&hfccard->hdlc_pending); ++ } ++ } ++ } ++ /* ++ * dahdi hdlc frame tx done ++ */ ++ ++ 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 ++ */ ++ dahdi_ec_chunk(&hfccard->chans[DAHDI_B1], ++ card->chans[B1].rx.zaptel_buffer, ++ card->chans[B1].tx.zaptel_buffer); ++ dahdi_ec_chunk(&hfccard->chans[DAHDI_B2], ++ card->chans[B2].rx.zaptel_buffer, ++ card->chans[B2].tx.zaptel_buffer); ++ ++ /* ++ * dahdi hdlc frame rx ++ */ ++ if (hfc_fifo_has_frames(&card->chans[D].rx)) ++ hfc_frame_arrived(&card->chans[D]); ++ ++ if (card->chans[D].rx.ugly_framebuf_size) { ++ frame_left = card->chans[D].rx.ugly_framebuf_size - ++ card->chans[D].rx.ugly_framebuf_off ; ++ if (frame_left > hfc_HDLC_BUF_LEN) { ++ dahdi_hdlc_putbuf(hfccard->sigchan, ++ card->chans[D].rx.ugly_framebuf + ++ card->chans[D].rx.ugly_framebuf_off, ++ hfc_HDLC_BUF_LEN); ++ card->chans[D].rx.ugly_framebuf_off += ++ hfc_HDLC_BUF_LEN; ++ } else { ++ dahdi_hdlc_putbuf(hfccard->sigchan, ++ card->chans[D].rx.ugly_framebuf + ++ card->chans[D].rx.ugly_framebuf_off, ++ frame_left); ++ dahdi_hdlc_finish(hfccard->sigchan); ++ card->chans[D].rx.ugly_framebuf_size = 0; ++ card->chans[D].rx.ugly_framebuf_off = 0; ++ } ++ } ++ /* ++ * dahdi hdlc frame rx done ++ */ ++ ++ if (hfccard->span.flags & DAHDI_FLAG_RUNNING) ++ dahdi_receive(&hfccard->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); ++ ++ ++ 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); ++ ++ ++ continue; ++ } ++ ++ if (chan->open_by_zaptel && ++ card->chans[D].rx.ugly_framebuf_size) { ++ ++ /* ++ * We have to wait for Dahdi 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); ++ ++ ++ continue; ++ } ++ ++ ++ /* ++ * HFC does the checksum ++ */ ++#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; ++ } ++ } ++ } ++ ++ if (!antiloop) ++ printk(KERN_CRIT hfc_DRIVER_PREFIX ++ "card %d: " ++ "Infinite loop detected\n", ++ card->cardnum); ++} ++ ++/****************************************** ++ * Module initialization and cleanup ++ ******************************************/ ++ ++static int __devinit hfc_probe(struct pci_dev *pci_dev, ++ const struct pci_device_id *ent) ++{ ++ static int cardnum; ++ int err; ++ int i; ++ ++ struct hfc_card *card = NULL; ++ struct dahdi_hfc *zthfc = 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); ++ ++ err = pci_enable_device(pci_dev); ++ if (err) ++ goto err_pci_enable_device; ++ ++ err = pci_set_dma_mask(pci_dev, PCI_DMA_32BIT); ++ if (err) { ++ 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); ++ err = pci_request_regions(pci_dev, hfc_DRIVER_NAME); ++ if (err) { ++ 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; ++ } ++ ++ card->io_mem = ioremap(card->io_bus_mem, hfc_PCI_MEM_SIZE); ++ if (!(card->io_mem)) { ++ 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); ++ ++ err = request_irq(card->pcidev->irq, &hfc_interrupt, ++ ++#if (KERNEL_VERSION(2, 6, 23) < LINUX_VERSION_CODE) ++ IRQF_SHARED, hfc_DRIVER_NAME, card); ++#else ++ SA_SHIRQ, hfc_DRIVER_NAME, card); ++#endif ++ ++ if (err) { ++ 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; i < nt_modes_count; i++) { ++ if (nt_modes[i] == card->cardnum) ++ card->nt_mode = TRUE; ++ } ++ ++ /* ++ * D Channel ++ */ ++ card->chans[D].card = card; ++ card->chans[D].name = "D"; ++ card->chans[D].status = free; ++ card->chans[D].number = D; ++ 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; ++ ++ /* ++ * B1 Channel ++ */ ++ 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; ++ ++ /* ++ * B2 Channel ++ */ ++ 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; ++ ++ /* ++ * All done ++ */ ++ ++ zthfc = kmalloc(sizeof(struct dahdi_hfc), GFP_KERNEL); ++ if (!zthfc) { ++ printk(KERN_CRIT hfc_DRIVER_PREFIX ++ "unable to kmalloc!\n"); ++ goto err_request_irq; ++ } ++ memset(zthfc, 0x0, sizeof(struct dahdi_hfc)); ++ ++ zthfc->card = card; ++ hfc_zap_initialize(zthfc); ++ card->ztdev = zthfc; ++ ++ 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); ++ SET_PROC_DIRENTRY_OWNER(card->proc_dir); ++ ++ 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; ++ ++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); ++ ++ ++ printk(KERN_INFO hfc_DRIVER_PREFIX ++ "card %d: " ++ "shutting down card at %p.\n", ++ card->cardnum, ++ card->io_mem); ++ ++ hfc_softreset(card); ++ ++ dahdi_unregister(&card->ztdev->span); ++ ++ ++ /* ++ * disable memio and bustmaster ++ */ ++ pci_write_config_word(pci_dev, PCI_COMMAND, 0); ++ ++ 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); ++ ++ kfree(card); ++} ++ ++/****************************************** ++ * Module stuff ++ ******************************************/ ++ ++static int __init hfc_init_module(void) ++{ ++ int ret; ++ ++ printk(KERN_INFO hfc_DRIVER_PREFIX ++ hfc_DRIVER_STRING " loading\n"); ++ ++#if (KERNEL_VERSION(2, 6, 26) <= LINUX_VERSION_CODE) ++ hfc_proc_zaphfc_dir = proc_mkdir(hfc_DRIVER_NAME, NULL); ++#else ++ hfc_proc_zaphfc_dir = proc_mkdir(hfc_DRIVER_NAME, proc_root_driver); ++#endif ++ ++ ret = dahdi_pci_module(&hfc_driver); ++ return ret; ++} ++ ++module_init(hfc_init_module); ++ ++static void __exit hfc_module_exit(void) ++{ ++ pci_unregister_driver(&hfc_driver); ++ ++#if (KERNEL_VERSION(2, 6, 26) <= LINUX_VERSION_CODE) ++ remove_proc_entry(hfc_DRIVER_NAME, NULL); ++#else ++ remove_proc_entry(hfc_DRIVER_NAME, proc_root_driver); ++#endif ++ ++ 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 , " ++ "Jose A. Deniz "); ++MODULE_ALIAS("vzaphfc"); ++#ifdef MODULE_LICENSE ++MODULE_LICENSE("GPL"); ++#endif ++ ++ ++module_param(modes, int, 0444); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10) ++module_param_array(nt_modes, int, &nt_modes_count, 0444); ++#else ++module_param_array(nt_modes, int, nt_modes_count, 0444); ++#endif ++ ++module_param(force_l1_up, int, 0444); ++#ifdef DEBUG ++module_param(debug_level, int, 0444); ++#endif ++ ++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"); ++#ifdef DEBUG ++MODULE_PARM_DESC(debug_level, "Debug verbosity level"); ++#endif +diff -Npur old/drivers/dahdi/zaphfc/fifo.c new/drivers/dahdi/zaphfc/fifo.c +--- old/drivers/dahdi/zaphfc/fifo.c 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/dahdi/zaphfc/fifo.c 2009-08-07 08:08:57.000000000 +0300 +@@ -0,0 +1,375 @@ ++/* ++ * 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 ++ ++#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; ++} ++ ++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++; ++ ++ 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; ++ } ++ ++ 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 < size; i++) ++ printk("%02x", ((u8 *)data)[i]); ++ ++ printk("\n"); ++ } ++#endif ++ ++ available_frames = hfc_fifo_free_frames(chan); ++ ++ if (available_frames >= 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 + ++ DAHDI_CHUNKSIZE + hfc_RX_FIFO_PRELOAD]; ++ memset(empty_fifo, 0x7f, sizeof(empty_fifo)); ++ ++ hfc_fifo_put(chan, empty_fifo, sizeof(empty_fifo)); ++ } ++} ++ +diff -Npur old/drivers/dahdi/zaphfc/fifo.h new/drivers/dahdi/zaphfc/fifo.h +--- old/drivers/dahdi/zaphfc/fifo.h 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/dahdi/zaphfc/fifo.h 2009-08-07 08:08:57.000000000 +0300 +@@ -0,0 +1,139 @@ ++/* ++ * fifo.h - Dahdi 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 "zaphfc.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 +diff -Npur old/drivers/dahdi/zaphfc/Kbuild new/drivers/dahdi/zaphfc/Kbuild +--- old/drivers/dahdi/zaphfc/Kbuild 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/dahdi/zaphfc/Kbuild 2009-08-07 08:08:57.000000000 +0300 +@@ -0,0 +1,10 @@ ++obj-m += zaphfc.o ++ ++EXTRA_CFLAGS := -I$(src)/.. -Wno-undef ++ ++zaphfc-objs := base.o fifo.o ++ ++$(obj)/base.o: $(src)/zaphfc.h ++$(obj)/fifo.o: $(src)/fifo.h ++ ++ +diff -Npur old/drivers/dahdi/zaphfc/Makefile new/drivers/dahdi/zaphfc/Makefile +--- old/drivers/dahdi/zaphfc/Makefile 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/dahdi/zaphfc/Makefile 2009-08-07 02:35:42.000000000 +0300 +@@ -0,0 +1,7 @@ ++ifdef KBUILD_EXTMOD ++# We only get here on kernels 2.6.0-2.6.9 . ++# For newer kernels, Kbuild will be included directly by the kernel ++# build system. ++include $(src)/Kbuild ++else ++endif +diff -Npur old/drivers/dahdi/zaphfc/zaphfc.h new/drivers/dahdi/zaphfc/zaphfc.h +--- old/drivers/dahdi/zaphfc/zaphfc.h 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/dahdi/zaphfc/zaphfc.h 2009-08-07 08:08:57.000000000 +0300 +@@ -0,0 +1,414 @@ ++/* ++ * zaphfc.h - Dahdi driver for HFC-S PCI A based ISDN BRI cards ++ * ++ * Dahdi port by Jose A. Deniz ++ * ++ * Copyright (C) 2009 Jose A. Deniz ++ * 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 ++ ++#define hfc_DRIVER_NAME "vzaphfc" ++#define hfc_DRIVER_PREFIX hfc_DRIVER_NAME ": " ++#define hfc_DRIVER_DESCR "HFC-S PCI A ISDN" ++#define hfc_DRIVER_VERSION "1.42" ++#define hfc_DRIVER_STRING hfc_DRIVER_DESCR " (V" hfc_DRIVER_VERSION ")" ++ ++#define hfc_MAX_BOARDS 32 ++ ++#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 (DAHDI_CHUNKSIZE + 2) ++#define hfc_RX_FIFO_PRELOAD 4 ++ ++/* HDLC STUFF */ ++#define hfc_HDLC_BUF_LEN 32 ++/* arbitrary, just the max # of byts we will send to DAHDI per call */ ++ ++ ++/* 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[DAHDI_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; ++ ++}; ++ ++typedef struct hfc_card { ++ int cardnum; ++ struct pci_dev *pcidev; ++ struct dahdi_hfc *ztdev; ++ 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 __iomem *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; ++ ++ ++ ++ int debug_event; ++ ++ spinlock_t lock; ++ unsigned int irq; ++ unsigned int iomem; ++ int ticks; ++ int clicks; ++ unsigned char *pci_io; ++ void *fifomem; /* start of the shared mem */ ++ ++ unsigned int pcibus; ++ unsigned int pcidevfn; ++ ++ int drecinframe; ++ ++ unsigned char cardno; ++ struct hfc_card *next; ++ ++} hfc_card; ++ ++typedef struct dahdi_hfc { ++ unsigned int usecount; ++ struct dahdi_span span; ++ struct dahdi_chan chans[3]; ++ struct dahdi_chan *_chans[3]; ++ struct hfc_card *card; ++ ++ /* pointer to the signalling channel for this span */ ++ struct dahdi_chan *sigchan; ++ /* nonzero means we're in the middle of sending an HDLC frame */ ++ int sigactive; ++ /* hdlc_hard_xmit() increments, hdlc_tx_frame() decrements */ ++ atomic_t hdlc_pending; ++ int frames_out; ++ int frames_in; ++ ++} dahdi_hfc; ++ ++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 --- dahdi-linux-2.2.1+dfsg.orig/debian/patches/series +++ dahdi-linux-2.2.1+dfsg/debian/patches/series @@ -0,0 +1,6 @@ +# Mega-patch of extra drivers: +dahdi_linux_extra +no_firmware_download +mmx_auto +uk_rotary +xpp_udev_rules_2632 --- dahdi-linux-2.2.1+dfsg.orig/debian/patches/xpp_udev_rules_2632 +++ dahdi-linux-2.2.1+dfsg/debian/patches/xpp_udev_rules_2632 @@ -0,0 +1,55 @@ +Subject: Fix udev rules to work with more recent kernels. +From: Tzafrir Cohen +Date: Mon, 4 Jan 2010 17:01:34 +0000 +Origin: http://svn.digium.com/svn/dahdi/linux/trunk@7753 +Bug-Debian: http://bugs.debian.org/562024 + +Patch currently applied in upstream trunk, and may be applied later in +upstream branch 2.2, pending testing. For Debian it is safer, as the older +udevs are not present (unless you use Etch or the likes). + +Note that this change will not work on most older systems (e.g.: +Centos5, with its udev 0.95) if the matching change of xpp_fxloader in +tools (r7752) is not applied. + +* Replace deprecated 'BUS' with 'SUBSYSTEM' +* Sysfs{} should be replaced with ATTR{}. Sadly the former is not + supported in recent udev, and the latter is not supported in udev + 0.95. Workaround: use PRODUCT from the environment. +* Use a single rule. +* While we're at it, add the missing comma to the astribank_hook line + (which surprisingly worked without it). + + +--- + drivers/dahdi/xpp/xpp.rules | 13 ++++--------- + 1 files changed, 4 insertions(+), 9 deletions(-) + +diff --git a/drivers/dahdi/xpp/xpp.rules b/drivers/dahdi/xpp/xpp.rules +index 300a437..93f772d 100644 +--- a/drivers/dahdi/xpp/xpp.rules ++++ b/drivers/dahdi/xpp/xpp.rules +@@ -1,16 +1,11 @@ +-BUS!="usb", ACTION!="add", GOTO="xpp_usb_add_end" +-KERNEL=="*_ep*", GOTO="xpp_usb_add_end" +-KERNEL=="[0-9]*", GOTO="xpp_usb_add_end" +- + # Load firmware into the Xorcom Astribank device: +-SYSFS{idVendor}=="e4e4", SYSFS{idProduct}=="11[3456][013]", \ +- RUN+="/usr/share/dahdi/xpp_fxloader udev $sysfs{idVendor}/$sysfs{idProduct}/$sysfs{bcdDevice}" +- +-LABEL="xpp_usb_add_end" ++SUBSYSTEM=="usb", ACTION=="add", \ ++ENV{PRODUCT}=="e4e4/11[3456][013]/*", ENV{DEVTYPE}!="usb_interface", \ ++ RUN+="/usr/share/dahdi/xpp_fxloader udev $env{PRODUCT}" + + # Hotplug hook for Astribank up/down + # If you need this functionality, copy the astribank_hook.sample + # to $XPP_INIT_DIR/astribank_hook + # + # By default XPP_INIT_DIR="/usr/share/dahdi" +-KERNEL=="xbus*" RUN+="%E{XPP_INIT_DIR}/astribank_hook udev $kernel $sysfs{status} $sysfs{connector}" ++KERNEL=="xbus*", RUN+="%E{XPP_INIT_DIR}/astribank_hook udev $kernel $sysfs{status} $sysfs{connector}" +-- +1.6.5 + --- dahdi-linux-2.2.1+dfsg.orig/debian/patches/oslec_kbuild +++ dahdi-linux-2.2.1+dfsg/debian/patches/oslec_kbuild @@ -0,0 +1,12 @@ +Add a Kbuild file to oslec. It has a very similar Makefile +but in it, the build is conditioned on a certain Kconfig variable. + +--- /dev/null ++++ b/drivers/staging/echo/Kbuild +@@ -0,0 +1,6 @@ ++ifdef DAHDI_USE_MMX ++EXTRA_CFLAGSi += USE_MMX ++endif ++ ++# An explicit 'obj-m' , unlike the Makefile ++obj-m += echo.o --- dahdi-linux-2.2.1+dfsg.orig/debian/patches/oslec_kernelorg +++ dahdi-linux-2.2.1+dfsg/debian/patches/oslec_kernelorg @@ -0,0 +1,10429 @@ +The oslec (echo) directory from from staging in linux-2.6.28-rc2 + +diff -Nurp base/drivers/staging/echo/bit_operations.h new/drivers/staging/echo/bit_operations.h +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/bit_operations.h 2008-10-30 17:13:48.000000000 +0200 +@@ -0,0 +1,228 @@ ++/* ++ * 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_ ++ ++#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; ++} ++ ++/*! \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; ++} ++#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; ++} ++ ++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; ++} ++#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 ++} ++ ++/*! \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)); ++} ++ ++/*! \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 ++} ++ ++/*! \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; ++} ++ ++/*! \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; ++} ++ ++/*! \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; ++} ++ ++#endif ++/*- End of file ------------------------------------------------------------*/ +diff -Nurp base/drivers/staging/echo/echo.c new/drivers/staging/echo/echo.c +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/echo.c 2008-10-26 05:10:13.000000000 +0200 +@@ -0,0 +1,639 @@ ++/* ++ * 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, 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. ++*/ ++ ++#include /* We're doing kernel work */ ++#include ++#include ++#include ++ ++#include "bit_operations.h" ++#include "echo.h" ++ ++#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 __bfin__ ++static void __inline__ lms_adapt_bg(struct oslec_state *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(struct oslec_state *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 ++ ++struct oslec_state *oslec_create(int len, int adaption_mode) ++{ ++ struct oslec_state *ec; ++ int i; ++ ++ ec = kzalloc(sizeof(*ec), GFP_KERNEL); ++ if (!ec) ++ return NULL; ++ ++ ec->taps = len; ++ ec->log2taps = top_bit(len); ++ ec->curr_pos = ec->taps - 1; ++ ++ for (i = 0; i < 2; i++) { ++ ec->fir_taps16[i] = ++ kcalloc(ec->taps, sizeof(int16_t), GFP_KERNEL); ++ if (!ec->fir_taps16[i]) ++ goto error_oom; ++ } ++ ++ 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; ++ oslec_adaption_mode(ec, adaption_mode); ++ ++ ec->snapshot = kcalloc(ec->taps, sizeof(int16_t), GFP_KERNEL); ++ if (!ec->snapshot) ++ goto error_oom; ++ ++ 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; ++ ++ error_oom: ++ for (i = 0; i < 2; i++) ++ kfree(ec->fir_taps16[i]); ++ ++ kfree(ec); ++ return NULL; ++} ++ ++EXPORT_SYMBOL_GPL(oslec_create); ++ ++void oslec_free(struct oslec_state *ec) ++{ ++ int i; ++ ++ fir16_free(&ec->fir_state); ++ fir16_free(&ec->fir_state_bg); ++ for (i = 0; i < 2; i++) ++ kfree(ec->fir_taps16[i]); ++ kfree(ec->snapshot); ++ kfree(ec); ++} ++ ++EXPORT_SYMBOL_GPL(oslec_free); ++ ++void oslec_adaption_mode(struct oslec_state *ec, int adaption_mode) ++{ ++ ec->adaption_mode = adaption_mode; ++} ++ ++EXPORT_SYMBOL_GPL(oslec_adaption_mode); ++ ++void oslec_flush(struct oslec_state *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; ++} ++ ++EXPORT_SYMBOL_GPL(oslec_flush); ++ ++void oslec_snapshot(struct oslec_state *ec) ++{ ++ memcpy(ec->snapshot, ec->fir_taps16[0], ec->taps * sizeof(int16_t)); ++} ++ ++EXPORT_SYMBOL_GPL(oslec_snapshot); ++ ++/* Dual Path Echo Canceller ------------------------------------------------*/ ++ ++int16_t oslec_update(struct oslec_state *ec, int16_t tx, int16_t rx) ++{ ++ int32_t echo_value; ++ int clean_bg; ++ int tmp, tmp1; ++ ++ /* 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; ++#if 1 ++ /* Make sure the gain of the HPF is 1.0. This can still saturate a little under ++ impulse conditions, and it might roll to 32768 and need clipping on sustained peak ++ level signals. However, the scale of such clipping is small, and the error due to ++ any saturation should not markedly affect the downstream processing. */ ++ tmp -= (tmp >> 4); ++#endif ++ ec->rx_1 += -(ec->rx_1 >> DC_LOG2BETA) + tmp - ec->rx_2; ++ ++ /* hard limit filter to prevent clipping. Note that at this stage ++ rx should be limited to +/- 16383 due to right shift above */ ++ tmp1 = ec->rx_1 >> 15; ++ if (tmp1 > 16383) ++ tmp1 = 16383; ++ if (tmp1 < -16383) ++ tmp1 = -16383; ++ rx = tmp1; ++ 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 << ec->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; ++} ++ ++EXPORT_SYMBOL_GPL(oslec_update); ++ ++/* 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. ++ ++ This is one of the classic DC removal filters, adjusted to provide sufficient ++ bass rolloff to meet the above requirement to protect hybrids from things that ++ upset them. The difference between successive samples produces a lousy HPF, and ++ then a suitably placed pole flattens things out. The final result is a nicely ++ rolled off bass end. The filtering is implemented with extended fractional ++ precision, which noise shapes things, giving very clean DC removal. ++*/ ++ ++int16_t oslec_hpf_tx(struct oslec_state * ec, int16_t tx) ++{ ++ int tmp, tmp1; ++ ++ if (ec->adaption_mode & ECHO_CAN_USE_TX_HPF) { ++ tmp = tx << 15; ++#if 1 ++ /* Make sure the gain of the HPF is 1.0. The first can still saturate a little under ++ impulse conditions, and it might roll to 32768 and need clipping on sustained peak ++ level signals. However, the scale of such clipping is small, and the error due to ++ any saturation should not markedly affect the downstream processing. */ ++ tmp -= (tmp >> 4); ++#endif ++ 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; ++} ++ ++EXPORT_SYMBOL_GPL(oslec_hpf_tx); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("David Rowe"); ++MODULE_DESCRIPTION("Open Source Line Echo Canceller"); ++MODULE_VERSION("0.3.0"); +diff -Nurp base/drivers/staging/echo/echo.h new/drivers/staging/echo/echo.h +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/echo.h 2008-10-26 05:10:13.000000000 +0200 +@@ -0,0 +1,172 @@ ++/* ++ * 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 $ ++ */ ++ ++#ifndef __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" ++#include "oslec.h" ++ ++/*! ++ G.168 echo canceller descriptor. This defines the working state for a line ++ echo canceller. ++*/ ++struct oslec_state { ++ 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; ++}; ++ ++#endif /* __ECHO_H */ +הזמ הז םינוש `base/drivers/staging/echo/echo.o'-ו `new/drivers/staging/echo/echo.o' םיירניב םיצבק +diff -Nurp base/drivers/staging/echo/echo-user.c new/drivers/staging/echo/echo-user.c +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/echo-user.c 2008-10-26 05:10:13.000000000 +0200 +@@ -0,0 +1,639 @@ ++/* ++ * 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, 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. ++*/ ++ ++#include /* We're doing kernel work */ ++#include ++#include ++#include ++ ++#include "bit_operations.h" ++#include "echo.h" ++ ++#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 __bfin__ ++static void __inline__ lms_adapt_bg(struct oslec_state *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(struct oslec_state *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 ++ ++struct oslec_state *oslec_create(int len, int adaption_mode) ++{ ++ struct oslec_state *ec; ++ int i; ++ ++ ec = kzalloc(sizeof(*ec), GFP_KERNEL); ++ if (!ec) ++ return NULL; ++ ++ ec->taps = len; ++ ec->log2taps = top_bit(len); ++ ec->curr_pos = ec->taps - 1; ++ ++ for (i = 0; i < 2; i++) { ++ ec->fir_taps16[i] = ++ kcalloc(ec->taps, sizeof(int16_t), GFP_KERNEL); ++ if (!ec->fir_taps16[i]) ++ goto error_oom; ++ } ++ ++ 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; ++ oslec_adaption_mode(ec, adaption_mode); ++ ++ ec->snapshot = kcalloc(ec->taps, sizeof(int16_t), GFP_KERNEL); ++ if (!ec->snapshot) ++ goto error_oom; ++ ++ 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; ++ ++ error_oom: ++ for (i = 0; i < 2; i++) ++ kfree(ec->fir_taps16[i]); ++ ++ kfree(ec); ++ return NULL; ++} ++ ++EXPORT_SYMBOL_GPL(oslec_create); ++ ++void oslec_free(struct oslec_state *ec) ++{ ++ int i; ++ ++ fir16_free(&ec->fir_state); ++ fir16_free(&ec->fir_state_bg); ++ for (i = 0; i < 2; i++) ++ kfree(ec->fir_taps16[i]); ++ kfree(ec->snapshot); ++ kfree(ec); ++} ++ ++EXPORT_SYMBOL_GPL(oslec_free); ++ ++void oslec_adaption_mode(struct oslec_state *ec, int adaption_mode) ++{ ++ ec->adaption_mode = adaption_mode; ++} ++ ++EXPORT_SYMBOL_GPL(oslec_adaption_mode); ++ ++void oslec_flush(struct oslec_state *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; ++} ++ ++EXPORT_SYMBOL_GPL(oslec_flush); ++ ++void oslec_snapshot(struct oslec_state *ec) ++{ ++ memcpy(ec->snapshot, ec->fir_taps16[0], ec->taps * sizeof(int16_t)); ++} ++ ++EXPORT_SYMBOL_GPL(oslec_snapshot); ++ ++/* Dual Path Echo Canceller ------------------------------------------------*/ ++ ++int16_t oslec_update(struct oslec_state *ec, int16_t tx, int16_t rx) ++{ ++ int32_t echo_value; ++ int clean_bg; ++ int tmp, tmp1; ++ ++ /* 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; ++#if 1 ++ /* Make sure the gain of the HPF is 1.0. This can still saturate a little under ++ impulse conditions, and it might roll to 32768 and need clipping on sustained peak ++ level signals. However, the scale of such clipping is small, and the error due to ++ any saturation should not markedly affect the downstream processing. */ ++ tmp -= (tmp >> 4); ++#endif ++ ec->rx_1 += -(ec->rx_1 >> DC_LOG2BETA) + tmp - ec->rx_2; ++ ++ /* hard limit filter to prevent clipping. Note that at this stage ++ rx should be limited to +/- 16383 due to right shift above */ ++ tmp1 = ec->rx_1 >> 15; ++ if (tmp1 > 16383) ++ tmp1 = 16383; ++ if (tmp1 < -16383) ++ tmp1 = -16383; ++ rx = tmp1; ++ 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 << ec->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; ++} ++ ++EXPORT_SYMBOL_GPL(oslec_update); ++ ++/* 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. ++ ++ This is one of the classic DC removal filters, adjusted to provide sufficient ++ bass rolloff to meet the above requirement to protect hybrids from things that ++ upset them. The difference between successive samples produces a lousy HPF, and ++ then a suitably placed pole flattens things out. The final result is a nicely ++ rolled off bass end. The filtering is implemented with extended fractional ++ precision, which noise shapes things, giving very clean DC removal. ++*/ ++ ++int16_t oslec_hpf_tx(struct oslec_state * ec, int16_t tx) ++{ ++ int tmp, tmp1; ++ ++ if (ec->adaption_mode & ECHO_CAN_USE_TX_HPF) { ++ tmp = tx << 15; ++#if 1 ++ /* Make sure the gain of the HPF is 1.0. The first can still saturate a little under ++ impulse conditions, and it might roll to 32768 and need clipping on sustained peak ++ level signals. However, the scale of such clipping is small, and the error due to ++ any saturation should not markedly affect the downstream processing. */ ++ tmp -= (tmp >> 4); ++#endif ++ 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; ++} ++ ++EXPORT_SYMBOL_GPL(oslec_hpf_tx); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("David Rowe"); ++MODULE_DESCRIPTION("Open Source Line Echo Canceller"); ++MODULE_VERSION("0.3.0"); +הזמ הז םינוש `base/drivers/staging/echo/echo-user.o'-ו `new/drivers/staging/echo/echo-user.o' םיירניב םיצבק +diff -Nurp base/drivers/staging/echo/fir.h new/drivers/staging/echo/fir.h +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/fir.h 2008-10-26 05:10:13.000000000 +0200 +@@ -0,0 +1,295 @@ ++/* ++ * 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; ++ ++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(__bfin__) ++ fir->history = kcalloc(2 * taps, sizeof(int16_t), GFP_KERNEL); ++#else ++ fir->history = kcalloc(taps, sizeof(int16_t), GFP_KERNEL); ++#endif ++ return fir->history; ++} ++ ++static __inline__ void fir16_flush(fir16_state_t * fir) ++{ ++#if defined(USE_MMX) || defined(USE_SSE2) || defined(__bfin__) ++ memset(fir->history, 0, 2 * fir->taps * sizeof(int16_t)); ++#else ++ memset(fir->history, 0, fir->taps * sizeof(int16_t)); ++#endif ++} ++ ++static __inline__ void fir16_free(fir16_state_t * fir) ++{ ++ kfree(fir->history); ++} ++ ++#ifdef __bfin__ ++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 ++ ++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(__bfin__) ++ 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); ++} ++ ++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 = kcalloc(taps, sizeof(int16_t), GFP_KERNEL); ++ return fir->history; ++} ++ ++static __inline__ void fir32_flush(fir32_state_t * fir) ++{ ++ memset(fir->history, 0, fir->taps * sizeof(int16_t)); ++} ++ ++static __inline__ void fir32_free(fir32_state_t * fir) ++{ ++ kfree(fir->history); ++} ++ ++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); ++} ++ ++#endif ++/*- End of file ------------------------------------------------------------*/ +diff -Nurp base/drivers/staging/echo/Kconfig new/drivers/staging/echo/Kconfig +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/Kconfig 2008-10-26 05:10:13.000000000 +0200 +@@ -0,0 +1,9 @@ ++config ECHO ++ tristate "Line Echo Canceller support" ++ default n ++ ---help--- ++ This driver provides line echo cancelling support for mISDN and ++ Zaptel drivers. ++ ++ To compile this driver as a module, choose M here. The module ++ will be called echo. +diff -Nurp base/drivers/staging/echo/kernel_compat.h new/drivers/staging/echo/kernel_compat.h +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/kernel_compat.h 2008-10-30 18:26:22.000000000 +0200 +@@ -0,0 +1,17 @@ ++#include ++#include ++#include ++ ++#define kmalloc(a,b) malloc((a)) ++#define kzalloc(a,b) calloc(1,(a)) ++#define kcalloc(a,b,c) calloc((a),(b)) ++#define kfree(a) free((a)) ++#define GFP_KERNEL ++#define EXPORT_SYMBOL_GPL(a) ++#define MODULE_LICENSE(a) ++#define MODULE_AUTHOR(a) ++#define MODULE_DESCRIPTION(a) ++#define MODULE_VERSION(a) ++ ++/* FIXME: get rid of this typedef? */ ++typedef struct oslec_state echo_can_state_t; +diff -Nurp base/drivers/staging/echo/Makefile new/drivers/staging/echo/Makefile +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/Makefile 2008-10-26 05:10:13.000000000 +0200 +@@ -0,0 +1 @@ ++obj-$(CONFIG_ECHO) += echo.o +diff -Nurp base/drivers/staging/echo/mmx.h new/drivers/staging/echo/mmx.h +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/mmx.h 2008-10-26 05:10:13.000000000 +0200 +@@ -0,0 +1,281 @@ ++/* ++ * 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 */ ++ ++/* SSE registers */ ++typedef union { ++ char b[16]; ++} xmm_t; ++ ++#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 */ +diff -Nurp base/drivers/staging/echo/oslec.h new/drivers/staging/echo/oslec.h +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/oslec.h 2008-10-26 05:10:13.000000000 +0200 +@@ -0,0 +1,86 @@ ++/* ++ * OSLEC - A line echo canceller. This code is being developed ++ * against and partially complies with G168. Using code from SpanDSP ++ * ++ * Written by Steve Underwood ++ * and David Rowe ++ * ++ * Copyright (C) 2001 Steve Underwood and 2007-2008 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_H ++#define __OSLEC_H ++ ++/* TODO: document interface */ ++ ++/* 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. ++*/ ++struct oslec_state; ++ ++/*! 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. ++*/ ++struct oslec_state *oslec_create(int len, int adaption_mode); ++ ++/*! Free a voice echo canceller context. ++ \param ec The echo canceller context. ++*/ ++void oslec_free(struct oslec_state *ec); ++ ++/*! Flush (reinitialise) a voice echo canceller context. ++ \param ec The echo canceller context. ++*/ ++void oslec_flush(struct oslec_state *ec); ++ ++/*! Set the adaption mode of a voice echo canceller context. ++ \param ec The echo canceller context. ++ \param adapt The mode. ++*/ ++void oslec_adaption_mode(struct oslec_state *ec, int adaption_mode); ++ ++void oslec_snapshot(struct oslec_state *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 oslec_update(struct oslec_state *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 oslec_hpf_tx(struct oslec_state *ec, int16_t tx); ++ ++#endif /* __OSLEC_H */ +diff -Nurp base/drivers/staging/echo/out.txt new/drivers/staging/echo/out.txt +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/out.txt 2008-10-30 18:26:45.000000000 +0200 +@@ -0,0 +1,8000 @@ ++210 ++98 ++194 ++198 ++226 ++48 ++82 ++192 ++68 ++138 ++118 ++156 ++90 ++128 ++238 ++228 ++-36 ++-6 ++-68 ++40 ++-46 ++8 ++2 ++76 ++-34 ++12 ++-22 ++-8 ++100 ++-38 ++6 ++48 ++-12 ++-36 ++16 ++-10 ++-6 ++46 ++-62 ++28 ++-14 ++20 ++-34 ++38 ++-48 ++-8 ++52 ++38 ++-72 ++54 ++-12 ++-54 ++-8 ++44 ++50 ++-34 ++-58 ++-14 ++36 ++-12 ++2 ++78 ++26 ++8 ++-64 ++-6 ++-8 ++30 ++-10 ++4 ++-8 ++-54 ++24 ++56 ++16 ++-16 ++-56 ++30 ++24 ++-12 ++22 ++-36 ++6 ++64 ++26 ++-28 ++-34 ++54 ++-14 ++18 ++50 ++-6 ++10 ++26 ++-20 ++40 ++-28 ++24 ++10 ++26 ++-18 ++-40 ++54 ++42 ++-58 ++42 ++8 ++-12 ++16 ++-22 ++40 ++-16 ++-4 ++-12 ++-16 ++10 ++34 ++2 ++-12 ++58 ++-36 ++-22 ++30 ++42 ++58 ++32 ++-4 ++-24 ++26 ++-16 ++-20 ++-16 ++28 ++-12 ++-16 ++50 ++-28 ++52 ++-24 ++46 ++-26 ++68 ++-38 ++20 ++-14 ++-4 ++54 ++28 ++10 ++38 ++-6 ++20 ++-52 ++-30 ++28 ++-26 ++-22 ++10 ++24 ++64 ++12 ++24 ++-46 ++-16 ++92 ++-28 ++56 ++-38 ++60 ++-46 ++44 ++-44 ++-36 ++70 ++22 ++-34 ++-12 ++10 ++68 ++36 ++-26 ++12 ++12 ++6 ++26 ++-50 ++18 ++40 ++-18 ++60 ++-2 ++30 ++14 ++-4 ++-56 ++44 ++34 ++36 ++14 ++14 ++26 ++0 ++20 ++20 ++-16 ++-22 ++-38 ++40 ++36 ++-62 ++16 ++-20 ++26 ++-12 ++32 ++-10 ++-4 ++-14 ++-14 ++38 ++-8 ++34 ++12 ++50 ++-26 ++10 ++26 ++-38 ++10 ++26 ++52 ++-10 ++-18 ++44 ++-8 ++-46 ++10 ++34 ++46 ++-16 ++26 ++10 ++4 ++-6 ++36 ++-32 ++32 ++-38 ++20 ++34 ++24 ++12 ++-16 ++-26 ++36 ++28 ++26 ++-22 ++26 ++-22 ++56 ++14 ++4 ++-26 ++14 ++-30 ++2 ++64 ++18 ++-44 ++-24 ++22 ++36 ++24 ++24 ++-20 ++40 ++-12 ++0 ++-20 ++64 ++-48 ++48 ++-10 ++-24 ++18 ++60 ++34 ++8 ++6 ++-38 ++28 ++-6 ++32 ++40 ++20 ++20 ++-48 ++28 ++0 ++-30 ++48 ++36 ++24 ++-16 ++0 ++-42 ++-14 ++52 ++4 ++-10 ++46 ++-6 ++-22 ++-24 ++34 ++36 ++-40 ++26 ++6 ++42 ++22 ++-46 ++-20 ++24 ++-20 ++-12 ++16 ++12 ++70 ++44 ++-2 ++24 ++20 ++-10 ++-18 ++0 ++32 ++14 ++-28 ++-28 ++-24 ++-6 ++52 ++2 ++-6 ++8 ++-12 ++28 ++72 ++8 ++-22 ++28 ++36 ++-4 ++-36 ++50 ++10 ++-2 ++-28 ++16 ++4 ++44 ++-12 ++-22 ++2 ++50 ++22 ++48 ++-30 ++44 ++-38 ++4 ++-34 ++48 ++34 ++-26 ++-32 ++62 ++24 ++-48 ++6 ++62 ++20 ++-30 ++2 ++34 ++-48 ++-34 ++-4 ++-18 ++18 ++20 ++-38 ++-38 ++-24 ++16 ++38 ++10 ++-12 ++-38 ++12 ++14 ++-10 ++-6 ++-20 ++-16 ++36 ++-18 ++-36 ++8 ++-8 ++4 ++8 ++-34 ++26 ++-48 ++-30 ++50 ++-40 ++-2 ++-16 ++26 ++-16 ++-20 ++28 ++-26 ++16 ++6 ++-58 ++-12 ++30 ++-2 ++14 ++-34 ++-26 ++-22 ++8 ++-30 ++18 ++14 ++8 ++-32 ++-10 ++-20 ++-2 ++-18 ++0 ++2 ++-24 ++10 ++-36 ++-6 ++0 ++-12 ++26 ++-26 ++12 ++8 ++26 ++6 ++0 ++-12 ++-30 ++4 ++-46 ++-2 ++32 ++-6 ++-4 ++-12 ++-18 ++-16 ++12 ++12 ++4 ++-42 ++-42 ++-26 ++30 ++-26 ++8 ++12 ++-6 ++-40 ++-36 ++-8 ++-8 ++22 ++32 ++-38 ++-16 ++20 ++26 ++-4 ++-34 ++24 ++-36 ++-36 ++12 ++28 ++0 ++18 ++-48 ++-16 ++20 ++24 ++-12 ++2 ++16 ++-38 ++20 ++-14 ++8 ++-22 ++-20 ++20 ++4 ++-6 ++-20 ++-18 ++20 ++8 ++-46 ++12 ++-20 ++-8 ++0 ++-6 ++0 ++-48 ++-10 ++-12 ++-42 ++24 ++20 ++12 ++-8 ++-34 ++-4 ++-6 ++-6 ++-12 ++-50 ++6 ++-38 ++12 ++-2 ++14 ++-12 ++-44 ++0 ++4 ++-42 ++30 ++-22 ++12 ++-4 ++-40 ++-4 ++-20 ++-6 ++-34 ++22 ++10 ++20 ++-4 ++-22 ++-12 ++36 ++-54 ++-36 ++20 ++6 ++-24 ++-24 ++2 ++38 ++4 ++-20 ++-20 ++20 ++24 ++14 ++2 ++-16 ++-40 ++-16 ++-40 ++-10 ++30 ++-24 ++18 ++16 ++-58 ++-14 ++12 ++4 ++14 ++-42 ++8 ++26 ++-44 ++-22 ++-6 ++-16 ++0 ++-44 ++-6 ++-12 ++8 ++24 ++-46 ++-2 ++26 ++-52 ++-8 ++-28 ++10 ++10 ++2 ++0 ++-50 ++0 ++10 ++-30 ++-40 ++-8 ++-30 ++56 ++6 ++10 ++20 ++16 ++-52 ++-14 ++-16 ++22 ++0 ++14 ++-54 ++-28 ++24 ++-28 ++20 ++6 ++-24 ++-24 ++28 ++-46 ++28 ++-10 ++-6 ++22 ++-52 ++-34 ++-26 ++-30 ++22 ++-30 ++-34 ++-6 ++48 ++-46 ++-26 ++-22 ++12 ++26 ++36 ++-48 ++6 ++-38 ++-8 ++6 ++-8 ++-18 ++-12 ++36 ++2 ++-38 ++-38 ++-2 ++40 ++18 ++-24 ++36 ++-54 ++2 ++-24 ++8 ++12 ++-28 ++24 ++6 ++-36 ++-38 ++14 ++18 ++8 ++0 ++14 ++-36 ++6 ++-34 ++8 ++-40 ++2 ++-42 ++-30 ++-18 ++-6 ++-24 ++20 ++-30 ++48 ++38 ++-50 ++36 ++-18 ++-34 ++14 ++-36 ++10 ++-2 ++10 ++22 ++-30 ++-14 ++10 ++-40 ++-16 ++12 ++-4 ++-44 ++42 ++-14 ++-32 ++6 ++-12 ++-16 ++22 ++4 ++-12 ++-24 ++8 ++-14 ++-30 ++20 ++-32 ++16 ++-46 ++30 ++26 ++-14 ++-28 ++10 ++-16 ++28 ++-40 ++-24 ++-32 ++10 ++38 ++-4 ++-14 ++18 ++22 ++4 ++-36 ++-30 ++-26 ++-18 ++-16 ++30 ++6 ++40 ++32 ++10 ++14 ++-10 ++6 ++22 ++52 ++-6 ++-24 ++2 ++16 ++16 ++-26 ++36 ++30 ++-22 ++10 ++26 ++16 ++-32 ++8 ++24 ++22 ++26 ++-28 ++12 ++36 ++-34 ++-8 ++-12 ++28 ++-16 ++22 ++-10 ++-14 ++36 ++24 ++-30 ++34 ++24 ++-18 ++22 ++-4 ++-2 ++16 ++-20 ++10 ++-24 ++-26 ++16 ++-12 ++18 ++-14 ++40 ++22 ++-14 ++30 ++16 ++-12 ++40 ++10 ++-24 ++-30 ++14 ++4 ++-16 ++10 ++-10 ++4 ++14 ++-20 ++26 ++-20 ++14 ++-4 ++34 ++34 ++22 ++28 ++20 ++-40 ++-20 ++8 ++-10 ++36 ++-28 ++-4 ++22 ++14 ++18 ++0 ++-2 ++36 ++8 ++30 ++-24 ++20 ++-10 ++-8 ++-20 ++28 ++-8 ++20 ++30 ++18 ++6 ++8 ++0 ++0 ++6 ++-2 ++2 ++8 ++-36 ++20 ++8 ++-24 ++-16 ++-8 ++36 ++34 ++14 ++-20 ++14 ++-4 ++-20 ++36 ++-22 ++10 ++-8 ++-18 ++6 ++24 ++32 ++-24 ++2 ++32 ++36 ++-26 ++-12 ++10 ++18 ++30 ++-12 ++10 ++12 ++24 ++16 ++4 ++-44 ++12 ++18 ++20 ++14 ++-22 ++-28 ++34 ++-8 ++-16 ++0 ++2 ++6 ++22 ++-24 ++6 ++32 ++8 ++-28 ++30 ++22 ++-4 ++-2 ++-6 ++-8 ++16 ++-24 ++42 ++0 ++24 ++20 ++-12 ++-2 ++-6 ++-14 ++-2 ++18 ++2 ++-14 ++32 ++8 ++8 ++-16 ++34 ++-16 ++-4 ++-8 ++-18 ++46 ++-8 ++26 ++14 ++12 ++-16 ++-22 ++46 ++26 ++-28 ++32 ++-22 ++44 ++6 ++4 ++-2 ++20 ++24 ++12 ++-14 ++-24 ++28 ++-14 ++14 ++0 ++-10 ++14 ++0 ++18 ++12 ++-8 ++6 ++-28 ++-2 ++-2 ++26 ++2 ++-10 ++18 ++-8 ++-2 ++14 ++2 ++-4 ++-20 ++-8 ++48 ++34 ++32 ++-6 ++-16 ++-8 ++4 ++4 ++26 ++14 ++18 ++-2 ++-22 ++4 ++-20 ++18 ++38 ++-28 ++34 ++-14 ++28 ++-8 ++-10 ++6 ++16 ++22 ++36 ++18 ++-32 ++-38 ++-22 ++36 ++38 ++-36 ++2 ++-6 ++-4 ++32 ++6 ++-26 ++14 ++10 ++12 ++20 ++26 ++-2 ++-20 ++28 ++24 ++-28 ++28 ++-2 ++-8 ++0 ++32 ++24 ++-36 ++22 ++34 ++-26 ++32 ++22 ++-36 ++28 ++32 ++-10 ++-40 ++-18 ++0 ++26 ++-12 ++26 ++-28 ++14 ++4 ++36 ++-14 ++10 ++14 ++32 ++2 ++12 ++-10 ++-30 ++36 ++-14 ++28 ++-24 ++-10 ++32 ++-18 ++-14 ++24 ++0 ++-18 ++38 ++30 ++-28 ++34 ++-24 ++26 ++-16 ++36 ++16 ++16 ++-32 ++2 ++-22 ++18 ++-20 ++-22 ++6 ++20 ++6 ++-6 ++18 ++14 ++-8 ++28 ++-24 ++-12 ++-16 ++2 ++22 ++8 ++-20 ++12 ++-10 ++-6 ++12 ++2 ++-28 ++12 ++6 ++8 ++8 ++14 ++-22 ++8 ++8 ++-2 ++8 ++-12 ++-10 ++-12 ++6 ++-10 ++-14 ++-24 ++-6 ++6 ++-10 ++12 ++22 ++-14 ++-16 ++-2 ++24 ++12 ++2 ++-12 ++-6 ++4 ++34 ++10 ++-26 ++-4 ++16 ++0 ++-22 ++2 ++24 ++20 ++6 ++-14 ++-14 ++-12 ++10 ++-6 ++0 ++8 ++32 ++-8 ++28 ++2 ++-6 ++20 ++2 ++-22 ++0 ++-10 ++4 ++2 ++6 ++-6 ++2 ++-28 ++28 ++-14 ++-4 ++-22 ++20 ++8 ++24 ++-6 ++2 ++-20 ++10 ++-10 ++6 ++-28 ++18 ++-4 ++0 ++16 ++-24 ++-26 ++18 ++22 ++0 ++-14 ++-12 ++-10 ++-18 ++-10 ++18 ++4 ++-12 ++18 ++30 ++10 ++14 ++0 ++-18 ++10 ++-20 ++-16 ++18 ++18 ++-18 ++-8 ++16 ++-18 ++22 ++-10 ++22 ++20 ++-10 ++10 ++20 ++-30 ++-26 ++-12 ++-12 ++-14 ++-6 ++-16 ++16 ++8 ++14 ++8 ++-12 ++2 ++2 ++8 ++-12 ++-4 ++10 ++26 ++-10 ++22 ++-22 ++-18 ++-20 ++-14 ++12 ++-16 ++-16 ++-6 ++28 ++36 ++-14 ++16 ++-14 ++14 ++26 ++-2 ++-2 ++0 ++18 ++-14 ++-34 ++-18 ++14 ++-6 ++8 ++20 ++20 ++-6 ++12 ++-20 ++-10 ++28 ++-8 ++2 ++-22 ++26 ++4 ++-28 ++-16 ++-4 ++-6 ++0 ++-4 ++-8 ++32 ++24 ++6 ++-6 ++-8 ++8 ++14 ++-14 ++14 ++8 ++6 ++-8 ++-18 ++-2 ++10 ++-26 ++6 ++-14 ++-30 ++-22 ++14 ++-18 ++22 ++-4 ++-16 ++-14 ++6 ++-8 ++-8 ++8 ++6 ++-8 ++4 ++-20 ++12 ++-30 ++4 ++2 ++-8 ++-2 ++-20 ++-22 ++28 ++-4 ++18 ++8 ++-4 ++-4 ++26 ++-8 ++-6 ++8 ++-4 ++-4 ++-36 ++4 ++-10 ++-2 ++-30 ++14 ++-6 ++2 ++-24 ++-24 ++14 ++18 ++-24 ++-2 ++-12 ++-4 ++-14 ++-4 ++6 ++24 ++4 ++-18 ++2 ++-16 ++0 ++8 ++12 ++-28 ++0 ++-2 ++-2 ++-8 ++-16 ++16 ++-22 ++-10 ++12 ++-4 ++12 ++10 ++-14 ++6 ++-6 ++-6 ++-22 ++14 ++14 ++-24 ++-16 ++-8 ++-22 ++-18 ++10 ++14 ++4 ++-12 ++-2 ++2 ++-4 ++14 ++-28 ++-28 ++4 ++-4 ++22 ++-10 ++-8 ++6 ++14 ++-8 ++-6 ++-12 ++-28 ++-16 ++-20 ++-6 ++-12 ++-24 ++-4 ++-2 ++6 ++8 ++6 ++-12 ++2 ++-12 ++20 ++-28 ++14 ++-8 ++-28 ++10 ++-8 ++-4 ++-20 ++-10 ++30 ++8 ++-26 ++22 ++-22 ++-2 ++8 ++-12 ++6 ++4 ++-6 ++12 ++-2 ++-24 ++-18 ++-22 ++10 ++10 ++-16 ++-16 ++16 ++-14 ++-16 ++12 ++-4 ++-6 ++0 ++18 ++14 ++-4 ++-24 ++4 ++-10 ++-22 ++16 ++0 ++8 ++-22 ++-36 ++8 ++12 ++2 ++-12 ++-10 ++4 ++-4 ++-8 ++-12 ++-16 ++16 ++4 ++-18 ++-18 ++14 ++14 ++2 ++-22 ++-10 ++4 ++-30 ++-16 ++-22 ++12 ++10 ++-18 ++0 ++-12 ++-4 ++18 ++-12 ++12 ++10 ++10 ++-18 ++-22 ++0 ++8 ++-6 ++-22 ++-26 ++6 ++6 ++-20 ++8 ++6 ++-20 ++8 ++-18 ++6 ++-4 ++-22 ++16 ++-2 ++12 ++-8 ++-2 ++-4 ++4 ++-24 ++-8 ++-22 ++-24 ++0 ++-6 ++0 ++8 ++22 ++-18 ++30 ++20 ++-18 ++8 ++8 ++-8 ++-2 ++6 ++-6 ++14 ++-4 ++-18 ++-6 ++6 ++18 ++10 ++-4 ++-12 ++10 ++-12 ++-12 ++6 ++-2 ++-16 ++14 ++16 ++-14 ++-22 ++-16 ++-12 ++-14 ++-8 ++16 ++-16 ++6 ++-2 ++-4 ++16 ++-14 ++12 ++18 ++0 ++18 ++-10 ++-16 ++6 ++-22 ++-14 ++-12 ++10 ++-16 ++-4 ++8 ++14 ++4 ++-2 ++14 ++14 ++-6 ++-22 ++14 ++2 ++-24 ++2 ++10 ++6 ++-10 ++-22 ++6 ++-6 ++-22 ++10 ++-14 ++-18 ++-16 ++14 ++10 ++-14 ++12 ++-14 ++-14 ++-12 ++8 ++12 ++-20 ++-12 ++-10 ++14 ++-8 ++8 ++6 ++-20 ++2 ++-8 ++12 ++-2 ++-26 ++-8 ++6 ++10 ++-12 ++6 ++4 ++-8 ++22 ++4 ++-12 ++18 ++-30 ++-12 ++-18 ++-12 ++-8 ++8 ++-20 ++0 ++-8 ++-12 ++0 ++2 ++-8 ++2 ++2 ++10 ++2 ++-2 ++-14 ++0 ++-18 ++-18 ++-6 ++-16 ++22 ++-16 ++4 ++18 ++6 ++16 ++12 ++6 ++-10 ++-18 ++-16 ++10 ++-24 ++-16 ++-14 ++-10 ++-4 ++14 ++12 ++4 ++-14 ++-4 ++10 ++14 ++18 ++14 ++-10 ++18 ++12 ++8 ++-26 ++2 ++12 ++-4 ++2 ++-6 ++2 ++0 ++0 ++8 ++-2 ++6 ++0 ++6 ++6 ++4 ++-22 ++-20 ++0 ++10 ++-22 ++-2 ++-22 ++-6 ++-6 ++-18 ++-2 ++2 ++-22 ++-6 ++2 ++-12 ++-10 ++-2 ++12 ++6 ++-14 ++4 ++0 ++-2 ++6 ++-12 ++-6 ++0 ++-8 ++-4 ++-16 ++-8 ++6 ++10 ++-18 ++2 ++2 ++-10 ++-10 ++16 ++-2 ++4 ++4 ++-2 ++8 ++-12 ++2 ++8 ++-6 ++-4 ++4 ++2 ++-16 ++-10 ++-2 ++6 ++0 ++10 ++-16 ++0 ++-10 ++-6 ++20 ++4 ++-12 ++-16 ++-6 ++4 ++-10 ++-6 ++-10 ++6 ++-16 ++-12 ++-6 ++10 ++-10 ++12 ++10 ++4 ++-6 ++-4 ++-8 ++-2 ++10 ++8 ++-4 ++-8 ++6 ++0 ++-22 ++10 ++8 ++12 ++2 ++6 ++-26 ++-16 ++0 ++-6 ++0 ++10 ++-16 ++-2 ++10 ++-10 ++-6 ++-20 ++-16 ++-4 ++12 ++-4 ++2 ++-12 ++-18 ++4 ++16 ++-2 ++10 ++6 ++-16 ++10 ++6 ++12 ++0 ++-8 ++10 ++8 ++-4 ++-12 ++-22 ++12 ++-10 ++-18 ++10 ++-14 ++-6 ++18 ++-10 ++-6 ++-16 ++-12 ++-12 ++16 ++-12 ++-14 ++-10 ++-16 ++14 ++-14 ++16 ++6 ++6 ++-2 ++6 ++0 ++12 ++-6 ++-10 ++-2 ++6 ++6 ++-8 ++-18 ++0 ++16 ++-14 ++-10 ++14 ++-6 ++4 ++-20 ++-2 ++6 ++16 ++2 ++2 ++-18 ++4 ++2 ++-20 ++0 ++-10 ++-20 ++-10 ++10 ++6 ++-12 ++-12 ++-16 ++12 ++-14 ++2 ++20 ++-14 ++-10 ++24 ++-10 ++-12 ++14 ++0 ++6 ++16 ++-14 ++-10 ++12 ++2 ++-12 ++12 ++-4 ++10 ++-12 ++-16 ++12 ++-16 ++-6 ++12 ++6 ++6 ++-18 ++12 ++-6 ++-18 ++-4 ++-8 ++-12 ++-2 ++-12 ++-6 ++4 ++-18 ++-8 ++6 ++-14 ++4 ++20 ++18 ++-12 ++-16 ++16 ++0 ++2 ++-10 ++8 ++8 ++-2 ++-14 ++0 ++-10 ++12 ++0 ++-14 ++-10 ++12 ++0 ++-8 ++-8 ++-14 ++4 ++6 ++8 ++2 ++0 ++-10 ++4 ++-14 ++12 ++-2 ++-20 ++-14 ++-2 ++10 ++6 ++-8 ++-8 ++2 ++10 ++4 ++-14 ++-14 ++6 ++12 ++-12 ++6 ++4 ++2 ++-10 ++6 ++2 ++0 ++-8 ++-6 ++-2 ++-14 ++2 ++10 ++-10 ++-8 ++-2 ++-8 ++4 ++-16 ++-14 ++-4 ++4 ++8 ++12 ++-10 ++-14 ++-16 ++0 ++16 ++12 ++-6 ++0 ++2 ++-12 ++8 ++-16 ++10 ++-6 ++-2 ++-4 ++8 ++2 ++2 ++0 ++12 ++4 ++-12 ++-10 ++-18 ++-8 ++-8 ++0 ++6 ++12 ++-4 ++-12 ++-18 ++6 ++2 ++-18 ++-2 ++-14 ++14 ++-4 ++-14 ++-8 ++-6 ++-10 ++-8 ++-16 ++-4 ++12 ++16 ++-10 ++-2 ++10 ++12 ++6 ++-18 ++8 ++-10 ++0 ++-4 ++6 ++-6 ++14 ++-16 ++4 ++-2 ++-2 ++0 ++-14 ++0 ++6 ++-6 ++4 ++-16 ++2 ++10 ++-12 ++8 ++-16 ++4 ++8 ++-6 ++-8 ++-6 ++-8 ++-6 ++4 ++-12 ++-10 ++-14 ++2 ++-10 ++6 ++4 ++-6 ++-14 ++-6 ++8 ++12 ++0 ++-8 ++-6 ++0 ++-14 ++-2 ++-4 ++10 ++4 ++-6 ++-8 ++4 ++8 ++0 ++8 ++-8 ++14 ++-8 ++14 ++14 ++-10 ++-14 ++4 ++2 ++6 ++-12 ++2 ++6 ++-8 ++-2 ++4 ++2 ++2 ++-6 ++-10 ++2 ++6 ++0 ++0 ++-12 ++2 ++2 ++6 ++-8 ++-18 ++6 ++8 ++6 ++-14 ++2 ++6 ++-2 ++4 ++-10 ++-14 ++0 ++4 ++4 ++-2 ++-18 ++-10 ++-12 ++6 ++10 ++-10 ++10 ++0 ++10 ++-4 ++-6 ++-8 ++-14 ++4 ++-10 ++-4 ++4 ++-20 ++-10 ++2 ++-12 ++10 ++8 ++2 ++6 ++-12 ++0 ++-2 ++4 ++-6 ++4 ++6 ++0 ++12 ++-2 ++-6 ++-10 ++-2 ++-8 ++-8 ++-14 ++-2 ++-2 ++-4 ++-8 ++4 ++2 ++-6 ++14 ++10 ++14 ++6 ++4 ++-6 ++-2 ++-2 ++-2 ++-18 ++0 ++2 ++-4 ++0 ++-12 ++-18 ++0 ++8 ++0 ++8 ++-8 ++6 ++-8 ++-6 ++-6 ++2 ++2 ++-2 ++-4 ++-12 ++0 ++-8 ++-18 ++-2 ++-8 ++6 ++-4 ++6 ++-6 ++8 ++14 ++-8 ++-8 ++-2 ++-12 ++4 ++0 ++-2 ++-6 ++-14 ++2 ++10 ++4 ++4 ++-8 ++-12 ++-2 ++12 ++-4 ++8 ++-14 ++-6 ++-6 ++-14 ++-10 ++-6 ++-14 ++0 ++0 ++-2 ++0 ++-6 ++2 ++8 ++8 ++8 ++2 ++-4 ++-6 ++8 ++-6 ++6 ++6 ++-10 ++2 ++-12 ++0 ++-18 ++-16 ++-8 ++14 ++-6 ++0 ++-10 ++-8 ++6 ++8 ++-4 ++-12 ++-12 ++8 ++2 ++-2 ++-2 ++-12 ++-4 ++-10 ++0 ++10 ++0 ++-2 ++-6 ++4 ++-2 ++10 ++-10 ++-2 ++-10 ++0 ++4 ++-4 ++-4 ++6 ++6 ++2 ++14 ++-6 ++-6 ++-8 ++4 ++-2 ++8 ++-12 ++6 ++-6 ++-8 ++0 ++-2 ++-12 ++-6 ++-6 ++4 ++2 ++8 ++-18 ++-2 ++-4 ++6 ++4 ++-6 ++-12 ++14 ++10 ++10 ++2 ++-12 ++0 ++-4 ++2 ++12 ++-18 ++6 ++10 ++-14 ++6 ++-4 ++-4 ++-2 ++6 ++4 ++-6 ++-12 ++-6 ++-6 ++-12 ++-4 ++4 ++-6 ++10 ++-4 ++-16 ++-12 ++-10 ++10 ++2 ++-8 ++-4 ++2 ++4 ++8 ++-2 ++0 ++12 ++2 ++14 ++8 ++0 ++-2 ++-8 ++4 ++0 ++4 ++12 ++-2 ++-10 ++-8 ++-8 ++8 ++4 ++-10 ++0 ++2 ++-6 ++6 ++-4 ++6 ++-6 ++6 ++6 ++4 ++-2 ++-12 ++0 ++-2 ++4 ++-4 ++-8 ++-4 ++10 ++-2 ++12 ++-8 ++12 ++12 ++10 ++-4 ++10 ++6 ++6 ++6 ++6 ++-2 ++12 ++8 ++-14 ++-2 ++6 ++-8 ++-10 ++0 ++4 ++6 ++2 ++-4 ++-4 ++-14 ++6 ++4 ++-2 ++2 ++8 ++-6 ++6 ++6 ++-8 ++-4 ++2 ++-4 ++-6 ++8 ++8 ++-12 ++-10 ++6 ++10 ++-8 ++-4 ++10 ++-2 ++0 ++2 ++-4 ++-8 ++12 ++4 ++-2 ++12 ++-6 ++10 ++0 ++2 ++2 ++2 ++2 ++2 ++8 ++6 ++-6 ++-8 ++14 ++-8 ++8 ++-8 ++-4 ++2 ++-8 ++-4 ++6 ++8 ++8 ++-8 ++-10 ++14 ++-6 ++8 ++-2 ++2 ++14 ++2 ++0 ++-8 ++-6 ++-12 ++14 ++2 ++2 ++8 ++-4 ++10 ++16 ++0 ++-8 ++6 ++6 ++-4 ++4 ++0 ++-4 ++-2 ++0 ++-10 ++0 ++2 ++8 ++4 ++-2 ++-6 ++-4 ++0 ++2 ++4 ++0 ++12 ++8 ++2 ++-4 ++-4 ++-6 ++-10 ++-2 ++-4 ++0 ++-4 ++-8 ++12 ++4 ++4 ++8 ++-10 ++-2 ++14 ++-6 ++4 ++-4 ++10 ++-2 ++8 ++12 ++0 ++-4 ++4 ++-6 ++4 ++6 ++8 ++-6 ++-12 ++0 ++-2 ++-6 ++6 ++4 ++6 ++-2 ++12 ++2 ++8 ++-6 ++-8 ++10 ++0 ++10 ++10 ++-14 ++-8 ++10 ++-4 ++-10 ++8 ++8 ++-2 ++0 ++-8 ++-10 ++10 ++12 ++4 ++-8 ++0 ++10 ++-6 ++-8 ++-4 ++8 ++-4 ++-8 ++-4 ++-8 ++0 ++-2 ++10 ++10 ++-8 ++14 ++-2 ++-4 ++6 ++-2 ++-6 ++4 ++2 ++2 ++-14 ++2 ++6 ++-6 ++0 ++-2 ++-6 ++6 ++12 ++-6 ++14 ++0 ++-4 ++0 ++-6 ++4 ++-4 ++6 ++12 ++-8 ++6 ++-6 ++6 ++14 ++-4 ++4 ++-2 ++10 ++0 ++4 ++0 ++-8 ++12 ++0 ++0 ++-2 ++-2 ++4 ++-8 ++6 ++8 ++6 ++6 ++-12 ++6 ++6 ++-2 ++-10 ++0 ++-4 ++-10 ++2 ++-2 ++-14 ++-4 ++2 ++2 ++0 ++0 ++6 ++-2 ++-4 ++-6 ++-8 ++-6 ++0 ++-4 ++2 ++-4 ++-2 ++-4 ++2 ++-4 ++6 ++10 ++-4 ++4 ++0 ++-6 ++-2 ++6 ++-4 ++-10 ++0 ++-2 ++-6 ++-6 ++-6 ++-10 ++4 ++-6 ++-6 ++-8 ++-6 ++-4 ++0 ++4 ++4 ++0 ++-6 ++-8 ++0 ++-8 ++-2 ++-4 ++-8 ++2 ++-2 ++0 ++-10 ++-6 ++2 ++0 ++2 ++4 ++-8 ++4 ++-6 ++-6 ++6 ++-6 ++-4 ++-6 ++-6 ++0 ++2 ++0 ++0 ++0 ++6 ++2 ++-2 ++-4 ++-2 ++-8 ++-2 ++0 ++-10 ++4 ++-14 ++-4 ++-12 ++-2 ++8 ++-2 ++-4 ++8 ++-2 ++-2 ++6 ++-4 ++-2 ++-4 ++-2 ++-2 ++4 ++2 ++-8 ++-6 ++0 ++-4 ++4 ++8 ++0 ++-2 ++-6 ++6 ++-8 ++-6 ++4 ++-8 ++-2 ++6 ++2 ++-8 ++-4 ++-8 ++-8 ++-4 ++-2 ++6 ++0 ++-4 ++-8 ++-10 ++2 ++8 ++4 ++4 ++-4 ++-6 ++6 ++-8 ++-10 ++2 ++4 ++-4 ++2 ++4 ++-2 ++2 ++4 ++-8 ++-8 ++0 ++-2 ++-2 ++2 ++-8 ++6 ++-10 ++-4 ++6 ++-8 ++-2 ++4 ++6 ++-2 ++2 ++2 ++0 ++2 ++2 ++-10 ++-10 ++-4 ++0 ++-4 ++-12 ++-4 ++6 ++2 ++-6 ++-8 ++4 ++8 ++-4 ++-6 ++-2 ++-2 ++-10 ++6 ++4 ++-2 ++-6 ++-6 ++-4 ++-14 ++6 ++-8 ++0 ++6 ++-8 ++-4 ++2 ++-8 ++6 ++2 ++2 ++-2 ++6 ++-2 ++0 ++-10 ++0 ++0 ++-8 ++6 ++-4 ++0 ++0 ++-2 ++0 ++0 ++-4 ++-2 ++-12 ++-14 ++-4 ++-16 ++-10 ++-4 ++8 ++-4 ++4 ++0 ++0 ++2 ++-4 ++-2 ++-12 ++-8 ++2 ++-2 ++-6 ++-6 ++-8 ++2 ++-4 ++-8 ++2 ++6 ++4 ++-10 ++-4 ++-6 ++0 ++0 ++-4 ++-6 ++-4 ++6 ++0 ++-6 ++-8 ++-2 ++-14 ++2 ++0 ++-10 ++4 ++0 ++-6 ++4 ++-2 ++4 ++-4 ++2 ++-2 ++-2 ++0 ++-12 ++-6 ++-4 ++-10 ++-10 ++8 ++2 ++-2 ++0 ++6 ++-6 ++-10 ++-2 ++-2 ++-8 ++-8 ++2 ++0 ++2 ++0 ++2 ++-6 ++-6 ++2 ++0 ++-10 ++6 ++-8 ++0 ++-6 ++-12 ++4 ++-12 ++0 ++8 ++-8 ++-2 ++6 ++-2 ++-6 ++0 ++0 ++-6 ++-6 ++-4 ++-6 ++-2 ++2 ++4 ++-2 ++2 ++0 ++2 ++6 ++-8 ++0 ++-6 ++-6 ++2 ++2 ++0 ++-4 ++4 ++2 ++-4 ++2 ++2 ++4 ++-6 ++-2 ++6 ++4 ++6 ++4 ++-6 ++-6 ++-10 ++4 ++4 ++4 ++-2 ++0 ++0 ++0 ++0 ++-4 ++-8 ++2 ++6 ++4 ++0 ++-2 ++-4 ++-10 ++-8 ++-6 ++-4 ++2 ++-6 ++2 ++4 ++-6 ++2 ++0 ++6 ++8 ++4 ++8 ++2 ++4 ++4 ++2 ++-2 ++-4 ++0 ++-10 ++-4 ++2 ++-4 ++-6 ++-4 ++-8 ++2 ++2 ++-8 ++2 ++-6 ++0 ++4 ++0 ++-8 ++-2 ++-2 ++-8 ++-4 ++-4 ++-8 ++-10 ++-2 ++4 ++-10 ++-4 ++4 ++2 ++-6 ++0 ++6 ++-2 ++0 ++0 ++2 ++-4 ++0 ++-2 ++2 ++0 ++-4 ++-6 ++-6 ++0 ++2 ++4 ++-4 ++2 ++4 ++2 ++-2 ++-6 ++4 ++4 ++6 ++-6 ++-2 ++4 ++2 ++2 ++-2 ++-2 ++-4 ++-6 ++-8 ++0 ++-8 ++0 ++-8 ++4 ++-2 ++-6 ++8 ++0 ++-2 ++4 ++0 ++0 ++-6 ++-12 ++-8 ++-2 ++-2 ++-4 ++-2 ++-8 ++-4 ++4 ++6 ++8 ++4 ++4 ++4 ++2 ++4 ++2 ++-2 ++4 ++-4 ++0 ++0 ++-4 ++0 ++-2 ++4 ++2 ++-2 ++2 ++-2 ++0 ++6 ++2 ++6 ++6 ++-2 ++2 ++-8 ++-2 ++-2 ++-10 ++-6 ++2 ++-2 ++0 ++-2 ++-10 ++-2 ++4 ++2 ++2 ++-8 ++-4 ++2 ++2 ++0 ++-6 ++-4 ++4 ++0 ++0 ++-4 ++-10 ++-4 ++-4 ++2 ++-2 ++-2 ++-8 ++-6 ++-6 ++-6 ++-2 ++2 ++2 ++0 ++-4 ++4 ++2 ++-4 ++-4 ++-4 ++-6 ++0 ++4 ++2 ++-6 ++-6 ++-6 ++4 ++-2 ++4 ++-4 ++0 ++8 ++-2 ++0 ++0 ++-6 ++2 ++0 ++-4 ++-2 ++-10 ++-6 ++-4 ++-2 ++-2 ++2 ++-6 ++0 ++-2 ++-4 ++6 ++0 ++-4 ++2 ++2 ++2 ++0 ++-2 ++4 ++0 ++6 ++-2 ++-2 ++-4 ++2 ++-2 ++-4 ++-6 ++-2 ++-4 ++2 ++4 ++-2 ++4 ++0 ++4 ++0 ++-2 ++-2 ++0 ++2 ++-2 ++-4 ++4 ++0 ++6 ++-4 ++-4 ++0 ++-4 ++6 ++-6 ++-4 ++-4 ++0 ++-4 ++0 ++2 ++-4 ++-2 ++6 ++6 ++-6 ++-6 ++-2 ++-2 ++-4 ++-4 ++-4 ++-4 ++4 ++-2 ++4 ++-6 ++0 ++0 ++-8 ++4 ++0 ++0 ++4 ++6 ++6 ++0 ++-2 ++-2 ++2 ++0 ++-4 ++4 ++-4 ++-6 ++2 ++-10 ++-4 ++-2 ++-4 ++2 ++2 ++-6 ++-6 ++2 ++0 ++4 ++2 ++-8 ++-2 ++2 ++4 ++-6 ++-6 ++2 ++-6 ++-2 ++-2 ++-6 ++2 ++2 ++0 ++4 ++2 ++0 ++-8 ++2 ++2 ++0 ++-2 ++2 ++4 ++-4 ++-6 ++-8 ++2 ++2 ++-6 ++0 ++0 ++-4 ++-2 ++0 ++0 ++0 ++0 ++4 ++-4 ++4 ++-4 ++4 ++4 ++4 ++6 ++0 ++-8 ++2 ++-2 ++0 ++-4 ++0 ++-2 ++-4 ++-4 ++-2 ++-2 ++-6 ++0 ++0 ++0 ++-4 ++-12 ++-12 ++-6 ++0 ++4 ++-2 ++2 ++2 ++0 ++-6 ++0 ++0 ++4 ++6 ++0 ++-8 ++-2 ++-10 ++-2 ++-4 ++-8 ++-2 ++0 ++-6 ++-8 ++-12 ++-10 ++0 ++2 ++-2 ++-6 ++0 ++-2 ++-8 ++-4 ++-8 ++-8 ++-4 ++-8 ++2 ++-2 ++-12 ++-4 ++-2 ++6 ++-2 ++0 ++0 ++-2 ++-2 ++-2 ++-4 ++2 ++6 ++2 ++4 ++6 ++2 ++2 ++4 ++-4 ++0 ++0 ++-4 ++2 ++-8 ++-2 ++2 ++2 ++0 ++2 ++-4 ++-4 ++-2 ++-2 ++-2 ++0 ++-6 ++-4 ++2 ++-2 ++-6 ++-6 ++-4 ++-6 ++0 ++-6 ++-10 ++-2 ++-2 ++2 ++2 ++-6 ++-4 ++-6 ++4 ++0 ++2 ++2 ++0 ++6 ++0 ++-2 ++-4 ++2 ++4 ++6 ++4 ++-2 ++0 ++0 ++-2 ++2 ++0 ++2 ++2 ++-2 ++4 ++0 ++0 ++0 ++0 ++0 ++2 ++4 ++-2 ++-4 ++-4 ++2 ++2 ++-2 ++4 ++-2 ++0 ++0 ++-10 ++0 ++-4 ++-2 ++2 ++0 ++0 ++0 ++0 ++-4 ++-4 ++2 ++-2 ++-2 ++-2 ++-6 ++0 ++-6 ++-4 ++-8 ++4 ++2 ++4 ++2 ++-2 ++2 ++2 ++0 ++4 ++-2 ++-6 ++4 ++-6 ++-4 ++-4 ++2 ++-4 ++-2 ++-4 ++-2 ++2 ++-2 ++-4 ++0 ++4 ++-4 ++4 ++-2 ++-4 ++0 ++4 ++-6 ++-6 ++-2 ++-6 ++2 ++4 ++4 ++2 ++4 ++0 ++2 ++0 ++-2 ++0 ++-4 ++-2 ++-4 ++-4 ++-4 ++0 ++6 ++-4 ++-4 ++2 ++2 ++-8 ++0 ++-4 ++-4 ++-4 ++-6 ++-4 ++-4 ++-2 ++-2 ++-2 ++-2 ++-2 ++-4 ++-4 ++-2 ++-2 ++4 ++-4 ++6 ++-4 ++0 ++0 ++0 ++2 ++6 ++0 ++2 ++6 ++-6 ++6 ++6 ++-6 ++6 ++-2 ++-4 ++4 ++-2 ++4 ++0 ++2 ++-2 ++0 ++-6 ++-4 ++-4 ++-2 ++-4 ++-6 ++0 ++-8 ++-4 ++-8 ++-4 ++-2 ++4 ++-6 ++-8 ++0 ++-6 ++-2 ++-4 ++-4 ++-2 ++0 ++-2 ++2 ++2 ++4 ++4 ++6 ++4 ++6 ++6 ++4 ++6 ++8 ++2 ++8 ++4 ++2 ++6 ++4 ++2 ++-2 ++6 ++8 ++2 ++6 ++4 ++2 ++-2 ++0 ++0 ++2 ++4 ++6 ++0 ++8 ++2 ++2 ++6 ++2 ++-2 ++2 ++0 ++2 ++2 ++-2 ++2 ++6 ++2 ++8 ++6 ++2 ++4 ++2 ++2 ++8 ++6 ++-4 ++-2 ++-2 ++-4 ++0 ++-2 ++4 ++0 ++0 ++2 ++0 ++2 ++2 ++0 ++2 ++2 ++0 ++0 ++4 ++4 ++-2 ++2 ++-4 ++4 ++0 ++0 ++2 ++0 ++-2 ++-2 ++-2 ++-2 ++0 ++4 ++2 ++2 ++-2 ++2 ++0 ++0 ++0 ++0 ++-4 ++-4 ++-2 ++-6 ++2 ++2 ++2 ++4 ++6 ++0 ++-2 ++-4 ++-2 ++-2 ++2 ++2 ++-2 ++-4 ++-2 ++0 ++-2 ++-4 ++0 ++-2 ++-4 ++-2 ++-8 ++-6 ++-6 ++0 ++-4 ++-2 ++-4 ++0 ++2 ++-4 ++-2 ++0 ++2 ++0 ++-4 ++-4 ++0 ++0 ++0 ++0 ++0 ++0 ++2 ++-4 ++-2 ++-4 ++-2 ++4 ++-2 ++2 ++0 ++-2 ++2 ++0 ++-4 ++4 ++0 ++-2 ++-2 ++4 ++-2 ++2 ++0 ++-2 ++-2 ++4 ++2 ++0 ++0 ++2 ++2 ++-4 ++-2 ++2 ++0 ++-2 ++-4 ++0 ++0 ++0 ++-2 ++0 ++0 ++4 ++0 ++0 ++0 ++0 ++6 ++4 ++2 ++-4 ++2 ++2 ++0 ++4 ++-2 ++4 ++2 ++6 ++-2 ++-2 ++-2 ++0 ++0 ++0 ++-4 ++-6 ++2 ++0 ++0 ++-2 ++-2 ++-2 ++0 ++-2 ++0 ++0 ++-2 ++4 ++-2 ++0 ++-4 ++0 ++-2 ++0 ++2 ++4 ++0 ++0 ++2 ++2 ++6 ++4 ++2 ++-2 ++-2 ++2 ++0 ++-4 ++-2 ++4 ++-4 ++2 ++2 ++-4 ++-2 ++0 ++-2 ++-2 ++0 ++0 ++2 ++-4 ++0 ++-4 ++-8 ++-2 ++0 ++-4 ++-6 ++-2 ++-6 ++-4 ++-6 ++0 ++-2 ++-2 ++2 ++2 ++2 ++4 ++2 ++4 ++2 ++0 ++4 ++4 ++0 ++-2 ++-2 ++0 ++2 ++-2 ++-4 ++-2 ++-4 ++-2 ++2 ++2 ++0 ++-4 ++-2 ++2 ++-2 ++0 ++0 ++-2 ++-2 ++-4 ++-4 ++-2 ++-4 ++-2 ++-2 ++0 ++-6 ++4 ++2 ++2 ++-4 ++-2 ++-2 ++-6 ++2 ++-2 ++-2 ++-2 ++2 ++0 ++-4 ++4 ++0 ++0 ++2 ++2 ++-2 ++0 ++4 ++-2 ++0 ++2 ++6 ++-2 ++2 ++-2 ++4 ++2 ++-4 ++0 ++-6 ++-2 ++4 ++0 ++-4 ++-2 ++0 ++2 ++2 ++2 ++0 ++2 ++2 ++0 ++-4 ++-2 ++2 ++-2 ++-4 ++2 ++0 ++4 ++2 ++0 ++2 ++6 ++2 ++2 ++4 ++0 ++2 ++4 ++0 ++0 ++4 ++4 ++4 ++-4 ++0 ++-4 ++2 ++2 ++-2 ++0 ++0 ++4 ++-2 ++0 ++-2 ++0 ++-2 ++2 ++-2 ++2 ++-4 ++0 ++2 ++-2 ++4 ++2 ++-2 ++-4 ++-2 ++0 ++2 ++2 ++2 ++0 ++-4 ++-2 ++-4 ++-6 ++0 ++2 ++2 ++-2 ++2 ++-4 ++-4 ++0 ++2 ++0 ++2 ++2 ++2 ++2 ++0 ++2 ++-4 ++-2 ++-4 ++-2 ++0 ++-2 ++-2 ++-2 ++-4 ++-4 ++-2 ++-4 ++-4 ++2 ++-2 ++0 ++0 ++-2 ++-2 ++-2 ++-2 ++0 ++-4 ++0 ++0 ++-2 ++-4 ++-4 ++-2 ++2 ++2 ++4 ++-2 ++2 ++0 ++4 ++0 ++2 ++2 ++0 ++4 ++4 ++2 ++2 ++-2 ++4 ++0 ++6 ++8 ++4 ++0 ++-2 ++0 ++-2 ++2 ++2 ++-2 ++-2 ++2 ++-2 ++-6 ++-2 ++-4 ++-4 ++-2 ++-2 ++-6 ++-2 ++-4 ++-2 ++-6 ++0 ++6 ++2 ++2 ++-2 ++-2 ++-2 ++-2 ++-2 ++-2 ++2 ++-2 ++2 ++2 ++-2 ++6 ++8 ++2 ++4 ++0 ++-2 ++0 ++-2 ++2 ++4 ++6 ++6 ++4 ++-4 ++-4 ++0 ++2 ++-2 ++-4 ++2 ++-2 ++-2 ++-4 ++-2 ++2 ++0 ++2 ++-2 ++0 ++0 ++2 ++-4 ++-2 ++2 ++-6 ++0 ++-4 ++-2 ++-2 ++2 ++2 ++-6 ++2 ++4 ++-4 ++-6 ++-2 ++-4 ++0 ++0 ++4 ++0 ++-2 ++4 ++2 ++-2 ++2 ++0 ++0 ++4 ++0 ++-2 ++0 ++4 ++0 ++-2 ++4 ++2 ++4 ++4 ++2 ++2 ++2 ++2 ++0 ++2 ++4 ++4 ++4 ++4 ++2 ++2 ++4 ++2 ++0 ++0 ++0 ++0 ++0 ++6 ++0 ++-4 ++4 ++-2 ++-2 ++-2 ++2 ++2 ++0 ++2 ++0 ++4 ++4 ++2 ++0 ++0 ++4 ++0 ++4 ++4 ++0 ++0 ++2 ++4 ++2 ++0 ++2 ++0 ++0 ++6 ++2 ++2 ++0 ++2 ++2 ++2 ++2 ++0 ++0 ++0 ++2 ++0 ++-2 ++2 ++2 ++0 ++-2 ++0 ++4 ++0 ++-2 ++-2 ++-2 ++2 ++-2 ++2 ++2 ++2 ++2 ++0 ++-2 ++-2 ++2 ++2 ++4 ++-2 ++2 ++0 ++0 ++4 ++0 ++0 ++4 ++4 ++0 ++4 ++4 ++2 ++0 ++4 ++6 ++2 ++0 ++4 ++6 ++0 ++4 ++4 ++4 ++6 ++4 ++8 ++0 ++2 ++0 ++2 ++2 ++-2 ++4 ++0 ++0 ++-2 ++2 ++0 ++4 ++4 ++4 ++4 ++2 ++4 ++2 ++2 ++2 ++4 ++0 ++-2 ++-4 ++0 ++2 ++2 ++4 ++2 ++6 ++4 ++2 ++-2 ++2 ++4 ++2 ++6 ++-2 ++2 ++-2 ++2 ++4 ++4 ++6 ++2 ++6 ++0 ++-4 ++0 ++2 ++0 ++0 ++-2 ++0 ++0 ++-2 ++-2 ++-2 ++2 ++0 ++4 ++2 ++-2 ++4 ++2 ++4 ++0 ++0 ++4 ++2 ++-2 ++4 ++2 ++-2 ++4 ++6 ++4 ++6 ++0 ++2 ++4 ++2 ++6 ++2 ++4 ++0 ++4 ++4 ++-2 ++2 ++2 ++4 ++4 ++2 ++2 ++0 ++0 ++4 ++0 ++0 ++2 ++2 ++-2 ++-4 ++0 ++2 ++0 ++0 ++2 ++-2 ++4 ++2 ++0 ++4 ++4 ++6 ++4 ++0 ++0 ++-4 ++0 ++0 ++4 ++-2 ++0 ++0 ++-2 ++2 ++2 ++4 ++6 ++4 ++0 ++0 ++2 ++2 ++-2 ++4 ++0 ++2 ++0 ++-4 ++2 ++-4 ++8 ++6 ++2 ++4 ++0 ++2 ++0 ++2 ++2 ++4 ++8 ++4 ++2 ++0 ++0 ++-2 ++4 ++2 ++0 ++4 ++2 ++-2 ++2 ++0 ++0 ++4 ++0 ++2 ++2 ++2 ++0 ++2 ++-2 ++4 ++2 ++0 ++4 ++-2 ++0 ++-4 ++0 ++0 ++2 ++0 ++-2 ++-2 ++0 ++0 ++2 ++2 ++0 ++0 ++4 ++4 ++4 ++6 ++4 ++2 ++2 ++4 ++4 ++2 ++4 ++2 ++0 ++0 ++2 ++0 ++-4 ++0 ++-4 ++-2 ++0 ++2 ++2 ++2 ++6 ++4 ++-2 ++4 ++4 ++0 ++0 ++2 ++0 ++0 ++2 ++-4 ++4 ++2 ++4 ++2 ++0 ++4 ++0 ++6 ++8 ++0 ++6 ++2 ++2 ++0 ++0 ++2 ++6 ++0 ++2 ++0 ++-4 ++0 ++-2 ++2 ++0 ++6 ++0 ++4 ++0 ++4 ++4 ++0 ++6 ++4 ++6 ++2 ++2 ++-2 ++-2 ++0 ++-2 ++4 ++0 ++2 ++-2 ++0 ++-2 ++2 ++2 ++0 ++4 ++2 ++0 ++0 ++-2 ++4 ++-2 ++2 ++4 ++0 ++2 ++2 ++2 ++-4 ++-2 ++0 ++0 ++0 ++-2 ++0 ++0 ++4 ++2 ++0 ++-2 ++0 ++0 ++0 ++-2 ++2 ++0 ++-2 ++4 ++-2 ++0 ++4 ++0 ++2 ++4 ++2 ++-2 ++4 ++2 ++4 ++0 ++0 ++4 ++-2 ++2 ++0 ++2 ++2 ++6 ++4 ++4 ++2 ++0 ++6 ++2 ++2 ++0 ++6 ++4 ++2 ++4 ++2 ++0 ++-2 ++2 ++2 ++6 ++0 ++2 ++0 ++2 ++4 ++0 ++6 ++4 ++4 ++4 ++2 ++-2 ++0 ++2 ++2 ++4 ++0 ++2 ++-2 ++2 ++-2 ++-2 ++0 ++0 ++-2 ++-4 ++0 ++-4 ++-4 ++-2 ++2 ++2 ++0 ++2 ++0 ++-4 ++2 ++0 ++2 ++4 ++2 ++0 ++-4 ++2 ++0 ++4 ++2 ++4 ++4 ++4 ++4 ++2 ++4 ++6 ++8 ++2 ++6 ++0 ++0 ++6 ++0 ++-2 ++0 ++4 ++-2 ++-2 ++2 ++2 ++-2 ++4 ++4 ++0 ++4 ++2 ++2 ++-2 ++2 ++4 ++4 ++-2 ++0 ++0 ++0 ++4 ++0 ++2 ++2 ++2 ++4 ++2 ++0 ++2 ++4 ++4 ++4 ++4 ++6 ++4 ++2 ++2 ++0 ++0 ++2 ++0 ++-4 ++0 ++0 ++2 ++2 ++2 ++4 ++-2 ++2 ++0 ++4 ++0 ++4 ++0 ++2 ++0 ++6 ++4 ++0 ++2 ++4 ++4 ++0 ++2 ++-2 ++6 ++2 ++4 ++2 ++0 ++2 ++0 ++0 ++0 ++2 ++2 ++4 ++0 ++-4 ++0 ++0 ++2 ++2 ++2 ++-2 ++0 ++-2 ++-4 ++-4 ++-2 ++0 ++-4 ++-2 ++4 ++2 ++2 ++2 ++0 ++2 ++0 ++4 ++2 ++0 ++6 ++4 ++4 ++4 ++4 ++2 ++4 ++4 ++6 ++4 ++2 ++0 ++2 ++0 ++2 ++0 ++6 ++6 ++0 ++6 ++6 ++0 ++0 ++2 ++-2 ++0 ++4 ++-2 ++0 ++-2 ++0 ++0 ++2 ++0 ++-2 ++-2 ++0 ++2 ++-2 ++0 ++0 ++0 ++4 ++6 ++6 ++0 ++2 ++4 ++0 ++0 ++-2 ++-4 ++0 ++4 ++0 ++4 ++2 ++4 ++4 ++2 ++4 ++4 ++4 ++6 ++2 ++2 ++6 ++4 ++4 ++0 ++0 ++4 ++4 ++2 ++0 ++-2 ++2 ++4 ++-2 ++2 ++4 ++2 ++8 ++4 ++-2 ++2 ++-2 ++2 ++-2 ++0 ++0 ++0 ++0 ++0 ++-2 ++0 ++2 ++-2 ++-2 ++4 ++0 ++-2 ++0 ++2 ++0 ++2 ++0 ++-2 ++0 ++0 ++2 ++-2 ++2 ++2 ++0 ++-4 ++0 ++0 ++4 ++2 ++0 ++-2 ++0 ++0 ++2 ++2 ++2 ++0 ++-2 ++0 ++0 ++0 ++0 ++-4 ++-2 ++-2 ++4 ++2 ++-2 ++-2 ++-2 ++-2 ++2 ++0 ++-2 ++-2 ++0 ++0 ++0 ++0 ++0 ++0 ++2 ++-2 ++0 ++2 ++0 ++0 ++0 ++-2 ++-2 ++0 ++-2 ++2 ++-4 ++0 ++2 ++0 ++2 ++-2 ++-2 ++2 ++-2 ++2 ++-2 ++2 ++0 ++0 ++-2 ++2 ++-2 ++-2 ++0 ++-4 ++-2 ++-2 ++-6 ++-2 ++0 ++-2 ++0 ++-4 ++-2 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++-4 ++0 ++0 ++2 ++-2 ++-2 ++-2 ++0 ++-2 ++2 ++-2 ++0 ++2 ++2 ++0 ++2 ++2 ++6 ++2 ++2 ++-2 ++0 ++0 ++2 ++-2 ++-2 ++-2 ++0 ++2 ++2 ++-2 ++-4 ++-2 ++2 ++-2 ++-2 ++-4 ++-2 ++-2 ++0 ++-4 ++0 ++2 ++-2 ++0 ++-4 ++0 ++-4 ++0 ++-4 ++0 ++2 ++-2 ++0 ++0 ++0 ++2 ++2 ++0 ++4 ++4 ++2 ++2 ++-2 ++0 ++-2 ++0 ++0 ++2 ++2 ++2 ++2 ++0 ++2 ++0 ++4 ++-2 ++0 ++-2 ++-2 ++2 ++-4 ++-2 ++-4 ++-2 ++2 ++2 ++0 ++0 ++-2 ++-2 ++0 ++0 ++-4 ++0 ++0 ++0 ++0 ++-2 ++0 ++-2 ++2 ++2 ++2 ++-2 ++2 ++2 ++2 ++0 ++4 ++-4 ++2 ++4 ++2 ++2 ++0 ++-2 ++2 ++2 ++-2 ++-4 ++0 ++2 ++2 ++2 ++0 ++2 ++2 ++6 ++4 ++2 ++0 ++4 ++-2 ++-2 ++-2 ++-4 ++0 ++-2 ++0 ++0 ++-2 ++-4 ++2 ++-2 ++2 ++0 ++0 ++2 ++-2 ++-2 ++0 ++-2 ++-4 ++-2 ++-4 ++0 ++-4 ++-2 ++-2 ++-6 ++0 ++-2 ++-6 ++-2 ++-2 ++-2 ++-4 ++0 ++0 ++4 ++2 ++2 ++0 ++0 ++0 ++-2 ++0 ++-2 ++0 ++-2 ++0 ++-2 ++-2 ++-2 ++2 ++-2 ++4 ++0 ++0 ++0 ++-2 ++0 ++-4 ++-2 ++0 ++2 ++2 ++4 ++0 ++0 ++4 ++0 ++-2 ++-2 ++0 ++0 ++0 ++0 ++2 ++-4 ++4 ++-2 ++0 ++2 ++2 ++0 ++-2 ++-2 ++0 ++-4 ++2 ++2 ++4 ++0 ++0 ++2 ++-2 ++-2 ++0 ++0 ++0 ++0 ++-2 ++0 ++2 ++0 ++-2 ++-2 ++0 ++2 ++-2 ++0 ++-2 ++0 ++0 ++0 ++0 ++-2 ++-2 ++-4 ++-4 ++-2 ++0 ++-2 ++0 ++-4 ++-4 ++0 ++-4 ++0 ++-2 ++0 ++0 ++-2 ++0 ++0 ++-2 ++2 ++0 ++0 ++2 ++2 ++2 ++6 ++2 ++2 ++2 ++4 ++2 ++4 ++4 ++2 ++-2 ++2 ++4 ++2 ++6 ++2 ++6 ++0 ++4 ++0 ++0 ++-2 ++-2 ++-2 ++0 ++0 ++0 ++0 ++-2 ++-2 ++0 ++0 ++-2 ++-2 ++-2 ++-2 ++-2 ++-4 ++-2 ++-2 ++-4 ++-2 ++-4 ++2 ++0 ++4 ++0 ++0 ++2 ++0 ++2 ++2 ++4 ++0 ++2 ++2 ++0 ++2 ++2 ++0 ++-2 ++0 ++2 ++0 ++0 ++-2 ++2 ++0 ++0 ++0 ++0 ++0 ++0 ++2 ++-2 ++0 ++0 ++0 ++-4 ++0 ++-2 ++-2 ++-2 ++-2 ++0 ++-4 ++-2 ++-2 ++-2 ++-2 ++-2 ++0 ++-4 ++-2 ++-2 ++0 ++-2 ++-2 ++0 ++0 ++0 ++2 ++-2 ++-4 ++0 ++-2 ++0 ++0 ++-2 ++2 ++0 ++-4 ++-4 ++0 ++0 ++2 ++2 ++2 ++4 ++2 ++0 ++2 ++-2 ++0 ++-2 ++0 ++0 ++2 ++2 ++0 ++2 ++2 ++0 ++0 ++0 ++0 ++-2 ++-4 ++-4 ++-2 ++-2 ++-2 ++-2 ++-2 ++2 ++0 ++-2 ++2 ++-2 ++-2 ++-4 ++-4 ++-2 ++-2 ++-4 ++-4 ++-4 ++-2 ++-4 ++-2 ++0 ++0 ++0 ++2 ++2 ++0 ++2 ++0 ++2 ++2 ++2 ++4 ++0 ++2 ++-2 ++2 ++2 ++0 ++4 ++2 ++2 ++4 ++2 ++2 ++2 ++0 ++4 ++2 ++2 ++0 ++0 ++2 ++2 ++2 ++2 ++-2 ++-2 ++-2 ++0 ++0 ++-2 ++-2 ++2 ++-2 ++-2 ++0 ++-4 ++2 ++-2 ++0 ++2 ++2 ++2 ++4 ++2 ++4 ++0 ++2 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++-2 ++0 ++0 ++2 ++2 ++0 ++0 ++0 ++2 ++0 ++0 ++4 ++0 ++0 ++0 ++-4 ++0 ++-6 ++0 ++0 ++0 ++2 ++0 ++0 ++-2 ++-2 ++-4 ++2 ++0 ++0 ++0 ++-2 ++0 ++-2 ++-2 ++2 ++-4 ++0 ++-2 ++-4 ++0 ++-4 ++-4 ++-2 ++-2 ++-2 ++-2 ++-2 ++-4 ++-2 ++0 ++0 ++-4 ++-2 ++2 ++-2 ++2 ++2 ++-2 ++0 ++-2 ++2 ++-2 ++2 ++2 ++0 ++2 ++2 ++2 ++0 ++-2 ++2 ++-2 ++0 ++-2 ++0 ++-2 ++0 ++-2 ++0 ++0 ++0 ++-2 ++2 ++-4 ++2 ++2 ++-2 ++2 ++0 ++2 ++0 ++0 ++2 ++2 ++-2 ++-2 ++2 ++-6 ++-2 ++2 ++-2 ++-2 ++0 ++0 ++2 ++2 ++2 ++0 ++2 ++0 ++-2 ++-2 ++-2 ++0 ++-2 ++-2 ++2 ++-2 ++2 ++2 ++-4 ++0 ++-2 ++2 ++-2 ++0 ++-2 ++0 ++0 ++-2 ++0 ++2 ++2 ++0 ++0 ++2 ++2 ++0 ++2 ++2 ++0 ++-4 ++4 ++0 ++4 ++0 ++2 ++4 ++2 ++2 ++2 ++0 ++-2 ++0 ++0 ++0 ++-2 ++0 ++0 ++2 ++-2 ++0 ++0 ++2 ++0 ++-2 ++-2 ++-2 ++-2 ++-2 ++-2 ++0 ++-2 ++-2 ++-2 ++-2 ++0 ++-2 ++-2 ++-4 ++-2 ++0 ++0 ++0 ++0 ++-2 ++-2 ++0 ++2 ++0 ++2 ++2 ++2 ++2 ++0 ++0 ++0 ++0 ++-2 ++0 ++0 ++2 ++2 ++-2 ++2 ++-2 ++0 ++0 ++0 ++2 ++2 ++0 ++0 ++-2 ++2 ++-2 ++-2 ++0 ++-2 ++-4 ++-2 ++-4 ++-4 ++0 ++-4 ++-2 ++0 ++0 ++2 ++2 ++-2 ++2 ++0 ++2 ++2 ++2 ++2 ++0 ++0 ++-2 ++-2 ++0 ++2 ++0 ++2 ++2 ++2 ++0 ++2 ++-2 ++0 ++0 ++0 ++-2 ++-2 ++0 ++-2 ++-2 ++-4 ++-4 ++-4 ++-4 ++-4 ++0 ++0 ++-2 ++-2 ++0 ++0 ++0 ++0 ++0 ++0 ++-2 ++-2 ++2 ++0 ++0 ++-2 ++0 ++0 ++0 ++2 ++2 ++2 ++4 ++2 ++0 ++0 ++0 ++-2 ++0 ++0 ++0 ++2 ++0 ++0 ++-2 ++-2 ++-2 ++0 ++0 ++0 ++2 ++0 ++0 ++0 ++0 ++2 ++2 ++0 ++0 ++0 ++-2 ++2 ++-2 ++-2 ++-4 ++0 ++0 ++0 ++0 ++2 ++2 ++-2 ++2 ++-2 ++-2 ++4 ++-2 ++-2 ++0 ++0 ++0 ++-2 ++-4 ++0 ++-2 ++-4 ++0 ++-4 ++-2 ++-2 ++-2 ++2 ++0 ++-2 ++0 ++-2 ++0 ++4 ++0 ++2 ++0 ++0 ++-2 ++-2 ++-2 ++-2 ++2 ++0 ++2 ++0 ++2 ++0 ++2 ++0 ++2 ++4 ++2 ++0 ++2 ++0 ++2 ++0 ++0 ++0 ++-2 ++0 ++0 ++2 ++0 ++0 ++2 ++4 ++2 ++0 ++-2 ++2 ++2 ++-2 ++0 ++-2 ++0 ++2 ++0 ++-2 ++2 ++0 ++2 ++0 ++0 ++4 ++4 ++0 ++0 ++-4 ++-2 ++-2 ++2 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++-2 ++-2 ++-2 ++-2 ++-2 ++0 ++0 ++2 ++0 ++2 ++0 ++2 ++-2 ++-4 ++0 ++0 ++2 ++0 ++0 ++2 ++2 ++0 ++-2 ++2 ++2 ++2 ++-2 ++0 ++0 ++-2 ++0 ++0 ++0 ++-2 ++-2 ++-2 ++2 ++0 ++0 ++0 ++-2 ++-2 ++-4 ++-2 ++-2 ++0 ++-2 ++0 ++-2 ++0 ++-2 ++-2 ++-4 ++-4 ++-4 ++-2 ++0 ++-2 ++0 ++-2 ++2 ++2 ++2 ++2 ++0 ++-2 ++4 ++-2 ++2 ++-2 ++2 ++0 ++2 ++0 ++2 ++-2 ++0 ++0 ++-4 ++0 ++2 ++0 ++0 ++0 ++2 ++2 ++0 ++0 ++2 ++0 ++2 ++-2 ++-2 ++0 ++0 ++-2 ++0 ++0 ++-2 ++0 ++2 ++2 ++4 ++0 ++2 ++2 ++2 ++0 ++-2 ++-2 ++-2 ++0 ++2 ++2 ++-2 ++-2 ++2 ++0 ++-2 ++0 ++-2 ++-4 ++-2 ++-2 ++-2 ++0 ++-2 ++-2 ++-4 ++-2 ++0 ++-2 ++-4 ++2 ++0 ++0 ++0 ++-2 ++-2 ++0 ++0 ++2 ++0 ++-2 ++-2 ++2 ++0 ++-2 ++-2 ++2 ++-2 ++0 ++0 ++-2 ++2 ++0 ++2 ++-2 ++-2 ++2 ++2 ++2 ++0 ++0 ++0 ++2 ++2 ++0 ++0 ++-2 ++0 ++0 ++0 ++0 ++0 ++2 ++0 ++-2 ++2 ++0 ++2 ++-2 ++-2 ++2 ++0 ++-2 ++0 ++0 ++0 ++2 ++2 ++-2 ++4 ++-2 ++-2 ++2 ++0 ++0 ++-2 ++2 ++0 ++-2 ++0 ++2 ++0 ++-2 ++2 ++0 ++-4 ++0 ++-4 ++2 ++0 ++-2 ++0 ++-2 ++0 ++0 ++-2 ++0 ++-2 ++-2 ++0 ++0 ++-4 ++0 ++-4 ++0 ++-2 ++-2 ++4 ++-2 ++0 ++2 ++-4 ++2 ++2 ++0 ++4 ++2 ++0 ++4 ++2 ++0 ++-2 ++-2 ++-2 ++0 ++-2 ++0 ++-2 ++0 ++0 ++2 ++0 ++0 ++2 ++0 ++0 ++2 ++0 ++2 ++-2 ++0 ++2 ++0 ++-2 ++0 ++2 ++0 ++2 ++-2 ++-2 ++0 ++-2 ++0 ++2 ++-2 ++2 ++0 ++2 ++-2 ++0 ++0 ++2 ++2 ++-2 ++0 ++2 ++2 ++0 ++0 ++0 ++0 ++-2 ++4 ++0 ++2 ++-2 ++-2 ++2 ++2 ++2 ++2 ++2 ++2 ++2 ++0 ++4 ++2 ++0 ++2 ++-2 ++2 ++0 ++-4 ++0 ++0 ++-2 ++2 ++-2 ++-2 ++0 ++-2 ++-2 ++0 ++-2 ++0 ++0 ++-2 ++2 ++0 ++-4 ++0 ++-4 ++0 ++-2 ++0 ++0 ++-2 ++0 ++0 ++0 ++2 ++2 ++-2 ++4 ++-2 ++2 ++2 ++-2 ++0 ++2 ++-2 ++4 ++-2 ++0 ++2 ++0 ++0 ++0 ++-2 ++2 ++0 ++2 ++-2 ++0 ++0 ++2 ++-2 ++-2 ++-2 ++-2 ++2 ++-4 ++-2 ++-4 ++-4 ++-2 ++0 ++0 ++0 ++-2 ++0 ++0 ++-2 ++2 ++0 ++0 ++0 ++2 ++2 ++0 ++2 ++0 ++2 ++2 ++2 ++2 ++0 ++-2 ++0 ++-2 ++2 ++-2 ++2 ++2 ++2 ++0 ++-2 ++0 ++2 ++0 ++0 ++2 ++-2 ++-2 ++0 ++-2 ++2 ++2 ++-2 ++0 ++2 ++2 ++-2 ++-4 ++-2 ++0 ++-2 ++2 ++-2 ++-2 ++-2 ++0 ++0 ++-2 ++0 ++-2 ++2 ++-2 ++-2 ++-2 ++-2 ++-2 ++0 ++2 ++0 ++-2 ++-2 ++-4 ++-2 ++-4 ++0 ++0 ++-4 ++-2 ++2 ++0 ++0 ++0 ++0 ++2 ++2 ++4 ++0 ++-2 ++2 ++2 ++0 ++2 ++2 ++4 ++2 ++0 ++-2 ++0 ++-2 ++2 ++-2 ++-4 ++0 ++-2 ++0 ++0 ++-4 ++-2 ++0 ++2 ++2 ++2 ++-2 ++0 ++0 ++2 ++-2 ++-2 ++-2 ++-4 ++-2 ++-2 ++0 ++-2 ++0 ++-2 ++-2 ++-2 ++0 ++-2 ++-4 ++0 ++-2 ++2 ++2 ++2 ++0 ++0 ++2 ++0 ++0 ++2 ++0 ++2 ++0 ++0 ++4 ++-2 ++2 ++-2 ++-2 ++2 ++0 ++0 ++0 ++0 ++2 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++2 ++2 ++0 ++2 ++0 ++-2 ++0 ++-4 ++-4 ++-4 ++-4 ++-4 ++-2 ++0 ++0 ++2 ++2 ++2 ++0 ++0 ++0 ++0 ++0 ++0 ++2 ++0 ++0 ++0 ++2 ++2 ++4 ++2 ++0 ++-2 ++-2 ++-4 ++-2 ++0 ++-2 ++0 ++-2 ++-2 ++0 ++2 ++-2 ++-2 ++0 ++-2 ++-2 ++-2 ++-2 ++-2 ++-4 ++0 ++-2 ++0 ++0 ++2 ++-2 ++-4 ++0 ++2 ++2 ++0 ++-2 ++0 ++0 ++0 ++2 ++0 ++0 ++2 ++2 ++2 ++2 ++2 ++0 ++0 ++0 ++0 ++0 ++0 ++2 ++2 ++2 ++-2 ++2 ++0 ++2 ++2 ++2 ++-2 ++2 ++0 ++4 ++2 ++0 ++0 ++-2 ++2 ++2 ++2 ++-2 ++-2 ++-4 ++-2 ++-4 ++-4 ++-6 ++-4 ++-2 ++0 ++2 ++-2 ++2 ++0 ++0 ++0 ++2 ++0 ++-2 ++-2 ++0 ++0 ++0 ++0 ++0 ++2 ++2 ++2 ++4 ++2 ++2 ++0 ++2 ++0 ++0 ++2 ++0 ++2 ++2 ++0 ++0 ++0 ++2 ++-2 ++0 ++-4 ++0 ++-4 ++-4 ++-2 ++-2 ++-2 ++-2 ++-2 ++-2 ++0 ++-2 ++0 ++-2 ++-4 ++-2 ++0 ++-2 ++-2 ++-2 ++0 ++4 ++-2 ++0 ++2 ++-2 ++0 ++0 ++0 ++0 ++0 ++2 ++0 ++-2 ++0 ++2 ++2 ++4 ++-2 ++-2 ++2 ++-2 ++-2 ++0 ++2 ++2 ++-2 ++2 ++2 ++0 ++0 ++0 ++0 ++0 ++0 ++-2 ++-2 ++-2 ++0 ++0 ++-2 ++0 ++0 ++-2 ++0 ++0 ++-4 ++2 ++0 ++0 ++-4 ++-2 ++-2 ++0 ++0 ++-2 ++0 ++0 ++-2 ++0 ++-2 ++0 ++-2 ++2 ++0 ++2 ++-2 ++-2 ++-2 ++0 ++2 ++0 ++0 ++-2 ++0 ++2 ++4 ++2 ++4 ++2 ++0 ++2 ++-2 ++2 ++0 ++4 ++-2 ++0 ++2 ++-2 ++-2 ++-4 ++0 ++-2 ++0 ++0 ++-2 ++2 ++0 ++0 ++0 ++0 ++-2 ++0 ++-2 ++-2 ++0 ++-2 ++-2 ++-2 ++0 ++0 ++-2 ++-2 ++-2 ++-2 ++-4 ++2 ++-4 ++2 ++-2 ++-2 ++-2 ++0 ++0 ++2 ++0 ++2 ++2 ++-2 ++0 ++2 ++-2 ++0 ++2 ++2 ++0 ++-2 ++0 ++0 ++0 ++0 ++2 ++2 ++2 ++0 ++-2 ++0 ++-2 ++-2 ++-2 ++0 ++-2 ++0 ++-4 ++2 ++0 ++0 ++4 ++-2 ++-2 ++0 ++0 ++-2 ++2 ++0 ++-2 ++-2 ++0 ++0 ++2 ++-2 ++0 ++0 ++2 ++0 ++2 ++2 ++4 ++2 ++0 ++2 ++4 ++4 ++4 ++-2 ++0 ++2 ++0 ++0 ++0 ++-4 ++2 ++-2 ++0 ++0 ++2 ++-2 ++0 ++0 ++-2 ++2 ++2 ++-2 ++0 ++0 ++-2 ++-2 ++-2 ++-4 ++-2 ++-2 ++-2 ++-2 ++-2 ++-2 ++-2 ++-4 ++-2 ++0 ++0 ++0 ++0 ++0 ++2 ++0 ++0 ++2 ++0 ++0 ++0 ++0 ++-2 ++0 ++2 ++-2 ++0 ++-2 ++-2 ++0 ++-2 ++0 ++2 ++2 ++0 ++2 ++-2 ++0 ++0 ++0 ++-2 ++-2 ++-2 ++-2 ++0 ++-2 ++-4 ++-4 ++0 ++0 ++0 ++-2 ++-2 ++-2 ++-4 ++0 ++-2 ++-4 ++0 ++-2 ++0 ++-4 ++-2 ++-2 ++2 ++-2 ++0 ++2 ++2 ++0 ++2 ++-2 ++-2 ++-2 ++-2 ++-2 ++0 ++0 ++0 ++0 ++-4 ++2 ++2 ++-2 ++2 ++0 ++2 ++4 ++2 ++2 ++2 ++4 ++4 ++4 ++4 ++4 ++2 ++2 ++0 ++-2 ++0 ++0 ++-2 ++-2 ++-2 ++0 ++-2 ++-2 ++-4 ++-4 ++0 ++-2 ++-2 ++-2 ++-2 ++-4 ++-2 ++-2 ++-6 ++-4 ++-4 ++-2 ++0 ++0 ++-2 ++2 ++0 ++2 ++2 ++2 ++4 ++2 ++2 ++0 ++0 ++2 ++2 ++2 ++4 ++4 ++2 ++4 ++2 ++4 ++0 ++-2 ++-2 ++2 ++4 ++0 ++2 ++0 ++0 ++2 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++-2 ++0 ++0 ++-2 ++0 ++0 ++-2 ++2 ++-2 ++0 ++0 ++0 ++-2 ++0 ++4 ++-2 ++2 ++0 ++2 ++2 ++0 ++4 ++2 ++0 ++0 ++0 ++-2 ++-2 ++-2 ++-2 ++2 ++-2 ++0 ++-2 ++0 ++2 ++2 ++-2 ++2 ++0 ++0 ++2 ++0 ++0 ++0 ++2 ++-2 ++-2 ++0 ++-2 ++0 ++0 ++-2 ++-2 ++2 ++0 ++-2 ++0 ++0 ++0 ++-2 ++2 ++2 ++-2 ++2 ++0 ++0 ++2 ++-2 ++-2 ++-4 ++-2 ++-2 ++-2 ++-2 ++-2 ++-2 ++2 ++-2 ++-2 ++-4 ++-2 ++-4 ++-2 ++-2 ++0 ++-4 ++-4 ++-2 ++-4 ++-2 ++-2 ++0 ++0 ++-4 ++-2 ++-2 ++0 ++-2 ++-4 ++-4 ++0 ++-2 ++0 ++-2 ++2 ++0 ++4 ++2 ++2 ++2 ++4 ++0 ++2 ++2 ++0 ++2 ++0 ++0 ++0 ++0 ++-2 ++-2 ++0 ++2 ++0 ++-2 ++-2 ++0 ++2 ++0 ++0 ++-2 ++0 ++4 ++0 ++2 ++0 ++-2 ++0 ++2 ++2 ++0 ++0 ++0 ++2 ++0 ++-2 ++2 ++-2 ++2 ++0 ++-2 ++-2 ++-2 ++0 ++-2 ++-2 ++2 ++-2 ++-2 ++-2 ++-2 ++0 ++-2 ++-2 ++-2 ++-2 ++0 ++-2 ++0 ++0 ++-2 ++-2 ++-2 ++-2 ++0 ++2 ++2 ++2 ++0 ++4 ++2 ++0 ++2 ++2 ++2 ++4 ++2 ++0 ++0 ++0 ++2 ++2 ++0 ++-2 ++-2 ++-4 ++-4 ++-4 ++-4 ++2 ++2 ++0 ++2 ++2 ++2 ++2 ++2 ++2 ++0 ++4 ++2 ++0 ++0 ++-2 ++0 ++0 ++-2 ++0 ++0 ++0 ++0 ++0 ++2 ++2 ++-2 ++2 ++4 ++2 ++6 ++4 ++0 ++2 ++0 ++-2 ++0 ++-4 ++-4 ++-2 ++-2 ++-4 ++-4 ++-4 ++0 ++2 ++-2 ++-2 ++-2 ++-4 ++0 ++2 ++0 ++0 ++-2 ++-2 ++-2 ++-2 ++-4 ++0 ++2 ++4 ++0 ++2 ++0 ++2 ++2 ++0 ++0 ++0 ++2 ++4 ++2 ++2 ++2 ++2 ++2 ++2 ++-2 ++0 ++0 ++0 ++0 ++0 ++-4 ++0 ++2 ++0 ++0 ++2 ++2 ++0 ++2 ++0 ++0 ++0 ++-2 ++0 ++-2 ++-2 ++-4 ++-2 ++0 ++-2 ++0 ++0 ++2 ++2 ++2 ++0 ++-2 ++-2 ++0 ++2 ++2 ++0 ++0 ++0 ++2 ++0 ++-2 ++-2 ++0 ++-2 ++-2 ++0 ++0 ++-2 ++0 ++2 ++0 ++0 ++0 ++2 ++0 ++-2 ++-4 ++0 ++0 ++-2 ++0 ++-2 ++-2 ++0 ++0 ++2 ++0 ++-2 ++-2 ++-2 ++-2 ++2 ++-2 ++-2 ++0 ++0 ++-2 ++2 ++-2 ++4 ++2 ++0 ++-2 ++0 ++2 ++2 ++0 ++0 ++-2 ++0 ++0 ++2 ++-2 ++-4 ++0 ++0 ++-2 ++-4 ++0 ++0 ++-2 ++0 ++2 ++2 ++0 ++4 ++0 ++-2 ++0 ++0 ++2 ++2 ++2 ++-2 ++-4 ++0 ++-2 ++0 ++0 ++-2 ++0 ++-4 ++2 ++2 ++0 ++0 ++-2 ++0 ++0 ++2 ++0 ++0 ++2 ++2 ++-2 ++-2 ++0 ++2 ++0 ++0 ++-4 ++-2 ++0 ++0 ++0 ++-2 ++-2 ++0 ++-2 ++-2 ++0 ++2 ++-2 ++2 ++0 ++0 ++-2 ++-2 ++-2 ++-2 ++-2 ++0 ++-4 ++-2 ++-4 ++-2 ++-2 ++-4 ++-2 ++0 ++2 ++-2 ++-2 ++-4 ++0 ++2 ++0 ++-2 ++-2 ++2 ++2 ++-2 ++0 ++0 ++0 ++4 ++0 ++2 ++2 ++0 ++0 ++2 ++2 ++2 ++0 ++2 ++4 ++4 ++4 ++6 ++2 ++6 ++0 ++0 ++4 ++2 ++2 ++-2 ++-2 ++-2 ++0 ++0 ++-2 ++-2 ++-4 ++0 ++0 ++-2 ++2 ++-4 ++-2 ++-4 ++-4 ++-4 ++-2 ++-4 ++-4 ++-2 ++-4 ++0 ++-2 ++0 ++2 ++0 ++-2 ++-2 ++-2 ++2 ++0 ++2 ++2 ++0 ++2 ++0 ++2 ++-2 ++0 ++2 ++2 ++4 ++0 ++-2 ++0 ++0 ++2 ++0 ++2 ++0 ++2 ++4 ++2 ++0 ++2 ++0 ++2 ++2 ++0 ++0 ++2 ++2 ++4 ++2 ++-2 ++-2 ++0 ++0 ++0 ++-2 ++0 ++-4 ++0 ++-4 ++-2 ++-4 ++0 ++0 ++-2 ++-2 ++-2 ++-2 ++-2 ++0 ++-2 ++-2 ++0 ++0 ++0 ++0 ++-2 ++0 ++-2 ++0 ++2 ++0 ++2 ++2 ++4 ++0 ++2 ++2 ++0 ++-2 ++-4 ++0 ++0 ++2 ++0 ++2 ++0 ++0 ++-2 ++-2 ++-2 ++0 ++0 ++0 ++-4 ++-2 ++-4 ++-2 ++0 ++-2 ++0 ++-2 ++-2 ++-2 ++-2 ++0 ++-2 ++0 ++0 ++2 ++2 ++0 ++2 ++-2 ++2 ++0 ++2 ++0 ++0 ++2 ++2 ++0 ++2 ++0 ++0 ++2 ++0 ++-2 ++0 ++0 ++0 ++0 ++0 ++2 ++0 ++4 ++2 ++2 ++2 ++2 ++0 ++0 ++0 ++4 ++0 ++0 ++-2 ++2 ++0 ++0 ++0 ++0 ++2 ++0 ++2 ++2 ++2 ++2 ++0 ++0 ++0 ++4 ++2 ++0 ++2 ++0 ++-2 ++-2 ++0 ++2 ++2 ++2 ++0 ++0 ++0 ++4 ++2 ++2 ++2 ++4 ++2 ++4 ++2 ++2 ++0 ++0 ++2 ++4 ++2 ++2 ++0 ++2 ++2 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++0 ++2 ++2 ++0 ++2 ++0 ++-2 ++0 ++0 ++-2 ++-2 ++0 ++0 ++0 ++-2 ++0 ++0 ++0 ++0 ++-2 ++0 ++0 ++0 ++0 ++0 ++-2 ++2 ++2 ++0 ++0 ++2 ++-2 ++2 ++2 ++2 ++0 ++0 ++2 ++4 ++2 ++4 ++2 ++0 ++2 ++4 ++2 ++2 ++2 ++2 ++2 ++0 ++2 ++2 ++2 ++0 ++2 ++0 ++2 ++2 ++2 ++2 ++2 ++0 ++-2 ++0 ++2 +diff -Nurp base/drivers/staging/echo/TODO new/drivers/staging/echo/TODO +--- /dev/null 1970-01-01 02:00:00.000000000 +0200 ++++ new/drivers/staging/echo/TODO 2008-10-26 05:10:13.000000000 +0200 +@@ -0,0 +1,10 @@ ++TODO: ++ - checkpatch.pl cleanups ++ - Lindent ++ - typedef removals ++ - handle bit_operations.h (merge in or make part of common code?) ++ - remove proc interface, only use echo.h interface (proc interface is ++ racy and not correct.) ++ ++Please send patches to Greg Kroah-Hartman and Cc: Steve ++Underwood and David Rowe --- dahdi-linux-2.2.1+dfsg.orig/debian/patches/mmx_fix +++ dahdi-linux-2.2.1+dfsg/debian/patches/mmx_fix @@ -0,0 +1,113 @@ +safer MMX support for echo canceller + +Properly save MMX registers on i386 (but not x86_64) + +Upstream issue: http://bugs.digium.com/view.php?id=13500 + +Index: drivers/dahdi/dahdi-base.c +=================================================================== +--- a/drivers/dahdi/dahdi-base.c (revision 6648) ++++ b/drivers/dahdi/dahdi-base.c (working copy) +@@ -296,9 +296,58 @@ static struct dahdi_dialparams global_dialparams = + static int dahdi_chan_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data, int unit); + + #if defined(CONFIG_DAHDI_MMX) || defined(ECHO_CAN_FP) ++#if (defined(CONFIG_X86) && !defined(CONFIG_X86_64)) || defined(CONFIG_I386) ++struct fpu_save_buf { ++ unsigned long cr0; ++ unsigned long fpu_buf[128]; ++}; ++ ++static DEFINE_PER_CPU(struct fpu_save_buf, fpu_buf); ++ ++/** dahdi_kernel_fpu_begin() - Save floating point registers ++ * ++ * This function is similar to kernel_fpu_begin() . However it is ++ * designed to work in an interrupt context. Restoring must be done with ++ * dahdi_kernel_fpu_end(). ++ * ++ * Furthermore, the whole code between the call to ++ * dahdi_kernel_fpu_begin() and dahdi_kernel_fpu_end() must reside ++ * inside a spinlock. Otherwise the context might be restored to the ++ * wrong process. ++ * ++ * Current implementation is x86/ia32-specific and will not even build on ++ * x86_64) ++ * */ ++static inline void dahdi_kernel_fpu_begin(void) ++{ ++ struct fpu_save_buf *buf = &__get_cpu_var(fpu_buf); ++ __asm__ __volatile__ ("movl %%cr0,%0; clts" : "=r" (buf->cr0)); ++ __asm__ __volatile__ ("fnsave %0" : "=m" (buf->fpu_buf)); ++} ++ ++/** dahdi_kernel_fpu_end() - restore floating point context ++ * ++ * Must be used with context saved by dahdi_kernel_fpu_begin(). See its ++ * documentation for further information. ++ */ ++static inline void dahdi_kernel_fpu_end(void) ++{ ++ struct fpu_save_buf *buf = &__get_cpu_var(fpu_buf); ++ __asm__ __volatile__ ("frstor %0" : "=m" (buf->fpu_buf)); ++ __asm__ __volatile__ ("movl %0,%%cr0" : : "r" (buf->cr0)); ++} ++ ++#else /* We haven't fixed FP context saving/restoring yet */ ++/* Very strange things can happen when the context is not properly ++ * restored. OTOH, some people do report success with this. Hence we ++ * so far just issue a warning */ ++#warning CONFIG_ZAPTEL_MMX may behave randomely on this platform + #define dahdi_kernel_fpu_begin kernel_fpu_begin ++#define dahdi_kernel_fpu_end kernel_fpu_end + #endif + ++#endif ++ + struct dahdi_timer { + int ms; /* Countdown */ + int pos; /* Position */ +@@ -6664,7 +6713,7 @@ static inline void __dahdi_ec_chunk(struct dahdi_c + + } + #if defined(CONFIG_DAHDI_MMX) || defined(ECHO_CAN_FP) +- kernel_fpu_end(); ++ dahdi_kernel_fpu_end(); + #endif + } + spin_unlock_irqrestore(&ss->lock, flags); +@@ -6997,6 +7046,7 @@ static inline void __putbuf_chunk(struct dahdi_cha + int res; + int left, x; + ++ + while(bytes) { + #if defined(CONFIG_DAHDI_NET) || defined(CONFIG_DAHDI_PPP) + skb = NULL; +@@ -7558,7 +7608,7 @@ static void __dahdi_transmit_chunk(struct dahdi_ch + #endif + __dahdi_process_getaudio_chunk(chan, buf); + #ifdef CONFIG_DAHDI_MMX +- kernel_fpu_end(); ++ dahdi_kernel_fpu_end(); + #endif + } + } +@@ -7643,7 +7693,7 @@ static void __dahdi_receive_chunk(struct dahdi_cha + #endif + __dahdi_process_putaudio_chunk(chan, buf); + #ifdef CONFIG_DAHDI_MMX +- kernel_fpu_end(); ++ dahdi_kernel_fpu_end(); + #endif + } + __dahdi_putbuf_chunk(chan, buf); +@@ -7865,7 +7915,7 @@ int dahdi_receive(struct dahdi_span *span) + } + } + #ifdef CONFIG_DAHDI_MMX +- kernel_fpu_end(); ++ dahdi_kernel_fpu_end(); + #endif + } + /* do all the pseudo/conferenced channel transmits (putbuf's) */ --- dahdi-linux-2.2.1+dfsg.orig/debian/patches/wcfxo_reset_fix +++ dahdi-linux-2.2.1+dfsg/debian/patches/wcfxo_reset_fix @@ -0,0 +1,44 @@ +wcfxo: Reset the DAA on module initialization. + +The X100p and clones will sometimes work and sometimes not depending on wether +the DAA powers up in running state- this seems to be related to the power +supply. This problem is caused by the driver not reseting the DAA and may be +the source of a great many intermittent problems with this card. + +(closes issue #14232) +Reported by: tallen8840 +Patch by: tallen8840 +Tested by: explidous, Flavio + +Source: http://svn.digium.com/svn/dahdi/linux/trunk@7008 + +To be included in dahdi-linux 2.3 . + +--- a/drivers/dahdi/wcfxo.c ++++ b/drivers/dahdi/wcfxo.c +@@ -673,6 +673,16 @@ static int wcfxo_hardware_init(struct wc + /* Hardware stuff */ + /* Reset PCI Interface chip and registers */ + outb(0x0e, wc->ioaddr + WC_CNTL); ++ ++ /* Set all to outputs except AUX 4, which is an input */ ++ outb(0xef, wc->ioaddr + WC_AUXC); ++ ++ /* Reset the DAA (DAA uses AUX5 for reset) */ ++ outb(0x00, wc->ioaddr + WC_AUXD); ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(1 + HZ / 800); ++ ++ /* Set hook state to on hook & un-reset the DAA */ + if (wc->flags & FLAG_RESET_ON_AUX5) { + /* Set hook state to on hook for when we switch. + Make sure reset is high */ +@@ -681,8 +691,6 @@ static int wcfxo_hardware_init(struct wc + /* Set hook state to on hook for when we switch */ + outb(0x24, wc->ioaddr + WC_AUXD); + } +- /* Set all to outputs except AUX 4, which is an input */ +- outb(0xef, wc->ioaddr + WC_AUXC); + + /* Back to normal, with automatic DMA wrap around */ + outb(0x01, wc->ioaddr + WC_CNTL); --- dahdi-linux-2.2.1+dfsg.orig/debian/patches/uk_rotary +++ dahdi-linux-2.2.1+dfsg/debian/patches/uk_rotary @@ -0,0 +1,38 @@ +Subject: Adds a parameter 'dialdebounce' to wctdm. +Author: Tilghman Lesher +Bug: https://issues.asterisk.org/view.php?id=16339 +Bug-Debian: http://bugs.debian.org/546329 +Last-Update: 2009-12-19 + +For UK rotary phones, set dialdebounce=32 . E.g. in +/etc/modprobe.d/dahdi.conf add the line: + + options wctdm dialdebounce=32 + +--- a/drivers/dahdi/wctdm.c ++++ b/drivers/dahdi/wctdm.c +@@ -284,6 +284,7 @@ static unsigned int battdebounce; + static unsigned int battalarm; + static unsigned int battthresh; + static int ringdebounce = DEFAULT_RING_DEBOUNCE; ++static int dialdebounce = 8 * 8 /* times 4, because must be a multiple of 4ms */; + static int fwringdetect = 0; + static int debug = 0; + static int robust = 0; +@@ -1002,7 +1003,7 @@ static inline void wctdm_proslic_check_h + hook = (res & 1); + if (hook != fxs->lastrxhook) { + /* Reset the debounce (must be multiple of 4ms) */ +- fxs->debounce = 8 * (4 * 8); ++ fxs->debounce = dialdebounce * 4; + #if 0 + printk(KERN_DEBUG "Resetting debounce card %d hook %d, %d\n", + card, hook, fxs->debounce); +@@ -2641,6 +2642,7 @@ module_param(battdebounce, uint, 0600); + module_param(battalarm, uint, 0600); + module_param(battthresh, uint, 0600); + module_param(ringdebounce, int, 0600); ++module_param(dialdebounce, int, 0600); + module_param(fwringdetect, int, 0600); + module_param(alawoverride, int, 0600); + module_param(fastpickup, int, 0600); --- dahdi-linux-2.2.1+dfsg.orig/debian/patches/mmx_auto +++ dahdi-linux-2.2.1+dfsg/debian/patches/mmx_auto @@ -0,0 +1,33 @@ +Enable MMX support if safe on the target CPU type (on most i386 systems, +not on x86_64). + +Patch dahdi_mmx_auto.diff from http://bugs.digium.com/view.php?id=13500 + +--- a/drivers/dahdi/Kbuild ++++ b/drivers/dahdi/Kbuild +@@ -134,3 +134,25 @@ $(obj)/makefw: $(src)/makefw.c + $(HOSTCC) -o $@ $^ + + clean-files := radfw.h tor2fw.h ++ ++# set CONFIG_DAHDI_MMX for a number of CPU types. ++DAHDI_MMX_AUTO=yes ++DAHDI_USE_MMX= ++DAHDI_MMX_WHITELIST_x86_32 = M586MMX M686 MPENTIUMII MPENTIUMIII MPENTIUMM \ ++ MPENTIUM4 MVIAC3_2 MVIAC3_2 MK7 MK8 ++ ++DAHDI_MMX_WHITELIST_x86_64 = ++ ++# A list of configuration variables to test: CONFIG_M686 , etc. ++DAHDI_MMX_CONFIG_VARS := $(DAHDI_MMX_WHITELIST_$(DAHDI_ARCH):%=CONFIG_%) ++ ++# expand them: ++DAHDI_MMX_CONFIG_VALS := $(strip $(foreach var,$(DAHDI_MMX_CONFIG_VARS),$(var)) ) ++ifneq (,$(DAHDI_MMX_AUTO)) ++ ifneq (,$(DAHDI_MMX_CONFIG_VALS)) ++ DAHDI_USE_MMX=yes ++ CFLAGS_zaptel-base.o += -DCONFIG_DAHDI_MMX ++ endif ++endif ++ ++export DAHDI_USE_MMX --- dahdi-linux-2.2.1+dfsg.orig/debian/patches/no_firmware_download +++ dahdi-linux-2.2.1+dfsg/debian/patches/no_firmware_download @@ -0,0 +1,21 @@ +The firmware downloaders are extra kernel objects that are now required for +the Hardware echo canceller support in some Digium cards (wctdm24xxp, +wcte12xp). They are downloaded at build time. The makefile will build +with support for them if they were indeed downloaded. + +This patch removes this downloading and thus keeps those modules +DFSG-compliant. Unlike the Digium firmwares, this is a compile-time +decision and hence cannot be reverted once a dahdi-modules package is +built. + +--- a/Makefile ++++ b/Makefile +@@ -88,7 +88,7 @@ include/dahdi/version.h: FORCE + fi + @rm -f $@.tmp + +-prereq: include/dahdi/version.h firmware-loaders ++prereq: include/dahdi/version.h + + stackcheck: checkstack modules + ./checkstack kernel/*.ko kernel/*/*.ko --- dahdi-linux-2.2.1+dfsg.orig/debian/patches/beronet +++ dahdi-linux-2.2.1+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");