[Pkg-voip-commits] [dahdi-linux] 01/12: Import packaging from subversion

tzafrir at debian.org tzafrir at debian.org
Fri Aug 14 09:41:25 UTC 2015


This is an automated email from the git hooks/post-receive script.

tzafrir pushed a commit to branch master
in repository dahdi-linux.

commit 5a39815e9e27b4df48f0ce36b6ce54f3dd9a832f
Author: Tzafrir Cohen <tzafrir at debian.org>
Date:   Wed Aug 12 11:00:40 2015 +0200

    Import packaging from subversion
---
 debian/NEWS.Debian                       |    13 +
 debian/README.Debian                     |   120 +
 debian/TODO.Debian                       |     5 +
 debian/backports/etch                    |     5 +
 debian/backports/lenny                   |     7 +
 debian/changelog                         |  1308 ++
 debian/compat                            |     1 +
 debian/control                           |    40 +
 debian/control.modules.in                |    26 +
 debian/copyright                         |   188 +
 debian/dahdi-linux.doc-base.readme       |    13 +
 debian/dahdi-linux.examples              |     2 +
 debian/dahdi-linux.install               |     3 +
 debian/dahdi-linux.lintian-overrides     |     2 +
 debian/dahdi-linux.udev                  |     2 +
 debian/dahdi-source.dirs                 |     1 +
 debian/docs                              |     3 +
 debian/make_static_nodes                 |    25 +
 debian/modulestest                       |    86 +
 debian/patches/chanmute                  |    19 +
 debian/patches/dahdi_linux_extra         | 18374 +++++++++++++++++++++++++++++
 debian/patches/hotplug_mod_params        |    17 +
 debian/patches/no_firmware_download      |    21 +
 debian/patches/notest                    |    15 +
 debian/patches/pciradio-request_firmware |   135 +
 debian/patches/series                    |     8 +
 debian/patches/tor2-request_firmware     |    87 +
 debian/rules                             |   131 +
 debian/source/format                     |     1 +
 debian/watch                             |     6 +
 30 files changed, 20664 insertions(+)

diff --git a/debian/NEWS.Debian b/debian/NEWS.Debian
new file mode 100644
index 0000000..8d3f17d
--- /dev/null
+++ b/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 <tzafrir.cohen at xorcom.com>  Thu, 20 Mar 2008 16:31:25 +0200
+
diff --git a/debian/README.Debian b/debian/README.Debian
new file mode 100644
index 0000000..3e5a380
--- /dev/null
+++ b/debian/README.Debian
@@ -0,0 +1,120 @@
+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-<version>.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-dummy
+-----------
+The core DAHDI module (dahdi) will now provide timing if there's no other
+module to provide timing. This means there's no more need for a separate
+dahdi_dummy module. For the sake of backward compatibility, the module
+dahdi itself also has an alias 'dahdi_dummy', which means that
+'modprobe dahdi_dummy' will still work.
+
diff --git a/debian/TODO.Debian b/debian/TODO.Debian
new file mode 100644
index 0000000..9fd532e
--- /dev/null
+++ b/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.
diff --git a/debian/backports/etch b/debian/backports/etch
new file mode 100755
index 0000000..e864d56
--- /dev/null
+++ b/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
diff --git a/debian/backports/lenny b/debian/backports/lenny
new file mode 100755
index 0000000..879befa
--- /dev/null
+++ b/debian/backports/lenny
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# Lenny backporting script
+
+# udev rules go to /etc/udev in Lenny
+sed -i -e '/xpp.rules/s/lib/etc/'    debian/dahdi-linux.install
+
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..aaef7a3
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,1308 @@
+dahdi-linux (1:2.10.0.1~dfsg-1) unstable; urgency=low
+
+  * New upstream release
+  * Fix VCS link.
+
+ -- Tzafrir Cohen <tzafrir at debian.org>  Sun, 26 Oct 2014 13:46:19 +0200
+
+dahdi-linux (1:2.10.0~dfsg-1) unstable; urgency=low
+
+  [ Tzafrir Cohen ]
+  * New upstream release (Closes: #753660):
+    - Patch dahdi_linux_extra updated to the 2.9.2 branch.
+  * Use hotplug support:
+    - patch hotplug_mod_params: change default of module parameters.
+  * Multiarch support. 
+  * udev rules moved to package dahdi (in source package dahdi-tools).
+  * Add a test for non-free files in case uscan was used.
+  * Remove unused variables from control file.
+
+  [ Mark Purcell ]
+  * Update debian/watch for ~dfsg
+
+ -- Tzafrir Cohen <tzafrir at debian.org>  Tue, 19 Aug 2014 08:48:32 +0300
+
+dahdi-linux (1:2.7.0.1~dfsg-1) unstable; urgency=low
+
+  * New upstream release
+    - Fixes "doorphone device ignored" (Closes: #687970)
+  * Update Vcs-Browser - fix vcs-field-bitrotted
+
+ -- Mark Purcell <msp at debian.org>  Sat, 28 Sep 2013 13:16:06 +1000
+
+dahdi-linux (1:2.7.0+dfsg-1) unstable; urgency=low
+
+  [ Tzafrir Cohen ] 
+  * New upstream release:
+    - Patch fix_define_dev dropped: merged upstream.
+    - Patch fix_xpp_usermode dropped: merged upstream.
+    - Patch fix_xpp_post_unregister dropped: merged upstream.
+
+ -- Mark Purcell <msp at debian.org>  Sun, 16 Jun 2013 16:49:06 +1000
+
+dahdi-linux (1:2.6.1+dfsg2-1) unstable; urgency=low
+
+  * Patch fix_define_dev: fix building with kernel 3.8.
+  * Remove non-free FPGA bitfiles and patch drivers to load them
+    (Closes: #693666) (Ben Hutchings).
+  * Patch fix_xpp_usermode: fixes odd xpp behaviour with later kernels.
+  * Patch fix_xpp_post_unregister: fixes a crash with manual
+    'dahdi_registration off'.
+
+ -- Tzafrir Cohen <tzafrir at debian.org>  Wed, 30 Jan 2013 00:18:38 +0200
+
+dahdi-linux (1:2.6.1+dfsg-1) unstable; urgency=low
+
+  * New upstream release candidate:
+    - Patch xpp_fix_2fxs6fxo removed: merged upstream.
+    - Patch oslec_auto removed: merged upstream.
+  * Removed all EXTRA_* build variables:
+    - wcopenpci builds on big-endial (sort of).
+    - All other logic is set through Kbuild.
+  * Don't strip.
+
+ -- Tzafrir Cohen <tzafrir at debian.org>  Mon, 23 Apr 2012 11:05:30 +0300
+
+dahdi-linux (1:2.5.0.1+dfsg-1) unstable; urgency=low
+
+  * New upstream release (Closes: #639702).
+    - Patch wcb4xxp_bn4s0e removed: merged upstream.
+    - Patch define_spinlock removed: merged upstream.
+    - Patch dahdi-linux-extra updated.
+  * Updated dahdi-linux-extra:
+    - "Upstream" is now a complete git mirror.
+    - Actually include ap400 in the list of modules to build.
+    - Updated OpenVox drivers: opvxa1200 is a subdirectory 
+    - Updated OpenVox drivers: opvxd115 added (digital cards).
+  * Patch define_spinlock: include a (slightly big) build fix from upstream.
+  * Standards version 3.9.2 (no change needed).
+  * Switch to dh.
+  * Patch notest: Remove a bogus upstream 'test' target.
+  * Lintian override for an odd interpteter a dummy kernel module init script. 
+  * Dahdi udev rules are now named 'dahdi-linux.conf'.
+  * Patch xpp_fix_2fxs6fxo: bugfix for Xorcom 2FXX6FXO module code.
+
+ -- Tzafrir Cohen <tzafrir at debian.org>  Sun, 25 Sep 2011 22:58:17 +0300
+
+dahdi-linux (1:2.4.1+dfsg-1) unstable; urgency=low
+
+  * New Upstream release.
+    - Patch uk_rotary dropped: merged upstream.
+    - Patch oslec_include_2634 dropped: merged upstream. 
+    - Patch xpp_usb_buffer_2635 dropped: merged upstream.
+    - Patch voicebus_sem_h_2635 dropped: merged upstream.
+  * dahdi_linux_extra now includes AP400 drivers (Closes: #582095).
+  * Patch oslec_auto: Makes adding OSLEC less painful.
+    - But for now it requires manually editing patch dahdi_linux_extra.
+
+ -- Tzafrir Cohen <tzafrir at debian.org>  Mon, 07 Mar 2011 12:01:55 +0200
+
+dahdi-linux (1:2.3.0.1+dfsg-2) unstable; urgency=medium
+
+  [ Faidon Liambotis ]
+  * Add myself to uploaders.
+
+  [ Tzafrir Cohen ]
+  * Disable mmx_auto for now (Closes: #593438).
+  * dahdi_linux_extra: Re-apply zaphfc r7 (Closes: #598886).
+  * Patch wcb4xxp_bn4s0e: missing PCI IDs for beroNet BN4S0e (Closes: #600839).
+
+ -- Tzafrir Cohen <tzafrir at debian.org>  Wed, 20 Oct 2010 21:21:58 +0200
+
+dahdi-linux (1:2.3.0.1+dfsg-1) unstable; urgency=low
+
+  * New upstream version (Closes: #546319).
+  * Patch no_dummy removed: merged upstream. 
+  * Patch wcb4xxp_extra_trunk removed: merged upstream.
+  * Patch chanmute: make it also explicitly disable the untested
+    DAHDI_AUDIO_NOTIFY.
+  * Patch oslec_include_2634: fix building with 2.6.34 (Closes: #586216).
+  * Patches xpp_usb_buffer_2635, voicebus_sem_h_2635: Likewise for 2.6.35.
+  * Patch dahdi_linux_extra: revert last fix to zaphfc.
+  * Standards version 3.9.1 (no change needed).
+
+ -- Tzafrir Cohen <tzafrir at debian.org>  Tue, 03 Aug 2010 03:58:11 +0300
+
+dahdi-linux (1:2.2.1.1+dfsg-1) unstable; urgency=low
+
+  * New upstream minor release.
+  * Updated copyrights file.
+  * Standards version 3.8.4: no change needed.
+  * Patch wcb4xxp_extra_trunk: backport extra PCI IDs for wcb4xxp
+    (more HFC-[248]S cards).
+  * Switch to a @debian.org address.
+  * Dpkg V3 format (it was quilt already). 
+  * Remove lintian override that is no longer needed.
+  * Patch dahdi_linux_extra: manually fix to remove a leading '---'.
+  * dahdi-modules packages now depend on the linux-image versions. 
+  * Other minor updates to the packaging of the dahdi-modules package.
+
+ -- Tzafrir Cohen <tzafrir at debian.org>  Sun, 18 Apr 2010 17:14:02 +0300
+
+dahdi-linux (1:2.2.1+dfsg-1) unstable; urgency=low
+
+  [ 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, Closes: #566876).
+  * Patch no_dummy: use dahdi internal timing instead of dahdi_dummy. 
+  * Patch chanmute: Enable CHANMUTE optimization (for xpp, mostly). 
+
+  [ Mark Purcell ]
+  * Cleanup debian/watch
+
+ -- Mark Purcell <msp at debian.org>  Sun, 07 Mar 2010 04:41:21 +1100
+
+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 <tzafrir.cohen at xorcom.com>  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 <tzafrir.cohen at xorcom.com>  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 <msp at debian.org>  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 <timo.teras at iki.fi>)
+  * 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 <msp at debian.org>  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 <tzafrir.cohen at xorcom.com>  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 <tzafrir.cohen at xorcom.com>  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 <msp at debian.org>  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 <tzafrir.cohen at xorcom.com>  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 <tzafrir.cohen at xorcom.com>  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 <tzafrir.cohen at xorcom.com>  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 <tzafrir.cohen at xorcom.com>  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 <paravoid at debian.org>  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 <kilian at debian.org>  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 <kilian at debian.org>  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 <msp at debian.org>  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 <msp at debian.org>  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 <tzafrir.cohen at xorcom.com>  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 <msp at debian.org>  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 <msp at debian.org>  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 <msp at debian.org>  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 <msp at debian.org>  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 <msp at debian.org>  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 <tzafrir.cohen at xorcom.com>  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 <tzafrir.cohen at xorcom.com>  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 <kilian at debian.org>  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 <kilian at debian.org>  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 <msp at debian.org>  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 <msp at debian.org>  Tue, 24 Oct 2006 22:41:01 +0100
+
+zaptel (1:1.2.10.dfsg-1) unstable; urgency=low
+
+  * New upstream release
+
+ -- Mark Purcell <msp at debian.org>  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 <msp at debian.org>  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 <msp at debian.org>  Sat, 23 Sep 2006 13:58:15 +0100
+
+zaptel (1:1.2.8.dfsg-1) unstable; urgency=low
+
+  * New Upstream Release
+
+ -- Mark Purcell <msp at debian.org>  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 <msp at debian.org>  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 <msp at debian.org>  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 <kilian at debian.org>  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 <msp at debian.org>  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 <msp at debian.org>  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 <msp at debian.org>  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 <msp at debian.org>  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 <msp at debian.org>  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 <kilian at debian.org>  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 <tzafrir.cohen at xorcom.com>  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 <tzafrir.cohen at xorcom.com>  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 <tzafrir.cohen at xorcom.com>  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 <tzafrir.cohen at xorcom.com>  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 <tzafrir.cohen at xorcom.com>  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 <msp at debian.org>  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 <msp at debian.org>  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 <msp at debian.org>  Thu, 17 Nov 2005 17:50:00 +0000
+
+zaptel (1:1.2.0-rc2-1) experimental; urgency=low
+
+  * New upstream release
+
+ -- Mark Purcell <msp at debian.org>  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 <msp at debian.org>  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 <msp at debian.org>  Mon,  7 Nov 2005 19:19:27 +0000
+
+zaptel (1:1.0.9.2-1) unstable; urgency=low
+
+  * New Upstream Release
+
+ -- Mark Purcell <msp at debian.org>  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 <msp at debian.org>  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 <msp at debian.org>  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 <msp at debian.org>  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 <kilian at debian.org>  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 <kilian at debian.org>  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 <santiago at unicauca.edu.co>  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 <msp at debian.org>  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 <santiago at unicauca.edu.co>  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 <msp at debian.org>  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 <msp at debian.org>  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 <msp at debian.org>  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 <msp at debian.org>  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 <kk at verfaction.de>  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 <vorlon at debian.org>  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 <grantma at anathoth.gen.nz>  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 <msp at debian.org>  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 <jsogo at debian.org>  Sat,  2 Apr 2005 01:14:23 +0200
+
+zaptel (1:1.0.7-1) unstable; urgency=low
+
+  * New upstream version.
+
+ -- Kilian Krause <kk at verfaction.de>  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 <kk at verfaction.de>  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 <kk at verfaction.de>  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 <santiago at unicauca.edu.co>  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 <santiago at unicauca.edu.co>  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 <santiago at unicauca.edu.co>  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 <santiago at unicauca.edu.co>  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 <santiago at unicauca.edu.co>  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 <msp at debian.org>  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 <msp at debian.org>  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 <msp at debian.org>  Wed, 21 Jul 2004 17:51:22 +1000
+
+zaptel (1:0.8.1-1) unstable; urgency=low
+
+  * New upstream release
+
+ -- Matt Zimmerman <mdz at debian.org>  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 <mdz at debian.org>  Wed, 14 Jan 2004 17:24:26 -0800
+
+zaptel (1:0.8.0-1) unstable; urgency=low
+
+  * New upstream release
+
+ -- Matt Zimmerman <mdz at debian.org>  Tue, 13 Jan 2004 14:44:56 -0800
+
+zaptel (1:0.6.0-2) unstable; urgency=low
+
+  * Rebuild with new libnewt
+
+ -- Matt Zimmerman <mdz at debian.org>  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 <mdz at debian.org>  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 <mdz at debian.org>  Wed, 12 Mar 2003 19:16:10 -0500
+
+zaptel (1:0.4.0-1) unstable; urgency=low
+
+  * New upstream release
+
+ -- Matt Zimmerman <mdz at debian.org>  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 <mdz at debian.org>  Tue, 29 Oct 2002 10:37:53 -0500
+
+zaptel (0.cvs.20020729-1) unstable; urgency=low
+
+  * New upstream CVS
+  * Include ztmonitor binary
+
+ -- Matt Zimmerman <mdz at debian.org>  Mon, 29 Jul 2002 12:36:58 -0400
+
+zaptel (0.cvs.20020708-1) unstable; urgency=low
+
+  * New upstream CVS
+
+ -- Matt Zimmerman <mdz at debian.org>  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 <mdz at debian.org>  Fri,  5 Jul 2002 11:00:08 -0400
+
+zaptel (0.cvs.20020624-1) unstable; urgency=low
+
+  * Initial Release (Closes: #150874)
+
+ -- Matt Zimmerman <mdz at debian.org>  Mon, 17 Jun 2002 10:31:21 -0400
+
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..7f8f011
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+7
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..3b783c8
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,40 @@
+Source: dahdi-linux
+Section: comm
+Priority: optional
+Maintainer: Debian VoIP Team <pkg-voip-maintainers at lists.alioth.debian.org>
+Uploaders: Tzafrir Cohen <tzafrir at debian.org>, Mark Purcell <msp at debian.org>, Faidon Liambotis <paravoid at debian.org>
+Build-Depends: debhelper (>=  7.0.50~), bzip2, asciidoc
+Standards-Version: 3.9.4
+Homepage: http://www.asterisk.org/
+Vcs-Svn: svn://anonscm.debian.org/pkg-voip/dahdi-linux/trunk/
+Vcs-Browser: http://anonscm.debian.org/viewvc/pkg-voip/dahdi-linux/
+
+Package: dahdi-linux
+Architecture: all
+Multi-Arch: foreign
+# Xorcom packages depend on dahdi-firmware. Debian zaptel will probably 
+# just recommend it.
+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
+Multi-Arch: foreign
+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
diff --git a/debian/control.modules.in b/debian/control.modules.in
new file mode 100644
index 0000000..eca2826
--- /dev/null
+++ b/debian/control.modules.in
@@ -0,0 +1,26 @@
+Source: dahdi-linux
+Section: comm
+Priority: extra
+Maintainer: Debian VoIP Team <pkg-voip-maintainers at lists.alioth.debian.org>
+Uploaders: Tzafrir Cohen <tzafrir at debian.org>, Mark Purcell <msp at debian.org>
+Build-Depends: debhelper (>> 7), bzip2
+Standards-Version: 3.8.4
+Homepage: http://www.asterisk.org/
+Vcs-Svn: svn://svn.debian.org/pkg-voip/dahdi-linux/trunk/
+Vcs-Browser: http://svn.debian.org/wsvn/pkg-voip/dahdi-linux/?op=log
+
+Package: dahdi-modules-_KVERS_
+Architecture: any
+Multi-Arch: same
+Provides: dahdi-modules
+Depends: linux-image-_KVERS_
+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
+ DAHDI telephony API.
+ This package contains the compiled kernel modules for _KVERS_
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..af8470a
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,188 @@
+This package was debianized by Matt Zimmerman <mdz at debian.org> on
+Mon, 17 Jun 2002 10:31:21 -0400.
+
+# FIXME: when we get the file properly formatted:
+Files-Exclude: drivers/dahdi/xpp/firmwares/*.hex drivers/dahdi/*.rbt
+
+It was downloaded from
+  http://downloads.asterisk.org/pub/telephony/dahdi-linux/
+
+The DAHDI-extra patch was generated from the repository:
+  http://git.tzafrir.org.il/?p=dahdi-extra.git;a=summary
+  http://git.tzafrir.org.il/git/dahdi-extra.git
+
+DAHDI-extra includes the following:
+
+* drivers/staging/echo: echo.ko (OSLEC)
+* drivers/dahdi/opvxa1200.c: opvxa1200.ko (OpenVox A800P/A1200P)
+* drivers/dahdi/wcopenpci.c: wcopenpci.ko (Voicetronix OpenPCI)
+* drivers/dahdi/zaphfc/: zaphfc.c (HFC-S-based PCI cards)
+
+Upstream source has been modified to comply with the Debian Free 
+Software Guildlines (DFSG), by the removal of the firmware files:
+
+    drivers/dahdi/xpp/firmwares/*.hex
+
+drivers/dahdi/xpp/firmwares/LICENSE.firmware permits redistribution but
+does not mention modification, which is a requirement of Debian Policy
+2.1 ("Derived works").
+
+
+Summary:
+The bulk of the code, under drivers/dahdi/ , is licensed under the terms
+of GPLv2. Various drivers (e.g.: all non-Digium drivers) are licensed as
+GPLv2.+ . Two other files (mmx.h, XppConfig.pm) are licensed under a more
+permissive license.
+
+
+Upstream Authors: Jim Dixon / Zapate Telephony, Linux Support Services, Inc.
+
+Copyright (from drivers/dahdi/tor2.c):
+
+/*
+ * Tormenta 2  Quad-T1 PCI Driver
+ *
+ * Written by Mark Spencer <markster at digium.com>
+ * Based on previous works, designs, and archetectures conceived and
+ * written by Jim Dixon <jim at lambdatel.com>.
+ *
+ * Copyright (C) 2001 Jim Dixon / Zapata Telephony.
+ * Copyright (C) 2001-2008, Digium, Inc.
+ *
+ */
+
+/*
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2 as published by the
+ * Free Software Foundation. See the LICENSE file included with
+ * this program for more details.
+ */
+
+The file LICENSE is a verbatim copy of the GNU General Public License, v.2.
+
+On Debian systems, a copy of the GNU General Public License v.2 may be
+found in /usr/share/common-licenses/GPL-2.
+
+Other Portions:
+
+drivers/dahdi/octapi: GPLv.2+
+
+    Copyright (c) 2001-2007 Octasic Inc.
+
+This file is part of the Octasic OCT6100 GPL API . The OCT6100 GPL API  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.
+
+The OCT6100 GPL API 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 the OCT6100 GPL API; if not, write to the Free Software 
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+
+
+drivers/dahdi/datamods/syncppp.c: GPLv.2+
+
+ *	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.
+ *
+ *	Port for Linux-2.1 by Jan "Yenya" Kasprzak <kas at fi.muni.cz>
+ * Copyright (C) 1994 Cronyx Ltd.
+ * Author: Serge Vakulenko, <vak at zebub.msk.su>
+
+drivers/dahdi/wcopenpci.c: GPLv.2+
+ * Written by Mark Spencer <markster at linux-support.net>
+ *            Matthew Fredrickson <creslin at linux-support.net>
+ *            Ben Kramer <ben at voicetronix.com.au>
+ *            Ron Lee <ron at voicetronix.com.au>
+ *
+ * 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.
+
+
+drivers/dahdi/opvxa1200.c: GPLv.2+
+ * Modify from wctdm.c by MiaoLin<miaolin at openvox.com.cn>
+ *
+ * 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.
+
+drivers/dahdi/zaphfc/*: GPLv.2
+ * Dahdi rewrite in hardhdlc mode
+ * Jose A. Deniz <odicha at hotmail.com>
+ *
+ * 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 <jw_vzaphfc at headissue.com>
+ *
+ * Original author of this code is
+ * Daniele "Vihai" Orlandi <daniele at orlandi.com>
+ *
+ * Major rewrite of the driver made by
+ * Klaus-Peter Junghanns <kpj at junghanns.net>
+ *
+ * This program is free software and may be modified and
+ * distributed under the terms of the GNU Public License.
+
+
+
+drivers/dahdi/xpp/XppConfig.pm: GPL 1+ || Artistic
+
+# Written by Oron Peled <oron at actcom.co.il>
+# Copyright (C) 2008, Xorcom
+# This program is free software; you can redistribute and/or
+# modify it under the same terms as Perl itself.
+
+
+drivers/staging/echo/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.
+
+
+Other copyright holders:
+
+drivers/dahdi/dahdi_dummy.c:  * Copyright (C) 2002, Hermes Softlab
+
+drivers/dahdi/dahdi_echocan_sec2.c: Copyright (C) 2001 Steve Underwood
+drivers/dahdi/dahdi_echocan_sec.c: Copyright (C) 2001 Steve Underwood
+drivers/dahdi/ecdis.h: Copyright (C) 2001 Steve Underwood
+drivers/dahdi/datamods/hdlc_*.c: * Copyright (C) 1999 - 2005 Krzysztof Halasa <khc at pm.waw.pl>
+
+Other files under drivers/staging/echo/: 
+Copyright (C) 2001, 2003 Steve Underwood, 2007,2008 David Rowe
diff --git a/debian/dahdi-linux.doc-base.readme b/debian/dahdi-linux.doc-base.readme
new file mode 100644
index 0000000..7f605a5
--- /dev/null
+++ b/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
+
diff --git a/debian/dahdi-linux.examples b/debian/dahdi-linux.examples
new file mode 100644
index 0000000..051138b
--- /dev/null
+++ b/debian/dahdi-linux.examples
@@ -0,0 +1,2 @@
+debian/make_static_nodes
+drivers/dahdi/xpp/xpp.conf
diff --git a/debian/dahdi-linux.install b/debian/dahdi-linux.install
new file mode 100644
index 0000000..b65182a
--- /dev/null
+++ b/debian/dahdi-linux.install
@@ -0,0 +1,3 @@
+drivers/dahdi/xpp/xpp.conf	etc/dahdi
+drivers/dahdi/xpp/init_card_*	usr/share/dahdi/
+drivers/dahdi/xpp/XppConfig.pm	usr/share/dahdi/
diff --git a/debian/dahdi-linux.lintian-overrides b/debian/dahdi-linux.lintian-overrides
new file mode 100644
index 0000000..944ab9c
--- /dev/null
+++ b/debian/dahdi-linux.lintian-overrides
@@ -0,0 +1,2 @@
+#/usr/share/dahdi/init_card_5_30 is a dummy script:
+unusual-interpreter usr/share/dahdi/init_card_5_30 #!/bin/true
diff --git a/debian/dahdi-linux.udev b/debian/dahdi-linux.udev
new file mode 100644
index 0000000..f6a4963
--- /dev/null
+++ b/debian/dahdi-linux.udev
@@ -0,0 +1,2 @@
+SUBSYSTEM=="dahdi",  GROUP="dialout", MODE="0660"
+
diff --git a/debian/dahdi-source.dirs b/debian/dahdi-source.dirs
new file mode 100644
index 0000000..b601f22
--- /dev/null
+++ b/debian/dahdi-source.dirs
@@ -0,0 +1 @@
+usr/src
diff --git a/debian/docs b/debian/docs
new file mode 100644
index 0000000..16e5e98
--- /dev/null
+++ b/debian/docs
@@ -0,0 +1,3 @@
+README
+README.html
+UPGRADE.txt
diff --git a/debian/make_static_nodes b/debian/make_static_nodes
new file mode 100755
index 0000000..93c297c
--- /dev/null
+++ b/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/*
+
diff --git a/debian/modulestest b/debian/modulestest
new file mode 100755
index 0000000..57ef3e0
--- /dev/null
+++ b/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
diff --git a/debian/patches/chanmute b/debian/patches/chanmute
new file mode 100644
index 0000000..5a9a68a
--- /dev/null
+++ b/debian/patches/chanmute
@@ -0,0 +1,19 @@
+Subject: Enable CHANMUTE optimization (for xpp, mostly)
+Author: Tzafrir Cohen <tzafrir.cohen at xorcom.com>
+Forwarded: no
+
+Enable the OPTIMIZE_CHANMUTE code: don't waste CPU time on channels
+whose audio is not useful. Currently only used by xpp drivers, and
+disabled by default by upstream.
+
+--- a/include/dahdi/dahdi_config.h
++++ b/include/dahdi/dahdi_config.h
+@@ -176,7 +176,7 @@
+ /*
+  * Skip processing PCM if low-level driver won't use it anyway
+  */
+-/* #define	OPTIMIZE_CHANMUTE */
++#define	OPTIMIZE_CHANMUTE
+ 
+ 
+ /*
diff --git a/debian/patches/dahdi_linux_extra b/debian/patches/dahdi_linux_extra
new file mode 100644
index 0000000..d7cc102
--- /dev/null
+++ b/debian/patches/dahdi_linux_extra
@@ -0,0 +1,18374 @@
+Subject: dahdi-extra: out-of-tree DAHDI drivers
+Origin: http://gitorious.org/dahdi-extra/dahdi-linux-extra
+Forwarded: No
+Last-Update: 2013-28-08
+ 
+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: a882f0d0124f391d2ad8f31c6cd3fbde108232e0
+Dahdi: v2.9.2-rc2
+---
+diff --git a/drivers/dahdi/Kbuild b/drivers/dahdi/Kbuild
+index ee2cd59..2eb039a 100644
+--- a/drivers/dahdi/Kbuild
++++ b/drivers/dahdi/Kbuild
+@@ -56,6 +56,14 @@ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_STEVE2)	+= dahdi_echocan_sec2.o
+ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_KB1)	+= dahdi_echocan_kb1.o
+ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_MG2)	+= dahdi_echocan_mg2.o
+ 
++ifdef CONFIG_PCI
++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_AP400)		+= ap400/
++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OPVXA1200)		+= opvxa1200/
++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OPVXD115)		+= opvxd115/
++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCOPENPCI)		+= wcopenpci.o
++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ZAPHFC)		+= zaphfc/
++endif
++
+ obj-m += $(DAHDI_MODULES_EXTRA)
+ 
+ # If you want to build OSLEC, include the code in the standard location:
+diff --git a/drivers/dahdi/Kconfig b/drivers/dahdi/Kconfig
+index 6952c6a..4275775 100644
+--- a/drivers/dahdi/Kconfig
++++ b/drivers/dahdi/Kconfig
+@@ -292,3 +292,89 @@ config DAHDI_WCTE11XP
+ 	  If unsure, say Y.
+ 
+ source "drivers/dahdi/xpp/Kconfig"
++
++config DAHDI_AP4XX
++	tristate "Aligera AP4XX PCI Card Driver"
++	depends on DAHDI && PCI
++	default DAHDI
++	---help---
++	  This driver provides support for the Aligera AP400 quad-span
++	  E1/T1 DAHDI cards:
++
++	  To compile this driver as a module, choose M here: the
++	  module will be called ap4xx.
++
++	  If unsure, say Y.
++
++config DAHDI_OPVXA1200
++	tristate "OpenVox 8/12 ports analog card Support"
++	depends on DAHDI && PCI
++	default DAHDI
++	---help---
++	  This driver provides support for the following OpenVox
++	  Wildcard products:
++
++	  * A1200P (PCI)
++	  * A1200E (PCI-E)
++	  * A800P (PCI)
++	  * A800E (PCI-E)
++
++	  To compile this driver as a module, choose M here: the
++	  module will be called opvxa1200.
++
++	  If unsure, say Y.
++
++config DAHDI_OPVXD115
++	tristate "OpenVox Single-T1/E1/J1 Support"
++	depends on DAHDI && PCI
++	default DAHDI
++	---help---
++	  This driver provides support for the following OpenVox
++	  Wildcard products:
++
++	  * D115P/DE115P/D130P/DE130P (PCI)
++	  * D115E/DE115E/D130E/DE130E (PCI-E)
++
++	  To compile this driver as a module, choose M here: the
++	  module will be called opvxd115.
++
++	  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 --git a/drivers/dahdi/ap400/Kbuild b/drivers/dahdi/ap400/Kbuild
+new file mode 100644
+index 0000000..d124261
+--- /dev/null
++++ b/drivers/dahdi/ap400/Kbuild
+@@ -0,0 +1,26 @@
++obj-m += ap400.o
++
++EXTRA_CFLAGS := -I$(src)/.. 
++
++ap400-objs := ap400_drv.o
++
++# APEC_SUPPORT
++ECHO_FIRMWARE := $(wildcard $(src)/OCT61*.ima)
++ifneq ($(strip $(ECHO_FIRMWARE)),)
++	EXTRA_CFLAGS+=-DAPEC_SUPPORT $(shell $(src)/../oct612x/octasic-helper cflags $(src)/../oct612x) -Wno-undef
++	ap400-objs += apec.o $(shell $(src)/../oct612x/octasic-helper objects ../oct612x) firmware_oct6104e-64d.o firmware_oct6104e-128d.o
++endif
++
++$(obj)/apec.o: $(src)/apec.h $(src)/../oct612x/include/oct6100api/oct6100_api.h
++
++$(obj)/firmware_oct6104e-64d.o: $(src)/OCT6104E-64D.ima $(obj)/ap400_drv.o $(src)/../firmware/make_firmware_object
++	@echo Making firmware object file for $(notdir $<)
++	@cd $(src) && ../firmware/make_firmware_object $(notdir $<) $@ $(obj)/ap400_drv.o
++
++$(obj)/firmware_oct6104e-128d.o: $(src)/OCT6104E-128D.ima $(obj)/ap400_drv.o $(src)/../firmware/make_firmware_object
++	@echo Making firmware object file for $(notdir $<)
++	@cd $(src) && ../firmware/make_firmware_object $(notdir $<) $@ $(obj)/ap400_drv.o
++
++$(src)/../firmware/make_firmware_object:
++	make -C $(src)/../firmware make_firmware_object
++
+diff --git a/drivers/dahdi/ap400/ap400.h b/drivers/dahdi/ap400/ap400.h
+new file mode 100644
+index 0000000..ba233d1
+--- /dev/null
++++ b/drivers/dahdi/ap400/ap400.h
+@@ -0,0 +1,107 @@
++/*
++ * AP4XX  T1/E1 PCI Driver
++ *
++ * Written by Ronaldo Valiati <aligera at aligera.com.br>
++ *
++ * Based on previous works, designs, and archetectures conceived and
++ * written by Jim Dixon <jim at lambdatel.com> and Mark Spencer <markster at digium.com>.
++ *
++ * Copyright (C) 2001 Jim Dixon / Zapata Telephony.
++ * Copyright (C) 2001-2005, Digium, 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.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ *
++ */
++
++#include <linux/ioctl.h>
++
++
++#define AP4_GET_ALARMS  _IOW (DAHDI_CODE, 60, int)
++#define AP4_GET_SLIPS	_IOW (DAHDI_CODE, 61, int)
++
++#define AP4XX_CARD_ID		0x41434532		// "ACE2"
++#define APE4XX_CARD_ID		0x41504534		// "APE4"
++
++#define AP_CAS_BASE		0x0080
++#define AP_DATA_BASE		0x0100
++
++#define AP_CARD_TYPE_REG	0x0001
++#define AP_T1E1_CONFIG_REG	0x0003
++#define AP_E1_CONFIG_REG	0x0004
++#define AP_E1_STATUS_REG	0x0005
++#define AP_LEDS_REG		0x0006
++#define AP_CLKSRC_REG		0x0007
++#define AP_HWCONFIG_REG		0x0008
++#define AP_INT_CONTROL_REG	0x0009
++#define AP_CNT_IRQ_REG		0x000B
++#define AP_CNT_CV_REG		0x000C
++#define AP_CNT_CRC_REG		0x000D
++#define AP_CLEAR_IRQ_REG	0x000E
++#define AP_CNT_SLIP_REG		0x000F
++
++#define AP_HWID_MASK		0x00F0
++
++#define AP_CLKSRC_MASK		0x07
++
++#define AP_LIU1_LINECODE	0x0080
++#define AP_LIU2_LINECODE	0x0100
++#define AP_LIU_RESET_BIT	0x0200
++
++#define AP_E1_AIS_STATUS	0x01
++#define AP_E1_BFAE_STATUS	0x02
++#define AP_E1_MFAE_STATUS	0x04
++#define AP_E1_SYNC_STATUS	0x08
++#define AP_E1_CAS_STATUS	0x10
++#define AP_E1_LOS_STATUS	0x20
++#define AP_E1_RAI_STATUS	0x40
++
++#define AP_E1_RAI_CONFIG	0x01
++#define AP_E1_LOOP_CONFIG	0x10
++#define AP_E1_CASEN_CONFIG	0x20
++#define AP_E1_PCM30_CONFIG	0x40
++#define AP_E1_CRCEN_CONFIG	0x80
++
++#define AP_INT_CTL_ENABLE	0x01
++#define AP_INT_CTL_ACTIVE	0x02
++
++#define AP_HWID_1E1_RJ		0x01
++#define AP_HWID_2E1_RJ		0x00
++#define AP_HWID_4E1_RJ		0x02
++#define AP_HWID_T1		0x04
++
++#define AP4_T1_NE1_SEL		0x04
++#define AP4_T1_ESF_NSF		0x02
++#define AP4_T1_CAS_ENABLE	0x01
++
++#define AP4_T1_FRAME_SYNC	0x01
++
++
++typedef enum {
++	AP_PULS_E1_75 = 0,
++	AP_PULS_E1_120,
++	AP_PULS_DSX1_0FT,
++	AP_PULS_DSX1_133FT,
++	AP_PULS_DSX1_266FT,
++	AP_PULS_DSX1_399FT,
++	AP_PULS_DSX1_533FT,
++	AP_PULS_J1_110,
++	AP_PULS_DS1_0DB,
++	AP_PULS_DS1_M075DB,
++	AP_PULS_DS1_M150DB,
++	AP_PULS_DS1_M225DB
++} liu_mode;
++
+diff --git a/drivers/dahdi/ap400/ap400_drv.c b/drivers/dahdi/ap400/ap400_drv.c
+new file mode 100644
+index 0000000..092aefb
+--- /dev/null
++++ b/drivers/dahdi/ap400/ap400_drv.c
+@@ -0,0 +1,2337 @@
++/*
++ * AP4XX PCI Card Driver
++ *
++ * Written by Ronaldo Valiati <aligera at aligera.com.br>
++ *
++ * Based on previous works, designs, and architectures conceived and
++ * written by Jim Dixon <jim at lambdatel.com> and Mark Spencer <markster at digium.com>.
++ *
++ * Copyright (C) 2001 Jim Dixon / Zapata Telephony.
++ * Copyright (C) 2001-2005, Digium, 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.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/module.h>
++#include <linux/pci.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/time.h>
++#include <linux/delay.h>
++#include <linux/proc_fs.h>
++#include <dahdi/kernel.h>
++#include <linux/moduleparam.h>
++
++#include "ap400.h"
++
++//#define AP400_DEBUG
++#ifdef AP400_DEBUG
++#define PDEBUG(fmt, args...) { \
++	printk(KERN_DEBUG "AP400 (%d): ",__LINE__); \
++	printk(fmt "\n", ## args); \
++}
++#else
++#define PDEBUG(fmt, args...)
++#endif
++
++/*
++ * Tasklets provide better system interactive response at the cost of the
++ * possibility of losing a frame of data at very infrequent intervals.  If
++ * you are more concerned with the performance of your machine, enable the
++ * tasklets.  If you are strict about absolutely no drops, then do not enable
++ * tasklets.
++ */
++
++/* #define ENABLE_TASKLETS */
++
++
++/* Work queues are a way to better distribute load on SMP systems */
++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20))
++/*
++ * Work queues can significantly improve performance and scalability
++ * on multi-processor machines, but requires bypassing some kernel
++ * API's, so it's not guaranteed to be compatible with all kernels.
++ */
++/* #define ENABLE_WORKQUEUES */
++#endif
++
++/* Enable HDLC support by hardware */
++#ifdef AP400_HDLC
++#include "ap400_hdlc/ap400_hdlc.c"
++#endif
++
++//#define APEC_SUPPORT
++#ifdef APEC_SUPPORT
++#include "apec.h"
++#endif
++
++/* Workarounds */
++#ifndef IRQF_SHARED
++#define IRQF_SHARED		SA_SHIRQ
++#endif
++#ifndef IRQF_DISABLED
++#define IRQF_DISABLED		SA_INTERRUPT
++#endif
++#ifndef __iomem
++#define __iomem
++#endif
++
++/* Enable prefetching may help performance */
++#define ENABLE_PREFETCH
++
++/* Define to get more attention-grabbing but slightly more I/O using
++   alarm status */
++#define FANCY_ALARM
++
++#define DEBUG_MAIN 		(1 << 0)
++#define DEBUG_DTMF 		(1 << 1)
++#define DEBUG_REGS 		(1 << 2)
++#define DEBUG_TSI  		(1 << 3)
++#define DEBUG_ECHOCAN 		(1 << 4)
++#define DEBUG_RBS 		(1 << 5)
++#define DEBUG_FRAMER		(1 << 6)
++
++static int clock_source = -1;
++static int tdm_loop = 0;
++static int apec_enable = 1;
++module_param(tdm_loop, int, 0600);
++module_param(apec_enable, int, 0600);
++
++#ifdef ENABLE_WORKQUEUES
++#include <linux/cpumask.h>
++
++/* XXX UGLY!!!! XXX  We have to access the direct structures of the workqueue which
++  are only defined within workqueue.c because they don't give us a routine to allow us
++  to nail a work to a particular thread of the CPU.  Nailing to threads gives us substantially
++  higher scalability in multi-CPU environments though! */
++
++/*
++ * The per-CPU workqueue (if single thread, we always use cpu 0's).
++ *
++ * The sequence counters are for flush_scheduled_work().  It wants to wait
++ * until until all currently-scheduled works are completed, but it doesn't
++ * want to be livelocked by new, incoming ones.  So it waits until
++ * remove_sequence is >= the insert_sequence which pertained when
++ * flush_scheduled_work() was called.
++ */
++
++struct cpu_workqueue_struct {
++
++	spinlock_t lock;
++
++	long remove_sequence;	/* Least-recently added (next to run) */
++	long insert_sequence;	/* Next to add */
++
++	struct list_head worklist;
++	wait_queue_head_t more_work;
++	wait_queue_head_t work_done;
++
++	struct workqueue_struct *wq;
++	task_t *thread;
++
++	int run_depth;		/* Detect run_workqueue() recursion depth */
++} ____cacheline_aligned;
++
++/*
++ * The externally visible workqueue abstraction is an array of
++ * per-CPU workqueues:
++ */
++struct workqueue_struct {
++	struct cpu_workqueue_struct cpu_wq[NR_CPUS];
++	const char *name;
++	struct list_head list; 	/* Empty if single thread */
++};
++
++/* Preempt must be disabled. */
++static void __ap4_queue_work(struct cpu_workqueue_struct *cwq,
++			 struct work_struct *work)
++{
++	unsigned long flags;
++
++	spin_lock_irqsave(&cwq->lock, flags);
++	work->wq_data = cwq;
++	list_add_tail(&work->entry, &cwq->worklist);
++	cwq->insert_sequence++;
++	wake_up(&cwq->more_work);
++	spin_unlock_irqrestore(&cwq->lock, flags);
++}
++
++/*
++ * Queue work on a workqueue. Return non-zero if it was successfully
++ * added.
++ *
++ * We queue the work to the CPU it was submitted, but there is no
++ * guarantee that it will be processed by that CPU.
++ */
++static inline int ap4_queue_work(struct workqueue_struct *wq, struct work_struct *work, int cpu)
++{
++	int ret = 0;
++
++	if (!test_and_set_bit(0, &work->pending)) {
++		BUG_ON(!list_empty(&work->entry));
++		__ap4_queue_work(wq->cpu_wq + cpu, work);
++		ret = 1;
++	}
++	return ret;
++}
++
++#endif
++
++static int debug=0;
++static int timingcable;
++static int highestorder;
++static int t1e1override = -1;
++static int j1mode = 0;
++static int loopback = 0;
++static int alarmdebounce = 0;
++
++/* Enabling bursting can more efficiently utilize PCI bus bandwidth, but
++   can also cause PCI bus starvation, especially in combination with other
++   aggressive cards.  Please note that burst mode has no effect on CPU
++   utilization / max number of calls / etc. */
++static int noburst = 1;
++static int debugslips = 0;
++static int polling = 0;
++
++#ifdef FANCY_ALARM
++static int altab[] = {
++0, 0, 0, 1, 2, 3, 4, 6, 8, 9, 11, 13, 16, 18, 20, 22, 24, 25, 27, 28, 29, 30, 31, 31, 32, 31, 31, 30, 29, 28, 27, 25, 23, 22, 20, 18, 16, 13, 11, 9, 8, 6, 4, 3, 2, 1, 0, 0,
++};
++#endif
++
++#define FLAG_STARTED (1 << 0)
++#define FLAG_NMF (1 << 1)
++#define FLAG_SENDINGYELLOW (1 << 2)
++
++#define	TYPE_T1	1		/* is a T1 card */
++#define	TYPE_E1	2		/* is an E1 card */
++#define TYPE_J1 3		/* is a running J1 */
++
++struct devtype {
++	char *desc;
++	unsigned int flags;
++};
++
++static struct devtype ap401  = { "Aligera AP401", 0 };
++static struct devtype ap402  = { "Aligera AP402", 0 };
++static struct devtype ap404  = { "Aligera AP404", 0 };
++static struct devtype ape401  = { "Aligera APE401", 0 };
++static struct devtype ape402  = { "Aligera APE402", 0 };
++static struct devtype ape404  = { "Aligera APE404", 0 };
++
++struct ap4;
++
++struct ap4_span {
++	struct ap4 *owner;
++	unsigned int *writechunk;					/* Double-word aligned write memory */
++	unsigned int *readchunk;					/* Double-word aligned read memory */
++	int spantype;		/* card type, T1 or E1 or J1 */
++	int sync;
++	int psync;
++	int alarmtimer;
++	int redalarms;
++	int notclear;
++	int alarmcount;
++	int spanflags;
++	int syncpos;
++	int e1check;			/* E1 check */
++	int reload_cas;
++	unsigned char casbuf[15];
++	unsigned int slipcount;
++	struct dahdi_span span;
++	unsigned char txsigs[16];	/* Transmit sigs */
++	int loopupcnt;
++	int loopdowncnt;
++	unsigned char ec_chunk1[31][DAHDI_CHUNKSIZE]; /* first EC chunk buffer */
++	unsigned char ec_chunk2[31][DAHDI_CHUNKSIZE]; /* second EC chunk buffer */
++	int irqmisses;
++#ifdef ENABLE_WORKQUEUES
++	struct work_struct swork;
++#endif
++	struct dahdi_chan *chans[32];		/* Individual channels */
++};
++
++struct ap4_regs {
++	volatile u32 card_id;		// 00h R0
++	volatile u16 fpga_ver;		// 04h R1
++	volatile u16 span_num;		// 06h R1
++	u32 __unused;			// 08h R2
++	volatile u32 liu_config;	// 0Ch R3
++	volatile u32 e1_config;		// 10h R4
++	volatile u32 e1_status;		// 14h R5
++	volatile u32 leds;		// 18h R6
++	volatile u32 clock_source;	// 1Ch R7
++	u32 __unused3[8];		// 20h - 3Ch R8 - R15
++	volatile u32 echo_ctrl;		// 40h R16
++	volatile u32 echo_data;		// 44h R17
++	volatile u32 t1_status;		// 48h R18
++	volatile u32 t1_config;		// 4Ch R19
++};
++
++struct ap4 {
++	/* This structure exists one per card */
++	struct pci_dev *dev;		/* Pointer to PCI device */
++	struct dahdi_device *ddev;
++	struct ap4_regs *hw_regs;
++	unsigned int intcount;
++	int flag_1st_irq;
++	int num;			/* Which card we are */
++	int fpgaver;		/* version of FPGA */
++	int hwid;			/* hardware ID */
++	int globalconfig;	/* Whether global setup has been done */
++	int syncsrc;			/* active sync source */
++	struct ap4_span *tspans[4];	/* Individual spans */
++	int numspans;			/* Number of spans on the card */
++	int blinktimer[4];
++#ifdef FANCY_ALARM
++	int alarmpos[4];
++#endif
++	int irq;			/* IRQ used by device */
++	int order;			/* Order */
++	int flags;			/* Device flags */
++	int ledreg;				/* LED Register */
++	int e1recover;			/* E1 recovery timer */
++	unsigned long memaddr;		/* Base address of card */
++	unsigned long memlen;
++	volatile unsigned int *membase;	/* Base address of card */
++	int spansstarted;		/* number of spans started */
++	/* spinlock_t lock; */		/* lock context */
++	spinlock_t reglock;		/* lock register access */
++	volatile unsigned int *writechunk;	/* Double-word aligned write memory */
++	volatile unsigned int *readchunk;	/* Double-word aligned read memory */
++#ifdef ENABLE_WORKQUEUES
++	atomic_t worklist;
++	struct workqueue_struct *workq;
++#else
++#ifdef ENABLE_TASKLETS
++	int taskletrun;
++	int taskletsched;
++	int taskletpending;
++	int taskletexec;
++	int txerrors;
++	struct tasklet_struct ap4_tlet;
++#endif
++#endif
++	unsigned int passno;	/* number of interrupt passes */
++	struct devtype *dt;
++	char *variety;
++	int last0;		/* for detecting double-missed IRQ */
++	int checktiming;	/* Set >0 to cause the timing source to be checked */
++#ifdef AP400_HDLC
++	struct card_s *hdlc_card;
++#endif
++#ifdef APEC_SUPPORT
++	int apec_enable;
++	struct apec_s *apec;
++#endif
++};
++
++
++static void __set_clear(struct ap4 *wc, int span);
++static int ap4_startup(struct file *file, struct dahdi_span *span);
++static int ap4_shutdown(struct dahdi_span *span);
++static int ap4_rbsbits(struct dahdi_chan *chan, int bits);
++static int ap4_maint(struct dahdi_span *span, int cmd);
++static int ap4_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data);
++static void __ap4_set_timing_source(struct ap4 *wc, int unit);
++static void __ap4_check_alarms(struct ap4 *wc, int span);
++static void __ap4_check_sigbits(struct ap4 *wc, int span);
++
++
++#define AP_ACTIVATE	(1 << 12)
++
++#define AP_OFF	(0)
++#define AP_ON	(1)
++
++#define MAX_AP4_CARDS 64
++
++#ifdef ENABLE_TASKLETS
++static void ap4_tasklet(unsigned long data);
++#endif
++
++static struct ap4 *cards[MAX_AP4_CARDS];
++
++//#define ap_debugk(fmt,args...) printk("ap400 -> %s: "fmt, __PRETTY_FUNCTION__, ##args)
++#define ap_debugk(fmt,args...)
++
++//#define TIMER_DEBUG	1
++
++#ifdef TIMER_DEBUG
++struct timer_list ap4xx_opt_timer;
++unsigned int delay = 1000;
++module_param(delay, uint, S_IRUGO);
++#endif
++
++#define PCI_DEVICE_ID_AP4XX		0x1004
++
++static inline void __ap4_set_led(struct ap4 *wc, int span, int color)
++{
++	wc->ledreg &= ~(AP_ON << span);
++	wc->ledreg |= (color << span);
++	*(wc->membase+AP_LEDS_REG) &= ~0x0000000F;
++	*(wc->membase+AP_LEDS_REG) |= ((wc->ledreg)&0x0F);
++}
++
++static inline void ap4_activate(struct ap4 *wc)
++{
++	wc->ledreg |= AP_ACTIVATE;
++}
++
++static void __set_clear(struct ap4 *wc, int span)
++{
++	int i,j;
++	int oldnotclear;
++	unsigned short val=0;
++	struct ap4_span *ts = wc->tspans[span];
++
++	oldnotclear = ts->notclear;
++	if (ts->spantype == TYPE_T1) {
++		for (i=0;i<24;i++) {
++			j = (i/8);
++			if (ts->span.chans[i]->flags & DAHDI_FLAG_CLEAR) {
++				val |= 1 << (7 - (i % 8));
++				ts->notclear &= ~(1 << i);
++			} else
++				ts->notclear |= (1 << i);
++			if ((i % 8)==7) {
++				val = 0;
++			}
++		}
++	} else {
++		for (i=0;i<31;i++) {
++			if (ts->span.chans[i]->flags & DAHDI_FLAG_CLEAR)
++				ts->notclear &= ~(1 << i);
++			else
++				ts->notclear |= (1 << i);
++		}
++	}
++}
++
++#ifdef APEC_SUPPORT
++
++#define APEC_CTRL_RESET		0x80000000
++#define APEC_CTRL_DDR_NCKE	0x40000000
++#define APEC_CTRL_EC_DISABLE	0x20000000
++#define APEC_CTRL_DAS		0x00080000
++#define APEC_CTRL_RD		0x00040000
++#define APEC_CTRL_REQ		0x00020000
++#define APEC_CTRL_READY		0x00010000
++
++#define APEC_ACCESS_TIMEOUT	1000
++
++static inline u16 oct_raw_read (struct ap4_regs *regs, unsigned short addr)
++{
++	unsigned short data;
++	// Poll ready bit
++	while ((regs->echo_ctrl & APEC_CTRL_READY) == 0);
++	// Write control bits and address
++	regs->echo_ctrl = APEC_CTRL_RD | APEC_CTRL_REQ | (addr & 0xFFFF);
++	while ((regs->echo_ctrl & APEC_CTRL_READY) == 0);
++	data = regs->echo_data & 0xFFFF;
++	//PDEBUG("Raw Read 0x%04hX @ 0x%08X", data, addr);
++	return data;
++}
++
++static inline void oct_raw_write (struct ap4_regs *regs, unsigned short addr,
++							unsigned short data)
++{
++	// Poll ready bit
++	while ((regs->echo_ctrl & APEC_CTRL_READY) == 0);
++	// Write data, then control bits and address
++	regs->echo_data = data & 0xFFFF;
++	regs->echo_ctrl = APEC_CTRL_REQ | (addr & 0xFFFF);
++	// Poll ready bit
++	while ((regs->echo_ctrl & APEC_CTRL_READY) == 0);
++	//PDEBUG("Raw Write 0x%04hX @ 0x%08X", data, addr);
++	//oct_raw_read(regs, addr);
++}
++
++static inline int oct_ext_wait (struct ap4_regs *regs)
++{
++	int i = APEC_ACCESS_TIMEOUT;
++	while ((oct_raw_read(regs, 0x0) & 0x100) && (i-- > 0));
++	if (i == -1) {
++		printk(KERN_WARNING "Wait access_req timeout\n");
++		return -1;
++	}
++	return 0;
++}
++
++static inline u16 oct_ind_read (struct ap4_regs *regs, unsigned int addr)
++{
++	// Poll access_req bit
++	if (oct_ext_wait(regs))
++		return 0;
++	// Write extended indirect registers
++	oct_raw_write(regs, 0x8, (addr >> 20) & 0x1FFF);
++	oct_raw_write(regs, 0xA, (addr >> 4) & 0xFFFF);
++	oct_raw_write(regs, 0x0, ((addr & 0xE) << 8) | 0x101);
++	// Poll access_req bit
++	if (oct_ext_wait(regs))
++		return 0;
++	// Return data
++	return oct_raw_read(regs, 0x4);
++}
++
++static inline void oct_ind_write (struct ap4_regs *regs, unsigned int addr,
++							unsigned short data)
++{
++	// Poll access_req bit
++	if (oct_ext_wait(regs))
++		return;
++	oct_raw_write(regs, 0x8, (addr >> 20) & 0x1FFF);
++	oct_raw_write(regs, 0xA, (addr >> 4) & 0xFFFF);
++	oct_raw_write(regs, 0x4, data);
++	oct_raw_write(regs, 0x0, ((addr & 0xE) << 8) | 0x3101);
++	// Poll access_req bit
++	if (oct_ext_wait(regs))
++		return;
++}
++
++static inline u16 oct_dir_read (struct ap4_regs *regs, unsigned int addr)
++{
++	// Poll access_req bit
++	if (oct_ext_wait(regs))
++		return 0;
++	// Write extended direct registers
++	oct_raw_write(regs, 0x8, (addr >> 20) & 0x1FFF);
++	oct_raw_write(regs, 0xA, (addr >> 4) & 0xFFFF);
++	oct_raw_write(regs, 0x0, 0x1);
++	regs->echo_ctrl = APEC_CTRL_DAS | APEC_CTRL_RD | APEC_CTRL_REQ | (addr & 0xFFFF);
++	while ((regs->echo_ctrl & APEC_CTRL_READY) == 0);
++	// Return data
++	return regs->echo_data;
++}
++
++static inline void oct_dir_write (struct ap4_regs *regs, unsigned int addr,
++							unsigned short data)
++{
++	// Poll access_req bit
++	if (oct_ext_wait(regs))
++		return;
++	// Write extended direct registers
++	oct_raw_write(regs, 0x8, (addr >> 20) & 0x1FFF);
++	oct_raw_write(regs, 0xA, (addr >> 4) & 0xFFFF);
++	oct_raw_write(regs, 0x0, 0x3001);
++	regs->echo_data = data & 0xFFFF;
++	regs->echo_ctrl = APEC_CTRL_DAS | APEC_CTRL_REQ | (addr & 0xFFFF);
++	while ((regs->echo_ctrl & APEC_CTRL_READY) == 0);
++}
++
++
++unsigned int oct_read (void *card, unsigned int addr)
++{
++	struct ap4 *wc = card;
++	int flags;
++	unsigned short data;
++	spin_lock_irqsave(&wc->reglock, flags);
++	data = oct_ind_read(wc->hw_regs, addr);
++	spin_unlock_irqrestore(&wc->reglock, flags);
++	PDEBUG("Read 0x%04hX @ 0x%08X", data, addr);
++	return data;
++}
++
++void oct_write (void *card, unsigned int addr, unsigned int data)
++{
++	struct ap4 *wc = card;
++	int flags;
++	spin_lock_irqsave(&wc->reglock, flags);
++	oct_ind_write(wc->hw_regs, addr, data);
++	spin_unlock_irqrestore(&wc->reglock, flags);
++	PDEBUG("Write 0x%04hX @ 0x%08X", data, addr);
++}
++
++static int ap4_apec_init(struct ap4 *wc)
++{
++	int laws[4];
++	int i;
++	unsigned int apec_capacity;
++	struct firmware embedded_firmware;
++	const struct firmware *firmware = &embedded_firmware;
++#if !defined(HOTPLUG_FIRMWARE)
++	extern void _binary_OCT6104E_64D_ima_size;
++	extern u8 _binary_OCT6104E_64D_ima_start[];
++	extern void _binary_OCT6104E_128D_ima_size;
++	extern u8 _binary_OCT6104E_128D_ima_start[];
++#else
++	static const char oct64_firmware[] = "OCT6104E-64D.ima";
++	static const char oct128_firmware[] = "OCT6104E-128D.ima";
++#endif
++
++	// Enable DDR and Reset Octasic
++	wc->hw_regs->echo_ctrl |= APEC_CTRL_RESET;
++	wc->hw_regs->echo_ctrl |= APEC_CTRL_DDR_NCKE;
++	udelay(500);
++	wc->hw_regs->echo_ctrl &= APEC_CTRL_RESET;
++	wc->hw_regs->echo_ctrl &= APEC_CTRL_DDR_NCKE;
++	wc->hw_regs->echo_ctrl &= APEC_CTRL_EC_DISABLE;
++
++	/* Setup alaw vs ulaw rules */
++	for (i = 0; i < wc->numspans; i++) {
++		if (wc->tspans[i]->span.channels > 24)
++			laws[i] = 1;	// E1: alaw
++		else
++			laws[i] = 0;	// T1: ulaw
++	}
++
++	switch ((apec_capacity = apec_capacity_get(wc))) {
++	case 64:
++#if defined(HOTPLUG_FIRMWARE)
++		if ((request_firmware(&firmware, oct64_firmware, &wc->dev->dev) != 0) ||
++		    !firmware) {
++			printk("%s: firmware %s not available from userspace\n",
++					wc->variety, oct64_firmware);
++			return -1;
++		}
++#else
++		embedded_firmware.data = _binary_OCT6104E_64D_ima_start;
++		/* Yes... this is weird. objcopy gives us a symbol containing
++		   the size of the firmware, not a pointer to a variable containing
++		   the size. The only way we can get the value of the symbol
++		   is to take its address, so we define it as a pointer and
++		   then cast that value to the proper type.
++		*/
++		embedded_firmware.size = (size_t) &_binary_OCT6104E_64D_ima_size;
++#endif
++		break;
++	case 128:
++#if defined(HOTPLUG_FIRMWARE)
++		if ((request_firmware(&firmware, oct128_firmware, &wc->dev->dev) != 0) ||
++		    !firmware) {
++			printk("%s: firmware %s not available from userspace\n",
++					wc->variety, oct128_firmware);
++			return -1;
++		}
++#else
++		embedded_firmware.data = _binary_OCT6104E_128D_ima_start;
++		/* Yes... this is weird. objcopy gives us a symbol containing
++		   the size of the firmware, not a pointer to a variable containing
++		   the size. The only way we can get the value of the symbol
++		   is to take its address, so we define it as a pointer and
++		   then cast that value to the proper type.
++		*/
++		embedded_firmware.size = (size_t) &_binary_OCT6104E_128D_ima_size;
++#endif
++		break;
++	default:
++		printk(KERN_INFO "Unsupported channel capacity found on"
++				"echo cancellation module (%d).\n", apec_capacity);
++		return -1;
++	}
++
++	if (!(wc->apec = apec_init(wc, laws, wc->numspans, firmware))) {
++		printk(KERN_WARNING "APEC: Failed to initialize\n");
++		if (firmware != &embedded_firmware)
++			release_firmware(firmware);
++		return -1;
++	}
++
++	if (firmware != &embedded_firmware)
++		release_firmware(firmware);
++
++	printk(KERN_INFO "APEC: Present and operational servicing %d span(s)\n", wc->numspans);
++	return 0;
++}
++
++void ap4_apec_release(struct ap4 *wc)
++{
++	// Disabel DDR and reset Octasic
++	wc->hw_regs->echo_ctrl |= APEC_CTRL_RESET;
++	wc->hw_regs->echo_ctrl |= APEC_CTRL_DDR_NCKE;
++	wc->hw_regs->echo_ctrl |= APEC_CTRL_EC_DISABLE;
++	if (wc->apec)
++		apec_release(wc->apec);
++}
++
++
++static int ap4_echocan(struct dahdi_chan *chan, int eclen)
++{
++	struct ap4 *wc = chan->pvt;
++	int channel;
++
++	if (!wc->apec)
++		return -ENODEV;
++	if (debug)
++		printk(KERN_DEBUG "AP400: ap4_echocan @ Span %d Channel %d Length: %d\n",
++				chan->span->offset, chan->chanpos, eclen);
++	channel = (chan->chanpos << 2) | chan->span->offset;
++	apec_setec(wc->apec, channel, eclen);
++	return 0;
++}
++
++#endif // APEC_SUPPORT
++
++
++static int ap4_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data)
++{
++	struct ap4 *wc = chan->pvt;
++	int span = 0;
++	int alarms = 0;
++	unsigned char c, e1_cfg;
++
++	switch(cmd) {
++		case AP4_GET_ALARMS:
++			if (copy_from_user(&span, (int *)data, sizeof(int)))
++				return -EFAULT;
++			// span starts in zero
++			span--;
++		if (wc->tspans[span]->spantype == TYPE_E1) {
++		        /* le status e configuracao do E1 */
++		        c = ((*(wc->membase+AP_E1_STATUS_REG))>>(8*span));
++		        e1_cfg = ((*(wc->membase+AP_E1_CONFIG_REG))>>(8*span));
++			if( c & AP_E1_LOS_STATUS) {
++				alarms = 0x01;
++			} else if( c & AP_E1_AIS_STATUS) {
++				alarms = 0x02;
++			} else if(!(c & AP_E1_BFAE_STATUS)) {
++				alarms = 0x04;
++				if (c & AP_E1_RAI_STATUS)
++					alarms |= 0x08;
++				// Erro de MFA: 00 - MFA desabilitado, 01 - erro de MFA, 10 - MFA OK
++				if ( (c & AP_E1_MFAE_STATUS) && (e1_cfg & AP_E1_CRCEN_CONFIG) )
++					alarms |= 0x10;
++				else if ( (!(c & AP_E1_MFAE_STATUS)) && (e1_cfg & AP_E1_CRCEN_CONFIG) )
++					alarms |= 0x20;
++				// Erro de CAS: 00 - desabilitado, 01 - erro de CAS, 10 - CAS OK
++				if ( (!(c & AP_E1_CAS_STATUS)) && (e1_cfg & AP_E1_PCM30_CONFIG))
++					alarms |= 0x40;
++				else if ( (c & AP_E1_CAS_STATUS) && (e1_cfg & AP_E1_PCM30_CONFIG))
++					alarms |= 0x80;
++			}
++		} else {
++			/* le status e configuracao do E1 */
++		        c = ((*(wc->membase+AP_E1_STATUS_REG))>>(8*span));
++		        if( c & AP_E1_LOS_STATUS)
++				alarms = 0x01;
++			else {
++			        c = wc->hw_regs->t1_status >> (8*span);
++			        if (!(c & AP4_T1_FRAME_SYNC))
++			        	alarms = 0x04;
++		        }
++		}
++			if(debug) printk("AP4_GET_ALARMS: span = %d, alarms = 0x%02x\n", span+1, alarms);
++			if (copy_to_user((int *)data, &alarms, sizeof(int)))
++				return -EFAULT;
++			break;
++
++		case AP4_GET_SLIPS:
++			if (copy_from_user(&span, (int *)data, sizeof(int)))
++				return -EFAULT;
++			// span starts in zero
++			span--;
++			if((span < wc->numspans) && (span >=0))
++				alarms = wc->tspans[span]->slipcount;
++			if(debug) printk("AP4_GET_SLIPS: span = %d, slips = 0x%02x\n", span+1, alarms);
++			if (copy_to_user((int *)data, &alarms, sizeof(int)))
++				return -EFAULT;
++			break;
++
++		default:
++			PDEBUG("%s: Unknown IOCTL CODE!", wc->variety);
++			return -ENOTTY;
++	}
++	return 0;
++}
++
++static inline struct ap4_span* ap4_span_from_span(struct dahdi_span *span) {
++	return container_of(span, struct ap4_span, span);
++}
++
++static int ap4_maint(struct dahdi_span *span, int cmd)
++{
++	struct ap4_span *ts = ap4_span_from_span(span);
++	struct ap4 *wc = ts->owner;
++
++
++	if (ts->spantype == TYPE_E1) {
++		switch(cmd) {
++		case DAHDI_MAINT_NONE:
++			printk("XXX Turn off local and remote loops E1 XXX\n");
++			*(wc->membase+AP_E1_CONFIG_REG) &= ~(AP_E1_LOOP_CONFIG<<((span->spanno-1)*8));
++			break;
++		case DAHDI_MAINT_LOCALLOOP:
++			printk("XXX Turn on local loopback E1 XXX\n");
++			break;
++		case DAHDI_MAINT_REMOTELOOP:
++			printk("XXX Turn on remote loopback E1 XXX\n");
++			break;
++		case DAHDI_MAINT_LOOPUP:
++			printk("XXX Turn on local loopback on E1 #%d instead of send loopup code XXX\n", span->spanno);
++			*(wc->membase+AP_E1_CONFIG_REG) |= (AP_E1_LOOP_CONFIG<<((span->spanno-1)*8));
++			break;
++		case DAHDI_MAINT_LOOPDOWN:
++			printk("XXX Turn on local loopback on E1 #%d instead of send loopdown code XXX\n", span->spanno);
++			*(wc->membase+AP_E1_CONFIG_REG) |= (AP_E1_LOOP_CONFIG<<((span->spanno-1)*8));
++			break;
++		default:
++			printk("%s: Unknown E1 maint command: %d\n", wc->variety, cmd);
++			break;
++		}
++	} else {
++		switch(cmd) {
++	    case DAHDI_MAINT_NONE:
++			printk("XXX Turn off local and remote loops T1 XXX\n");
++			break;
++	    case DAHDI_MAINT_LOCALLOOP:
++			printk("XXX Turn on local loop and no remote loop XXX\n");
++			break;
++	    case DAHDI_MAINT_REMOTELOOP:
++			printk("XXX Turn on remote loopup XXX\n");
++			break;
++	    case DAHDI_MAINT_LOOPUP:
++			break;
++	    case DAHDI_MAINT_LOOPDOWN:
++			break;
++	    default:
++			printk("%s: Unknown T1 maint command: %d\n", wc->variety, cmd);
++			break;
++	   }
++    }
++	return 0;
++}
++
++static int ap4_rbsbits(struct dahdi_chan *chan, int bits)
++{
++	u_char m,c;
++	int k,n,b;
++	struct ap4 *wc = chan->pvt;
++	struct ap4_span *ts = wc->tspans[chan->span->offset];
++	unsigned long flags;
++	volatile unsigned int *writecas = (wc->membase+AP_CAS_BASE);
++	unsigned int allspansbits;
++
++	//ap_debugk("chan->channo = %d, int bits = 0x%08x\n", chan->channo, bits);
++	if(debug & DEBUG_RBS) printk("Setting bits to %d on channel %s\n", bits, chan->name);
++	spin_lock_irqsave(&wc->reglock, flags);
++	k = chan->span->offset;
++	if (ts->spantype == TYPE_E1) { /* do it E1 way */
++		if (chan->chanpos == 16) {
++			spin_unlock_irqrestore(&wc->reglock, flags);
++			return 0;
++		}
++		n = chan->chanpos - 1;
++		if (chan->chanpos > 15) n--;
++		b = (n % 15);
++		c = ts->txsigs[b];
++		m = (n / 15) << 2; /* nibble selector */
++		c &= (0xf << m); /* keep the other nibble */
++		c |= (bits & 0xf) << (4 - m); /* put our new nibble here */
++		ts->txsigs[b] = c;
++		/* monta a word de 32 bits com informacao de todos os spans */
++		allspansbits =  wc->tspans[0]->txsigs[b];
++		if (wc->numspans > 1) {
++			allspansbits |=	(wc->tspans[1]->txsigs[b] << 8);
++		}
++		if (wc->numspans == 4) {
++			allspansbits |=	(wc->tspans[2]->txsigs[b] << 16) |
++							(wc->tspans[3]->txsigs[b] << 24);
++		}
++		/* output them to the chip */
++		writecas[b] = allspansbits;
++		ap_debugk("escrito 0x%08x para ser transmitido pelo CAS (b = %d)\n", allspansbits, b);
++#if 0
++	} else if (ts->span.lineconfig & DAHDI_CONFIG_D4) {
++		n = chan->chanpos - 1;
++		b = (n/4);
++		c = ts->txsigs[b];
++		m = ((3 - (n % 4)) << 1); /* nibble selector */
++		c &= ~(0x3 << m); /* keep the other nibble */
++		c |= ((bits >> 2) & 0x3) << m; /* put our new nibble here */
++		ts->txsigs[b] = c;
++		  /* output them to the chip */
++		//__ap4_out( ... );
++	} else if (ts->span.lineconfig & DAHDI_CONFIG_ESF) {
++#endif
++	} else {
++		n = chan->chanpos - 1;
++		b = (n/2);
++		c = ts->txsigs[b];
++		m = ((n % 2) << 2); /* nibble selector */
++		c &= (0xf << m); /* keep the other nibble */
++		c |= (bits & 0xf) << (4 - m); /* put our new nibble here */
++		ts->txsigs[b] = c;
++		  /* output them to the chip */
++		/* monta a word de 32 bits com informacao de todos os spans */
++		allspansbits =  wc->tspans[0]->txsigs[b];
++		if (wc->numspans > 1) {
++			allspansbits |=	(wc->tspans[1]->txsigs[b] << 8);
++		}
++		if (wc->numspans == 4) {
++			allspansbits |=	(wc->tspans[2]->txsigs[b] << 16) |
++							(wc->tspans[3]->txsigs[b] << 24);
++		}
++		/* output them to the chip */
++		writecas[b] = allspansbits;
++		ap_debugk("escrito 0x%08x para ser transmitido pelo CAS (b = %d)\n", allspansbits, b);
++	}
++	spin_unlock_irqrestore(&wc->reglock, flags);
++	if (debug & DEBUG_RBS)
++		printk("Finished setting RBS bits\n");
++	return 0;
++}
++
++static int ap4_shutdown(struct dahdi_span *span)
++{
++	int tspan;
++	int wasrunning;
++	unsigned long flags;
++	struct ap4_span *ts = ap4_span_from_span(span);
++	struct ap4 *wc = ts->owner;
++
++	tspan = span->offset + 1;
++	if (tspan < 0) {
++		printk("%s: '%d' isn't us?\n", wc->variety, span->spanno);
++		return -1;
++	}
++
++	spin_lock_irqsave(&wc->reglock, flags);
++	wasrunning = span->flags & DAHDI_FLAG_RUNNING;
++
++	span->flags &= ~DAHDI_FLAG_RUNNING;
++	if (wasrunning)
++		wc->spansstarted--;
++	__ap4_set_led(wc, span->offset, AP_OFF);
++	if (((wc->numspans == 4) &&
++	    (!(wc->tspans[0]->span.flags & DAHDI_FLAG_RUNNING)) &&
++	    (!(wc->tspans[1]->span.flags & DAHDI_FLAG_RUNNING)) &&
++	    (!(wc->tspans[2]->span.flags & DAHDI_FLAG_RUNNING)) &&
++	    (!(wc->tspans[3]->span.flags & DAHDI_FLAG_RUNNING)))
++	    			||
++	    ((wc->numspans == 2) &&
++	    (!(wc->tspans[0]->span.flags & DAHDI_FLAG_RUNNING)) &&
++	    (!(wc->tspans[1]->span.flags & DAHDI_FLAG_RUNNING)))
++	    			||
++	    ((wc->numspans == 1) &&
++	    (!(wc->tspans[0]->span.flags & DAHDI_FLAG_RUNNING)))) {
++		/* No longer in use, disable interrupts */
++		printk("%s: Disabling interrupts since there are no active spans\n",
++				wc->variety);
++	} else wc->checktiming = 1;
++	spin_unlock_irqrestore(&wc->reglock, flags);
++	if (debug & DEBUG_MAIN)
++		printk("Span %d (%s) shutdown\n", span->spanno, span->name);
++	return 0;
++}
++
++static int ap4_spanconfig(struct file *file, struct dahdi_span *span,
++		struct dahdi_lineconfig *lc)
++{
++	int i;
++	struct ap4_span *ts = ap4_span_from_span(span);
++	struct ap4 *wc = ts->owner;
++	unsigned int val;
++
++	printk("About to enter spanconfig!\n");
++	if (debug & DEBUG_MAIN)
++		printk("%s: Configuring span %d\n", wc->variety, span->spanno);
++	/* XXX We assume lineconfig is okay and shouldn't XXX */
++	span->lineconfig = lc->lineconfig;
++	span->txlevel = lc->lbo;
++	span->rxlevel = 0;
++	if (lc->sync < 0)
++		lc->sync = 0;
++	if (lc->sync > 4)
++		lc->sync = 0;
++
++	/* remove this span number from the current sync sources, if there */
++	for(i = 0; i < wc->numspans; i++) {
++		if (wc->tspans[i]->sync == span->spanno) {
++			wc->tspans[i]->sync = 0;
++			wc->tspans[i]->psync = 0;
++		}
++	}
++	wc->tspans[span->offset]->syncpos = lc->sync;
++	/* if a sync src, put it in proper place */
++	if (lc->sync) {
++		wc->tspans[lc->sync - 1]->sync = span->spanno;
++		wc->tspans[lc->sync - 1]->psync = span->offset + 1;
++	}
++	wc->checktiming = 1;
++	/* If we're already running, then go ahead and apply the changes */
++	if (span->flags & DAHDI_FLAG_RUNNING)
++		return ap4_startup(file, span);
++
++	// Limpa contadores de slips, crc e bpv
++	val = (*(wc->membase + AP_CNT_SLIP_REG));
++	val = (*(wc->membase + AP_CNT_CRC_REG));
++	val = (*(wc->membase + AP_CNT_CV_REG));
++
++	ap_debugk("habilitando interrupcao!\n");
++	// Nao considera as primeiras interrupcoes na soma das IRQs perdidas
++	wc->flag_1st_irq = 16;
++	// Enable interrupt
++	*(wc->membase + AP_INT_CONTROL_REG) |= AP_INT_CTL_ENABLE;
++	// Limpa interrupcao da FPGA para forcar borda de subida na proxima
++	val = *(wc->membase + AP_CLEAR_IRQ_REG);
++
++	printk("Done with spanconfig!\n");
++	return 0;
++}
++
++static int ap4_chanconfig(struct file *file, struct dahdi_chan *chan,
++		int sigtype)
++{
++	int alreadyrunning;
++	unsigned long flags;
++	struct ap4 *wc = chan->pvt;
++
++	alreadyrunning = wc->tspans[chan->span->offset]->span.flags & DAHDI_FLAG_RUNNING;
++	if (debug & DEBUG_MAIN) {
++		if (alreadyrunning)
++			printk("%s: Reconfigured channel %d (%s) sigtype %d\n",
++					wc->variety, chan->channo, chan->name, sigtype);
++		else
++			printk("%s: Configured channel %d (%s) sigtype %d\n",
++					wc->variety, chan->channo, chan->name, sigtype);
++	}
++	spin_lock_irqsave(&wc->reglock, flags);
++	if (alreadyrunning)
++		__set_clear(wc, chan->span->offset);
++	spin_unlock_irqrestore(&wc->reglock, flags);
++	return 0;
++}
++
++static int ap4_open(struct dahdi_chan *chan)
++{
++	try_module_get(THIS_MODULE);
++	return 0;
++}
++
++static int ap4_close(struct dahdi_chan *chan)
++{
++	module_put(THIS_MODULE);
++	return 0;
++}
++
++static const struct dahdi_span_ops ap4_span_ops = {
++	.owner = THIS_MODULE,
++	.spanconfig = ap4_spanconfig,
++	.chanconfig = ap4_chanconfig,
++	.startup = ap4_startup,
++	.shutdown = ap4_shutdown,
++	.rbsbits = ap4_rbsbits,
++	.maint = ap4_maint,
++	.open = ap4_open,
++	.close  = ap4_close,
++#ifdef APEC_SUPPORT
++	.echocan = ap4_echocan,
++#endif
++	.ioctl = ap4_ioctl
++};
++
++static void init_spans(struct ap4 *wc)
++{
++	int x,y;
++	struct ap4_span *ts;
++
++	for (x=0;x<wc->numspans;x++) {
++		ts = wc->tspans[x];
++		sprintf(ts->span.name, "AP4%d%d/%d/%d", 0, wc->numspans, wc->num, x + 1);
++		snprintf(ts->span.desc, sizeof(ts->span.desc) - 1, "AP4%d%d Card %d Span %d", 0, wc->numspans, wc->num+1, x+1);
++		ts->span.ops = &ap4_span_ops;
++		if (ts->spantype == TYPE_E1) {
++			ts->span.channels = 31;
++			ts->span.spantype = SPANTYPE_DIGITAL_E1;
++			ts->span.linecompat = DAHDI_CONFIG_HDB3 | DAHDI_CONFIG_CCS | DAHDI_CONFIG_CRC4;
++			ts->span.deflaw = DAHDI_LAW_ALAW;
++		} else {
++			ts->span.channels = 24;
++			ts->span.spantype = SPANTYPE_DIGITAL_T1;
++			ts->span.linecompat = DAHDI_CONFIG_AMI | DAHDI_CONFIG_B8ZS | DAHDI_CONFIG_D4 | DAHDI_CONFIG_ESF;
++			ts->span.deflaw = DAHDI_LAW_MULAW;
++		}
++		ts->span.chans = ts->chans;
++		ts->span.flags = DAHDI_FLAG_RBS;
++		ts->owner = wc;
++		ts->span.offset = x;
++		ts->writechunk = (void *)(wc->writechunk + x * 32 * 2);
++		ts->readchunk = (void *)(wc->readchunk + x * 32 * 2);
++		for (y=0;y<wc->tspans[x]->span.channels;y++) {
++			struct dahdi_chan *mychans = ts->chans[y];
++			sprintf(mychans->name, "AP4%d%d/%d/%d/%d", 0, wc->numspans, wc->num, x + 1, y + 1);
++			mychans->sigcap = DAHDI_SIG_EM | DAHDI_SIG_CLEAR | DAHDI_SIG_FXSLS | DAHDI_SIG_FXSGS | DAHDI_SIG_FXSKS |
++									 DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_FXOKS | DAHDI_SIG_CAS | DAHDI_SIG_EM_E1 | DAHDI_SIG_DACS_RBS;
++			mychans->pvt = wc;
++			mychans->chanpos = y + 1;
++		}
++	}
++	printk("%s: Spans initialized\n", wc->variety);
++}
++
++
++
++static void __ap4_set_timing_source(struct ap4 *wc, int unit)
++{
++	unsigned int timing;
++	int x;
++
++	if (unit != wc->syncsrc) {
++		if ((unit > -1) && (unit < 4)) {
++			/* define fonte de clock para interface escolhida */
++			timing = *(wc->membase+AP_CLKSRC_REG);
++			timing &= ~AP_CLKSRC_MASK;
++			timing |= unit+1;
++			*(wc->membase+AP_CLKSRC_REG) = timing;
++		} else {
++			/* define clock para interno */
++			timing = *(wc->membase+AP_CLKSRC_REG);
++			timing &= ~AP_CLKSRC_MASK;
++			*(wc->membase+AP_CLKSRC_REG) = timing;
++		}
++		wc->syncsrc = unit;
++		if ((unit < 0) || (unit > 3))
++			unit = 0;
++		else
++			unit++;
++		for (x=0;x<wc->numspans;x++)
++			wc->tspans[x]->span.syncsrc = unit;
++	} else {
++		if (debug & DEBUG_MAIN)
++			printk("%s: Timing source already set to %d\n",
++					wc->variety, unit);
++	}
++	printk("%s: Timing source set to %d (clksrc_reg = 0x%08x)\n",
++			wc->variety, unit, *(wc->membase+AP_CLKSRC_REG));
++}
++
++static void __ap4_set_timing_source_auto(struct ap4 *wc)
++{
++	int x;
++
++	wc->checktiming = 0;
++	for (x=0;x<wc->numspans;x++) {
++		if (wc->tspans[x]->sync) {
++			if ((wc->tspans[wc->tspans[x]->psync - 1]->span.flags & DAHDI_FLAG_RUNNING) &&
++				!(wc->tspans[wc->tspans[x]->psync - 1]->span.alarms & (DAHDI_ALARM_RED | DAHDI_ALARM_BLUE) )) {
++					/* Valid timing source */
++					__ap4_set_timing_source(wc, wc->tspans[x]->psync - 1);
++					return;
++			}
++		}
++	}
++	__ap4_set_timing_source(wc, 4);
++}
++
++static void __ap4_configure_t1(struct ap4 *wc, int unit, int lineconfig, int txlevel)
++{
++	char *framing, *line;
++	unsigned int config = 0;
++	unsigned int param = 0;
++	unsigned int linecode = 0;
++
++	wc->tspans[unit]->spantype = TYPE_T1;
++	wc->tspans[unit]->span.channels = 24;
++	wc->tspans[unit]->span.deflaw = DAHDI_LAW_MULAW;
++
++	/* Configure line code */
++	if (unit < 2)
++		linecode = AP_LIU1_LINECODE;
++	else
++		linecode = AP_LIU2_LINECODE;
++	if (lineconfig & DAHDI_CONFIG_AMI) {
++		*(wc->membase+AP_LEDS_REG) |= linecode;
++		line = "AMI";
++	} else {
++		*(wc->membase+AP_LEDS_REG) &= ~linecode;
++		line = "B8ZS";
++	}
++
++	/* loopback test*/
++	//wc->hw_regs->e1_config |= (AP_E1_LOOP_CONFIG  << (8 * unit));
++	//printk("E1 config = 0x%08x\n", wc->hw_regs->e1_config);
++
++	/* Configure T1 */
++	config = wc->hw_regs->liu_config;
++	config &= ~(0x000000ff << (8 * unit));
++	config |= (AP_PULS_DSX1_0FT << (8 * unit));
++	wc->hw_regs->liu_config = config;
++
++	param = AP4_T1_NE1_SEL | AP4_T1_CAS_ENABLE;
++	if (lineconfig & DAHDI_CONFIG_D4) {
++		framing = "D4";
++	} else {
++		framing = "ESF";
++		param |= AP4_T1_ESF_NSF;
++	}
++	config = wc->hw_regs->t1_config;
++	config &= ~(0x000000ff << (8 * unit));
++	config |= (param << (8 * unit));
++	wc->hw_regs->t1_config = config;
++
++	printk("T1 Status: 0x%08x\tT1 Config: 0x%08x\tPARAM: 0x%08x\n",
++			wc->hw_regs->t1_status, wc->hw_regs->t1_config, param);
++
++	if (!polling) {
++		__ap4_check_alarms(wc, unit);
++		__ap4_check_sigbits(wc, unit);
++	}
++	printk("%s: Span %d configured for %s/%s\n", wc->variety, unit + 1, framing, line);
++}
++
++static void __ap4_configure_e1(struct ap4 *wc, int unit, int lineconfig)
++{
++	char *crc4 = "";
++	char *framing, *line;
++	unsigned int e1s_cfg, config = 0;
++	unsigned int linecode = 0;
++
++	wc->tspans[unit]->spantype = TYPE_E1;
++	wc->tspans[unit]->span.channels = 31;
++	wc->tspans[unit]->span.deflaw = DAHDI_LAW_ALAW;
++
++	if (loopback) {
++	}
++
++	if (lineconfig & DAHDI_CONFIG_CRC4) {
++		crc4 = "/CRC4";
++		config |= AP_E1_CRCEN_CONFIG;
++	}
++
++	if(unit < 2)
++		linecode = AP_LIU1_LINECODE;
++	else
++		linecode = AP_LIU2_LINECODE;
++	/* Configure line interface */
++	if (lineconfig & DAHDI_CONFIG_AMI) {
++		*(wc->membase+AP_LEDS_REG) |= linecode;
++		line = "AMI";
++	} else {
++		*(wc->membase+AP_LEDS_REG) &= ~linecode;
++		line = "HDB3";
++	}
++
++	if (lineconfig & DAHDI_CONFIG_CCS) {
++		framing = "CCS";
++	} else {
++		framing = "CAS";
++		config |= (AP_E1_CASEN_CONFIG | AP_E1_PCM30_CONFIG);
++	}
++
++	e1s_cfg = *(wc->membase+AP_E1_CONFIG_REG);
++	e1s_cfg &= ~(0x000000ff<<(8*unit));
++	e1s_cfg |= (config<<(8*unit));
++	*(wc->membase+AP_E1_CONFIG_REG) = e1s_cfg;
++
++	/* Disable T1 framer */
++	config = wc->hw_regs->t1_config;
++	config &= ~(0x000000ff << (8 * unit));
++	wc->hw_regs->t1_config = config;
++
++	/* Configure LIU Signalling */
++	e1s_cfg = *(wc->membase+AP_T1E1_CONFIG_REG);
++	e1s_cfg &= ~(0x000000ff<<(8*unit));
++	e1s_cfg |= (AP_PULS_E1_120<<(8*unit));
++	*(wc->membase+AP_T1E1_CONFIG_REG) = e1s_cfg;
++
++	if (!polling) {
++		__ap4_check_alarms(wc, unit);
++		__ap4_check_sigbits(wc, unit);
++	}
++	printk("%s: Span %d configured for %s/%s%s\n",
++ 			wc->variety, unit + 1, framing, line, crc4);
++}
++
++static int ap4_startup(struct file *file, struct dahdi_span *span)
++{
++	int i;
++	int tspan;
++	unsigned long flags;
++	int alreadyrunning;
++	struct ap4_span *ts = ap4_span_from_span(span);
++	struct ap4 *wc = ts->owner;
++
++	printk("About to enter startup!\n");
++	tspan = span->offset + 1;
++	if (tspan < 0) {
++		printk("%s: Span '%d' isn't us?\n", wc->variety, span->spanno);
++		return -1;
++	}
++
++	spin_lock_irqsave(&wc->reglock, flags);
++
++	alreadyrunning = span->flags & DAHDI_FLAG_RUNNING;
++
++	/* initialize the start value for the entire chunk of last ec buffer */
++	for(i = 0; i < span->channels; i++)
++	{
++		memset(ts->ec_chunk1[i],
++			DAHDI_LIN2X(0, span->chans[i]),DAHDI_CHUNKSIZE);
++		memset(ts->ec_chunk2[i],
++			DAHDI_LIN2X(0, span->chans[i]),DAHDI_CHUNKSIZE);
++	}
++
++	/* Force re-evaluation fo timing source */
++//	if (timingcable)
++		wc->syncsrc = -1;
++
++	if ((span->lineconfig & DAHDI_CONFIG_D4) || (span->lineconfig & DAHDI_CONFIG_ESF)) {
++		/* is a T1 card */
++		__ap4_configure_t1(wc, span->offset, span->lineconfig, span->txlevel);
++	} else { /* is a E1 card */
++		__ap4_configure_e1(wc, span->offset, span->lineconfig);
++	}
++
++	/* Note clear channel status */
++	wc->tspans[span->offset]->notclear = 0;
++	__set_clear(wc, span->offset);
++
++	if (!alreadyrunning) {
++		span->flags |= DAHDI_FLAG_RUNNING;
++		wc->spansstarted++;
++		/* enable interrupts */
++
++		if (!polling) {
++			__ap4_check_alarms(wc, span->offset);
++			__ap4_check_sigbits(wc, span->offset);
++		}
++	}
++	spin_unlock_irqrestore(&wc->reglock, flags);
++
++	if (wc->tspans[0]->sync == span->spanno) printk("SPAN %d: Primary Sync Source\n",span->spanno);
++	if (wc->numspans > 1) {
++		if (wc->tspans[1]->sync == span->spanno) printk("SPAN %d: Secondary Sync Source\n",span->spanno);
++	}
++	if (wc->numspans == 4) {
++		if (wc->tspans[2]->sync == span->spanno) printk("SPAN %d: Tertiary Sync Source\n",span->spanno);
++		if (wc->tspans[3]->sync == span->spanno) printk("SPAN %d: Quaternary Sync Source\n",span->spanno);
++	}
++
++#ifdef APEC_SUPPORT
++	if (!apec_enable || !wc->apec_enable)
++		wc->hw_regs->echo_ctrl = 0xe0000000;
++	else if (!alreadyrunning && !wc->apec)
++			if (ap4_apec_init(wc))
++				ap4_apec_release(wc);
++#else
++	wc->hw_regs->echo_ctrl = 0xe0000000;
++#endif
++
++	printk("Completed startup!\n");
++	return 0;
++}
++
++
++static void ap4_receiveprep(struct ap4 *wc)
++{
++	volatile unsigned int *readchunk;
++	unsigned int buffer[32];
++	unsigned char *byte = (unsigned char *) buffer;
++	int i, j, k;
++
++	readchunk = (wc->membase + (AP_DATA_BASE));
++	for (i = 0; i < DAHDI_CHUNKSIZE; i++) {
++		/* Prefetch Card data */
++		for (j = 0; j < 32; ++j) {
++			buffer[j] = readchunk[j];
++		}
++		for (j = 0; j < wc->numspans; j++) {
++			/* Set first timeslot for first channel */
++			if (wc->tspans[j]->spantype == TYPE_E1) {
++				for (k = 0; k < 31; ++k) {
++					/* Skip first timeslot from E1 */
++					wc->tspans[j]->span.chans[k]->readchunk[i] =
++							byte[4*(k+1)+j];
++				}
++			}
++			else {
++				for (k = 0; k < 24; ++k) {
++					wc->tspans[j]->span.chans[k]->readchunk[i] =
++							byte[4*k+j];
++				}
++			}
++		}
++		readchunk += 32;
++	}
++
++	for (i = 0; i < wc->numspans; i++) {
++		if (wc->tspans[i]->span.flags & DAHDI_FLAG_RUNNING) {
++			for (j = 0; j < wc->tspans[i]->span.channels; j++) {
++				/* Echo cancel double buffered data */
++				dahdi_ec_chunk(wc->tspans[i]->span.chans[j],
++				    wc->tspans[i]->span.chans[j]->readchunk,
++					wc->tspans[i]->ec_chunk2[j]);
++				memcpy(wc->tspans[i]->ec_chunk2[j],wc->tspans[i]->ec_chunk1[j],
++					DAHDI_CHUNKSIZE);
++				memcpy(wc->tspans[i]->ec_chunk1[j],
++					wc->tspans[i]->span.chans[j]->writechunk,
++						DAHDI_CHUNKSIZE);
++			}
++			dahdi_receive(&wc->tspans[i]->span);
++		}
++	}
++}
++
++#if (DAHDI_CHUNKSIZE != 8)
++#error Sorry, AP400 driver does not support chunksize != 8
++#endif
++
++#ifdef ENABLE_WORKQUEUES
++static void workq_handlespan(void *data)
++{
++	struct ap4_span *ts = data;
++	struct ap4 *wc = ts->owner;
++
++//	__receive_span(ts);
++//	__transmit_span(ts);
++	atomic_dec(&wc->worklist);
++	atomic_read(&wc->worklist);
++
++}
++#endif
++
++static void ap4_transmitprep(struct ap4 *wc)
++{
++	volatile unsigned int *writechunk;
++	int x,y,z;
++	unsigned int tmp;
++
++	for (y=0;y<wc->numspans;y++) {
++		if (wc->tspans[y]->span.flags & DAHDI_FLAG_RUNNING)
++			dahdi_transmit(&wc->tspans[y]->span);
++	}
++
++	writechunk = (wc->membase+(AP_DATA_BASE));
++	for (x=0;x<DAHDI_CHUNKSIZE;x++) {
++		// Once per chunk
++		for (z=0;z<32;z++) {
++			// All channels
++			tmp = 0;
++			for (y = 0; y < wc->numspans; ++y) {
++				if (wc->tspans[y]->spantype == TYPE_T1 && z < 24)
++					tmp |= (wc->tspans[y]->span.chans[z]->writechunk[x]
++					                           << (8*y));
++				else /* Span Type is E1 */
++					if (z > 0) /* Skip first timeslot */
++						tmp |= (wc->tspans[y]->span.chans[z-1]->writechunk[x]
++									<< (8*y));
++			}
++			writechunk[z] = tmp;
++		}
++		// Advance pointer by 4 TDM frame lengths
++		writechunk += 32;
++	}
++
++}
++
++static void ap4_tdm_loop(struct ap4 *wc)
++{
++	volatile unsigned int *buf_ptr;
++	int x,z;
++	unsigned int tmp;
++
++	buf_ptr = (wc->membase+AP_DATA_BASE);
++
++	for (x=0;x<DAHDI_CHUNKSIZE;x++) {
++		// Once per chunk
++		for (z=0;z<32;z++) {
++			tmp = buf_ptr[z];
++			buf_ptr[z] = tmp;
++		}
++		buf_ptr += 32;
++	}
++}
++
++static void __ap4_check_sigbits(struct ap4 *wc, int span)
++{
++	int a,i,rxs;
++	struct ap4_span *ts = wc->tspans[span];
++	volatile unsigned int *readcas = (wc->membase+AP_CAS_BASE);
++
++//	if (debug & DEBUG_RBS)
++//		printk("Checking sigbits on span %d\n", span + 1);
++
++	if (!(ts->span.flags & DAHDI_FLAG_RUNNING))
++		return;
++	// se span estiver com alarme RED ou BLUE...
++	if( (ts->span.alarms & DAHDI_ALARM_RED) || (ts->span.alarms & DAHDI_ALARM_BLUE) ) {
++		ts->reload_cas = 4;
++	} else if(ts->reload_cas > 0) {
++		// da mais um tempo para framer recuperar e enviar bits de CAS validos
++		ts->reload_cas--;
++	}
++
++	if (ts->spantype == TYPE_E1) {
++		for (i = 0; i < 15; i++) {
++
++			// Se estamos em alarme ou recuperando de um entao mascara os bits para "1101" (bloqueado)
++			if(ts->reload_cas) {
++				a = 0xdd;
++			} else {
++				a = (int) ts->casbuf[i];
++			}
++			ts->casbuf[i] = (unsigned char) (readcas[i] >> (8*span))&0xff;
++
++			/* Get high channel in low bits */
++			rxs = (a & 0xf);
++			if (!(ts->span.chans[i+16]->sig & DAHDI_SIG_CLEAR)) {
++				if (ts->span.chans[i+16]->rxsig != rxs) {
++					ap_debugk("CAS no canal %d mudou de 0x%02x para 0x%02x\n", i+16, ts->span.chans[i+16]->rxsig, rxs);
++					dahdi_rbsbits(ts->span.chans[i+16], rxs);
++				}
++			}
++			rxs = (a >> 4) & 0xf;
++			if (!(ts->span.chans[i]->sig & DAHDI_SIG_CLEAR)) {
++				if (ts->span.chans[i]->rxsig != rxs) {
++					ap_debugk("CAS no canal %d mudou de 0x%02x para 0x%02x\n", i, ts->span.chans[i]->rxsig, rxs);
++					dahdi_rbsbits(ts->span.chans[i], rxs);
++				}
++			}
++		}
++	} else if (ts->span.lineconfig & DAHDI_CONFIG_D4) {
++		for (i = 0; i < 12; i++) {
++			a = (unsigned char) (readcas[i] >> (8*span)) & 0xcc;
++			rxs = a & 0xc;
++			//rxs = (a & 0xc) >> 2;
++			if (!(ts->span.chans[2*i]->sig & DAHDI_SIG_CLEAR)) {
++				if (ts->span.chans[2*i]->rxsig != rxs)
++					dahdi_rbsbits(ts->span.chans[2*i], rxs);
++			}
++			rxs = (a >> 4) & 0xc;
++			//rxs = ((a >> 4) & 0xc) >> 2;
++			if (!(ts->span.chans[2*i+1]->sig & DAHDI_SIG_CLEAR)) {
++				if (ts->span.chans[2*i+1]->rxsig != rxs)
++					dahdi_rbsbits(ts->span.chans[2*i+1], rxs);
++			}
++		}
++	} else { // ESF
++		for (i = 0; i < 12; i++) {
++			a = (unsigned char) (readcas[i] >> (8*span)) & 0xff;
++			rxs = (a & 0xf);
++			if (!(ts->span.chans[2*i+1]->sig & DAHDI_SIG_CLEAR)) {
++				/* XXX Not really reset on every trans! XXX */
++				if (ts->span.chans[2*i+1]->rxsig != rxs) {
++					dahdi_rbsbits(ts->span.chans[2*i+1], rxs);
++				}
++			}
++			rxs = (a >> 4) & 0xf;
++			if (!(ts->span.chans[2*i]->sig & DAHDI_SIG_CLEAR)) {
++				/* XXX Not really reset on every trans! XXX */
++				if (ts->span.chans[2*i]->rxsig != rxs) {
++					dahdi_rbsbits(ts->span.chans[2*i], rxs);
++				}
++			}
++		}
++	}
++}
++
++static void __ap4_check_alarms(struct ap4 *wc, int span)
++{
++	unsigned char c;
++	int alarms;
++	int x,j;
++	struct ap4_span *ts = wc->tspans[span];
++	unsigned int e1_cfg;
++
++	if (!(ts->span.flags & DAHDI_FLAG_RUNNING))
++		return;
++
++	/* Assume no alarms */
++	alarms = DAHDI_ALARM_NONE;
++
++	/* And consider only carrier alarms */
++	ts->span.alarms &= (DAHDI_ALARM_RED | DAHDI_ALARM_BLUE | DAHDI_ALARM_NOTOPEN);
++
++	if (ts->span.lineconfig & DAHDI_CONFIG_NOTOPEN) {
++		for (x=0,j=0;x < ts->span.channels;x++)
++			if ((ts->span.chans[x]->flags & DAHDI_FLAG_OPEN)
++#ifdef CONFIG_DAHDI_NET
++					||
++			    (ts->span.chans[x]->flags & DAHDI_FLAG_NETDEV)
++#endif
++			    )
++				j++;
++		if (!j)
++			alarms |= DAHDI_ALARM_NOTOPEN;
++	}
++
++/* le status e configuracao do E1 */
++	if (wc->tspans[span]->spantype == TYPE_E1) {
++		c = ((*(wc->membase+AP_E1_STATUS_REG))>>(8*span));
++		e1_cfg = ((*(wc->membase+AP_E1_CONFIG_REG))>>(8*span));
++
++		if ((c & AP_E1_LOS_STATUS)||(c & AP_E1_BFAE_STATUS)||(c & AP_E1_AIS_STATUS)) {
++			if (ts->alarmcount >= alarmdebounce)
++				alarms |= DAHDI_ALARM_RED;
++			else
++				ts->alarmcount++;
++		} else
++			ts->alarmcount = 0;
++
++		if ( c & AP_E1_MFAE_STATUS )
++			alarms |= DAHDI_ALARM_BLUE;
++
++		if ( (!(c & AP_E1_CAS_STATUS)) && (e1_cfg & AP_E1_PCM30_CONFIG))
++			alarms |= DAHDI_ALARM_BLUE;
++	} else {
++		c = ((*(wc->membase+AP_E1_STATUS_REG))>>(8*span));
++		if (c & AP_E1_LOS_STATUS) {
++			if (ts->alarmcount >= alarmdebounce)
++				alarms |= DAHDI_ALARM_RED;
++			else
++				ts->alarmcount++;
++		} else
++			ts->alarmcount = 0;
++		c = wc->hw_regs->t1_status >> (8 * span);
++		if (!(c & AP4_T1_FRAME_SYNC))
++			alarms |= DAHDI_ALARM_RED;
++	}
++
++	if (((!ts->span.alarms) && alarms) ||
++	    (ts->span.alarms && (!alarms)))
++		wc->checktiming = 1;
++
++	/* Keep track of recovering */
++	if ((!alarms) && ts->span.alarms)
++		ts->alarmtimer = DAHDI_ALARMSETTLE_TIME;
++	if (ts->alarmtimer)
++		alarms |= DAHDI_ALARM_RECOVER;
++
++
++	// If receiving alarms, go into Yellow alarm state
++	if (alarms && !(ts->spanflags & FLAG_SENDINGYELLOW)) {
++		printk("Setting yellow alarm on span %d\n", span + 1);
++		e1_cfg = *(wc->membase+AP_E1_CONFIG_REG);
++		e1_cfg |= (AP_E1_RAI_CONFIG<<(8*span));
++		*(wc->membase+AP_E1_CONFIG_REG) = e1_cfg;
++		ts->spanflags |= FLAG_SENDINGYELLOW;
++	} else if ((!alarms) && (ts->spanflags & FLAG_SENDINGYELLOW)) {
++		printk("Clearing yellow alarm on span %d\n", span + 1);
++		e1_cfg = *(wc->membase+AP_E1_CONFIG_REG);
++		e1_cfg &= ~(AP_E1_RAI_CONFIG<<(8*span));
++		*(wc->membase+AP_E1_CONFIG_REG) = e1_cfg;
++		ts->spanflags &= ~FLAG_SENDINGYELLOW;
++	}
++
++	// Re-check the timing source when we enter/leave alarm, not withstanding yellow alarm
++	if (c & AP_E1_RAI_STATUS)
++		alarms |= DAHDI_ALARM_YELLOW;
++
++	if (ts->span.mainttimer || ts->span.maintstat)
++		alarms |= DAHDI_ALARM_LOOPBACK;
++
++	ts->span.alarms = alarms;
++	dahdi_alarm_notify(&ts->span);
++}
++
++static void __ap4_do_counters(struct ap4 *wc)
++{
++	int span;
++
++	for (span=0;span<wc->numspans;span++) {
++		struct ap4_span *ts = wc->tspans[span];
++		int docheck=0;
++		if (ts->loopupcnt || ts->loopdowncnt)
++			docheck++;
++		if (ts->alarmtimer) {
++			if (!--ts->alarmtimer) {
++				docheck++;
++				ts->span.alarms &= ~(DAHDI_ALARM_RECOVER);
++			}
++		}
++		if (docheck) {
++			if (!polling)
++				__ap4_check_alarms(wc, span);
++			dahdi_alarm_notify(&ts->span);
++		}
++	}
++}
++
++static inline void __handle_leds(struct ap4 *wc)
++{
++	int x, span_status;
++	#define MAX_BLINKTIMER	0x14
++
++	for (x=0;x<wc->numspans;x++) {
++		struct ap4_span *ts = wc->tspans[x];
++		/* le status do E1 (para avaliar LOS) */
++		span_status = ((*(wc->membase+AP_E1_STATUS_REG))>>(8*x));
++		if (ts->span.flags & DAHDI_FLAG_RUNNING) {
++			if(span_status&AP_E1_LOS_STATUS) {
++				if (wc->blinktimer[x] >= (altab[wc->alarmpos[x]] /*>> 1*/)) {
++					__ap4_set_led(wc, x, AP_ON);
++				}
++				if (wc->blinktimer[x] >= (MAX_BLINKTIMER-1)) {
++					__ap4_set_led(wc, x, AP_OFF);
++				}
++				wc->blinktimer[x] += 1;
++			} else if (ts->span.alarms & (DAHDI_ALARM_RED | DAHDI_ALARM_BLUE)) {
++				if (wc->blinktimer[x] >= (altab[wc->alarmpos[x]] /*>> 1*/)) {
++					__ap4_set_led(wc, x, AP_ON);
++				}
++				if (wc->blinktimer[x] >= (MAX_BLINKTIMER-2)) {
++					__ap4_set_led(wc, x, AP_OFF);
++				}
++				wc->blinktimer[x] += 3;
++			} /*else if (ts->span.alarms & DAHDI_ALARM_YELLOW) {
++				// Yellow Alarm
++				__ap4_set_led(wc, x, AP_ON);
++			} else if (ts->span.mainttimer || ts->span.maintstat) {
++
++				if (wc->blinktimer == (altab[wc->alarmpos] >> 1)) {
++					__ap4_set_led(wc, x, AP_GREEN);
++				}
++				if (wc->blinktimer == 0xf) {
++					__ap4_set_led(wc, x, AP_OFF);
++				}
++
++			} */else {
++				/* No Alarm */
++				__ap4_set_led(wc, x, AP_ON);
++			}
++		}	else
++				__ap4_set_led(wc, x, AP_OFF);
++
++		if (wc->blinktimer[x] > MAX_BLINKTIMER) {
++			wc->blinktimer[x] = 0;
++			wc->alarmpos[x]++;
++			if (wc->alarmpos[x] >= (sizeof(altab) / sizeof(altab[0])))
++				wc->alarmpos[x] = 0;
++		}
++
++	}
++}
++
++
++DAHDI_IRQ_HANDLER(ap4_interrupt)
++{
++	struct ap4 *wc = dev_id;
++	unsigned long flags;
++	int x;
++	static unsigned int val, cfg;
++	unsigned int cnt_irq_misses;
++	static unsigned int cnt_tmp;
++	int ret = 0;
++
++	/* retorna se interrupcao nao foi habilitada ou nao esta ativa */
++	cfg = *(wc->membase + AP_INT_CONTROL_REG);
++	if((cfg & AP_INT_CTL_ENABLE) == 0 || (cfg & AP_INT_CTL_ACTIVE) == 0) {
++		ret = 0;
++		goto out;
++	}
++	/* se chegamos aqui eh porque a interrupcao esta habilitada
++	 * e esta ativa, ou seja, foi gerada pelo nosso cartao.
++	 * Agora damos o ack da interrupcao */
++	val = *(wc->membase + AP_CLEAR_IRQ_REG);
++
++	/* conta interrupcoes perdidas */
++	if (wc->flag_1st_irq > 0) {
++		// nao considera as primeiras passagens pela rotina
++		cnt_irq_misses = (*(wc->membase+AP_CNT_IRQ_REG));
++		// so considera int. para o cartao
++		if(cnt_irq_misses) {
++			wc->flag_1st_irq--;
++			*(wc->membase+AP_CNT_IRQ_REG)=0;
++		}
++		// zera erro de CRC
++		cnt_tmp = (*(wc->membase + AP_CNT_CRC_REG));
++	} else {
++		// neste registro da FPGA temos o numero de interrupcoes que aconteceram
++		// desde o ultimo reset do contador de interrupcoes. O normal eh ler 1.
++		cnt_irq_misses = (*(wc->membase+AP_CNT_IRQ_REG));
++		// Se for zero significa que a interrupcao nao foi gerada pelo nosso cartao
++		if(cnt_irq_misses == 0) {
++			if(debug) printk("Interrupcao gerada mas nao pela FPGA?!\n");
++			ret = 0;
++			goto out;
++		}
++		// reseta o contador
++		*(wc->membase+AP_CNT_IRQ_REG)=0;
++		for(x=0;x<(wc->numspans);x++)
++			wc->ddev->irqmisses += (cnt_irq_misses-1);
++	}
++
++	if (!wc->spansstarted) {
++		/* Not prepped yet! */
++		ret = 0;
++		goto out;
++	}
++
++	wc->intcount++;
++
++#ifdef ENABLE_WORKQUEUES
++	int cpus = num_online_cpus();
++	atomic_set(&wc->worklist, wc->numspans);
++	if (wc->tspans[0]->span.flags & DAHDI_FLAG_RUNNING)
++		ap4_queue_work(wc->workq, &wc->tspans[0]->swork, 0);
++	else
++		atomic_dec(&wc->worklist);
++	if (wc->numspans > 1) {
++		if (wc->tspans[1]->span.flags & DAHDI_FLAG_RUNNING)
++			ap4_queue_work(wc->workq, &wc->tspans[1]->swork, 1 % cpus);
++		else
++			atomic_dec(&wc->worklist);
++	}
++	if (wc->numspans == 4) {
++		if (wc->tspans[2]->span.flags & DAHDI_FLAG_RUNNING)
++			ap4_queue_work(wc->workq, &wc->tspans[2]->swork, 2 % cpus);
++		else
++			atomic_dec(&wc->worklist);
++		if (wc->tspans[3]->span.flags & DAHDI_FLAG_RUNNING)
++			ap4_queue_work(wc->workq, &wc->tspans[3]->swork, 3 % cpus);
++		else
++			atomic_dec(&wc->worklist);
++	}
++#else
++	if (tdm_loop == 1)
++		ap4_tdm_loop(wc);
++	else {
++		ap4_receiveprep(wc);
++		ap4_transmitprep(wc);
++	}
++#endif
++
++	// Estatisticas a cada 128ms
++	if(!(wc->intcount&0x7f)){
++		clock_source = wc->hw_regs->clock_source;
++		cnt_tmp = (*(wc->membase + AP_CNT_CV_REG));
++		for(x=0;x<(wc->numspans);x++)
++			wc->tspans[x]->span.count.bpv += (cnt_tmp>>(8*x))&0xff;
++		cnt_tmp = (*(wc->membase + AP_CNT_CRC_REG));
++		for(x=0;x<(wc->numspans);x++)
++			wc->tspans[x]->span.count.crc4 += (cnt_tmp>>(8*x))&0xff;
++		cnt_tmp = (*(wc->membase + AP_CNT_SLIP_REG));
++		for(x=0;x<(wc->numspans);x++) {
++			if (((cnt_tmp>>(8*x))&0xff) && (!(wc->tspans[x]->span.alarms & DAHDI_ALARM_RED)) ){
++				wc->tspans[x]->slipcount++;
++				if(debug) printk("Slip detected on span %d: slipcount = %d\n", x+1, wc->tspans[x]->slipcount);
++			}
++		}
++	}
++
++	spin_lock_irqsave(&wc->reglock, flags);
++
++	__handle_leds(wc);
++
++	__ap4_do_counters(wc);
++
++	//x = wc->intcount & 15;
++	x = wc->intcount & 7;
++	switch(x) {
++	case 0:
++	case 1:
++	case 2:
++	case 3:
++		__ap4_check_alarms(wc, x);
++		break;
++	case 4:
++	case 5:
++	case 6:
++	case 7:
++		__ap4_check_sigbits(wc, x - 4);
++		break;
++	}
++
++	if (wc->checktiming > 0)
++		__ap4_set_timing_source_auto(wc);
++	spin_unlock_irqrestore(&wc->reglock, flags);
++	/* IRQ was treated */
++	ret = 1;
++out:
++#ifdef AP400_HDLC
++	/* Call AP400_HDLC_CARD IRQ handler before leave */
++	ret |= ap400_intr_handler(irq, wc->hdlc_card);
++#endif
++
++	return IRQ_RETVAL(ret);
++}
++
++
++static int __devinit ap4_launch(struct ap4 *wc)
++{
++	int x;
++	unsigned long flags;
++
++	if (wc->tspans[0]->span.flags & DAHDI_FLAG_REGISTERED)
++		return 0;
++	printk("%s: Launching card: %d\n", wc->variety, wc->order);
++
++	/* Setup serial parameters and system interface */
++	for (x=0;x<4;x++) {
++		//ap4_serial_setup(wc, x);
++		wc->globalconfig = 1;
++	}
++
++	for (x=0; x<wc->numspans; x++)
++		list_add_tail(&wc->tspans[x]->span.device_node,
++				&wc->ddev->spans);
++	if (dahdi_register_device(wc->ddev, &wc->dev->dev)) {
++		printk(KERN_ERR "Unable to register span %s\n", wc->tspans[0]->span.name);
++		return -1; /* FIXME: proper error handling */
++	}
++	wc->checktiming = 1;
++	spin_lock_irqsave(&wc->reglock, flags);
++//	__ap4_set_timing_source(wc,4);
++	spin_unlock_irqrestore(&wc->reglock, flags);
++#ifdef ENABLE_TASKLETS
++	tasklet_init(&wc->ap4_tlet, ap4_tasklet, (unsigned long)wc);
++#endif
++	return 0;
++}
++
++
++static int ap4xx_liu_reset(struct ap4 *wc)
++{
++ 	unsigned int jiffies_hold = jiffies;
++	*(wc->membase+AP_LEDS_REG) |= AP_LIU_RESET_BIT;
++	while(jiffies<=(jiffies_hold+2));
++	*(wc->membase+AP_LEDS_REG) &= ~AP_LIU_RESET_BIT;
++	return 0;
++}
++
++
++static int ap4xx_bus_test(struct ap4 *wc)
++{
++	int tst_result = 0;
++	unsigned int val;
++
++	*(wc->membase+AP_E1_CONFIG_REG) = 0xAAAAAAAA;
++	*wc->membase = 0; // flush
++	val = *(wc->membase+AP_E1_CONFIG_REG);
++	if(val != 0xAAAAAAAA) {
++		printk("Escrito 0xAAAAAAAA, lido 0x%08X!\n", val);
++		tst_result++;
++	}
++	*(wc->membase+AP_E1_CONFIG_REG) = 0x55555555;
++	*wc->membase = 0; // flush
++	val = *(wc->membase+AP_E1_CONFIG_REG);
++	if(val != 0x55555555) {
++		printk("Escrito 0x55555555, lido 0x%08X!\n", val);
++		tst_result++;
++	}
++	*(wc->membase+AP_E1_CONFIG_REG) = 0xFFFFFFFF;
++	*wc->membase = 0; // flush
++	val = *(wc->membase+AP_E1_CONFIG_REG);
++	if(val != 0xFFFFFFFF) {
++		printk("Escrito 0xFFFFFFFF, lido 0x%08X!\n", val);
++		tst_result++;
++	}
++	*(wc->membase+AP_E1_CONFIG_REG) = 0x00000000;
++	*wc->membase = 0xFFFFFFFF; // flush
++	val = *(wc->membase+AP_E1_CONFIG_REG);
++	if(val != 0x00000000) {
++		printk("Escrito 0x00000000, lido 0x%08X!\n", val);
++		tst_result++;
++	}
++	return tst_result;
++}
++
++#ifdef TIMER_DEBUG
++void ap4xx_opt_timeout(unsigned long arg)
++{
++	struct pci_dev *dev = (struct pci_dev *)arg;
++	struct ap4 *wc = pci_get_drvdata(dev);
++
++//	ap_debugk("wc->tspans[0]->span.chans[1].readchunk[1] = 0x%02x\n", wc->tspans[0]->span.chans[0].readchunk[1]);
++//	ap_debugk("e1s_cfg = 0x%08x\n", *(wc->membase+AP_E1_CONFIG_REG));
++//	ap_debugk("e1_status = 0x%08x\n", *(wc->membase + AP_E1_STATUS_REG));
++//	ap_debugk("clk_cfg = 0x%08x\n", *(wc->membase+0x07));
++//	ap_debugk("e1_data = 0x%08x\n", *(wc->membase + (AP_DATA_BASE + 1)));
++//	ap_debugk("cas_data = 0x%08x\n", *(wc->membase + AP_CAS_BASE));
++
++	// dispara timer novamente
++	init_timer(&ap4xx_opt_timer);
++	ap4xx_opt_timer.function = ap4xx_opt_timeout;
++	ap4xx_opt_timer.data = arg;
++	ap4xx_opt_timer.expires = jiffies + (delay/4);
++	add_timer(&ap4xx_opt_timer);
++
++}
++#endif
++
++static inline int ap4_card_detect (struct ap4 *wc) {
++	int i;
++	if ((wc->hw_regs->card_id != AP4XX_CARD_ID) &&
++			(wc->hw_regs->card_id != APE4XX_CARD_ID)) {
++		printk("AP400: Unknown card ID(0x%08X)! Aborting...\n", wc->hw_regs->card_id);
++		return -EPERM;
++	}
++	// Test bus integrity
++	for (i=0; i < 1000; i++) {
++		if (ap4xx_bus_test(wc)) {
++			printk("AP400: Bus integrity test failed! Aborting...\n");
++			return -EIO;
++		}
++	}
++	printk("AP400: Bus integrity OK!\n");
++
++	wc->fpgaver = wc->hw_regs->fpga_ver;
++	wc->numspans = wc->hw_regs->span_num;
++	wc->hwid = ((*(wc->membase+AP_HWCONFIG_REG))&AP_HWID_MASK)>>4;
++
++	if ((wc->hwid == AP_HWID_1E1_RJ && wc->numspans != 1) ||
++			(wc->hwid == AP_HWID_2E1_RJ && wc->numspans != 2) ||
++			(wc->hwid == AP_HWID_4E1_RJ && wc->numspans != 4)) {
++		printk("AP400: Incompatible Hardware ID(0x%02x)! Aborting...\n", wc->hwid);
++		return -EIO;
++	}
++
++	if (wc->hw_regs->card_id == AP4XX_CARD_ID)
++		switch (wc->numspans) {
++		case 1:
++			wc->dt = (struct devtype *) &ap401;
++			break;
++		case 2:
++			wc->dt = (struct devtype *) &ap402;
++			break;
++		case 4:
++			wc->dt = (struct devtype *) &ap404;
++			break;
++		default:
++			printk("AP400: Unsupported spans number(%d)! Aborting...\n",
++					wc->numspans);
++			return -EPERM;
++		}
++	else
++		switch (wc->numspans) {
++		case 1:
++			wc->dt = (struct devtype *) &ape401;
++			break;
++		case 2:
++			wc->dt = (struct devtype *) &ape402;
++			break;
++		case 4:
++			wc->dt = (struct devtype *) &ape404;
++			break;
++		default:
++			printk("APE400: Unsupported spans number(%d)! Aborting...\n",
++					wc->numspans);
++			return -EPERM;
++	}
++
++	wc->variety = wc->dt->desc;
++	printk("Found a %s (firmware version %d.%d) at base address %08lx, remapped to %p\n",
++			wc->variety, wc->fpgaver >> 8, wc->fpgaver & 0xFF,
++			wc->memaddr, wc->membase);
++
++	return 0;
++}
++
++static void __devexit ap4_remove_one(struct pci_dev *pdev);
++
++static int __devinit ap4_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
++{
++	int res;
++	struct ap4 *wc;
++	int x,f;
++	int basesize;
++	static int initd_ifaces=0;
++	// Initialize pointer struct
++	if(!initd_ifaces){
++		memset((void *)cards,0,(sizeof(struct ap4 *))*MAX_AP4_CARDS);
++		initd_ifaces=1;
++	}
++
++	if ((res = pci_enable_device(pdev)) != 0) {
++		goto out;
++	}
++	// Allocate card struct
++	wc = kmalloc(sizeof(struct ap4), GFP_KERNEL);
++	if (wc == NULL) {
++		res = -ENOMEM;
++		goto out;
++	}
++
++	memset(wc, 0x0, sizeof(struct ap4));
++	spin_lock_init(&wc->reglock);
++
++	basesize = DAHDI_MAX_CHUNKSIZE * 32 * 2 * 4;
++
++	// Request PCI regions
++	if ((res = pci_request_regions(pdev, "ap400")) != 0) {
++		printk("AP400: Unable to request regions!\n");
++		goto out;
++	}
++
++	// Remap PCI address
++	wc->memaddr = pci_resource_start(pdev, 2);
++	wc->memlen = pci_resource_len(pdev, 2);
++	wc->membase = ioremap_nocache(wc->memaddr, wc->memlen);
++	if(wc->membase == NULL) {
++		printk("AP400: ioremap failed!\n");
++		res = -EIO;
++		goto out;
++	}
++	wc->hw_regs = (struct ap4_regs *) wc->membase;
++
++	// Detect Card model
++	if ((res = ap4_card_detect(wc)) != 0)
++		goto out;
++
++	ap4xx_liu_reset(wc);
++
++	// This rids of the Double missed interrupt message after loading
++	wc->last0 = 1;
++
++	wc->dev = pdev;
++
++	// 32 channels, Double-buffer, Read/Write, 4 spans
++	wc->writechunk = kmalloc(basesize * 2, GFP_KERNEL);
++	if (!wc->writechunk) {
++		printk("%s: Unable to allocate memory!\n", wc->variety);
++		res = -ENOMEM;
++		goto out;
++	}
++
++	// Read is after the whole write piece (in words)
++	wc->readchunk = wc->writechunk + basesize / 4;
++
++
++	// Initialize Write/Buffers to all blank data
++	memset((void *) wc->writechunk, 0x00, basesize);
++	memset((void *) wc->readchunk, 0xff, basesize);
++
++	/* Keep track of which device we are */
++	pci_set_drvdata(pdev, wc);
++
++	/* inicializa contador de interrupcao */
++	wc->intcount = 0;
++
++	for(x = 0; x < MAX_AP4_CARDS; x++) {
++		if (!cards[x]) break;
++	}
++
++	if (x >= MAX_AP4_CARDS) {
++		printk("No cards[] slot available!!\n");
++		res = -ENOMEM;
++		goto out;
++	}
++
++	wc->num = x;
++	cards[x] = wc;
++
++	/* Allocate pieces we need here, consider 31 channels for E1*/
++	for (x=0;x<4;x++) {
++		wc->tspans[x] = kmalloc(sizeof(struct ap4_span), GFP_KERNEL);
++		if (wc->tspans[x]) {
++			memset(wc->tspans[x], 0, sizeof(struct ap4_span));
++			wc->tspans[x]->spantype = TYPE_E1;
++		} else {
++			res = -ENOMEM;
++			goto out;
++		}
++		for (f = 0; f < 31; f++) {
++			if (!(wc->tspans[x]->chans[f] = kmalloc(sizeof(*wc->tspans[x]->chans[f]), GFP_KERNEL))) {
++				res = -ENOMEM;
++				goto out;
++			}
++			memset(wc->tspans[x]->chans[f], 0, sizeof(*wc->tspans[x]->chans[f]));
++		}
++#ifdef ENABLE_WORKQUEUES
++		INIT_WORK(&wc->tspans[x]->swork, workq_handlespan, wc->tspans[x]);
++#endif
++		wc->tspans[x]->spanflags |= wc->dt->flags;
++	}
++
++	if (request_irq(pdev->irq, ap4_interrupt, IRQF_DISABLED | IRQF_SHARED, "ap400", wc))
++	{
++		printk("%s: Unable to request IRQ %d\n", wc->variety, pdev->irq);
++		res = -EIO;
++		goto out;
++	}
++
++	wc->ddev = dahdi_create_device();
++	wc->ddev->location = kasprintf(GFP_KERNEL, "PCI Bus %02d Slot %02d",
++			wc->dev->bus->number, PCI_SLOT(wc->dev->devfn) + 1);
++	if (!wc->ddev->location)
++		return -ENOMEM; /* FIXME: proper error handling */
++	wc->ddev->manufacturer = "Aligera";
++	wc->ddev->devicetype = wc->variety;
++	wc->ddev->irqmisses = 0;
++	init_spans(wc);
++
++	/* Launch cards as appropriate */
++	x = 0;
++	for(;;) {
++		/* Find a card to activate */
++		f = 0;
++		for (x=0;cards[x];x++) {
++			if (cards[x]->order <= highestorder) {
++				ap4_launch(cards[x]);
++				if (cards[x]->order == highestorder)
++					f = 1;
++			}
++		}
++		/* If we found at least one, increment the highest order and search again, otherwise stop */
++		if (f)
++			highestorder++;
++		else
++			break;
++	}
++
++#ifdef APEC_SUPPORT
++	if (wc->fpgaver >= 0x0400)
++		wc->apec_enable = 1;
++#endif
++
++#ifdef TIMER_DEBUG
++	// dispara timer de debug
++	init_timer(&ap4xx_opt_timer);
++	ap4xx_opt_timer.function = ap4xx_opt_timeout;
++	ap4xx_opt_timer.data = (unsigned long) pdev;
++	ap4xx_opt_timer.expires = jiffies + 100;
++	add_timer(&ap4xx_opt_timer);
++#endif
++
++	/* Initialize HDLC_CARD */
++#ifdef AP400_HDLC
++	u8 __iomem *base_addr[3];
++	unsigned int bar_size[3];
++	int i;
++	base_addr[2] = (void *) wc->membase;
++	bar_size[2] = wc->memlen;
++	for (i = 0; i < 2; i++) {
++		bar_size[i] = (u32) pci_resource_len(pdev, i);
++		base_addr[i] = ioremap_nocache(pci_resource_start(pdev, i),
++								bar_size[i]);
++		if (base_addr[i] == NULL) {
++			printk(KERN_ERR "Memory map failed\n");
++			res = -ENODEV;
++			goto out;
++		}
++	}
++	ap400_card_init(&wc->hdlc_card, base_addr, bar_size);
++	ap400_intr_enable(wc->hdlc_card);
++#endif
++
++	res = 0;
++out:
++	if (res != 0) {
++		ap4_remove_one(pdev);
++	}
++	return res;
++}
++
++static void __devexit ap4_remove_one(struct pci_dev *pdev)
++{
++	struct ap4 *wc = pci_get_drvdata(pdev);
++	int x;
++
++	if (wc) {
++		ap_debugk("desabilita interrupcao!\n");
++		// desabilita interrupcao
++		*(wc->membase + AP_INT_CONTROL_REG) &= ~AP_INT_CTL_ENABLE;
++
++#ifdef APEC_SUPPORT
++		// Stop echo cancellation module
++		ap4_apec_release(wc);
++#endif
++		/* Unregister spans */
++		dahdi_unregister_device(wc->ddev);
++		kfree(wc->ddev->location);
++		dahdi_free_device(wc->ddev);
++#ifdef ENABLE_WORKQUEUES
++		if (wc->workq) {
++			flush_workqueue(wc->workq);
++			destroy_workqueue(wc->workq);
++		}
++#endif
++
++#ifdef TIMER_DEBUG
++		del_timer(&ap4xx_opt_timer);
++#endif
++
++		wc->hw_regs = NULL;
++		if(wc->membase)
++			iounmap((void *)wc->membase);
++
++		/* Immediately free resources */
++		kfree((void *) wc->writechunk);
++
++#ifdef AP400_HDLC
++		/* Remove HDLC Card */
++		ap400_card_remove(wc->hdlc_card);
++		if (wc->hdlc_card->cfg_base_addr)
++			iounmap(wc->hdlc_card->cfg_base_addr);
++		if (wc->hdlc_card->buf_base_addr)
++			iounmap(wc->hdlc_card->buf_base_addr);
++		kfree(wc->hdlc_card);
++#endif
++		free_irq(pdev->irq, wc);
++
++		cards[wc->num] = NULL;
++		for (x=0;x<wc->numspans;x++) {
++			if (wc->tspans[x])
++				kfree(wc->tspans[x]);
++		}
++		kfree(wc);
++	}
++	pci_release_regions(pdev);
++	pci_disable_device(pdev);
++	pci_set_drvdata(pdev, NULL);
++	printk(KERN_INFO "AP400 driver removed\n");
++}
++
++
++static struct pci_device_id ap4_pci_tbl[] __devinitdata =
++{
++	{ PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_AP4XX), },
++	{ 0, }
++};
++
++
++static struct pci_driver ap4_driver = {
++	.name = 	"Unified ap4xx driver",
++	.probe = 	ap4_init_one,
++#ifdef LINUX26
++	.remove =	__devexit_p(ap4_remove_one),
++#else
++	.remove =	ap4_remove_one,
++#endif
++	.id_table = ap4_pci_tbl,
++};
++
++static int __init ap4_init(void)
++{
++	int res;
++	printk("Unified AP4XX PCI Card Driver\n");
++	res = dahdi_pci_module(&ap4_driver);
++	if (res) {
++		return -ENODEV;
++	}
++	return 0;
++}
++
++static void __exit ap4_cleanup(void)
++{
++	printk("Unified AP4XX PCI Card Driver Cleanup\n");
++	pci_unregister_driver(&ap4_driver);
++}
++
++
++MODULE_AUTHOR("Aligera (aligera at aligera.com.br)");
++MODULE_DESCRIPTION("Unified AP4XX PCI Card Driver");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL");
++#endif
++module_param(debug, int, 0600);
++module_param(loopback, int, 0600);
++module_param(noburst, int, 0600);
++module_param(debugslips, int, 0600);
++module_param(polling, int, 0600);
++module_param(timingcable, int, 0600);
++module_param(t1e1override, int, 0600);
++module_param(alarmdebounce, int, 0600);
++module_param(j1mode, int, 0600);
++
++MODULE_DEVICE_TABLE(pci, ap4_pci_tbl);
++
++module_init(ap4_init);
++module_exit(ap4_cleanup);
+diff --git a/drivers/dahdi/ap400/apec.c b/drivers/dahdi/ap400/apec.c
+new file mode 100644
+index 0000000..b43655e
+--- /dev/null
++++ b/drivers/dahdi/ap400/apec.c
+@@ -0,0 +1,390 @@
++/*
++ * AP400 Echo Cancelation Hardware support
++ *
++ * Written by Wagner Gegler <aligera at aligera.com.br>
++ *
++ * Based on previous work written by Mark Spencer <markster at digium.com>
++ *
++ * Copyright (C) 2005-2006 Digium, Inc.
++ *
++ * Mark Spencer <markster at digium.com>
++ *
++ * 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.
++ *
++ */
++
++#include <linux/slab.h>
++#include <linux/vmalloc.h>
++#include <linux/string.h>
++#include <linux/time.h>
++#include <linux/version.h>
++#include <linux/delay.h>
++
++#include "apec.h"
++#include "oct6100api/oct6100_api.h"
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
++#include <linux/config.h>
++#else
++#include <linux/autoconf.h>
++#endif
++
++/* API for Octasic access */
++UINT32 Oct6100UserGetTime(tPOCT6100_GET_TIME f_pTime)
++{
++	/* Why couldn't they just take a timeval like everyone else? */
++	struct timeval tv;
++	unsigned long long total_usecs;
++	unsigned int mask = ~0;
++
++	do_gettimeofday(&tv);
++	total_usecs = (((unsigned long long)(tv.tv_sec)) * 1000000) +
++				  (((unsigned long long)(tv.tv_usec)));
++	f_pTime->aulWallTimeUs[0] = (total_usecs & mask);
++	f_pTime->aulWallTimeUs[1] = (total_usecs >> 32);
++	return cOCT6100_ERR_OK;
++}
++
++UINT32 Oct6100UserMemSet(PVOID f_pAddress, UINT32 f_ulPattern, UINT32 f_ulLength)
++{
++	memset(f_pAddress, f_ulPattern, f_ulLength);
++	return cOCT6100_ERR_OK;
++}
++
++UINT32 Oct6100UserMemCopy(PVOID f_pDestination, const void *f_pSource, UINT32 f_ulLength)
++{
++	memcpy(f_pDestination, f_pSource, f_ulLength);
++	return cOCT6100_ERR_OK;
++}
++
++UINT32 Oct6100UserCreateSerializeObject(tPOCT6100_CREATE_SERIALIZE_OBJECT f_pCreate)
++{
++	return cOCT6100_ERR_OK;
++}
++
++UINT32 Oct6100UserDestroySerializeObject(tPOCT6100_DESTROY_SERIALIZE_OBJECT f_pDestroy)
++{
++#ifdef OCTASIC_DEBUG
++	printk("I should never be called! (destroy serialize object)\n");
++#endif
++	return cOCT6100_ERR_OK;
++}
++
++UINT32 Oct6100UserSeizeSerializeObject(tPOCT6100_SEIZE_SERIALIZE_OBJECT f_pSeize)
++{
++	/* Not needed */
++	return cOCT6100_ERR_OK;
++}
++
++UINT32 Oct6100UserReleaseSerializeObject(tPOCT6100_RELEASE_SERIALIZE_OBJECT f_pRelease)
++{
++	/* Not needed */
++	return cOCT6100_ERR_OK;
++}
++
++UINT32 Oct6100UserDriverWriteApi(tPOCT6100_WRITE_PARAMS f_pWriteParams)
++{
++	oct_write(f_pWriteParams->pProcessContext, f_pWriteParams->ulWriteAddress, f_pWriteParams->usWriteData);
++	return cOCT6100_ERR_OK;
++}
++
++UINT32 Oct6100UserDriverWriteSmearApi(tPOCT6100_WRITE_SMEAR_PARAMS f_pSmearParams)
++{
++	unsigned int x;
++	for (x=0;x<f_pSmearParams->ulWriteLength;x++) {
++		oct_write(f_pSmearParams->pProcessContext, f_pSmearParams->ulWriteAddress + (x << 1), f_pSmearParams->usWriteData);
++	}
++	return cOCT6100_ERR_OK;
++}
++
++UINT32 Oct6100UserDriverWriteBurstApi(tPOCT6100_WRITE_BURST_PARAMS f_pBurstParams)
++{
++	unsigned int x;
++	for (x=0;x<f_pBurstParams->ulWriteLength;x++) {
++		oct_write(f_pBurstParams->pProcessContext, f_pBurstParams->ulWriteAddress + (x << 1), f_pBurstParams->pusWriteData[x]);
++	}
++	return cOCT6100_ERR_OK;
++}
++
++UINT32 Oct6100UserDriverReadApi(tPOCT6100_READ_PARAMS f_pReadParams)
++{
++	*(f_pReadParams->pusReadData) = oct_read(f_pReadParams->pProcessContext, f_pReadParams->ulReadAddress);
++	return cOCT6100_ERR_OK;
++}
++
++UINT32 Oct6100UserDriverReadBurstApi(tPOCT6100_READ_BURST_PARAMS f_pBurstParams)
++{
++	unsigned int x;
++	for (x=0;x<f_pBurstParams->ulReadLength;x++) {
++		f_pBurstParams->pusReadData[x] = oct_read(f_pBurstParams->pProcessContext, f_pBurstParams->ulReadAddress + (x << 1));
++	}
++	return cOCT6100_ERR_OK;
++}
++
++#if 0
++#define cOCT6100_ECHO_OP_MODE_DIGITAL cOCT6100_ECHO_OP_MODE_HT_FREEZE
++#else
++#define cOCT6100_ECHO_OP_MODE_DIGITAL cOCT6100_ECHO_OP_MODE_POWER_DOWN
++#endif
++
++struct apec_s {
++	tPOCT6100_INSTANCE_API pApiInstance;
++	UINT32 aulEchoChanHndl[128];
++	int chanflags[128];
++	int ecmode[128];
++	int numchans;
++};
++
++#define FLAG_DTMF	 (1 << 0)
++#define FLAG_MUTE	 (1 << 1)
++#define FLAG_ECHO	 (1 << 2)
++
++static void apec_setecmode(struct apec_s *apec, int channel, int mode)
++{
++	tOCT6100_CHANNEL_MODIFY *modify;
++	UINT32 ulResult;
++
++	if (apec->ecmode[channel] == mode)
++		return;
++	modify = kmalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_ATOMIC);
++	if (!modify) {
++		printk("APEC: Unable to allocate memory for setec!\n");
++		return;
++	}
++	Oct6100ChannelModifyDef(modify);
++	modify->ulEchoOperationMode = mode;
++	modify->ulChannelHndl = apec->aulEchoChanHndl[channel];
++	ulResult = Oct6100ChannelModify(apec->pApiInstance, modify);
++	if (ulResult != GENERIC_OK) {
++		printk("Failed to apply echo can changes on channel %d!\n", channel);
++	} else {
++#ifdef OCTASIC_DEBUG
++		printk("Echo can on channel %d set to %d\n", channel, mode);
++#endif
++		apec->ecmode[channel] = mode;
++	}
++	kfree(modify);
++}
++
++void apec_setec(struct apec_s *apec, int channel, int eclen)
++{
++	if (eclen) {
++		apec->chanflags[channel] |= FLAG_ECHO;
++		apec_setecmode(apec, channel, cOCT6100_ECHO_OP_MODE_HT_RESET);
++		apec_setecmode(apec, channel, cOCT6100_ECHO_OP_MODE_NORMAL);
++	} else {
++		apec->chanflags[channel] &= ~FLAG_ECHO;
++		if (apec->chanflags[channel] & (FLAG_DTMF | FLAG_MUTE)) {
++			apec_setecmode(apec, channel, cOCT6100_ECHO_OP_MODE_HT_RESET);
++			apec_setecmode(apec, channel, cOCT6100_ECHO_OP_MODE_HT_FREEZE);
++		} else
++			apec_setecmode(apec, channel, cOCT6100_ECHO_OP_MODE_DIGITAL);
++	}
++	printk("APEC: Setting EC on channel %d to %d\n", channel, eclen);
++}
++
++int apec_checkirq(struct apec_s *apec)
++{
++	tOCT6100_INTERRUPT_FLAGS InterruptFlags;
++
++	Oct6100InterruptServiceRoutineDef(&InterruptFlags);
++	Oct6100InterruptServiceRoutine(apec->pApiInstance, &InterruptFlags);
++
++	return InterruptFlags.fToneEventsPending ? 1 : 0;
++}
++
++unsigned int apec_capacity_get(void *wc)
++{
++	UINT32 ulResult;
++
++	tOCT6100_API_GET_CAPACITY_PINS CapacityPins;
++
++	Oct6100ApiGetCapacityPinsDef(&CapacityPins);
++	CapacityPins.pProcessContext = wc;
++	CapacityPins.ulMemoryType = cOCT6100_MEM_TYPE_DDR;
++	CapacityPins.fEnableMemClkOut = TRUE;
++	CapacityPins.ulMemClkFreq = cOCT6100_MCLK_FREQ_133_MHZ;
++
++	ulResult = Oct6100ApiGetCapacityPins(&CapacityPins);
++	if (ulResult != cOCT6100_ERR_OK) {
++		printk("Failed to get chip capacity, code %08x!\n", ulResult);
++		return 0;
++	}
++	return CapacityPins.ulCapacityValue;
++}
++
++struct apec_s *apec_init(void *wc, int *isalaw, int numspans, const struct firmware *firmware)
++{
++	tOCT6100_CHIP_OPEN *ChipOpen;
++	tOCT6100_GET_INSTANCE_SIZE InstanceSize;
++	tOCT6100_CHANNEL_OPEN *ChannelOpen;
++	UINT32 ulResult;
++	struct apec_s *apec;
++	int x, law;
++#ifdef CONFIG_4KSTACKS
++	unsigned long flags;
++#endif
++
++	if (!(apec = kmalloc(sizeof(struct apec_s), GFP_KERNEL)))
++		return NULL;
++
++	memset(apec, 0, sizeof(struct apec_s));
++
++	if (!(ChipOpen = kmalloc(sizeof(tOCT6100_CHIP_OPEN), GFP_KERNEL))) {
++		kfree(apec);
++		return NULL;
++	}
++
++	memset(ChipOpen, 0, sizeof(tOCT6100_CHIP_OPEN));
++
++	if (!(ChannelOpen = kmalloc(sizeof(tOCT6100_CHANNEL_OPEN), GFP_KERNEL))) {
++		kfree(apec);
++		kfree(ChipOpen);
++		return NULL;
++	}
++
++	memset(ChannelOpen, 0, sizeof(tOCT6100_CHANNEL_OPEN));
++
++	for (x=0;x<128;x++)
++		apec->ecmode[x] = -1;
++
++	apec->numchans = numspans * 32;
++	printk("APEC: echo cancellation for %d channels\n", apec->numchans);
++
++	Oct6100ChipOpenDef(ChipOpen);
++
++	/* Setup Chip Open Parameters */
++	ChipOpen->ulUpclkFreq = cOCT6100_UPCLK_FREQ_33_33_MHZ;
++	Oct6100GetInstanceSizeDef(&InstanceSize);
++
++	ChipOpen->pProcessContext = wc;
++
++	ChipOpen->pbyImageFile = firmware->data;
++	ChipOpen->ulImageSize = firmware->size;
++
++	ChipOpen->fEnableMemClkOut = TRUE;
++	ChipOpen->ulMemClkFreq = cOCT6100_MCLK_FREQ_133_MHZ;
++	ChipOpen->ulMaxChannels = apec->numchans;
++	ChipOpen->ulMemoryType = cOCT6100_MEM_TYPE_DDR;
++	ChipOpen->ulMemoryChipSize = cOCT6100_MEMORY_CHIP_SIZE_32MB;
++	ChipOpen->ulNumMemoryChips = 1;
++	ChipOpen->ulMaxTdmStreams = 4;
++	ChipOpen->aulTdmStreamFreqs[0] = cOCT6100_TDM_STREAM_FREQ_8MHZ;
++	ChipOpen->ulTdmSampling = cOCT6100_TDM_SAMPLE_AT_FALLING_EDGE;
++#if 0
++	ChipOpen->fEnableAcousticEcho = TRUE;
++#endif
++
++	ulResult = Oct6100GetInstanceSize(ChipOpen, &InstanceSize);
++	if (ulResult != cOCT6100_ERR_OK) {
++		printk("Failed to get instance size, code %08x!\n", ulResult);
++		kfree(apec);
++		return NULL;
++	}
++
++
++	apec->pApiInstance = vmalloc(InstanceSize.ulApiInstanceSize);
++	if (!apec->pApiInstance) {
++		printk("Out of memory (can't allocate %d bytes)!\n", InstanceSize.ulApiInstanceSize);
++		kfree(apec);
++		kfree(ChipOpen);
++		kfree(ChannelOpen);
++		return NULL;
++	}
++
++	/* I don't know what to curse more in this comment, the problems caused by
++	 * the 4K kernel stack limit change or the octasic API for being so darn
++	 * stack unfriendly.  Stupid, stupid, stupid.  So we disable IRQs so we
++	 * don't run the risk of overflowing the stack while we initialize the
++	 * octasic. */
++#ifdef CONFIG_4KSTACKS
++	local_irq_save(flags);
++#endif
++	ulResult = Oct6100ChipOpen(apec->pApiInstance, ChipOpen);
++	if (ulResult != cOCT6100_ERR_OK) {
++		printk("Failed to open chip, code %08x!\n", ulResult);
++#ifdef CONFIG_4KSTACKS
++		local_irq_restore(flags);
++#endif
++		kfree(apec);
++		kfree(ChipOpen);
++		kfree(ChannelOpen);
++		return NULL;
++	}
++	for (x=0; x < 128; x++) {
++		/* execute this loop always on 4 span cards but
++		*  on 2 span cards only execute for the channels related to our spans */
++		if ((x & 0x3) < numspans) {
++			/* span timeslots are interleaved 12341234...
++		 	*  therefore, the lower 2 bits tell us which span this
++			*  timeslot/channel
++		 	*/
++			if (isalaw[x & 0x03])
++				law = cOCT6100_PCM_A_LAW;
++			else
++				law = cOCT6100_PCM_U_LAW;
++			Oct6100ChannelOpenDef(ChannelOpen);
++			ChannelOpen->pulChannelHndl = &apec->aulEchoChanHndl[x];
++			ChannelOpen->ulUserChanId = x;
++			ChannelOpen->TdmConfig.ulRinPcmLaw = law;
++			ChannelOpen->TdmConfig.ulRinStream = 0;
++			ChannelOpen->TdmConfig.ulRinTimeslot = x;
++			ChannelOpen->TdmConfig.ulSinPcmLaw = law;
++			ChannelOpen->TdmConfig.ulSinStream = 1;
++			ChannelOpen->TdmConfig.ulSinTimeslot = x;
++			ChannelOpen->TdmConfig.ulSoutPcmLaw = law;
++			ChannelOpen->TdmConfig.ulSoutStream = 2;
++			ChannelOpen->TdmConfig.ulSoutTimeslot = x;
++			ChannelOpen->TdmConfig.ulRoutPcmLaw = law;
++			ChannelOpen->TdmConfig.ulRoutStream = 3;
++			ChannelOpen->TdmConfig.ulRoutTimeslot = x;
++			ChannelOpen->VqeConfig.fEnableNlp = TRUE;
++			ChannelOpen->VqeConfig.fRinDcOffsetRemoval = TRUE;
++			ChannelOpen->VqeConfig.fSinDcOffsetRemoval = TRUE;
++
++			ChannelOpen->fEnableToneDisabler = TRUE;
++			ChannelOpen->ulEchoOperationMode = cOCT6100_ECHO_OP_MODE_DIGITAL;
++
++			ulResult = Oct6100ChannelOpen(apec->pApiInstance, ChannelOpen);
++			if (ulResult != GENERIC_OK) {
++				printk("Failed to open channel %d!\n", x);
++			}
++		}
++	}
++
++#ifdef CONFIG_4KSTACKS
++	local_irq_restore(flags);
++#endif
++	kfree(ChipOpen);
++	kfree(ChannelOpen);
++	return apec;
++}
++
++void apec_release(struct apec_s *apec)
++{
++	UINT32 ulResult;
++	tOCT6100_CHIP_CLOSE ChipClose;
++
++	Oct6100ChipCloseDef(&ChipClose);
++	ulResult = Oct6100ChipClose(apec->pApiInstance, &ChipClose);
++	if (ulResult != cOCT6100_ERR_OK) {
++		printk("Failed to close chip, code %08x!\n", ulResult);
++	}
++	vfree(apec->pApiInstance);
++	kfree(apec);
++	printk(KERN_INFO "APEC: Releasing...\n");
++}
+diff --git a/drivers/dahdi/ap400/apec.h b/drivers/dahdi/ap400/apec.h
+new file mode 100644
+index 0000000..483b182
+--- /dev/null
++++ b/drivers/dahdi/ap400/apec.h
+@@ -0,0 +1,48 @@
++/*
++ * AP400 Echo Cancelation Hardware support
++ *
++ * Written by Wagner Gegler <aligera at aligera.com.br>
++ * 
++ * Based on previous work written by Mark Spencer <markster at digium.com>
++ * 
++ * Copyright (C) 2005-2006 Digium, Inc.
++ *
++ * Mark Spencer <markster at digium.com>
++ *
++ * 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.
++ *
++ */
++
++#ifndef _APEC_H_
++#define _APEC_H_
++
++#include <linux/firmware.h>
++
++struct apec_s;
++
++/* From AP400 */
++unsigned int oct_read(void *card, unsigned int addr);
++void oct_write(void *card, unsigned int addr, unsigned int data);
++
++/* From APEC */
++struct apec_s *apec_init(void *wc, int *isalaw, int numspans, const struct firmware *firmware);
++unsigned int apec_capacity_get(void *wc);
++void apec_setec(struct apec_s *instance, int channel, int eclen);
++int apec_checkirq(struct apec_s *apec);
++void apec_release(struct apec_s *instance);
++
++#endif /*_APEC_H_*/
+diff --git a/drivers/dahdi/opvxa1200/Kbuild b/drivers/dahdi/opvxa1200/Kbuild
+new file mode 100644
+index 0000000..8f90819
+--- /dev/null
++++ b/drivers/dahdi/opvxa1200/Kbuild
+@@ -0,0 +1,19 @@
++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OPVXA1200) += opvxa1200.o
++
++EXTRA_CFLAGS += -I$(src)/.. -Wno-undef
++
++opvxa1200-objs := base.o
++
++DAHDI_KERNEL_H_NAME:=kernel.h
++DAHDI_KERNEL_H_PATH:=$(DAHDI_INCLUDE)/dahdi/$(DAHDI_KERNEL_H_NAME)
++ifneq ($(DAHDI_KERNEL_H_PATH),)
++        DAHDI_SPAN_MODULE:=$(shell if grep -C 5 "struct dahdi_span {" $(DAHDI_KERNEL_H_PATH) | grep -q "struct module \*owner"; then echo "yes"; else echo "no"; fi)
++        DAHDI_SPAN_OPS:=$(shell if grep -q "struct dahdi_span_ops {" $(DAHDI_KERNEL_H_PATH); then echo "yes"; else echo "no"; fi)
++        ifeq ($(DAHDI_SPAN_MODULE),yes)
++                EXTRA_CFLAGS+=-DDAHDI_SPAN_MODULE
++        else
++                ifeq ($(DAHDI_SPAN_OPS),yes)
++                        EXTRA_CFLAGS+=-DDAHDI_SPAN_OPS
++                endif
++        endif
++endif
+diff --git a/drivers/dahdi/opvxa1200/Makefile b/drivers/dahdi/opvxa1200/Makefile
+new file mode 100644
+index 0000000..baaab35
+--- /dev/null
++++ b/drivers/dahdi/opvxa1200/Makefile
+@@ -0,0 +1,8 @@
++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 --git a/drivers/dahdi/opvxa1200/base.c b/drivers/dahdi/opvxa1200/base.c
+new file mode 100644
+index 0000000..25ce3c4
+--- /dev/null
++++ b/drivers/dahdi/opvxa1200/base.c
+@@ -0,0 +1,3065 @@
++/*
++ * OpenVox A1200P FXS/FXO Interface Driver for DAHDI Telephony interface
++ *
++ * Written by MiaoLin<miaolin at openvox.cn>
++ *
++ * Copyright (C) 2005-2010 OpenVox Communication Co. Ltd,
++ *
++ * 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 <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/pci.h>
++#include <linux/interrupt.h>
++#include <linux/moduleparam.h>
++#include <asm/io.h>
++#include <linux/sched.h>
++#include "proslic.h"
++   
++/* MiaoLin debug start */
++#include <linux/string.h>
++#include <asm/uaccess.h> 	/* get_fs(), set_fs(), KERNEL_DS */
++#include <linux/file.h> 	/* 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 <dahdi/kernel.h>
++#include <dahdi/wctdm_user.h>
++
++#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;
++	struct dahdi_device *ddev;
++	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;
++/* times 4, because must be a multiple of 4ms: */
++static int dialdebounce = 8 * 8;
++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<<card);
++	wc->ledstate |= (onoff<<card);
++	c = (inb(wc->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<<i))
++			if(wc->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;y<DAHDI_CHUNKSIZE;y++) {
++#ifdef __BIG_ENDIAN
++	// operation pending...
++#else
++		for (x=0;x<(MAX_NUM_CARDS+NUM_FLAG);x++) {
++			pos = y * (MAX_NUM_CARDS+NUM_FLAG) + ((x + chan_offset + MAX_NUM_CARDS+NUM_FLAG - WC_OFFSET)&0x0f);
++			if(x<wc->max_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;x<DAHDI_CHUNKSIZE;x++) {
++		/* Look for pegging to indicate ringing */
++		sample = DAHDI_XLAW(wc->chans[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;x<DAHDI_CHUNKSIZE;x++) {
++#ifdef __BIG_ENDIAN
++	// operation pending...
++#else
++		for (y=0;y<wc->max_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; x<wc->max_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;x<wc->max_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;x<sizeof(indirect_regs)/sizeof(indirect_regs[0]);x++) {
++		if (indirect_regs[x].address == address) {
++			address = indirect_regs[x].altaddr;
++			break;
++		}
++	}
++	return address;
++}
++
++static int wctdm_proslic_setreg_indirect(struct wctdm *wc, int card, unsigned char address, unsigned short data)
++{
++	unsigned long flags;
++	int res = -1;
++	/* 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, 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; i<sizeof(indirect_regs) / sizeof(indirect_regs[0]); i++)
++	{
++		if(wctdm_proslic_setreg_indirect(wc, card, indirect_regs[i].address,indirect_regs[i].initial))
++			return -1;
++	}
++
++	return 0;
++}
++
++static int wctdm_proslic_verify_indirect_regs(struct wctdm *wc, int card)
++{ 
++	int passed = 1;
++	unsigned short i, initial;
++	int j;
++
++	for (i=0; i<sizeof(indirect_regs) / sizeof(indirect_regs[0]); i++) 
++	{
++		if((j = wctdm_proslic_getreg_indirect(wc, card, (unsigned char) indirect_regs[i].address)) < 0) {
++			printk(KERN_NOTICE "Failed to read indirect register %d\n", i);
++			return -1;
++		}
++		initial= indirect_regs[i].initial;
++
++		if ( j != initial && (!(wc->flags[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 = dialdebounce * 4;
++
++#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;x<wc->max_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; y<wc->max_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;x<wc->max_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;x<NUM_CAL_REGS;x++)
++			wc->mod[card].fxs.calregs.vals[x] = wctdm_getreg(wc, card, 96 + x);
++#endif
++
++	} else {
++		/* Restore calibration registers */
++		for (x=0;x<NUM_CAL_REGS;x++)
++			wctdm_setreg(wc, card, 96 + x, wc->mod[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;x<NUM_INDIRECT_REGS;x++)
++				regs.indirect[x] = wctdm_proslic_getreg_indirect(wc, chan->chanpos -1, x);
++			for (x=0;x<NUM_REGS;x++)
++				regs.direct[x] = wctdm_getreg(wc, chan->chanpos - 1, x);
++		} else {
++			memset(&regs, 0, sizeof(regs));
++			for (x=0;x<NUM_FXO_REGS;x++)
++				regs.direct[x] = wctdm_getreg(wc, chan->chanpos - 1, x);
++		}
++		if (copy_to_user((__user void *)data, &regs, sizeof(regs)))
++			return -EFAULT;
++		break;
++	case WCTDM_SET_REG:
++		if (copy_from_user(&regop, (__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 inline struct wctdm *wctdm_from_span(struct dahdi_span *span)
++{
++	return container_of(span, struct wctdm, span);
++}
++
++static int wctdm_watchdog(struct dahdi_span *span, int event)
++{
++	printk(KERN_INFO "opvxa1200: Restarting DMA\n");
++	wctdm_restart_dma(wctdm_from_span(span));
++	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;
++}
++
++#ifdef DAHDI_SPAN_OPS
++static const struct dahdi_span_ops wctdm_span_ops = {
++	.owner = THIS_MODULE,
++	.hooksig = wctdm_hooksig,
++	.open = wctdm_open,
++	.close = wctdm_close,
++	.ioctl = wctdm_ioctl,
++	.watchdog = wctdm_watchdog,
++};
++#endif
++
++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);
++	wc->ddev->location = kasprintf(GFP_KERNEL,
++				      "PCI Bus %02d Slot %02d",
++				      wc->dev->bus->number,
++				      PCI_SLOT(wc->dev->devfn) + 1);
++	if (!wc->ddev->location) {
++		dahdi_free_device(wc->ddev);
++		wc->ddev = NULL;
++		return -ENOMEM;
++	}
++	wc->ddev->manufacturer = "OpenVox";
++	wc->ddev->devicetype = wc->variety;
++	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;
++	}
++
++#ifdef DAHDI_SPAN_MODULE	
++		wc->span.owner = THIS_MODULE;
++#endif
++
++#ifdef DAHDI_SPAN_OPS
++	wc->span.ops = &wctdm_span_ops;
++#else
++		wc->span.hooksig = wctdm_hooksig,
++		wc->span.watchdog = wctdm_watchdog,
++		wc->span.open = wctdm_open;
++		wc->span.close  = wctdm_close;
++		wc->span.ioctl = wctdm_ioctl;
++		wc->span.pvt = wc;
++#endif
++	wc->span.chans = wc->chans;
++	wc->span.channels = wc->max_cards;	/*MAX_NUM_CARDS;*/
++	wc->span.flags = DAHDI_FLAG_RBS;
++	wc->span.ops = &wctdm_span_ops;
++
++	list_add_tail(&wc->span.device_node, &wc->ddev->spans);
++	if (dahdi_register_device(wc->ddev, &wc->dev->dev)) {
++		printk(KERN_NOTICE "Unable to register device %s with DAHDI\n",
++				wc->span.name);
++		kfree(wc->ddev->location);
++		dahdi_free_device(wc->ddev);
++		wc->ddev = NULL;
++		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;x++)
++		if (!ifaces[x]) break;
++	if (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;y<MAX_NUM_CARDS;y++)
++				wc->flags[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_device(wc->ddev);
++				kfree(wc->ddev->location);
++				dahdi_free_device(wc->ddev);
++				kfree(wc);
++				return -EIO;
++
++			}
++
++#ifdef TEST_LOG_INCOME_VOICE
++			for(x=0; x<MAX_NUM_CARDS+NUM_FLAG; x++)
++			{
++				wc->voc_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_device(wc->ddev);
++	kfree(wc->ddev->location);
++	dahdi_free_device(wc->ddev);
++	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; i<MAX_NUM_CARDS + NUM_FLAG; i++)
++	{
++		sprintf(fname, "//usr//%d.pcm", i); 
++		f = filp_open(fname, O_RDWR|O_CREAT, 00);
++	
++		if (!f || !f->f_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 "Free an 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<sizeof(fxo_modes) / sizeof(fxo_modes[0]); x++)
++			printk(KERN_INFO "  %s\n", fxo_modes[x].name);
++		printk(KERN_INFO "Note this option is CASE SENSITIVE!\n");
++		return -ENODEV;
++	}
++	if (!strcmp(fxo_modes[_opermode].name, "AUSTRALIA")) {
++		boostringer=1;
++		fxshonormode=1;
++}
++	if (battdebounce == 0) {
++		battdebounce = fxo_modes[_opermode].battdebounce;
++	}
++	if (battalarm == 0) {
++		battalarm = fxo_modes[_opermode].battalarm;
++	}
++	if (battthresh == 0) {
++		battthresh = fxo_modes[_opermode].battthresh;
++	}
++
++	res = dahdi_pci_module(&wctdm_driver);
++	if (res)
++		return -ENODEV;
++	return 0;
++}
++
++static void __exit wctdm_cleanup(void)
++{
++	pci_unregister_driver(&wctdm_driver);
++}
++
++module_param(debug, int, 0600);
++module_param(loopcurrent, int, 0600);
++module_param(reversepolarity, int, 0600);
++module_param(robust, int, 0600);
++module_param(opermode, charp, 0600);
++module_param(timingonly, int, 0600);
++module_param(lowpower, int, 0600);
++module_param(boostringer, int, 0600);
++module_param(fastringer, int, 0600);
++module_param(fxshonormode, int, 0600);
++module_param(battdebounce, uint, 0600);
++module_param(battthresh, uint, 0600);
++module_param(battalarm, 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);
++module_param(fxotxgain, int, 0600);
++module_param(fxorxgain, int, 0600);
++module_param(fxstxgain, int, 0600);
++module_param(fxsrxgain, int, 0600);
++module_param(spibyhw, int, 0600);
++module_param(usememio, int, 0600);
++module_param(cidbeforering, int, 0600);
++module_param(cidbuflen, int, 0600);
++module_param(cidtimeout, int, 0600);
++module_param(fxofullscale, int, 0600);
++module_param(fixedtimepolarity, int, 0600);
++
++MODULE_DESCRIPTION("OpenVox A1200 Driver");
++MODULE_AUTHOR("MiaoLin <miaolin at openvox.com.cn>");
++MODULE_LICENSE("GPL v2");
++
++module_init(wctdm_init);
++module_exit(wctdm_cleanup);
+diff --git a/drivers/dahdi/opvxd115/Kbuild b/drivers/dahdi/opvxd115/Kbuild
+new file mode 100644
+index 0000000..474997d
+--- /dev/null
++++ b/drivers/dahdi/opvxd115/Kbuild
+@@ -0,0 +1,32 @@
++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OPVXD115) += opvxd115.o
++
++FIRM_DIR	:= ../firmware
++
++EXTRA_CFLAGS += -I$(src)/.. $(shell $(src)/../oct612x/octasic-helper cflags $(src)/../oct612x) -Wno-undef
++
++ifeq ($(HOTPLUG_FIRMWARE),yes)
++  EXTRA_CFLAGS+=-DHOTPLUG_FIRMWARE
++endif
++
++opvxd115-objs := base.o vpm450m.o $(shell $(src)/../oct612x/octasic-helper objects ../oct612x)
++
++DAHDI_KERNEL_H_NAME:=kernel.h
++DAHDI_KERNEL_H_PATH:=$(DAHDI_INCLUDE)/dahdi/$(DAHDI_KERNEL_H_NAME)
++ifneq ($(DAHDI_KERNEL_H_PATH),)
++        DAHDI_SPAN_MODULE:=$(shell if grep -C 5 "struct dahdi_span {" $(DAHDI_KERNEL_H_PATH) | grep -q "struct module \*owner"; then echo "yes"; else echo "no"; fi)
++        DAHDI_SPAN_OPS:=$(shell if grep -q "struct dahdi_span_ops {" $(DAHDI_KERNEL_H_PATH); then echo "yes"; else echo "no"; fi)
++        ifeq ($(DAHDI_SPAN_MODULE),yes)
++                EXTRA_CFLAGS+=-DDAHDI_SPAN_MODULE
++        else
++                ifeq ($(DAHDI_SPAN_OPS),yes)
++                        EXTRA_CFLAGS+=-DDAHDI_SPAN_OPS
++                endif
++        endif
++endif
++
++ifneq ($(HOTPLUG_FIRMWARE),yes)
++opvxd115-objs += $(FIRM_DIR)/dahdi-fw-oct6114-032.o
++endif
++
++$(obj)/$(FIRM_DIR)/dahdi-fw-oct6114-032.o: $(obj)/base.o
++	$(MAKE) -C $(obj)/$(FIRM_DIR) dahdi-fw-oct6114-032.o
+diff --git a/drivers/dahdi/opvxd115/Makefile b/drivers/dahdi/opvxd115/Makefile
+new file mode 100644
+index 0000000..baaab35
+--- /dev/null
++++ b/drivers/dahdi/opvxd115/Makefile
+@@ -0,0 +1,8 @@
++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 --git a/drivers/dahdi/opvxd115/base.c b/drivers/dahdi/opvxd115/base.c
+new file mode 100644
+index 0000000..8ef1796
+--- /dev/null
++++ b/drivers/dahdi/opvxd115/base.c
+@@ -0,0 +1,4908 @@
++/*
++ * OpenVox D115P/D115E PCI/PCI-E Driver version 0.1 01/07/2011
++ *
++ * Written by Mark Spencer <markster at digium.com>
++ * Modify from wct4xxp module by mark.liu at openvox.cn
++
++ * Based on previous works, designs, and archetectures conceived and
++ * written by Jim Dixon <jim at lambdatel.com>.
++ *
++ * Copyright (C) 2001 Jim Dixon / Zapata Telephony.
++ * Copyright (C) 2001-2010, Digium, Inc.
++ *
++ * All rights reserved.
++ *
++ */
++
++/*
++ * See http://www.asterisk.org for more information about
++ * the Asterisk project. Please do not directly contact
++ * any of the maintainers of this project for assistance;
++ * the project provides a web site, mailing lists and IRC
++ * channels for your use.
++ *
++ * This program is free software, distributed under the terms of
++ * the GNU General Public License Version 2 as published by the
++ * Free Software Foundation. See the LICENSE file included with
++ * this program for more details.
++ */
++
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/module.h>
++#include <linux/pci.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/spinlock.h>
++#include <asm/io.h>
++#include <linux/version.h>
++#include <linux/delay.h>
++#include <linux/moduleparam.h>
++
++#include <dahdi/kernel.h>
++
++#include "opvxd115.h"
++#include "vpm450m.h"
++
++/* Work queues are a way to better distribute load on SMP systems */
++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20))
++/*
++ * Work queues can significantly improve performance and scalability
++ * on multi-processor machines, but requires bypassing some kernel
++ * API's, so it's not guaranteed to be compatible with all kernels.
++ */
++/* #define ENABLE_WORKQUEUES */
++#endif
++
++/* Enable prefetching may help performance */
++#define ENABLE_PREFETCH
++
++/* Support first generation cards? */
++#define SUPPORT_GEN1 
++
++/* Define to get more attention-grabbing but slightly more I/O using
++   alarm status */
++#define FANCY_ALARM
++
++/* Define to support Digium Voice Processing Module expansion card */
++#define VPM_SUPPORT
++
++#define DEBUG_MAIN 		(1 << 0)
++#define DEBUG_DTMF 		(1 << 1)
++#define DEBUG_REGS 		(1 << 2)
++#define DEBUG_TSI  		(1 << 3)
++#define DEBUG_ECHOCAN 	(1 << 4)
++#define DEBUG_RBS 		(1 << 5)
++#define DEBUG_FRAMER		(1 << 6)
++
++/* Maximum latency to be used with Gen 5 */
++#define GEN5_MAX_LATENCY	127
++
++#define T4_BASE_SIZE (DAHDI_MAX_CHUNKSIZE * 32 * 4) 
++
++#ifdef ENABLE_WORKQUEUES
++#include <linux/cpu.h>
++
++/* XXX UGLY!!!! XXX  We have to access the direct structures of the workqueue which
++  are only defined within workqueue.c because they don't give us a routine to allow us
++  to nail a work to a particular thread of the CPU.  Nailing to threads gives us substantially
++  higher scalability in multi-CPU environments though! */
++
++/*
++ * The per-CPU workqueue (if single thread, we always use cpu 0's).
++ *
++ * The sequence counters are for flush_scheduled_work().  It wants to wait
++ * until until all currently-scheduled works are completed, but it doesn't
++ * want to be livelocked by new, incoming ones.  So it waits until
++ * remove_sequence is >= the insert_sequence which pertained when
++ * flush_scheduled_work() was called.
++ */
++ 
++struct cpu_workqueue_struct {
++
++	spinlock_t lock;
++
++	long remove_sequence;	/* Least-recently added (next to run) */
++	long insert_sequence;	/* Next to add */
++
++	struct list_head worklist;
++	wait_queue_head_t more_work;
++	wait_queue_head_t work_done;
++
++	struct workqueue_struct *wq;
++	task_t *thread;
++
++	int run_depth;		/* Detect run_workqueue() recursion depth */
++} ____cacheline_aligned;
++
++/*
++ * The externally visible workqueue abstraction is an array of
++ * per-CPU workqueues:
++ */
++struct workqueue_struct {
++	/* TODO: Find out exactly where the API changed */
++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15)
++	struct cpu_workqueue_struct *cpu_wq;
++#else
++	struct cpu_workqueue_struct cpu_wq[NR_CPUS];
++#endif
++	const char *name;
++	struct list_head list; 	/* Empty if single thread */
++};
++
++/* Preempt must be disabled. */
++static void __t4_queue_work(struct cpu_workqueue_struct *cwq,
++			 struct work_struct *work)
++{
++	unsigned long flags;
++
++	spin_lock_irqsave(&cwq->lock, flags);
++	work->wq_data = cwq;
++	list_add_tail(&work->entry, &cwq->worklist);
++	cwq->insert_sequence++;
++	wake_up(&cwq->more_work);
++	spin_unlock_irqrestore(&cwq->lock, flags);
++}
++
++/*
++ * Queue work on a workqueue. Return non-zero if it was successfully
++ * added.
++ *
++ * We queue the work to the CPU it was submitted, but there is no
++ * guarantee that it will be processed by that CPU.
++ */
++static inline int t4_queue_work(struct workqueue_struct *wq, struct work_struct *work, int cpu)
++{
++	int ret = 0;
++	get_cpu();
++	if (!test_and_set_bit(0, &work->pending)) {
++		BUG_ON(!list_empty(&work->entry));
++		__t4_queue_work(wq->cpu_wq + cpu, work);
++		ret = 1;
++	}
++	put_cpu();
++	return ret;
++}
++
++#endif
++
++/*
++ * Define CONFIG_EXTENDED_RESET to allow the qfalc framer extra time
++ * to reset itself upon hardware initialization. This exits for rare
++ * cases for customers who are seeing the qfalc returning unexpected
++ * information at initialization
++ */
++#undef CONFIG_EXTENDED_RESET
++
++static int pedanticpci = 1;
++static int debug=0;
++static int timingcable = 0;
++static int t1e1override = -1;  /* 0xff for E1, 0x00 for T1 */
++static int j1mode = 0;
++static int sigmode = FRMR_MODE_NO_ADDR_CMP;
++static int alarmdebounce = 2500; /* LOF/LFA def to 2.5s AT&T TR54016*/
++static int losalarmdebounce = 2500;/* LOS def to 2.5s AT&T TR54016*/
++static int aisalarmdebounce = 2500;/* AIS(blue) def to 2.5s AT&T TR54016*/
++static int yelalarmdebounce = 500;/* RAI(yellow) def to 0.5s AT&T devguide */
++static int max_latency = GEN5_MAX_LATENCY;  /* Used to set a maximum latency (if you don't wish it to hard cap it at a certain value) in milliseconds */
++#ifdef VPM_SUPPORT
++static int vpmsupport = 1;
++/* If set to auto, vpmdtmfsupport is enabled for VPM400M and disabled for VPM450M */
++static int vpmdtmfsupport = -1; /* -1=auto, 0=disabled, 1=enabled*/
++static int vpmspans = 1;
++#define VPM_DEFAULT_DTMFTHRESHOLD 1000
++static int dtmfthreshold = VPM_DEFAULT_DTMFTHRESHOLD;
++static int lastdtmfthreshold = VPM_DEFAULT_DTMFTHRESHOLD;
++#endif
++/* Enabling bursting can more efficiently utilize PCI bus bandwidth, but
++   can also cause PCI bus starvation, especially in combination with other
++   aggressive cards.  Please note that burst mode has no effect on CPU
++   utilization / max number of calls / etc. */
++static int noburst;
++/* For 56kbps links, set this module parameter to 0x7f */
++static int hardhdlcmode = 0xff;
++
++static int latency = 1;
++
++static int ms_per_irq = 1;
++
++#ifdef FANCY_ALARM
++static int altab[] = {
++0, 0, 0, 1, 2, 3, 4, 6, 8, 9, 11, 13, 16, 18, 20, 22, 24, 25, 27, 28, 29, 30, 31, 31, 32, 31, 31, 30, 29, 28, 27, 25, 23, 22, 20, 18, 16, 13, 11, 9, 8, 6, 4, 3, 2, 1, 0, 0, 
++};
++#endif
++
++#define MAX_SPANS 16
++
++#define FLAG_STARTED (1 << 0)
++#define FLAG_NMF (1 << 1)
++#define FLAG_SENDINGYELLOW (1 << 2)
++
++
++#define	TYPE_T1	1		/* is a T1 card */
++#define	TYPE_E1	2		/* is an E1 card */
++#define TYPE_J1 3		/* is a running J1 */
++
++#define FLAG_2NDGEN  (1 << 3)
++#define FLAG_2PORT   (1 << 4)
++#define FLAG_VPM2GEN (1 << 5)
++#define FLAG_OCTOPT  (1 << 6)
++#define FLAG_3RDGEN  (1 << 7)
++#define FLAG_BURST   (1 << 8)
++#define FLAG_5THGEN  (1 << 10)
++
++#define CANARY 0xc0de
++
++
++#define PORTS_PER_FRAMER 4
++
++struct devtype {
++	char *desc;
++	unsigned int flags;
++};
++
++static struct devtype opvxd115 = { "OpenVox D115P/D115E ", FLAG_2NDGEN};
++static struct devtype opvxd130 = { "OpenVox D130P/D130E", FLAG_5THGEN | FLAG_BURST | FLAG_2NDGEN | FLAG_3RDGEN};
++	
++
++struct t4;
++
++struct t4_span {
++	struct t4 *owner;
++	unsigned int *writechunk;					/* Double-word aligned write memory */
++	unsigned int *readchunk;					/* Double-word aligned read memory */
++	int spantype;		/* card type, T1 or E1 or J1 */
++	int sync;
++	int psync;
++	int alarmtimer;
++	int redalarms;
++	int notclear;
++	int alarmcount;
++	int losalarmcount;
++	int aisalarmcount;
++	int yelalarmcount;
++	int spanflags;
++	int syncpos;
++#ifdef SUPPORT_GEN1
++	int e1check;			/* E1 check */
++#endif
++	struct dahdi_span span;
++	unsigned char txsigs[16];	/* Transmit sigs */
++	int loopupcnt;
++	int loopdowncnt;
++#ifdef SUPPORT_GEN1
++	unsigned char ec_chunk1[31][DAHDI_CHUNKSIZE]; /* first EC chunk buffer */
++	unsigned char ec_chunk2[31][DAHDI_CHUNKSIZE]; /* second EC chunk buffer */
++#endif
++	int irqmisses;
++	
++	/* HDLC controller fields */
++	struct dahdi_chan *sigchan;
++	unsigned char sigmode;
++	int sigactive;
++	int frames_out;
++	int frames_in;
++
++#ifdef VPM_SUPPORT
++	unsigned long dtmfactive;
++	unsigned long dtmfmask;
++	unsigned long dtmfmutemask;
++	short dtmfenergy[31];
++	short dtmfdigit[31];
++#endif
++#ifdef ENABLE_WORKQUEUES
++	struct work_struct swork;
++#endif	
++	struct dahdi_chan *chans[32];		/* Individual channels */
++	struct dahdi_echocan_state *ec[32];	/* Echocan state for each channel */
++};
++
++struct t4 {
++	/* This structure exists one per card */
++	struct pci_dev *dev;		/* Pointer to PCI device */
++	struct dahdi_device *ddev;	/* Pointer to DAHDI device */
++	unsigned int intcount;
++	int num;			/* Which card we are */
++	int t1e1;			/* T1/E1 select pins */
++	int globalconfig;	/* Whether global setup has been done */
++	int syncsrc;			/* active sync source */
++	struct t4_span *tspans[4];	/* Individual spans */
++	int numspans;			/* Number of spans on the card */
++	int blinktimer;
++#ifdef FANCY_ALARM
++	int alarmpos;
++#endif
++	int irq;			/* IRQ used by device */
++	int order;			/* Order */
++	int flags;                      /* Device flags */
++	unsigned int falc31 : 1;	/* are we falc v3.1 (atomic not necessary) */
++	int master;				/* Are we master */
++	int ledreg;				/* LED Register */
++	unsigned int gpio;
++	unsigned int gpioctl;
++	int e1recover;			/* E1 recovery timer */
++	spinlock_t reglock;		/* lock register access */
++	int spansstarted;		/* number of spans started */
++	volatile unsigned int *writechunk;					/* Double-word aligned write memory */
++	volatile unsigned int *readchunk;					/* Double-word aligned read memory */
++	unsigned short canary;
++#ifdef ENABLE_WORKQUEUES
++	atomic_t worklist;
++	struct workqueue_struct *workq;
++#endif
++	unsigned int passno;	/* number of interrupt passes */
++	char *variety;
++	int last0;		/* for detecting double-missed IRQ */
++
++	/* DMA related fields */
++	unsigned int dmactrl;
++	dma_addr_t 	readdma;
++	dma_addr_t	writedma;
++	unsigned long memaddr;		/* Base address of card */
++	unsigned long memlen;
++	__iomem volatile unsigned int *membase;	/* Base address of card */
++
++	/* Add this for our softlockup protector */
++	unsigned int oct_rw_count;
++
++	/* Flags for our bottom half */
++	unsigned long checkflag;
++	struct tasklet_struct t4_tlet;
++	unsigned int vpm400checkstatus;
++	/* Latency related additions */
++	unsigned char rxident;
++	unsigned char lastindex;
++	int numbufs;
++	int needed_latency;
++	
++#ifdef VPM_SUPPORT
++	struct vpm450m *vpm450m;
++	int vpm;
++#endif	
++
++};
++
++#define T4_VPM_PRESENT (1 << 28)
++
++#ifdef VPM_SUPPORT
++static void t4_vpm400_init(struct t4 *wc);
++static void t4_vpm450_init(struct t4 *wc);
++static void t4_vpm_set_dtmf_threshold(struct t4 *wc, unsigned int threshold);
++
++static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec);
++
++static const struct dahdi_echocan_features vpm400m_ec_features = {
++	.NLP_automatic = 1,
++	.CED_tx_detect = 1,
++	.CED_rx_detect = 1,
++};
++
++static const struct dahdi_echocan_features vpm450m_ec_features = {
++	.NLP_automatic = 1,
++	.CED_tx_detect = 1,
++	.CED_rx_detect = 1,
++};
++
++static const struct dahdi_echocan_ops vpm400m_ec_ops = {
++	.echocan_free = echocan_free,
++};
++
++static const struct dahdi_echocan_ops vpm450m_ec_ops = {
++	.echocan_free = echocan_free,
++};
++#endif
++
++static void __set_clear(struct t4 *wc, int span);
++static int t4_startup(struct file *file, struct dahdi_span *span);
++static int t4_shutdown(struct dahdi_span *span);
++static int t4_rbsbits(struct dahdi_chan *chan, int bits);
++static int t4_maint(struct dahdi_span *span, int cmd);
++static int t4_clear_maint(struct dahdi_span *span);
++#if (defined(DAHDI_SPAN_OPS) || defined(DAHDI_SPAN_MODULE) )
++static int t4_reset_counters(struct dahdi_span *span);
++#endif
++#ifdef SUPPORT_GEN1
++static int t4_reset_dma(struct t4 *wc);
++#endif
++static void t4_hdlc_hard_xmit(struct dahdi_chan *chan);
++static int t4_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data);
++static void t4_tsi_assign(struct t4 *wc, int fromspan, int fromchan, int tospan, int tochan);
++static void t4_tsi_unassign(struct t4 *wc, int tospan, int tochan);
++static void __t4_set_rclk_src(struct t4 *wc, int span);
++static void __t4_set_sclk_src(struct t4 *wc, int mode, int master, int slave);
++static void t4_check_alarms(struct t4 *wc, int span);
++static void t4_check_sigbits(struct t4 *wc, int span);
++
++#define WC_RDADDR	0
++#define WC_WRADDR	1
++#define WC_COUNT	2
++#define WC_DMACTRL	3	
++#define WC_INTR		4
++/* #define WC_GPIO		5 */
++#define WC_VERSION	6
++#define WC_LEDS		7
++#define WC_GPIOCTL	8
++#define WC_GPIO		9
++#define WC_LADDR	10
++#define WC_LDATA		11
++#define WC_LCS		(1 << 11)
++#define WC_LCS2		(1 << 12)
++#define WC_LALE			(1 << 13)
++#define WC_LFRMR_CS	(1 << 10)	/* Framer's ChipSelect signal */
++#define WC_ACTIVATE	(1 << 12)
++#define WC_LREAD			(1 << 15)
++#define WC_LWRITE		(1 << 16)
++
++#define WC_OFF    (0)
++#define WC_RED    (1)
++#define WC_GREEN  (2)
++#define WC_YELLOW (3)
++
++#define WC_RECOVER 	0
++#define WC_SELF 	1
++
++#define LIM0_T 0x36 		/* Line interface mode 0 register */
++#define LIM0_LL (1 << 1)	/* Local Loop */
++#define LIM1_T 0x37		/* Line interface mode 1 register */
++#define LIM1_RL (1 << 1)	/* Remote Loop */
++
++#define FMR0 0x1C		/* Framer Mode Register 0 */
++#define FMR0_SIM (1 << 0)	/* Alarm Simulation */
++#define FMR1_T 0x1D		/* Framer Mode Register 1 */
++#define FMR1_ECM (1 << 2)	/* Error Counter 1sec Interrupt Enable */
++#define DEC_T 0x60		/* Diable Error Counter */
++#define IERR_T 0x1B		/* Single Bit Defect Insertion Register */
++#define IBV	0	 /* Bipolar violation */
++#define IPE	(1 << 1) /* PRBS defect */
++#define ICASE	(1 << 2) /* CAS defect */
++#define ICRCE	(1 << 3) /* CRC defect */
++#define IMFE	(1 << 4) /* Multiframe defect */
++#define IFASE	(1 << 5) /* FAS defect */
++#define ISR3_SEC (1 << 6)	/* Internal one-second interrupt bit mask */
++#define ISR3_ES (1 << 7)	/* Errored Second interrupt bit mask */
++#define ESM 0x47		/* Errored Second mask register */
++
++#define FMR2_T 0x1E		/* Framer Mode Register 2 */
++#define FMR2_PLB (1 << 2)	/* Framer Mode Register 2 */
++
++#define FECL_T 0x50		/* Framing Error Counter Lower Byte */
++#define FECH_T 0x51		/* Framing Error Counter Higher Byte */
++#define CVCL_T 0x52		/* Code Violation Counter Lower Byte */
++#define CVCH_T 0x53		/* Code Violation Counter Higher Byte */
++#define CEC1L_T 0x54		/* CRC Error Counter 1 Lower Byte */
++#define CEC1H_T 0x55		/* CRC Error Counter 1 Higher Byte */
++#define EBCL_T 0x56		/* E-Bit Error Counter Lower Byte */
++#define EBCH_T 0x57		/* E-Bit Error Counter Higher Byte */
++#define BECL_T 0x58		/* Bit Error Counter Lower Byte */
++#define BECH_T 0x59		/* Bit Error Counter Higher Byte */
++#define COEC_T 0x5A		/* COFA Event Counter */
++#define PRBSSTA_T 0xDA		/* PRBS Status Register */
++
++#define LCR1_T 0x3B		/* Loop Code Register 1 */
++#define EPRM (1 << 7)		/* Enable PRBS rx */
++#define XPRBS (1 << 6)		/* Enable PRBS tx */
++#define FLLB (1 << 1)		/* Framed line loop/Invert */
++#define LLBP (1 << 0)		/* Line Loopback Pattern */
++#define TPC0_T 0xA8		/* Test Pattern Control Register */
++#define FRA (1 << 6)		/* Framed/Unframed Selection */
++#define PRBS23 (3 << 4)		/* Pattern selection (23 poly) */
++#define PRM (1 << 2)		/* Non framed mode */
++#define FRS1_T 0x4D		/* Framer Receive Status Reg 1 */
++#define LLBDD (1 << 4)
++#define LLBAD (1 << 3)
++
++#define MAX_T4_CARDS 64
++
++static void t4_isr_bh(unsigned long data);
++
++static struct t4 *cards[MAX_T4_CARDS];
++
++
++#define MAX_TDM_CHAN 32
++#define MAX_DTMF_DET 16
++
++#define HDLC_IMR0_MASK (FRMR_IMR0_RME | FRMR_IMR0_RPF)
++#if 0
++#define HDLC_IMR1_MASK (FRMR_IMR1_ALLS | FRMR_IMR1_XDU | FRMR_IMR1_XPR)
++#else
++#define HDLC_IMR1_MASK	(FRMR_IMR1_XDU | FRMR_IMR1_XPR)
++#endif
++
++static inline unsigned int __t4_pci_in(struct t4 *wc, const unsigned int addr)
++{
++	unsigned int res = readl(&wc->membase[addr]);
++	return res;
++}
++
++static inline void __t4_pci_out(struct t4 *wc, const unsigned int addr, const unsigned int value)
++{
++	unsigned int tmp;
++	writel(value, &wc->membase[addr]);
++	if (pedanticpci) {
++		tmp = __t4_pci_in(wc, WC_VERSION);
++		if ((tmp & 0xffff0000) != 0xc01a0000)
++			dev_notice(&wc->dev->dev,
++					"Version Synchronization Error!\n");
++	}
++#if 0
++	tmp = __t4_pci_in(wc, addr);
++	if ((value != tmp) && (addr != WC_LEDS) && (addr != WC_LDATA) &&
++		(addr != WC_GPIO) && (addr != WC_INTR))
++		dev_info(&wc->dev->dev, "Tried to load %08x into %08x, "
++				"but got %08x instead\n", value, addr, tmp);
++#endif		
++}
++
++static inline void __t4_gpio_set(struct t4 *wc, unsigned bits, unsigned int val)
++{
++	unsigned int newgpio;
++	newgpio = wc->gpio & (~bits);
++	newgpio |= val;
++	if (newgpio != wc->gpio) {
++		wc->gpio = newgpio;
++		__t4_pci_out(wc, WC_GPIO, wc->gpio);
++	}	
++}
++
++static inline void __t4_gpio_setdir(struct t4 *wc, unsigned int bits, unsigned int val)
++{
++	unsigned int newgpioctl;
++	newgpioctl = wc->gpioctl & (~bits);
++	newgpioctl |= val;
++	if (newgpioctl != wc->gpioctl) {
++		wc->gpioctl = newgpioctl;
++		__t4_pci_out(wc, WC_GPIOCTL, wc->gpioctl);
++	}
++}
++
++static inline void t4_gpio_setdir(struct t4 *wc, unsigned int bits, unsigned int val)
++{
++	unsigned long flags;
++	spin_lock_irqsave(&wc->reglock, flags);
++	__t4_gpio_setdir(wc, bits, val);
++	spin_unlock_irqrestore(&wc->reglock, flags);
++}
++
++static inline void t4_gpio_set(struct t4 *wc, unsigned int bits, unsigned int val)
++{
++	unsigned long flags;
++	spin_lock_irqsave(&wc->reglock, flags);
++	__t4_gpio_set(wc, bits, val);
++	spin_unlock_irqrestore(&wc->reglock, flags);
++}
++
++static inline void t4_pci_out(struct t4 *wc, const unsigned int addr, const unsigned int value)
++{
++	unsigned long flags;
++	spin_lock_irqsave(&wc->reglock, flags);
++	__t4_pci_out(wc, addr, value);
++	spin_unlock_irqrestore(&wc->reglock, flags);
++}
++
++static inline void __t4_set_led(struct t4 *wc, int span, int color)
++{
++	int oldreg = wc->ledreg;
++	wc->ledreg &= ~(0x3 << (span << 1));
++	wc->ledreg |= (color << (span << 1));
++	if (oldreg != wc->ledreg)
++		__t4_pci_out(wc, WC_LEDS, wc->ledreg);
++}
++
++static inline void t4_activate(struct t4 *wc)
++{
++	wc->ledreg |= WC_ACTIVATE;
++	t4_pci_out(wc, WC_LEDS, wc->ledreg);
++}
++
++static inline unsigned int t4_pci_in(struct t4 *wc, const unsigned int addr)
++{
++	unsigned int ret;
++	unsigned long flags;
++	
++	spin_lock_irqsave(&wc->reglock, flags);
++	ret = __t4_pci_in(wc, addr);
++	spin_unlock_irqrestore(&wc->reglock, flags);
++	return ret;
++}
++
++static inline unsigned int __t4_framer_in(struct t4 *wc, int unit, const unsigned int addr)
++{
++	unsigned int ret;
++	unit &= 0x3;
++	__t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff));
++	if (!pedanticpci)
++		__t4_pci_in(wc, WC_VERSION);
++	__t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | WC_LFRMR_CS | WC_LREAD);
++	if (!pedanticpci) {
++		__t4_pci_in(wc, WC_VERSION);
++	} else {
++		__t4_pci_out(wc, WC_VERSION, 0);
++	}
++	ret = __t4_pci_in(wc, WC_LDATA);
++ 	__t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff));
++
++	if (unlikely(debug & DEBUG_REGS))
++		dev_info(&wc->dev->dev, "Reading unit %d address %02x is "
++				"%02x\n", unit, addr, ret & 0xff);
++
++	if (!pedanticpci)
++		__t4_pci_in(wc, WC_VERSION);
++
++	return ret & 0xff;
++}
++
++static inline unsigned int t4_framer_in(struct t4 *wc, int unit, const unsigned int addr)
++{
++	unsigned long flags;
++	unsigned int ret;
++	spin_lock_irqsave(&wc->reglock, flags);
++	ret = __t4_framer_in(wc, unit, addr);
++	spin_unlock_irqrestore(&wc->reglock, flags);
++	return ret;
++
++}
++
++static inline void __t4_framer_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value)
++{
++	unit &= 0x3;
++	if (unlikely(debug & DEBUG_REGS))
++		dev_info(&wc->dev->dev, "Writing %02x to address %02x of "
++				"unit %d\n", value, addr, unit);
++	__t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff));
++	__t4_pci_out(wc, WC_LDATA, value);
++	if (!pedanticpci)
++		__t4_pci_in(wc, WC_VERSION);
++	__t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | WC_LFRMR_CS | WC_LWRITE);
++	if (!pedanticpci)
++		__t4_pci_in(wc, WC_VERSION);
++	__t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff));	
++	if (!pedanticpci)
++		__t4_pci_in(wc, WC_VERSION);
++	if (unlikely(debug & DEBUG_REGS))
++		dev_info(&wc->dev->dev, "Write complete\n");
++#if 0
++	if ((addr != FRMR_TXFIFO) && (addr != FRMR_CMDR) && (addr != 0xbc))
++	{ unsigned int tmp;
++	tmp = __t4_framer_in(wc, unit, addr);
++	if (tmp != value) {
++		dev_notice(&wc->dev->dev, "Expected %d from unit %d "
++				"register %d but got %d instead\n",
++				value, unit, addr, tmp);
++	} }
++#endif	
++}
++
++static inline void t4_framer_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value)
++{
++	unsigned long flags;
++	spin_lock_irqsave(&wc->reglock, flags);
++	__t4_framer_out(wc, unit, addr, value);
++	spin_unlock_irqrestore(&wc->reglock, flags);
++}
++
++#ifdef VPM_SUPPORT
++
++static inline void wait_a_little(void)
++{
++	unsigned long newjiffies=jiffies+2;
++	while(jiffies < newjiffies);
++}
++
++static inline unsigned int __t4_vpm_in(struct t4 *wc, int unit, const unsigned int addr)
++{
++	unsigned int ret;
++	unit &= 0x7;
++	__t4_pci_out(wc, WC_LADDR, (addr & 0x1ff) | ( unit << 12));
++	__t4_pci_out(wc, WC_LADDR, (addr & 0x1ff) | ( unit << 12) | (1 << 11) | WC_LREAD);
++	ret = __t4_pci_in(wc, WC_LDATA);
++	__t4_pci_out(wc, WC_LADDR, 0);
++	return ret & 0xff;
++}
++
++static inline void __t4_raw_oct_out(struct t4 *wc, const unsigned int addr, const unsigned int value)
++{
++	int octopt = wc->tspans[0]->spanflags & FLAG_OCTOPT;
++	if (!octopt) 
++		__t4_gpio_set(wc, 0xff, (addr >> 8));
++	__t4_pci_out(wc, WC_LDATA, 0x10000 | (addr & 0xffff));
++	if (!octopt)
++		__t4_pci_out(wc, WC_LADDR, (WC_LWRITE));
++	__t4_pci_out(wc, WC_LADDR, (WC_LWRITE | WC_LALE));
++	if (!pedanticpci)
++		__t4_pci_in(wc, WC_VERSION);
++	if (!octopt)
++		__t4_gpio_set(wc, 0xff, (value >> 8));
++	__t4_pci_out(wc, WC_LDATA, (value & 0xffff));
++	__t4_pci_out(wc, WC_LADDR, (WC_LWRITE | WC_LALE | WC_LCS));
++	if (!pedanticpci)
++		__t4_pci_in(wc, WC_VERSION);
++	__t4_pci_out(wc, WC_LADDR, (0));
++	if (!pedanticpci)
++		__t4_pci_in(wc, WC_VERSION);
++}
++
++static inline unsigned int __t4_raw_oct_in(struct t4 *wc, const unsigned int addr)
++{
++	unsigned int ret;
++	int octopt = wc->tspans[0]->spanflags & FLAG_OCTOPT;
++	if (!octopt)
++		__t4_gpio_set(wc, 0xff, (addr >> 8));
++	__t4_pci_out(wc, WC_LDATA, 0x10000 | (addr & 0xffff));
++	if (!octopt)
++		__t4_pci_out(wc, WC_LADDR, (WC_LWRITE));
++	if (!pedanticpci)
++		__t4_pci_in(wc, WC_VERSION);
++	__t4_pci_out(wc, WC_LADDR, (WC_LWRITE | WC_LALE));
++	if (!pedanticpci)
++		__t4_pci_in(wc, WC_VERSION);
++#ifdef PEDANTIC_OCTASIC_CHECKING 
++	__t4_pci_out(wc, WC_LADDR, (WC_LALE));
++	if (!pedanticpci)
++		__t4_pci_in(wc, WC_VERSION);
++#endif
++	if (!octopt) {
++		__t4_gpio_setdir(wc, 0xff, 0x00);
++		__t4_gpio_set(wc, 0xff, 0x00);
++	}
++	__t4_pci_out(wc, WC_LADDR, (WC_LREAD | WC_LALE | WC_LCS));
++	if (!pedanticpci)
++		__t4_pci_in(wc, WC_VERSION);
++	if (octopt) {
++		ret = __t4_pci_in(wc, WC_LDATA) & 0xffff;
++	} else {
++		ret = __t4_pci_in(wc, WC_LDATA) & 0xff;
++		ret |= (__t4_pci_in(wc, WC_GPIO) & 0xff) << 8;
++	}
++	__t4_pci_out(wc, WC_LADDR, (0));
++	if (!pedanticpci)
++		__t4_pci_in(wc, WC_VERSION);
++	if (!octopt)
++		__t4_gpio_setdir(wc, 0xff, 0xff);
++	return ret & 0xffff;
++}
++
++static inline unsigned int __t4_oct_in(struct t4 *wc, unsigned int addr)
++{
++#ifdef PEDANTIC_OCTASIC_CHECKING
++	int count = 1000;
++#endif
++	__t4_raw_oct_out(wc, 0x0008, (addr >> 20));
++	__t4_raw_oct_out(wc, 0x000a, (addr >> 4) & ((1 << 16) - 1));
++	__t4_raw_oct_out(wc, 0x0000, (((addr >> 1) & 0x7) << 9) | (1 << 8) | (1));
++#ifdef PEDANTIC_OCTASIC_CHECKING
++	while((__t4_raw_oct_in(wc, 0x0000) & (1 << 8)) && --count);
++	if (count != 1000)
++		dev_notice(&wc->dev->dev, "Yah, read can be slow...\n");
++	if (!count)
++		dev_notice(&wc->dev->dev, "Read timed out!\n");
++#endif
++	return __t4_raw_oct_in(wc, 0x0004);
++}
++
++static inline unsigned int t4_oct_in(struct t4 *wc, const unsigned int addr)
++{
++	unsigned long flags;
++	unsigned int ret;
++
++	spin_lock_irqsave(&wc->reglock, flags);
++	ret = __t4_oct_in(wc, addr);
++	spin_unlock_irqrestore(&wc->reglock, flags);
++	return ret;
++}
++
++static inline unsigned int t4_vpm_in(struct t4 *wc, int unit, const unsigned int addr)
++{
++	unsigned long flags;
++	unsigned int ret;
++	spin_lock_irqsave(&wc->reglock, flags);
++	ret = __t4_vpm_in(wc, unit, addr);
++	spin_unlock_irqrestore(&wc->reglock, flags);
++	return ret;
++}
++
++static inline void __t4_vpm_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value)
++{
++	unit &= 0x7;
++	if (debug & DEBUG_REGS)
++		dev_notice(&wc->dev->dev, "Writing %02x to address %02x of "
++				"ec unit %d\n", value, addr, unit);
++	__t4_pci_out(wc, WC_LADDR, (addr & 0xff));
++	__t4_pci_out(wc, WC_LDATA, value);
++	__t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff) | (1 << 11));
++	__t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff) | (1 << 11) | WC_LWRITE);
++	__t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff) | (1 << 11));
++	__t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff));	
++	__t4_pci_out(wc, WC_LADDR, 0);
++	if (debug & DEBUG_REGS)
++		dev_notice(&wc->dev->dev, "Write complete\n");
++
++      
++#if 0
++	{ unsigned int tmp;
++	tmp = t4_vpm_in(wc, unit, addr);
++	if (tmp != value) {
++		dev_notice(&wc->dev->dev, "Expected %d from unit %d echo "
++				"register %d but got %d instead\n",
++				value, unit, addr, tmp);
++	} }
++#endif
++}
++
++static inline void __t4_oct_out(struct t4 *wc, unsigned int addr, unsigned int value)
++{
++#ifdef PEDANTIC_OCTASIC_CHECKING
++	int count = 1000;
++#endif
++	__t4_raw_oct_out(wc, 0x0008, (addr >> 20));
++	__t4_raw_oct_out(wc, 0x000a, (addr >> 4) & ((1 << 16) - 1));
++	__t4_raw_oct_out(wc, 0x0004, value);
++	__t4_raw_oct_out(wc, 0x0000, (((addr >> 1) & 0x7) << 9) | (1 << 8) | (3 << 12) | 1);
++#ifdef PEDANTIC_OCTASIC_CHECKING
++	while((__t4_raw_oct_in(wc, 0x0000) & (1 << 8)) && --count);
++	if (count != 1000)
++		dev_notice(&wc->dev->dev, "Yah, write can be slow\n");
++	if (!count)
++		dev_notice(&wc->dev->dev, "Write timed out!\n");
++#endif
++}
++
++static inline void t4_oct_out(struct t4 *wc, const unsigned int addr, const unsigned int value)
++{
++	unsigned long flags;
++
++	spin_lock_irqsave(&wc->reglock, flags);
++	__t4_oct_out(wc, addr, value);
++	spin_unlock_irqrestore(&wc->reglock, flags);
++}
++
++static inline void t4_vpm_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value)
++{
++	unsigned long flags;
++	spin_lock_irqsave(&wc->reglock, flags);
++	__t4_vpm_out(wc, unit, addr, value);
++	spin_unlock_irqrestore(&wc->reglock, flags);
++}
++
++static const char vpm_digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', '*', '#'};
++
++static void t4_check_vpm450(struct t4 *wc)
++{
++	int channel, tone, start, span;
++
++	if (vpm450m_checkirq(wc->vpm450m)) {
++		while(vpm450m_getdtmf(wc->vpm450m, &channel, &tone, &start)) {
++			span = channel & 0x3;
++			channel >>= 2;
++			if (!wc->t1e1)
++				channel -= 5;
++			else
++				channel -= 1;
++			if (unlikely(debug))
++				dev_info(&wc->dev->dev, "Got tone %s of '%c' "
++					"on channel %d of span %d\n",
++					(start ? "START" : "STOP"),
++					tone, channel, span + 1);
++			if (test_bit(channel, &wc->tspans[span]->dtmfmask) && (tone != 'u')) {
++				if (start) {
++					/* The octasic is supposed to mute us, but...  Yah, you
++					   guessed it.  */
++					if (test_bit(channel, &wc->tspans[span]->dtmfmutemask)) {
++						unsigned long flags;
++						struct dahdi_chan *chan = wc->tspans[span]->span.chans[channel];
++						int y;
++						spin_lock_irqsave(&chan->lock, flags);
++						for (y=0;y<chan->numbufs;y++) {
++							if ((chan->inreadbuf > -1) && (chan->readidx[y]))
++								memset(chan->readbuf[chan->inreadbuf], DAHDI_XLAW(0, chan), chan->readidx[y]);
++						}
++						spin_unlock_irqrestore(&chan->lock, flags);
++					}
++					set_bit(channel, &wc->tspans[span]->dtmfactive);
++					dahdi_qevent_lock(wc->tspans[span]->span.chans[channel], (DAHDI_EVENT_DTMFDOWN | tone));
++				} else {
++					clear_bit(channel, &wc->tspans[span]->dtmfactive);
++					dahdi_qevent_lock(wc->tspans[span]->span.chans[channel], (DAHDI_EVENT_DTMFUP | tone));
++				}
++			}
++		}
++	}
++}
++
++static void t4_check_vpm400(struct t4 *wc, unsigned int newio)
++{
++	unsigned int digit, regval = 0;
++	unsigned int regbyte;
++	int x, i;
++	short energy=0;
++	static unsigned int lastio = 0;
++	struct t4_span *ts;
++
++	if (debug && (newio != lastio)) 
++		dev_notice(&wc->dev->dev, "Last was %08x, new is %08x\n",
++				lastio, newio);
++
++	lastio = newio;
++ 
++	for(x = 0; x < 8; x++) {
++		if (newio & (1 << (7 - x)))
++			continue;
++		ts = wc->tspans[x%4];
++		/* Start of DTMF detection process */	
++		regbyte = t4_vpm_in(wc, x, 0xb8);
++		t4_vpm_out(wc, x, 0xb8, regbyte); /* Write 1 to clear */
++		regval = regbyte << 8;
++		regbyte = t4_vpm_in(wc, x, 0xb9);
++		t4_vpm_out(wc, x, 0xb9, regbyte);
++		regval |= regbyte;
++
++		for(i = 0; (i < MAX_DTMF_DET) && regval; i++) {
++			if(regval & 0x0001) {
++				int channel = (i << 1) + (x >> 2);
++				int base = channel - 1;
++
++				if (!wc->t1e1)
++					base -= 4;
++				regbyte = t4_vpm_in(wc, x, 0xa8 + i);
++				digit = vpm_digits[regbyte];
++				if (!(wc->tspans[0]->spanflags & FLAG_VPM2GEN)) {
++					energy = t4_vpm_in(wc, x, 0x58 + channel);
++					energy = DAHDI_XLAW(energy, ts->chans[0]);
++					ts->dtmfenergy[base] = energy;
++				}
++				set_bit(base, &ts->dtmfactive);
++				if (ts->dtmfdigit[base]) {
++					if (ts->dtmfmask & (1 << base))
++						dahdi_qevent_lock(ts->span.chans[base], (DAHDI_EVENT_DTMFUP | ts->dtmfdigit[base]));
++				}
++				ts->dtmfdigit[base] = digit;
++				if (test_bit(base, &ts->dtmfmask))
++					dahdi_qevent_lock(ts->span.chans[base], (DAHDI_EVENT_DTMFDOWN | digit));
++				if (test_bit(base, &ts->dtmfmutemask)) {
++					/* Mute active receive buffer*/
++					unsigned long flags;
++					struct dahdi_chan *chan = ts->span.chans[base];
++					int y;
++					spin_lock_irqsave(&chan->lock, flags);
++					for (y=0;y<chan->numbufs;y++) {
++						if ((chan->inreadbuf > -1) && (chan->readidx[y]))
++							memset(chan->readbuf[chan->inreadbuf], DAHDI_XLAW(0, chan), chan->readidx[y]);
++					}
++					spin_unlock_irqrestore(&chan->lock, flags);
++				}
++				if (debug)
++					dev_notice(&wc->dev->dev, "Digit "
++						"Seen: %d, Span: %d, channel:"
++						" %d, energy: %02x, 'channel "
++						"%d' chip %d\n", digit, x % 4,
++						base + 1, energy, channel, x);
++				
++			}
++			regval = regval >> 1;
++		}
++		if (!(wc->tspans[0]->spanflags & FLAG_VPM2GEN))
++			continue;
++
++		/* Start of DTMF off detection process */	
++		regbyte = t4_vpm_in(wc, x, 0xbc);
++		t4_vpm_out(wc, x, 0xbc, regbyte); /* Write 1 to clear */
++		regval = regbyte << 8;
++		regbyte = t4_vpm_in(wc, x, 0xbd);
++		t4_vpm_out(wc, x, 0xbd, regbyte);
++		regval |= regbyte;
++
++		for(i = 0; (i < MAX_DTMF_DET) && regval; i++) {
++			if(regval & 0x0001) {
++				int channel = (i << 1) + (x >> 2);
++				int base = channel - 1;
++
++				if (!wc->t1e1)
++					base -= 4;
++				clear_bit(base, &ts->dtmfactive);
++				if (ts->dtmfdigit[base]) {
++					if (test_bit(base, &ts->dtmfmask))
++						dahdi_qevent_lock(ts->span.chans[base], (DAHDI_EVENT_DTMFUP | ts->dtmfdigit[base]));
++				}
++				digit = ts->dtmfdigit[base];
++				ts->dtmfdigit[base] = 0;
++				if (debug)
++					dev_notice(&wc->dev->dev, "Digit "
++						"Gone: %d, Span: %d, channel:"
++						" %d, energy: %02x, 'channel "
++						"%d' chip %d\n", digit, x % 4,
++						base + 1, energy, channel, x);
++				
++			}
++			regval = regval >> 1;
++		}
++
++	}
++}
++#endif
++
++static void hdlc_stop(struct t4 *wc, unsigned int span)
++{
++	struct t4_span *t = wc->tspans[span];
++	unsigned char imr0, imr1, mode;
++	int i = 0;
++
++	if (debug & DEBUG_FRAMER)
++		dev_notice(&wc->dev->dev, "Stopping HDLC controller on span "
++				"%d\n", span+1);
++	
++	/* Clear receive and transmit timeslots */
++	for (i = 0; i < 4; i++) {
++		t4_framer_out(wc, span, FRMR_RTR_BASE + i, 0x00);
++		t4_framer_out(wc, span, FRMR_TTR_BASE + i, 0x00);
++	}
++
++	imr0 = t4_framer_in(wc, span, FRMR_IMR0);
++	imr1 = t4_framer_in(wc, span, FRMR_IMR1);
++
++	/* Disable HDLC interrupts */
++	imr0 |= HDLC_IMR0_MASK;
++	t4_framer_out(wc, span, FRMR_IMR0, imr0);
++
++	imr1 |= HDLC_IMR1_MASK;
++	t4_framer_out(wc, span, FRMR_IMR1, imr1);
++
++	mode = t4_framer_in(wc, span, FRMR_MODE);
++	mode &= ~FRMR_MODE_HRAC;
++	t4_framer_out(wc, span, FRMR_MODE, mode);
++
++	t->sigactive = 0;
++}
++
++static inline void __t4_framer_cmd(struct t4 *wc, unsigned int span, int cmd)
++{
++	__t4_framer_out(wc, span, FRMR_CMDR, cmd);
++}
++
++static inline void t4_framer_cmd_wait(struct t4 *wc, unsigned int span, int cmd)
++{
++	int sis;
++	int loops = 0;
++
++	/* XXX could be time consuming XXX */
++	for (;;) {
++		sis = t4_framer_in(wc, span, FRMR_SIS);
++		if (!(sis & 0x04))
++			break;
++		if (!loops++ && (debug & DEBUG_FRAMER)) {
++			dev_notice(&wc->dev->dev, "!!!SIS Waiting before cmd "
++					"%02x\n", cmd);
++		}
++	}
++	if (loops && (debug & DEBUG_FRAMER))
++		dev_notice(&wc->dev->dev, "!!!SIS waited %d loops\n", loops);
++
++	t4_framer_out(wc, span, FRMR_CMDR, cmd);
++}
++
++static int hdlc_start(struct t4 *wc, unsigned int span, struct dahdi_chan *chan, unsigned char mode)
++{
++	struct t4_span *t = wc->tspans[span];
++	unsigned char imr0, imr1;
++	int offset = chan->chanpos;
++	unsigned long flags;
++
++	if (debug & DEBUG_FRAMER)
++		dev_info(&wc->dev->dev, "Starting HDLC controller for channel "
++				"%d span %d\n", offset, span+1);
++
++	if (mode != FRMR_MODE_NO_ADDR_CMP)
++		return -1;
++
++	mode |= FRMR_MODE_HRAC;
++
++	/* Make sure we're in the right mode */
++	t4_framer_out(wc, span, FRMR_MODE, mode);
++	t4_framer_out(wc, span, FRMR_TSEO, 0x00);
++	t4_framer_out(wc, span, FRMR_TSBS1, hardhdlcmode);
++
++	/* Set the interframe gaps, etc */
++	t4_framer_out(wc, span, FRMR_CCR1, FRMR_CCR1_ITF|FRMR_CCR1_EITS);
++
++	t4_framer_out(wc, span, FRMR_CCR2, FRMR_CCR2_RCRC);
++	
++	/* Set up the time slot that we want to tx/rx on */
++	t4_framer_out(wc, span, FRMR_TTR_BASE + (offset / 8), (0x80 >> (offset % 8)));
++	t4_framer_out(wc, span, FRMR_RTR_BASE + (offset / 8), (0x80 >> (offset % 8)));
++
++	imr0 = t4_framer_in(wc, span, FRMR_IMR0);
++	imr1 = t4_framer_in(wc, span, FRMR_IMR1);
++
++	/* Enable our interrupts again */
++	imr0 &= ~HDLC_IMR0_MASK;
++	t4_framer_out(wc, span, FRMR_IMR0, imr0);
++
++	imr1 &= ~HDLC_IMR1_MASK;
++	t4_framer_out(wc, span, FRMR_IMR1, imr1);
++
++	/* Reset the signaling controller */
++	t4_framer_cmd_wait(wc, span, FRMR_CMDR_SRES);
++
++	spin_lock_irqsave(&wc->reglock, flags);
++	t->sigchan = chan;
++	spin_unlock_irqrestore(&wc->reglock, flags);
++
++	t->sigactive = 0;
++
++	return 0;
++}
++
++static void __set_clear(struct t4 *wc, int span)
++{
++	int i,j;
++	int oldnotclear;
++	unsigned short val=0;
++	struct t4_span *ts = wc->tspans[span];
++
++	oldnotclear = ts->notclear;
++	if ((ts->spantype == TYPE_T1) || (ts->spantype == TYPE_J1)) {
++		for (i=0;i<24;i++) {
++			j = (i/8);
++			if (ts->span.chans[i]->flags & DAHDI_FLAG_CLEAR) {
++				val |= 1 << (7 - (i % 8));
++				ts->notclear &= ~(1 << i);
++			} else
++				ts->notclear |= (1 << i);
++			if ((i % 8)==7) {
++				if (debug)
++					dev_notice(&wc->dev->dev, "Putting %d "
++						"in register %02x on span %d"
++						"\n", val, 0x2f + j, span + 1);
++				__t4_framer_out(wc, span, 0x2f + j, val);
++				val = 0;
++			}
++		}
++	} else {
++		for (i=0;i<31;i++) {
++			if (ts->span.chans[i]->flags & DAHDI_FLAG_CLEAR)
++				ts->notclear &= ~(1 << i);
++			else 
++				ts->notclear |= (1 << i);
++		}
++	}
++	if (ts->notclear != oldnotclear) {
++		unsigned char reg;
++		reg = __t4_framer_in(wc, span, FRMR_IMR0);
++		if (ts->notclear)
++			reg &= ~0x08;
++		else
++			reg |= 0x08;
++		__t4_framer_out(wc, span, FRMR_IMR0, reg);
++	}
++}
++
++#if 0
++static void set_clear(struct t4 *wc, int span)
++{
++	unsigned long flags;
++	spin_lock_irqsave(&wc->reglock, flags);
++	__set_clear(wc, span);
++	spin_unlock_irqrestore(&wc->reglock, flags);
++}
++#endif
++
++static int t4_dacs(struct dahdi_chan *dst, struct dahdi_chan *src)
++{
++	struct t4 *wc;
++	struct t4_span *ts;
++	wc = dst->pvt;
++	ts = wc->tspans[dst->span->offset];
++	if (src && (src->pvt != dst->pvt)) {
++		if (ts->spanflags & FLAG_2NDGEN)
++			t4_tsi_unassign(wc, dst->span->offset, dst->chanpos);
++		wc = src->pvt;
++		if (ts->spanflags & FLAG_2NDGEN)
++			t4_tsi_unassign(wc, src->span->offset, src->chanpos);
++		if (debug)
++			dev_notice(&wc->dev->dev, "Unassigning %d/%d by "
++				"default and...\n", src->span->offset,
++				src->chanpos);
++		if (debug)
++			dev_notice(&wc->dev->dev, "Unassigning %d/%d by "
++				"default\n", dst->span->offset, dst->chanpos);
++		return -1;
++	}
++	if (src) {
++		t4_tsi_assign(wc, src->span->offset, src->chanpos, dst->span->offset, dst->chanpos);
++		if (debug)
++			dev_notice(&wc->dev->dev, "Assigning channel %d/%d -> "
++				"%d/%d!\n", src->span->offset, src->chanpos,
++				dst->span->offset, dst->chanpos);
++	} else {
++		t4_tsi_unassign(wc, dst->span->offset, dst->chanpos);
++		if (debug)
++			dev_notice(&wc->dev->dev, "Unassigning channel %d/%d!"
++				"\n", dst->span->offset, dst->chanpos);
++	}
++	return 0;
++}
++
++#ifdef VPM_SUPPORT
++
++void oct_set_reg(void *data, unsigned int reg, unsigned int val)
++{
++	struct t4 *wc = data;
++	t4_oct_out(wc, reg, val);
++}
++
++unsigned int oct_get_reg(void *data, unsigned int reg)
++{
++	struct t4 *wc = data;
++	unsigned int ret;
++	ret = t4_oct_in(wc, reg);
++	return ret;
++}
++
++static int t4_vpm_unit(int span, int channel)
++{
++	int unit = 0;
++	switch(vpmspans) {
++	case 4:
++		unit = span;
++		unit += (channel & 1) << 2;
++		break;
++	case 2:
++		unit = span;
++		unit += (channel & 0x3) << 1;
++		break;
++	case 1:
++		unit = span;
++		unit += (channel & 0x7);
++	}
++	return unit;
++}
++
++static inline struct t4_span *t4_from_span(struct dahdi_span *span)
++{
++	return container_of(span, struct t4_span, span);
++}
++
++static int t4_echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
++			  struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec)
++{
++	struct t4 *wc = chan->pvt;
++	struct t4_span *tspan = container_of(chan->span, struct t4_span, span);
++	int channel;
++	const struct dahdi_echocan_ops *ops;
++	const struct dahdi_echocan_features *features;
++
++	if (!vpmsupport || !wc->vpm)
++		return -ENODEV;
++
++	if (chan->span->offset >= vpmspans)
++		return -ENODEV;
++
++	if (wc->vpm450m) {
++		ops = &vpm450m_ec_ops;
++		features = &vpm450m_ec_features;
++	} else {
++		ops = &vpm400m_ec_ops;
++		features = &vpm400m_ec_features;
++	}
++
++	if (ecp->param_count > 0) {
++		dev_warn(&wc->dev->dev, "echo canceller does not support "
++				"parameters; failing request\n");
++		return -EINVAL;
++	}
++
++	*ec = tspan->ec[chan->chanpos - 1];
++	(*ec)->ops = ops;
++	(*ec)->features = *features;
++
++	channel = wc->t1e1 ? chan->chanpos : chan->chanpos + 4;
++
++	if (wc->vpm450m) {
++		channel = channel << 2;
++		channel |= chan->span->offset;
++		if (debug & DEBUG_ECHOCAN)
++			dev_notice(&wc->dev->dev, "echocan: Card is %d, "
++				"Channel is %d, Span is %d, offset is %d "
++				"length %d\n", wc->num, chan->chanpos,
++				chan->span->offset, channel, ecp->tap_length);
++		vpm450m_setec(wc->vpm450m, channel, ecp->tap_length);
++	} else {
++		int unit = t4_vpm_unit(chan->span->offset, channel);
++
++		if (debug & DEBUG_ECHOCAN)
++			dev_notice(&wc->dev->dev, "echocan: Card is %d, "
++				"Channel is %d, Span is %d, unit is %d, "
++				"unit offset is %d length %d\n", wc->num,
++				chan->chanpos, chan->span->offset, unit,
++				channel, ecp->tap_length);
++		t4_vpm_out(wc, unit, channel, 0x3e);
++	}
++
++	return 0;
++}
++
++static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec)
++{
++	struct t4 *wc = chan->pvt;
++	int channel;
++
++	memset(ec, 0, sizeof(*ec));
++
++	channel = wc->t1e1 ? chan->chanpos : chan->chanpos + 4;
++
++	if (wc->vpm450m) {
++		channel = channel << 2;
++		channel |= chan->span->offset;
++		if (debug & DEBUG_ECHOCAN)
++			dev_notice(&wc->dev->dev, "echocan: Card is %d, "
++				"Channel is %d, Span is %d, offset is %d "
++				"length 0\n", wc->num, chan->chanpos,
++				chan->span->offset, channel);
++		vpm450m_setec(wc->vpm450m, channel, 0);
++	} else {
++		int unit = t4_vpm_unit(chan->span->offset, channel);
++
++		if (debug & DEBUG_ECHOCAN)
++			dev_notice(&wc->dev->dev, "echocan: Card is %d, "
++				"Channel is %d, Span is %d, unit is %d, "
++				"unit offset is %d length 0\n", wc->num,
++				chan->chanpos, chan->span->offset, unit,
++				channel);
++		t4_vpm_out(wc, unit, channel, 0x01);
++	}
++}
++#endif
++
++static int t4_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data)
++{
++	struct t4_regs regs;
++	int x;
++	struct t4 *wc = chan->pvt;
++#ifdef VPM_SUPPORT
++	int j;
++	int channel;
++	struct t4_span *ts = wc->tspans[chan->span->offset];
++#endif
++
++#ifdef VPM_SUPPORT
++	if (dtmfthreshold == 0)
++		dtmfthreshold = VPM_DEFAULT_DTMFTHRESHOLD;
++	if (lastdtmfthreshold != dtmfthreshold) {
++		lastdtmfthreshold = dtmfthreshold;
++		t4_vpm_set_dtmf_threshold(wc, dtmfthreshold);
++	}
++#endif
++
++	switch(cmd) {
++	case WCT4_GET_REGS:
++		for (x=0;x<NUM_PCI;x++)
++			regs.pci[x] = t4_pci_in(wc, x);
++		for (x=0;x<NUM_REGS;x++)
++			regs.regs[x] = t4_framer_in(wc, chan->span->offset, x);
++		if (copy_to_user((__user void *) data, &regs, sizeof(regs)))
++			return -EFAULT;
++		break;
++#ifdef VPM_SUPPORT
++	case DAHDI_TONEDETECT:
++		if (get_user(j, (__user int *) data))
++			return -EFAULT;
++		if (!wc->vpm)
++			return -ENOSYS;
++		if (j && (vpmdtmfsupport == 0))
++			return -ENOSYS;
++		if (j & DAHDI_TONEDETECT_ON)
++			set_bit(chan->chanpos - 1, &ts->dtmfmask);
++		else
++			clear_bit(chan->chanpos - 1, &ts->dtmfmask);
++		if (j & DAHDI_TONEDETECT_MUTE)
++			set_bit(chan->chanpos - 1, &ts->dtmfmutemask);
++		else
++			clear_bit(chan->chanpos - 1, &ts->dtmfmutemask);
++		if (wc->vpm450m) {
++			channel = (chan->chanpos) << 2;
++			if (!wc->t1e1)
++				channel += (4 << 2);
++			channel |= chan->span->offset;
++			vpm450m_setdtmf(wc->vpm450m, channel, j & DAHDI_TONEDETECT_ON, j & DAHDI_TONEDETECT_MUTE);
++		}
++		return 0;
++#endif
++	default:
++		return -ENOTTY;
++	}
++	return 0;
++}
++
++static void inline t4_hdlc_xmit_fifo(struct t4 *wc, unsigned int span, struct t4_span *ts)
++{
++	int res, i;
++	unsigned int size = 32;
++	unsigned char buf[32];
++
++	res = dahdi_hdlc_getbuf(ts->sigchan, buf, &size);
++	if (debug & DEBUG_FRAMER)
++		dev_notice(&wc->dev->dev, "Got buffer sized %d and res %d "
++				"for %d\n", size, res, span);
++	if (size > 0) {
++		ts->sigactive = 1;
++
++		if (debug & DEBUG_FRAMER) {
++			dev_notice(&wc->dev->dev, "TX(");
++			for (i = 0; i < size; i++)
++				dev_notice(&wc->dev->dev, "%s%02x",
++						(i ? " " : ""), buf[i]);
++			dev_notice(&wc->dev->dev, ")\n");
++		}
++
++		for (i = 0; i < size; i++)
++			t4_framer_out(wc, span, FRMR_TXFIFO, buf[i]);
++
++		if (res) /* End of message */ {
++			if (debug & DEBUG_FRAMER)
++				dev_notice(&wc->dev->dev,
++					"transmiting XHF|XME\n");
++			t4_framer_cmd_wait(wc, span, FRMR_CMDR_XHF | FRMR_CMDR_XME);
++#if 0
++			ts->sigactive = (__t4_framer_in(wc, span, FRMR_SIS) & FRMR_SIS_XFW) ? 0 : 1;
++#endif
++			++ts->frames_out;
++			if ((debug & DEBUG_FRAMER) && !(ts->frames_out & 0x0f))
++				dev_notice(&wc->dev->dev, "Transmitted %d "
++					"frames on span %d\n", ts->frames_out,
++					span);
++		} else { /* Still more to transmit */
++			if (debug & DEBUG_FRAMER)
++				dev_notice(&wc->dev->dev, "transmiting XHF\n");
++			t4_framer_cmd_wait(wc, span, FRMR_CMDR_XHF);
++		}
++	}
++	else if (res < 0)
++		ts->sigactive = 0;
++}
++
++static void t4_hdlc_hard_xmit(struct dahdi_chan *chan)
++{
++	struct t4 *wc = chan->pvt;
++	int span = chan->span->offset;
++	struct t4_span *ts = wc->tspans[span];
++	unsigned long flags; 
++
++	spin_lock_irqsave(&wc->reglock, flags);
++	if (!ts->sigchan) {
++		dev_notice(&wc->dev->dev, "t4_hdlc_hard_xmit: Invalid (NULL) "
++				"signalling channel\n");
++		spin_unlock_irqrestore(&wc->reglock, flags);
++		return;
++	}
++	spin_unlock_irqrestore(&wc->reglock, flags);
++
++	if (debug & DEBUG_FRAMER)
++		dev_notice(&wc->dev->dev, "t4_hdlc_hard_xmit on channel %s "
++				"(sigchan %s), sigactive=%d\n", chan->name,
++				ts->sigchan->name, ts->sigactive);
++
++	if ((ts->sigchan == chan) && !ts->sigactive)
++		t4_hdlc_xmit_fifo(wc, span, ts);
++}
++
++static int t4_maint(struct dahdi_span *span, int cmd)
++{
++	struct t4_span *ts = t4_from_span(span);
++	struct t4 *wc = ts->owner;
++	unsigned int reg;
++#ifdef DAHDI_SPAN_OPS
++	unsigned long flags;
++#endif
++
++	if (ts->spantype == TYPE_E1) {
++		switch(cmd) {
++		case DAHDI_MAINT_NONE:
++			dev_info(&wc->dev->dev, "Clearing all maint modes\n");
++			t4_clear_maint(span);
++			break;
++		case DAHDI_MAINT_LOCALLOOP:
++			dev_info(&wc->dev->dev,
++				 "Turning on local loopback\n");
++			t4_clear_maint(span);
++			reg = t4_framer_in(wc, span->offset, LIM0_T);
++			t4_framer_out(wc, span->offset, LIM0_T, (reg|LIM0_LL));
++			break;
++#ifdef DAHDI_SPAN_OPS
++		case DAHDI_MAINT_NETWORKLINELOOP:
++			dev_info(&wc->dev->dev,
++				 "Turning on network line loopback\n");
++			t4_clear_maint(span);
++			reg = t4_framer_in(wc, span->offset, LIM1_T);
++			t4_framer_out(wc, span->offset, LIM1_T, (reg|LIM1_RL));
++			break;
++		case DAHDI_MAINT_NETWORKPAYLOADLOOP:
++			dev_info(&wc->dev->dev,
++				 "Turning on network payload loopback\n");
++			t4_clear_maint(span);
++			reg = t4_framer_in(wc, span->offset, FMR2_T);
++			t4_framer_out(wc, span->offset, FMR2_T, (reg|FMR2_PLB));
++			break;
++#endif
++		case DAHDI_MAINT_LOOPUP:
++		case DAHDI_MAINT_LOOPDOWN:
++			dev_info(&wc->dev->dev,
++				"Loopup & loopdown supported in E1 mode\n");
++			return -ENOSYS;
++#ifdef DAHDI_SPAN_OPS
++		case DAHDI_MAINT_FAS_DEFECT:
++			t4_framer_out(wc, span->offset, IERR_T, IFASE);
++			break;
++		case DAHDI_MAINT_MULTI_DEFECT:
++			t4_framer_out(wc, span->offset, IERR_T, IMFE);
++			break;
++		case DAHDI_MAINT_CRC_DEFECT:
++			t4_framer_out(wc, span->offset, IERR_T, ICRCE);
++			break;
++		case DAHDI_MAINT_CAS_DEFECT:
++			t4_framer_out(wc, span->offset, IERR_T, ICASE);
++			break;
++		case DAHDI_MAINT_PRBS_DEFECT:
++			t4_framer_out(wc, span->offset, IERR_T, IPE);
++			break;
++		case DAHDI_MAINT_BIPOLAR_DEFECT:
++			t4_framer_out(wc, span->offset, IERR_T, IBV);
++			break;
++		case DAHDI_RESET_COUNTERS:
++			t4_reset_counters(span);
++			break;
++		case DAHDI_MAINT_ALARM_SIM:
++			dev_info(&wc->dev->dev, "Invoking alarm state");
++			reg = t4_framer_in(wc, span->offset, FMR0);
++			t4_framer_out(wc, span->offset, FMR0, (reg|FMR0_SIM));
++			break;
++#endif
++		default:
++			dev_info(&wc->dev->dev,
++					"Unknown E1 maint command: %d\n", cmd);
++			return -ENOSYS;
++		}
++	} else {
++		switch(cmd) {
++		case DAHDI_MAINT_NONE:
++			dev_info(&wc->dev->dev, "Clearing all maint modes\n");
++			t4_clear_maint(span);
++			break;
++		case DAHDI_MAINT_LOCALLOOP:
++			dev_info(&wc->dev->dev,
++				 "Turning on local loopback\n");
++			t4_clear_maint(span);
++			reg = t4_framer_in(wc, span->offset, LIM0_T);
++			t4_framer_out(wc, span->offset, LIM0_T, (reg|LIM0_LL));
++			break;
++#if (defined(DAHDI_SPAN_OPS) || defined(DAHDI_SPAN_MODULE) )
++		case DAHDI_MAINT_NETWORKLINELOOP:
++			dev_info(&wc->dev->dev,
++				 "Turning on network line loopback\n");
++			t4_clear_maint(span);
++			reg = t4_framer_in(wc, span->offset, LIM1_T);
++			t4_framer_out(wc, span->offset, LIM1_T, (reg|LIM1_RL));
++			break;
++		case DAHDI_MAINT_NETWORKPAYLOADLOOP:
++			dev_info(&wc->dev->dev,
++				 "Turning on network payload loopback\n");
++			t4_clear_maint(span);
++			reg = t4_framer_in(wc, span->offset, FMR2_T);
++			t4_framer_out(wc, span->offset, FMR2_T, (reg|FMR2_PLB));
++			break;
++#endif
++		case DAHDI_MAINT_LOOPUP:
++			dev_info(&wc->dev->dev, "Transmitting loopup code\n");
++			t4_clear_maint(span);
++			t4_framer_out(wc, span->offset, 0x21, 0x50);
++			break;
++		case DAHDI_MAINT_LOOPDOWN:
++			dev_info(&wc->dev->dev, "Transmitting loopdown code\n");
++			t4_clear_maint(span);
++			t4_framer_out(wc, span->offset, 0x21, 0x60);
++			break;
++#if (defined(DAHDI_SPAN_OPS) || defined(DAHDI_SPAN_MODULE) )
++		case DAHDI_MAINT_FAS_DEFECT:
++			t4_framer_out(wc, span->offset, IERR_T, IFASE);
++			break;
++		case DAHDI_MAINT_MULTI_DEFECT:
++			t4_framer_out(wc, span->offset, IERR_T, IMFE);
++			break;
++		case DAHDI_MAINT_CRC_DEFECT:
++			t4_framer_out(wc, span->offset, IERR_T, ICRCE);
++			break;
++		case DAHDI_MAINT_CAS_DEFECT:
++			t4_framer_out(wc, span->offset, IERR_T, ICASE);
++			break;
++		case DAHDI_MAINT_PRBS_DEFECT:
++			t4_framer_out(wc, span->offset, IERR_T, IPE);
++			break;
++		case DAHDI_MAINT_BIPOLAR_DEFECT:
++			t4_framer_out(wc, span->offset, IERR_T, IBV);
++			break;
++		case DAHDI_MAINT_PRBS:
++			dev_info(&wc->dev->dev, "PRBS not supported\n");
++#if 0
++			dev_notice(&wc->dev->dev, "Enabling PRBS!\n");
++			span->mainttimer = 1;
++			/* Enable PRBS monitor */
++			reg = t4_framer_in(wc, span->offset, LCR1_T);
++			reg |= EPRM;
++
++			/* Setup PRBS xmit */
++			t4_framer_out(wc, span->offset, TPC0_T, 0);
++
++			/* Enable PRBS transmit */
++			reg |= XPRBS;
++			reg &= ~LLBP;
++			reg &= ~FLLB;
++			t4_framer_out(wc, span->offset, LCR1_T, reg);
++#endif
++			return -ENOSYS;
++		case DAHDI_RESET_COUNTERS:
++			t4_reset_counters(span);
++			break;
++#endif
++#ifdef DAHDI_SPAN_OPS
++		case DAHDI_MAINT_ALARM_SIM:
++			reg = t4_framer_in(wc, span->offset, FMR0);
++
++			/*
++			 * The alarm simulation state machine requires us to
++			 * bring this bit up and down for at least 1 clock cycle
++			 */
++			spin_lock_irqsave(&wc->reglock, flags);
++			__t4_framer_out(wc, span->offset,
++					FMR0, (reg | FMR0_SIM));
++			udelay(1);
++			__t4_framer_out(wc, span->offset,
++					FMR0, (reg & ~FMR0_SIM));
++			udelay(1);
++			spin_unlock_irqrestore(&wc->reglock, flags);
++
++			reg = t4_framer_in(wc, span->offset, 0x4e);
++			if (debug & DEBUG_MAIN) {
++				dev_info(&wc->dev->dev,
++					"FRS2(alarm state): %d\n",
++					((reg & 0xe0) >> 5));
++			}
++			break;
++#endif
++		default:
++			dev_info(&wc->dev->dev, "Unknown T1 maint command:%d\n",
++									cmd);
++			break;
++	   }
++    }
++	return 0;
++}
++
++static int t4_clear_maint(struct dahdi_span *span)
++{
++	struct t4_span *ts = t4_from_span(span);
++	struct t4 *wc = ts->owner;
++	unsigned int reg;
++
++	/* Clear local loop */
++	reg = t4_framer_in(wc, span->offset, LIM0_T);
++	t4_framer_out(wc, span->offset, LIM0_T, (reg & ~LIM0_LL));
++
++	/* Clear Remote Loop */
++	reg = t4_framer_in(wc, span->offset, LIM1_T);
++	t4_framer_out(wc, span->offset, LIM1_T, (reg & ~LIM1_RL));
++
++	/* Clear Remote Payload Loop */
++	reg = t4_framer_in(wc, span->offset, FMR2_T);
++	t4_framer_out(wc, span->offset, FMR2_T, (reg & ~FMR2_PLB));
++
++	/* Clear PRBS */
++	reg = t4_framer_in(wc, span->offset, LCR1_T);
++	t4_framer_out(wc, span->offset, LCR1_T, (reg & ~(XPRBS | EPRM)));
++
++	span->mainttimer = 0;
++
++	return 0;
++}
++
++#if (defined(DAHDI_SPAN_OPS) || defined(DAHDI_SPAN_MODULE) )
++static int t4_reset_counters(struct dahdi_span *span)
++{
++	struct t4_span *ts = t4_from_span(span);
++	memset(&ts->span.count, 0, sizeof(ts->span.count));
++	return 0;
++}
++#endif
++
++static int t4_rbsbits(struct dahdi_chan *chan, int bits)
++{
++	u_char m,c;
++	int k,n,b;
++	struct t4 *wc = chan->pvt;
++	struct t4_span *ts = wc->tspans[chan->span->offset];
++	unsigned long flags;
++	
++	if (debug & DEBUG_RBS)
++		dev_notice(&wc->dev->dev, "Setting bits to %d on channel %s\n",
++				bits, chan->name);
++	spin_lock_irqsave(&wc->reglock, flags);	
++	k = chan->span->offset;
++	if (ts->spantype == TYPE_E1) { /* do it E1 way */
++		if (chan->chanpos == 16) {
++			spin_unlock_irqrestore(&wc->reglock, flags);
++			return 0;
++		}
++		n = chan->chanpos - 1;
++		if (chan->chanpos > 15) n--;
++		b = (n % 15);
++		c = ts->txsigs[b];
++		m = (n / 15) << 2; /* nibble selector */
++		c &= (0xf << m); /* keep the other nibble */
++		c |= (bits & 0xf) << (4 - m); /* put our new nibble here */
++		ts->txsigs[b] = c;
++		  /* output them to the chip */
++		__t4_framer_out(wc,k,0x71 + b,c); 
++	} else if (ts->span.lineconfig & DAHDI_CONFIG_D4) {
++		n = chan->chanpos - 1;
++		b = (n/4);
++		c = ts->txsigs[b];
++		m = ((3 - (n % 4)) << 1); /* nibble selector */
++		c &= ~(0x3 << m); /* keep the other nibble */
++		c |= ((bits >> 2) & 0x3) << m; /* put our new nibble here */
++		ts->txsigs[b] = c;
++		  /* output them to the chip */
++		__t4_framer_out(wc,k,0x70 + b,c); 
++		__t4_framer_out(wc,k,0x70 + b + 6,c); 
++	} else if (ts->span.lineconfig & DAHDI_CONFIG_ESF) {
++		n = chan->chanpos - 1;
++		b = (n/2);
++		c = ts->txsigs[b];
++		m = ((n % 2) << 2); /* nibble selector */
++		c &= (0xf << m); /* keep the other nibble */
++		c |= (bits & 0xf) << (4 - m); /* put our new nibble here */
++		ts->txsigs[b] = c;
++		  /* output them to the chip */
++		__t4_framer_out(wc,k,0x70 + b,c); 
++	} 
++	spin_unlock_irqrestore(&wc->reglock, flags);
++	if (debug & DEBUG_RBS)
++		dev_notice(&wc->dev->dev, "Finished setting RBS bits\n");
++	return 0;
++}
++
++static int t4_shutdown(struct dahdi_span *span)
++{
++	int tspan;
++	int wasrunning;
++	unsigned long flags;
++	struct t4_span *ts = t4_from_span(span);
++	struct t4 *wc = ts->owner;
++
++	tspan = span->offset + 1;
++	if (tspan < 0) {
++		dev_notice(&wc->dev->dev, "opvxd115: Span '%d' isn't us?\n",
++				span->spanno);
++		return -1;
++	}
++
++	if (debug & DEBUG_MAIN)
++		dev_notice(&wc->dev->dev, "Shutting down span %d (%s)\n",
++				span->spanno, span->name);
++
++	/* Stop HDLC controller if runned */
++	if (ts->sigchan)
++		hdlc_stop(wc, span->offset);
++	
++	spin_lock_irqsave(&wc->reglock, flags);
++	wasrunning = span->flags & DAHDI_FLAG_RUNNING;
++
++	span->flags &= ~DAHDI_FLAG_RUNNING;
++	__t4_set_led(wc, span->offset, WC_OFF);
++	if ((wc->numspans == 1) && 
++	    (!(wc->tspans[0]->span.flags & DAHDI_FLAG_RUNNING))) {
++		/* No longer in use, disable interrupts */
++		dev_info(&wc->dev->dev, "opvxd115: Disabling interrupts since "
++				"there are no active spans\n");
++		set_bit(T4_STOP_DMA, &wc->checkflag);
++	} else
++		set_bit(T4_CHECK_TIMING, &wc->checkflag);
++
++	spin_unlock_irqrestore(&wc->reglock, flags);
++
++	/* Wait for interrupt routine to shut itself down */
++	msleep(10);
++	if (wasrunning)
++		wc->spansstarted--;
++
++	if (debug & DEBUG_MAIN)
++		dev_notice(&wc->dev->dev, "Span %d (%s) shutdown\n",
++				span->spanno, span->name);
++	return 0;
++}
++
++static void t4_chan_set_sigcap(struct dahdi_span *span, int x)
++{
++	struct t4_span *wc = container_of(span, struct t4_span, span);
++	struct dahdi_chan *chan = wc->chans[x];
++	chan->sigcap = DAHDI_SIG_CLEAR;
++	/* E&M variant supported depends on span type */
++	if (wc->spantype == TYPE_E1) {
++		/* E1 sigcap setup */
++		if (span->lineconfig & DAHDI_CONFIG_CCS) {
++			/* CCS setup */
++			chan->sigcap |= DAHDI_SIG_MTP2 | DAHDI_SIG_SF |
++				DAHDI_SIG_HARDHDLC;
++			return;
++		}
++		/* clear out sig and sigcap for channel 16 on E1 CAS
++		 * lines, otherwise, set it correctly */
++		if (x == 15) {
++			/* CAS signaling channel setup */
++			wc->chans[15]->sigcap = 0;
++			wc->chans[15]->sig = 0;
++			return;
++		}
++		/* normal CAS setup */
++		chan->sigcap |= DAHDI_SIG_EM_E1 | DAHDI_SIG_FXSLS |
++			DAHDI_SIG_FXSGS | DAHDI_SIG_FXSKS | DAHDI_SIG_SF |
++			DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_FXOKS |
++			DAHDI_SIG_CAS | DAHDI_SIG_DACS_RBS;
++	} else {
++		/* T1 sigcap setup */
++		chan->sigcap |= DAHDI_SIG_EM | DAHDI_SIG_FXSLS |
++			DAHDI_SIG_FXSGS | DAHDI_SIG_FXSKS | DAHDI_SIG_MTP2 |
++			DAHDI_SIG_SF | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS |
++			DAHDI_SIG_FXOKS | DAHDI_SIG_CAS | DAHDI_SIG_DACS_RBS |
++			DAHDI_SIG_HARDHDLC;
++	}
++}
++
++static int t4_spanconfig(struct file *file, struct dahdi_span *span,
++		struct dahdi_lineconfig *lc)
++{
++	int i;
++	struct t4_span *ts = t4_from_span(span);
++	struct t4 *wc = ts->owner;
++
++	if (debug)
++		dev_info(&wc->dev->dev, "About to enter spanconfig!\n");
++	if (debug & DEBUG_MAIN)
++		dev_notice(&wc->dev->dev, "opvxd115: Configuring span %d\n",
++				span->spanno);
++
++	if (lc->sync < 0)
++		lc->sync = 0;
++	if (lc->sync > wc->numspans)
++		lc->sync = 0;
++	
++	/* remove this span number from the current sync sources, if there */
++	for(i = 0; i < wc->numspans; i++) {
++		if (wc->tspans[i]->sync == span->spanno) {
++			wc->tspans[i]->sync = 0;
++			wc->tspans[i]->psync = 0;
++		}
++	}
++	wc->tspans[span->offset]->syncpos = lc->sync;
++	/* if a sync src, put it in proper place */
++	if (lc->sync) {
++		wc->tspans[lc->sync - 1]->sync = span->spanno;
++		wc->tspans[lc->sync - 1]->psync = span->offset + 1;
++	}
++	set_bit(T4_CHECK_TIMING, &wc->checkflag);
++
++	/* Make sure this is clear in case of multiple startup and shutdown
++	 * iterations */
++	clear_bit(T4_STOP_DMA, &wc->checkflag);
++	
++	/* make sure that sigcaps gets updated if necessary */
++	for (i = 0; i < span->channels; i++)
++		t4_chan_set_sigcap(span, i);
++
++	/* If we're already running, then go ahead and apply the changes */
++	if (span->flags & DAHDI_FLAG_RUNNING)
++		return t4_startup(file, span);
++
++	if (debug)
++		dev_info(&wc->dev->dev, "Done with spanconfig!\n");
++	return 0;
++}
++
++static int t4_chanconfig(struct file *file, struct dahdi_chan *chan,
++		int sigtype)
++{
++	int alreadyrunning;
++	unsigned long flags;
++	struct t4 *wc = chan->pvt;
++	struct t4_span *ts = wc->tspans[chan->span->offset];
++
++	alreadyrunning = ts->span.flags & DAHDI_FLAG_RUNNING;
++	if (debug & DEBUG_MAIN) {
++		if (alreadyrunning)
++			dev_notice(&wc->dev->dev, "opvxd115: Reconfigured "
++				"channel %d (%s) sigtype %d\n",
++				chan->channo, chan->name, sigtype);
++		else
++			dev_notice(&wc->dev->dev, "opvxd115: Configured channel"
++				" %d (%s) sigtype %d\n",
++				chan->channo, chan->name, sigtype);
++	}
++
++	spin_lock_irqsave(&wc->reglock, flags);	
++
++	if (alreadyrunning)
++		__set_clear(wc, chan->span->offset);
++
++	spin_unlock_irqrestore(&wc->reglock, flags);	
++
++	/* (re)configure signalling channel */
++	if ((sigtype == DAHDI_SIG_HARDHDLC) || (ts->sigchan == chan)) {
++		if (debug & DEBUG_FRAMER)
++			dev_notice(&wc->dev->dev, "%sonfiguring hardware HDLC "
++				"on %s\n",
++				((sigtype == DAHDI_SIG_HARDHDLC) ? "C" : "Unc"),
++				chan->name);
++		if (alreadyrunning) {
++			if (ts->sigchan)
++				hdlc_stop(wc, ts->sigchan->span->offset);
++			if (sigtype == DAHDI_SIG_HARDHDLC) {
++				if (hdlc_start(wc, chan->span->offset, chan, ts->sigmode)) {
++					dev_notice(&wc->dev->dev, "Error "
++						"initializing signalling "
++						"controller\n");
++					return -1;
++				}
++			} else {
++				spin_lock_irqsave(&wc->reglock, flags);
++				ts->sigchan = NULL;
++				spin_unlock_irqrestore(&wc->reglock, flags);
++			}
++		
++		}
++		else {
++			spin_lock_irqsave(&wc->reglock, flags);
++			ts->sigchan = (sigtype == DAHDI_SIG_HARDHDLC) ? chan : NULL;
++			spin_unlock_irqrestore(&wc->reglock, flags);
++			ts->sigactive = 0;
++		}
++	}
++	return 0;
++}
++
++static int t4_open(struct dahdi_chan *chan)
++{
++	return 0;
++}
++
++static int t4_close(struct dahdi_chan *chan)
++{
++	return 0;
++}
++
++static void set_span_devicetype(struct t4 *wc)
++{
++	struct dahdi_device *ddev = wc->ddev;
++	const char *devicetype_old = ddev->devicetype;
++	char *extra_str = "";
++
++	if (wc->vpm == T4_VPM_PRESENT)
++		extra_str = (!wc->vpm450m) ? " (VPM400M)" : " (VPMOCT032)",
++	wc->ddev->devicetype = kasprintf(GFP_KERNEL, "%s%s",
++			wc->variety, extra_str);
++
++	/* On the off chance that we were able to allocate it previously. */
++	if (!wc->ddev->devicetype)
++		wc->ddev->devicetype = devicetype_old;
++	else
++		kfree(devicetype_old);
++}
++
++/* The number of cards we have seen with each
++   possible 'order' switch setting.
++*/
++static unsigned int order_index[16];
++
++static void setup_chunks(struct t4 *wc, int which)
++{
++	struct t4_span *ts;
++	int offset = 1;
++	int x, y;
++	int gen2;
++
++	if (!wc->t1e1)
++		offset += 4;
++
++	gen2 = (wc->tspans[0]->spanflags & FLAG_2NDGEN);
++
++	for (x = 0; x < wc->numspans; x++) {
++		ts = wc->tspans[x];
++		ts->writechunk = (void *)(wc->writechunk + (x * 32 * 2) + (which * (1024 >> 2)));
++		ts->readchunk = (void *)(wc->readchunk + (x * 32 * 2) + (which * (1024 >> 2)));
++		for (y=0;y<wc->tspans[x]->span.channels;y++) {
++			struct dahdi_chan *mychans = ts->chans[y];
++			if (gen2) {
++				mychans->writechunk = (void *)(wc->writechunk + ((x * 32 + y + offset) * 2) + (which * (1024 >> 2)));
++				mychans->readchunk = (void *)(wc->readchunk + ((x * 32 + y + offset) * 2) + (which * (1024 >> 2)));
++			}
++		}
++	}
++}
++
++#ifdef DAHDI_SPAN_OPS
++static const struct dahdi_span_ops t4_gen1_span_ops = {
++	.owner = THIS_MODULE,
++	.spanconfig = t4_spanconfig,
++	.chanconfig = t4_chanconfig,
++	.startup = t4_startup,
++	.shutdown = t4_shutdown,
++	.rbsbits = t4_rbsbits,
++	.maint = t4_maint,
++	.open = t4_open,
++	.close  = t4_close,
++	.ioctl = t4_ioctl,
++	.hdlc_hard_xmit = t4_hdlc_hard_xmit,
++};
++
++static const struct dahdi_span_ops t4_gen2_span_ops = {
++	.owner = THIS_MODULE,
++	.spanconfig = t4_spanconfig,
++	.chanconfig = t4_chanconfig,
++	.startup = t4_startup,
++	.shutdown = t4_shutdown,
++	.rbsbits = t4_rbsbits,
++	.maint = t4_maint,
++	.open = t4_open,
++	.close  = t4_close,
++	.ioctl = t4_ioctl,
++	.hdlc_hard_xmit = t4_hdlc_hard_xmit,
++	.dacs = t4_dacs,
++#ifdef VPM_SUPPORT
++	.echocan_create = t4_echocan_create,
++#endif
++};
++#endif
++
++static void init_spans(struct t4 *wc)
++{
++	int x,y;
++	int gen2;
++	struct t4_span *ts;
++	unsigned int reg;
++
++	wc->ddev->manufacturer = "OpenVox";
++	if (order_index[wc->order] == 1)
++		wc->ddev->location = kasprintf(GFP_KERNEL,
++				"Board ID Switch %d", wc->order);
++	else
++		wc->ddev->location = kasprintf(GFP_KERNEL,
++				"PCI Bus %02d Slot %02d",
++				wc->dev->bus->number,
++				PCI_SLOT(wc->dev->devfn) + 1);
++	if (!wc->ddev->location)
++		return; /* FIXME: Error handling */
++
++	gen2 = (wc->tspans[0]->spanflags & FLAG_2NDGEN);
++	for (x = 0; x < wc->numspans; x++) {
++		ts = wc->tspans[x];
++		sprintf(ts->span.name, "D115/D130/%d/%d", wc->num, x + 1);
++		snprintf(ts->span.desc, sizeof(ts->span.desc) - 1,
++			 "D115/D130 (E1|T1) Card %d Span %d", wc->num, x+1);
++		switch (ts->spantype) {
++		case TYPE_T1:
++			ts->span.spantype = SPANTYPE_DIGITAL_T1;
++			break;
++		case TYPE_E1:
++			ts->span.spantype = SPANTYPE_DIGITAL_E1;
++			break;
++		case TYPE_J1:
++			ts->span.spantype = SPANTYPE_DIGITAL_J1;
++			break;
++		}
++#ifdef DAHDI_SPAN_MODULE	
++		ts->span.owner = THIS_MODULE;
++#endif
++#ifdef DAHDI_SPAN_OPS
++		if (gen2) {
++			ts->span.ops = &t4_gen2_span_ops;
++		} else {
++			ts->span.ops = &t4_gen1_span_ops;
++		}
++#else
++		ts->span.spanconfig = t4_spanconfig;
++		ts->span.chanconfig = t4_chanconfig;
++		ts->span.startup = t4_startup;
++		ts->span.shutdown = t4_shutdown;
++		ts->span.rbsbits = t4_rbsbits;
++		ts->span.maint = t4_maint;
++		ts->span.open = t4_open;
++		ts->span.close  = t4_close;
++		ts->span.ioctl = t4_ioctl;
++		ts->span.hdlc_hard_xmit = t4_hdlc_hard_xmit;
++		if (gen2) {
++#ifdef VPM_SUPPORT
++		if (vpmsupport)
++			ts->span.echocan_create = t4_echocan_create;
++#endif			
++			ts->span.dacs = t4_dacs;
++		}
++		ts->span.pvt = ts;
++#endif
++
++		/* HDLC Specific init */
++		ts->sigchan = NULL;
++		ts->sigmode = sigmode;
++		ts->sigactive = 0;
++		
++		if (ts->spantype == TYPE_T1 || ts->spantype == TYPE_J1) {
++			ts->span.channels = 24;
++			ts->span.deflaw = DAHDI_LAW_MULAW;
++			ts->span.linecompat = DAHDI_CONFIG_AMI |
++				DAHDI_CONFIG_B8ZS | DAHDI_CONFIG_D4 |
++				DAHDI_CONFIG_ESF;
++		} else {
++			ts->span.channels = 31;
++			ts->span.deflaw = DAHDI_LAW_ALAW;
++			ts->span.linecompat = DAHDI_CONFIG_AMI |
++				DAHDI_CONFIG_HDB3 | DAHDI_CONFIG_CCS |
++				DAHDI_CONFIG_CRC4;
++		}
++		ts->span.chans = ts->chans;
++		ts->span.flags = DAHDI_FLAG_RBS;
++
++		ts->owner = wc;
++		ts->span.offset = x;
++		ts->writechunk = (void *)(wc->writechunk + x * 32 * 2);
++		ts->readchunk = (void *)(wc->readchunk + x * 32 * 2);
++
++		for (y=0;y<wc->tspans[x]->span.channels;y++) {
++			struct dahdi_chan *mychans = ts->chans[y];
++			sprintf(mychans->name, "D115/D130/%d/%d/%d", wc->num, x + 1, y + 1);
++			t4_chan_set_sigcap(&ts->span, x);
++			mychans->pvt = wc;
++			mychans->chanpos = y + 1;
++		}
++
++		/* Enable 1sec timer interrupt */
++		reg = t4_framer_in(wc, x, FMR1_T);
++		t4_framer_out(wc, x, FMR1_T, (reg | FMR1_ECM));
++
++		/* Enable Errored Second interrupt */
++		t4_framer_out(wc, x, ESM, 0);
++
++#if (defined(DAHDI_SPAN_OPS) || defined(DAHDI_SPAN_MODULE) )
++		t4_reset_counters(&ts->span);
++#endif
++	}
++
++	set_span_devicetype(wc);
++	setup_chunks(wc, 0);
++	wc->lastindex = 0;
++}
++
++static void t4_serial_setup(struct t4 *wc, int unit)
++{
++	if (!wc->globalconfig) {
++		wc->globalconfig = 1;
++		if (debug)
++			dev_info(&wc->dev->dev, "opvxd115: Setting up global "
++					"serial parameters\n");
++		t4_framer_out(wc, 0, 0x85, 0xe0);	/* GPC1: Multiplex mode enabled, FSC is output, active low, RCLK from channel 0 */
++		t4_framer_out(wc, 0, 0x08, 0x01);	/* IPC: Interrupt push/pull active low */
++	
++		/* Global clocks (8.192 Mhz CLK) */
++		t4_framer_out(wc, 0, 0x92, 0x00);	
++		t4_framer_out(wc, 0, 0x93, 0x18);
++		t4_framer_out(wc, 0, 0x94, 0xfb);
++		t4_framer_out(wc, 0, 0x95, 0x0b);
++		t4_framer_out(wc, 0, 0x96, 0x00);
++		t4_framer_out(wc, 0, 0x97, 0x0b);
++		t4_framer_out(wc, 0, 0x98, 0xdb);
++		t4_framer_out(wc, 0, 0x99, 0xdf);
++	}
++
++	/* Configure interrupts */	
++	t4_framer_out(wc, unit, FRMR_GCR, 0x00);	/* GCR: Interrupt on Activation/Deactivation of each */
++
++	/* Configure system interface */
++	t4_framer_out(wc, unit, FRMR_SIC1, 0xc2);	/* SIC1: 8.192 Mhz clock/bus, double buffer receive / transmit, byte interleaved */
++	t4_framer_out(wc, unit, FRMR_SIC2, 0x20 | (unit << 1)); /* SIC2: No FFS, no center receive eliastic buffer, phase */
++	t4_framer_out(wc, unit, FRMR_SIC3, 0x04);	/* SIC3: Edges for capture */
++	t4_framer_out(wc, unit, FRMR_CMR2, 0x00);	/* CMR2: We provide sync and clock for tx and rx. */
++	if (!wc->t1e1) { /* T1 mode */
++		t4_framer_out(wc, unit, FRMR_XC0, 0x03);	/* XC0: Normal operation of Sa-bits */
++		t4_framer_out(wc, unit, FRMR_XC1, 0x84);	/* XC1: 0 offset */
++		if (wc->tspans[unit]->spantype == TYPE_J1)
++			t4_framer_out(wc, unit, FRMR_RC0, 0x83);	/* RC0: Just shy of 1023 */
++		else
++			t4_framer_out(wc, unit, FRMR_RC0, 0x03);	/* RC0: Just shy of 1023 */
++		t4_framer_out(wc, unit, FRMR_RC1, 0x84);	/* RC1: The rest of RC0 */
++	} else { /* E1 mode */
++		t4_framer_out(wc, unit, FRMR_XC0, 0x00);	/* XC0: Normal operation of Sa-bits */
++		t4_framer_out(wc, unit, FRMR_XC1, 0x04);	/* XC1: 0 offset */
++		t4_framer_out(wc, unit, FRMR_RC0, 0x04);	/* RC0: Just shy of 1023 */
++		t4_framer_out(wc, unit, FRMR_RC1, 0x04);	/* RC1: The rest of RC0 */
++	}
++	
++	/* Configure ports */
++	t4_framer_out(wc, unit, 0x80, 0x00);	/* PC1: SPYR/SPYX input on RPA/XPA */
++	if (wc->falc31) {
++			  t4_framer_out(wc, unit, 0x81, 0xBB);	/* PC2: RMFB/XSIG output/input on RPB/XPB */
++			  t4_framer_out(wc, unit, 0x82, 0xBB);	/* PC3: Some unused stuff */
++			  t4_framer_out(wc, unit, 0x83, 0xBB);	/* PC4: Some more unused stuff */
++	} else {
++			  t4_framer_out(wc, unit, 0x81, 0x22);	/* PC2: RMFB/XSIG output/input on RPB/XPB */
++			  t4_framer_out(wc, unit, 0x82, 0x65);	/* PC3: Some unused stuff */
++			  t4_framer_out(wc, unit, 0x83, 0x35);	/* PC4: Some more unused stuff */
++	}
++	t4_framer_out(wc, unit, 0x84, 0x01);	/* PC5: XMFS active low, SCLKR is input, RCLK is output */
++	if (debug & DEBUG_MAIN)
++		dev_notice(&wc->dev->dev, "Successfully initialized serial "
++				"bus for unit %d\n", unit);
++}
++
++static int syncsrc = 0;
++static int syncnum = 0 /* -1 */;
++static int syncspan = 0;
++#ifdef DEFINE_SPINLOCK
++static DEFINE_SPINLOCK(synclock);
++#else
++static spinlock_t synclock = SPIN_LOCK_UNLOCKED;
++#endif
++
++static void __t4_set_rclk_src(struct t4 *wc, int span)
++{
++	int cmr1 = 0x38;	/* Clock Mode: RCLK sourced by DCO-R1
++				   by default, Disable Clock-Switching */
++
++	cmr1 |= (span << 6);
++	__t4_framer_out(wc, 0, 0x44, cmr1);
++
++	dev_info(&wc->dev->dev, "RCLK source set to span %d\n", span+1);
++}
++
++static void __t4_set_sclk_src(struct t4 *wc, int mode, int master, int slave)
++{
++	if (slave) {
++		wc->dmactrl |= (1 << 25);
++		dev_info(&wc->dev->dev, "SCLK is slaved to timing cable\n");
++	} else {
++		wc->dmactrl &= ~(1 << 25);
++	}
++
++	if (master) {
++		wc->dmactrl |= (1 << 24);
++		dev_info(&wc->dev->dev, "SCLK is master to timing cable\n");
++	} else {
++		wc->dmactrl &= ~(1 << 24);
++	}
++
++	if (mode == WC_RECOVER)
++		wc->dmactrl |= (1 << 29); /* Recover timing from RCLK */
++
++	if (mode == WC_SELF)
++		wc->dmactrl &= ~(1 << 29);/* Provide timing from MCLK */
++
++	__t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++}
++
++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18))
++static ssize_t t4_timing_master_show(struct device *dev,
++				     struct device_attribute *attr,
++				     char *buf)
++{
++	struct t4 *wc = dev_get_drvdata(dev);
++	if (wc->dmactrl & (1 << 29))
++		return sprintf(buf, "%d\n", wc->syncsrc);
++	else
++		return sprintf(buf, "%d\n", -1);
++}
++
++static DEVICE_ATTR(timing_master, 0400, t4_timing_master_show, NULL);
++
++static void create_sysfs_files(struct t4 *wc)
++{
++	int ret;
++	ret = device_create_file(&wc->dev->dev,
++				 &dev_attr_timing_master);
++	if (ret) {
++		dev_info(&wc->dev->dev,
++			"Failed to create device attributes.\n");
++	}
++}
++
++static void remove_sysfs_files(struct t4 *wc)
++{
++	device_remove_file(&wc->dev->dev,
++			   &dev_attr_timing_master);
++}
++
++#else
++
++static inline void create_sysfs_files(struct t4 *wc) { return; }
++static inline void remove_sysfs_files(struct t4 *wc) { return; }
++
++#endif /* LINUX_KERNEL > 2.6.18 */
++
++static inline void __t4_update_timing(struct t4 *wc)
++{
++	int i;
++	/* update sync src info */
++	if (wc->syncsrc != syncsrc) {
++		dev_info(&wc->dev->dev, "Swapping card %d from %d to %d\n",
++				wc->num, wc->syncsrc, syncsrc);
++		wc->syncsrc = syncsrc;
++		/* Update sync sources */
++		for (i = 0; i < wc->numspans; i++) {
++			wc->tspans[i]->span.syncsrc = wc->syncsrc;
++		}
++		if (syncnum == wc->num) {
++			__t4_set_rclk_src(wc, syncspan-1);
++			__t4_set_sclk_src(wc, WC_RECOVER, 1, 0);
++			if (debug)
++				dev_notice(&wc->dev->dev, "Card %d, using sync "
++					"span %d, master\n", wc->num, syncspan);
++		} else {
++			__t4_set_sclk_src(wc, WC_RECOVER, 0, 1);
++			if (debug)
++				dev_notice(&wc->dev->dev, "Card %d, using "
++					"Timing Bus, NOT master\n", wc->num);
++		}
++	}
++}
++
++static int __t4_findsync(struct t4 *wc)
++{
++	int i;
++	int x;
++	unsigned long flags;
++	int p;
++	int nonzero;
++	int newsyncsrc = 0;			/* DAHDI span number */
++	int newsyncnum = 0;			/* opvxd115 card number */
++	int newsyncspan = 0;		/* span on given opvxd115 card */
++	spin_lock_irqsave(&synclock, flags);
++#if 1
++	if (!wc->num) {
++		/* If we're the first card, go through all the motions, up to 8 levels
++		   of sync source */
++		p = 1;
++		while (p < 8) {
++			nonzero = 0;
++			for (x=0;cards[x];x++) {
++				for (i = 0; i < wc->numspans; i++) {
++					if (cards[x]->tspans[i]->syncpos) {
++						nonzero = 1;
++						if ((cards[x]->tspans[i]->syncpos == p) &&
++						    !(cards[x]->tspans[i]->span.alarms & (DAHDI_ALARM_RED | DAHDI_ALARM_BLUE | DAHDI_ALARM_LOOPBACK)) &&
++							(cards[x]->tspans[i]->span.flags & DAHDI_FLAG_RUNNING)) {
++								/* This makes a good sync source */
++								newsyncsrc = cards[x]->tspans[i]->span.spanno;
++								newsyncnum = x;
++								newsyncspan = i + 1;
++								/* Jump out */
++								goto found;
++						}
++					}
++				}		
++			}
++			if (nonzero)
++				p++;
++			else 
++				break;
++		}
++found:		
++		if ((syncnum != newsyncnum) || (syncsrc != newsyncsrc) || (newsyncspan != syncspan)) {
++			if (debug)
++				dev_notice(&wc->dev->dev, "New syncnum: %d "
++					"(was %d), syncsrc: %d (was %d), "
++					"syncspan: %d (was %d)\n", newsyncnum,
++					syncnum, newsyncsrc, syncsrc,
++					newsyncspan, syncspan);
++			syncnum = newsyncnum;
++			syncsrc = newsyncsrc;
++			syncspan = newsyncspan;
++			for (x=0;cards[x];x++) {
++				__t4_update_timing(cards[x]);
++			}
++		}
++	}
++	__t4_update_timing(wc);
++#endif	
++	spin_unlock_irqrestore(&synclock, flags);
++	return 0;
++}
++
++static void __t4_set_timing_source_auto(struct t4 *wc)
++{
++	int x;
++	int firstprio, secondprio;
++	firstprio = secondprio = 4;
++
++	if (debug)
++		dev_info(&wc->dev->dev, "timing source auto\n");
++	clear_bit(T4_CHECK_TIMING, &wc->checkflag);
++	if (timingcable) {
++		__t4_findsync(wc);
++	} else {
++		if (debug)
++			dev_info(&wc->dev->dev, "Evaluating spans for timing "
++					"source\n");
++		for (x=0;x<wc->numspans;x++) {
++			if ((wc->tspans[x]->span.flags & DAHDI_FLAG_RUNNING) &&
++			   !(wc->tspans[x]->span.alarms & (DAHDI_ALARM_RED |
++							   DAHDI_ALARM_BLUE))) {
++				if (debug)
++					dev_info(&wc->dev->dev, "span %d is "
++						"green : syncpos %d\n", x+1,
++						wc->tspans[x]->syncpos);
++				if (wc->tspans[x]->syncpos) {
++					/* Valid rsync source in recovered
++					   timing mode */
++					if (firstprio == 4)
++						firstprio = x;
++					else if (wc->tspans[x]->syncpos <
++						wc->tspans[firstprio]->syncpos)
++						firstprio = x;
++				} else {
++					/* Valid rsync source in system timing
++					   mode */
++					if (secondprio == 4)
++						secondprio = x;
++				}
++			}
++		}
++		if (firstprio != 4) {
++			wc->syncsrc = firstprio;
++			__t4_set_rclk_src(wc, firstprio);
++			__t4_set_sclk_src(wc, WC_RECOVER, 0, 0);
++			dev_info(&wc->dev->dev, "Recovered timing mode, "\
++						"RCLK set to span %d\n",
++						firstprio+1);
++		} else if (secondprio != 4) {
++			wc->syncsrc = -1;
++			__t4_set_rclk_src(wc, secondprio);
++			__t4_set_sclk_src(wc, WC_SELF, 0, 0);
++			dev_info(&wc->dev->dev, "System timing mode, "\
++						"RCLK set to span %d\n",
++						secondprio+1);
++		} else {
++			wc->syncsrc = -1;
++			dev_info(&wc->dev->dev, "All spans in alarm : No valid"\
++						"span to source RCLK from\n");
++			/* Default rclk to lock with span 1 */
++			__t4_set_rclk_src(wc, 0);
++			__t4_set_sclk_src(wc, WC_SELF, 0, 0);
++		}
++	}
++}
++
++static void __t4_configure_t1(struct t4 *wc, int unit, int lineconfig, int txlevel)
++{
++	unsigned int fmr4, fmr2, fmr1, fmr0, lim2;
++	char *framing, *line;
++	int mytxlevel;
++	if ((txlevel > 7) || (txlevel < 4))
++		mytxlevel = 0;
++	else
++		mytxlevel = txlevel - 4;
++	fmr1 = 0x9c; /* FMR1: Mode 1, T1 mode, CRC on for ESF, 8.192 Mhz system data rate, no XAIS */
++	fmr2 = 0x20; /* FMR2: no payload loopback, don't auto yellow */
++	fmr4 = 0x0c; /* FMR4: Lose sync on 2 out of 5 framing bits, auto resync */
++	lim2 = 0x21; /* LIM2: 50% peak is a "1", Advanced Loss recovery */
++	lim2 |= (mytxlevel << 6);	/* LIM2: Add line buildout */
++	__t4_framer_out(wc, unit, 0x1d, fmr1);
++	__t4_framer_out(wc, unit, 0x1e, fmr2);
++
++	/* Configure line interface */
++	if (lineconfig & DAHDI_CONFIG_AMI) {
++		line = "AMI";
++		/* workaround for errata #2 in ES v3 09-10-16 */
++		fmr0 = (wc->falc31) ? 0xb0 : 0xa0;
++	} else {
++		line = "B8ZS";
++		fmr0 = 0xf0;
++	}
++	if (lineconfig & DAHDI_CONFIG_D4) {
++		framing = "D4";
++	} else {
++		framing = "ESF";
++		fmr4 |= 0x2;
++		fmr2 |= 0xc0;
++	}
++	__t4_framer_out(wc, unit, 0x1c, fmr0);
++	__t4_framer_out(wc, unit, 0x20, fmr4);
++	__t4_framer_out(wc, unit, 0x21, 0x40);	/* FMR5: Enable RBS mode */
++
++	__t4_framer_out(wc, unit, 0x37, 0xf0 );	/* LIM1: Clear data in case of LOS, Set receiver threshold (0.5V), No remote loop, no DRS */
++	__t4_framer_out(wc, unit, 0x36, 0x08);	/* LIM0: Enable auto long haul mode, no local loop (must be after LIM1) */
++
++	__t4_framer_out(wc, unit, 0x02, 0x50);	/* CMDR: Reset the receiver and transmitter line interface */
++	__t4_framer_out(wc, unit, 0x02, 0x00);	/* CMDR: Reset the receiver and transmitter line interface */
++
++	if (wc->falc31) {
++		if (debug)
++			dev_info(&wc->dev->dev, "card %d span %d: setting Rtx "
++					"to 0ohm for T1\n", wc->num, unit);
++		__t4_framer_out(wc, unit, 0x86, 0x00);	/* PC6: set Rtx to 0ohm for T1 */
++
++		// Hitting the bugfix register to fix errata #3
++		__t4_framer_out(wc, unit, 0xbd, 0x05);
++	}
++
++	__t4_framer_out(wc, unit, 0x3a, lim2);	/* LIM2: 50% peak amplitude is a "1" */
++	__t4_framer_out(wc, unit, 0x38, 0x0a);	/* PCD: LOS after 176 consecutive "zeros" */
++	__t4_framer_out(wc, unit, 0x39, 0x15);	/* PCR: 22 "ones" clear LOS */
++	
++	/* Generate pulse mask for T1 */
++	switch(mytxlevel) {
++	case 3:
++		__t4_framer_out(wc, unit, 0x26, 0x07);	/* XPM0 */
++		__t4_framer_out(wc, unit, 0x27, 0x01);	/* XPM1 */
++		__t4_framer_out(wc, unit, 0x28, 0x00);	/* XPM2 */
++		break;
++	case 2:
++		__t4_framer_out(wc, unit, 0x26, 0x8c);	/* XPM0 */
++		__t4_framer_out(wc, unit, 0x27, 0x11);	/* XPM1 */
++		__t4_framer_out(wc, unit, 0x28, 0x01);	/* XPM2 */
++		break;
++	case 1:
++		__t4_framer_out(wc, unit, 0x26, 0x8c);	/* XPM0 */
++		__t4_framer_out(wc, unit, 0x27, 0x01);	/* XPM1 */
++		__t4_framer_out(wc, unit, 0x28, 0x00);	/* XPM2 */
++		break;
++	case 0:
++	default:
++		__t4_framer_out(wc, unit, 0x26, 0xd7);	/* XPM0 */
++		__t4_framer_out(wc, unit, 0x27, 0x22);	/* XPM1 */
++		__t4_framer_out(wc, unit, 0x28, 0x01);	/* XPM2 */
++		break;
++	}
++
++	/* Don't mask framer interrupts if hardware HDLC is in use */
++	__t4_framer_out(wc, unit, FRMR_IMR0, 0xff & ~((wc->tspans[unit]->sigchan) ? HDLC_IMR0_MASK : 0));	/* IMR0: We care about CAS changes, etc */
++	__t4_framer_out(wc, unit, FRMR_IMR1, 0xff & ~((wc->tspans[unit]->sigchan) ? HDLC_IMR1_MASK : 0));	/* IMR1: We care about nothing */
++	__t4_framer_out(wc, unit, 0x16, 0x00);	/* IMR2: All the alarm stuff! */
++	__t4_framer_out(wc, unit, 0x17, 0x34);	/* IMR3: AIS and friends */
++	__t4_framer_out(wc, unit, 0x18, 0x3f);  /* IMR4: Slips on transmit */
++
++	dev_info(&wc->dev->dev, "Span %d configured for %s/%s\n", unit + 1,
++			framing, line);
++}
++
++static void __t4_configure_e1(struct t4 *wc, int unit, int lineconfig)
++{
++	unsigned int fmr2, fmr1, fmr0;
++	unsigned int cas = 0;
++	unsigned int imr3extra=0;
++	char *crc4 = "";
++	char *framing, *line;
++	fmr1 = 0x44; /* FMR1: E1 mode, Automatic force resync, PCM30 mode, 8.192 Mhz backplane, no XAIS */
++	fmr2 = 0x03; /* FMR2: Auto transmit remote alarm, auto loss of multiframe recovery, no payload loopback */
++	if (lineconfig & DAHDI_CONFIG_CRC4) {
++		fmr1 |= 0x08;	/* CRC4 transmit */
++		fmr2 |= 0xc0;	/* CRC4 receive */
++		crc4 = "/CRC4";
++	}
++	__t4_framer_out(wc, unit, 0x1d, fmr1);
++	__t4_framer_out(wc, unit, 0x1e, fmr2);
++
++	/* Configure line interface */
++	if (lineconfig & DAHDI_CONFIG_AMI) {
++		line = "AMI";
++		/* workaround for errata #2 in ES v3 09-10-16 */
++		fmr0 = (wc->falc31) ? 0xb0 : 0xa0;
++	} else {
++		line = "HDB3";
++		fmr0 = 0xf0;
++	}
++	if (lineconfig & DAHDI_CONFIG_CCS) {
++		framing = "CCS";
++		imr3extra = 0x28;
++	} else {
++		framing = "CAS";
++		cas = 0x40;
++	}
++	__t4_framer_out(wc, unit, 0x1c, fmr0);
++
++	__t4_framer_out(wc, unit, 0x37, 0xf0 /*| 0x6 */ );	/* LIM1: Clear data in case of LOS, Set receiver threshold (0.5V), No remote loop, no DRS */
++	__t4_framer_out(wc, unit, 0x36, 0x08);	/* LIM0: Enable auto long haul mode, no local loop (must be after LIM1) */
++
++	__t4_framer_out(wc, unit, 0x02, 0x50);	/* CMDR: Reset the receiver and transmitter line interface */
++	__t4_framer_out(wc, unit, 0x02, 0x00);	/* CMDR: Reset the receiver and transmitter line interface */
++
++	if (wc->falc31) {
++		if (debug)
++			dev_info(&wc->dev->dev,
++					"setting Rtx to 7.5ohm for E1\n");
++		__t4_framer_out(wc, unit, 0x86, 0x40);	/* PC6: turn on 7.5ohm Rtx for E1 */
++	}
++
++	/* Condition receive line interface for E1 after reset */
++	__t4_framer_out(wc, unit, 0xbb, 0x17);
++	__t4_framer_out(wc, unit, 0xbc, 0x55);
++	__t4_framer_out(wc, unit, 0xbb, 0x97);
++	__t4_framer_out(wc, unit, 0xbb, 0x11);
++	__t4_framer_out(wc, unit, 0xbc, 0xaa);
++	__t4_framer_out(wc, unit, 0xbb, 0x91);
++	__t4_framer_out(wc, unit, 0xbb, 0x12);
++	__t4_framer_out(wc, unit, 0xbc, 0x55);
++	__t4_framer_out(wc, unit, 0xbb, 0x92);
++	__t4_framer_out(wc, unit, 0xbb, 0x0c);
++	__t4_framer_out(wc, unit, 0xbb, 0x00);
++	__t4_framer_out(wc, unit, 0xbb, 0x8c);
++	
++	__t4_framer_out(wc, unit, 0x3a, 0x20);	/* LIM2: 50% peak amplitude is a "1" */
++	__t4_framer_out(wc, unit, 0x38, 0x0a);	/* PCD: LOS after 176 consecutive "zeros" */
++	__t4_framer_out(wc, unit, 0x39, 0x15);	/* PCR: 22 "ones" clear LOS */
++	
++	__t4_framer_out(wc, unit, 0x20, 0x9f);	/* XSW: Spare bits all to 1 */
++	__t4_framer_out(wc, unit, 0x21, 0x1c|cas);	/* XSP: E-bit set when async. AXS auto, XSIF to 1 */
++	
++	
++	/* Generate pulse mask for E1 */
++	__t4_framer_out(wc, unit, 0x26, 0x54);	/* XPM0 */
++	__t4_framer_out(wc, unit, 0x27, 0x02);	/* XPM1 */
++	__t4_framer_out(wc, unit, 0x28, 0x00);	/* XPM2 */
++
++	/* Don't mask framer interrupts if hardware HDLC is in use */
++	__t4_framer_out(wc, unit, FRMR_IMR0, 0xff & ~((wc->tspans[unit]->sigchan) ? HDLC_IMR0_MASK : 0));	/* IMR0: We care about CRC errors, CAS changes, etc */
++	__t4_framer_out(wc, unit, FRMR_IMR1, 0x3f & ~((wc->tspans[unit]->sigchan) ? HDLC_IMR1_MASK : 0));	/* IMR1: We care about loopup / loopdown */
++	__t4_framer_out(wc, unit, 0x16, 0x00);	/* IMR2: We care about all the alarm stuff! */
++	__t4_framer_out(wc, unit, 0x17, 0x04 | imr3extra); /* IMR3: AIS */
++	__t4_framer_out(wc, unit, 0x18, 0x3f);  /* IMR4: We care about slips on transmit */
++
++	dev_info(&wc->dev->dev, "opvxd115: Span %d configured for %s/%s%s\n",
++			unit + 1, framing, line, crc4);
++}
++
++static int t4_startup(struct file *file, struct dahdi_span *span)
++{
++#ifdef SUPPORT_GEN1
++	int i;
++#endif
++	int tspan;
++	unsigned long flags;
++	int alreadyrunning;
++	struct t4_span *ts = t4_from_span(span);
++	struct t4 *wc = ts->owner;
++
++	set_bit(T4_IGNORE_LATENCY, &wc->checkflag);
++	if (debug)
++		dev_info(&wc->dev->dev, "About to enter startup!\n");
++	tspan = span->offset + 1;
++	if (tspan < 0) {
++		dev_info(&wc->dev->dev, "opvxd115: Span '%d' isn't us?\n",
++				span->spanno);
++		return -1;
++	}
++
++	spin_lock_irqsave(&wc->reglock, flags);
++
++	alreadyrunning = span->flags & DAHDI_FLAG_RUNNING;
++
++#ifdef SUPPORT_GEN1
++	/* initialize the start value for the entire chunk of last ec buffer */
++	for(i = 0; i < span->channels; i++)
++	{
++		memset(ts->ec_chunk1[i],
++			DAHDI_LIN2X(0,span->chans[i]),DAHDI_CHUNKSIZE);
++		memset(ts->ec_chunk2[i],
++			DAHDI_LIN2X(0,span->chans[i]),DAHDI_CHUNKSIZE);
++	}
++#endif
++	/* Force re-evaluation of timing source */
++	wc->syncsrc = -1;
++	set_bit(T4_CHECK_TIMING, &wc->checkflag);
++
++	if (ts->spantype == TYPE_E1) { /* if this is an E1 card */
++		__t4_configure_e1(wc, span->offset, span->lineconfig);
++	} else { /* is a T1 card */
++		__t4_configure_t1(wc, span->offset, span->lineconfig, span->txlevel);
++	}
++
++	/* Note clear channel status */
++	wc->tspans[span->offset]->notclear = 0;
++	__set_clear(wc, span->offset);
++	
++	if (!alreadyrunning) {
++		span->flags |= DAHDI_FLAG_RUNNING;
++		wc->spansstarted++;
++
++		if (wc->flags & FLAG_5THGEN)
++			__t4_pci_out(wc, 5, (ms_per_irq << 16) | wc->numbufs);
++		/* enable interrupts */
++		/* Start DMA, enabling DMA interrupts on read only */
++#if 0
++		/* Enable framer only interrupts */
++		wc->dmactrl |= 1 << 27;
++#endif
++		wc->dmactrl |= (ts->spanflags & FLAG_2NDGEN) ? 0xc0000000 : 0xc0000003;
++#ifdef VPM_SUPPORT
++		wc->dmactrl |= wc->vpm;
++#endif
++		/* Seed interrupt register */
++		__t4_pci_out(wc, WC_INTR, 0x0c);
++		if (noburst || !(ts->spanflags & FLAG_BURST))
++			wc->dmactrl |= (1 << 26);
++		__t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++
++		/* Startup HDLC controller too */
++	}
++
++	if (ts->sigchan) {
++		struct dahdi_chan *sigchan = ts->sigchan;
++
++		spin_unlock_irqrestore(&wc->reglock, flags);
++		if (hdlc_start(wc, span->offset, sigchan, ts->sigmode)) {
++			dev_notice(&wc->dev->dev, "Error initializing "
++					"signalling controller\n");
++			return -1;
++		}
++		spin_lock_irqsave(&wc->reglock, flags);
++	}
++
++	spin_unlock_irqrestore(&wc->reglock, flags);
++
++	t4_check_alarms(wc, span->offset);
++	t4_check_sigbits(wc, span->offset);
++
++	if (wc->tspans[0]->sync == span->spanno)
++		dev_info(&wc->dev->dev, "SPAN %d: Primary Sync Source\n",
++				span->spanno);
++#ifdef VPM_SUPPORT
++	if (!alreadyrunning && !wc->vpm) {
++		wait_a_little();
++		t4_vpm400_init(wc);
++		if (!wc->vpm)
++			t4_vpm450_init(wc);
++		wc->dmactrl |= wc->vpm;
++		t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++		if (wc->vpm)
++			set_span_devicetype(wc);
++	}
++#endif
++	if (debug)
++		dev_info(&wc->dev->dev, "Completed startup!\n");
++	clear_bit(T4_IGNORE_LATENCY, &wc->checkflag);
++	return 0;
++}
++
++#ifdef SUPPORT_GEN1
++static inline void e1_check(struct t4 *wc, int span, int val)
++{
++	struct t4_span *ts = wc->tspans[span];
++	if ((ts->span.channels > 24) &&
++	    (ts->span.flags & DAHDI_FLAG_RUNNING) &&
++	    !(ts->span.alarms) &&
++	    (!wc->e1recover))   {
++		if (val != 0x1b) {
++			ts->e1check++;
++		} else
++			ts->e1check = 0;
++		if (ts->e1check > 100) {
++			/* Wait 1000 ms */
++			wc->e1recover = 1000 * 8;
++			wc->tspans[0]->e1check = 0;
++			if (debug & DEBUG_MAIN)
++				dev_notice(&wc->dev->dev, "Detected loss of "
++					"E1 alignment on span %d!\n", span);
++			t4_reset_dma(wc);
++		}
++	}
++}
++
++static void t4_receiveprep(struct t4 *wc, int irq)
++{
++	volatile unsigned int *readchunk;
++	int dbl = 0;
++	int x,y,z;
++	unsigned int tmp;
++	int offset=0;
++	if (!wc->t1e1)
++		offset = 4;
++	if (irq & 1) {
++		/* First part */
++		readchunk = wc->readchunk;
++		if (!wc->last0) 
++			dbl = 1;
++		wc->last0 = 0;
++	} else {
++		readchunk = wc->readchunk + DAHDI_CHUNKSIZE * 32;
++		if (wc->last0) 
++			dbl = 1;
++		wc->last0 = 1;
++	}
++	if (dbl) {
++		for (x=0;x<wc->numspans;x++)
++			wc->ddev->irqmisses++;
++		if (debug & DEBUG_MAIN)
++			dev_notice(&wc->dev->dev, "opvxd115: Double/missed "
++				"interrupt detected\n");
++	}
++	for (x=0;x<DAHDI_CHUNKSIZE;x++) {
++		for (z=0;z<24;z++) {
++			/* All T1/E1 channels */
++			tmp = readchunk[z+1+offset];
++			wc->tspans[0]->span.chans[z]->readchunk[x] = tmp >> 24;
++		}
++		if (wc->t1e1) {
++			if (wc->e1recover > 0)
++				wc->e1recover--;
++			tmp = readchunk[0];
++			e1_check(wc, 0, (tmp & 0x7f000000) >> 24);
++			for (z=24;z<31;z++) {
++				/* Only E1 channels now */
++				tmp = readchunk[z+1];
++				if (wc->tspans[0]->span.channels > 24)
++					wc->tspans[0]->span.chans[z]->readchunk[x] = tmp >> 24;
++			}
++		}
++		/* Advance pointer by 4 TDM frame lengths */
++		readchunk += 32;
++	}
++	for (x=0;x<wc->numspans;x++) {
++		if (wc->tspans[x]->span.flags & DAHDI_FLAG_RUNNING) {
++			for (y=0;y<wc->tspans[x]->span.channels;y++) {
++				/* Echo cancel double buffered data */
++				dahdi_ec_chunk(wc->tspans[x]->span.chans[y], 
++				    wc->tspans[x]->span.chans[y]->readchunk, 
++					wc->tspans[x]->ec_chunk2[y]);
++				memcpy(wc->tspans[x]->ec_chunk2[y],wc->tspans[x]->ec_chunk1[y],
++					DAHDI_CHUNKSIZE);
++				memcpy(wc->tspans[x]->ec_chunk1[y],
++					wc->tspans[x]->span.chans[y]->writechunk,
++						DAHDI_CHUNKSIZE);
++			}
++			dahdi_receive(&wc->tspans[x]->span);
++		}
++	}
++}
++#endif
++
++#if (DAHDI_CHUNKSIZE != 8)
++#error Sorry, nextgen does not support chunksize != 8
++#endif
++
++static inline void __receive_span(struct t4_span *ts)
++{
++#ifdef VPM_SUPPORT
++	int y;
++	unsigned long merged;
++	merged = ts->dtmfactive & ts->dtmfmutemask;
++	if (merged) {
++		for (y=0;y<ts->span.channels;y++) {
++			/* Mute any DTMFs which are supposed to be muted */
++			if (test_bit(y, &merged)) {
++				memset(ts->span.chans[y]->readchunk, DAHDI_XLAW(0, ts->span.chans[y]), DAHDI_CHUNKSIZE);
++			}
++		}
++	}
++#endif	
++
++#ifdef ENABLE_PREFETCH
++	prefetch((void *)(ts->readchunk));
++	prefetch((void *)(ts->writechunk));
++	prefetch((void *)(ts->readchunk + 8));
++	prefetch((void *)(ts->writechunk + 8));
++	prefetch((void *)(ts->readchunk + 16));
++	prefetch((void *)(ts->writechunk + 16));
++	prefetch((void *)(ts->readchunk + 24));
++	prefetch((void *)(ts->writechunk + 24));
++	prefetch((void *)(ts->readchunk + 32));
++	prefetch((void *)(ts->writechunk + 32));
++	prefetch((void *)(ts->readchunk + 40));
++	prefetch((void *)(ts->writechunk + 40));
++	prefetch((void *)(ts->readchunk + 48));
++	prefetch((void *)(ts->writechunk + 48));
++	prefetch((void *)(ts->readchunk + 56));
++	prefetch((void *)(ts->writechunk + 56));
++#endif
++
++	dahdi_ec_span(&ts->span);
++	dahdi_receive(&ts->span);
++}
++
++static inline void __transmit_span(struct t4_span *ts)
++{
++	dahdi_transmit(&ts->span);
++}
++
++#ifdef ENABLE_WORKQUEUES
++static void workq_handlespan(void *data)
++{
++	struct t4_span *ts = data;
++	struct t4 *wc = ts->owner;
++	
++	__receive_span(ts);
++	__transmit_span(ts);
++	atomic_dec(&wc->worklist);
++	if (!atomic_read(&wc->worklist))
++		t4_pci_out(wc, WC_INTR, 0);
++}
++#else
++static void t4_prep_gen2(struct t4 *wc)
++{
++	int x;
++	for (x=0;x<wc->numspans;x++) {
++		if (wc->tspans[x]->span.flags & DAHDI_FLAG_RUNNING) {
++			__receive_span(wc->tspans[x]);
++			__transmit_span(wc->tspans[x]);
++		}
++	}
++}
++
++#endif
++#ifdef SUPPORT_GEN1
++static void t4_transmitprep(struct t4 *wc, int irq)
++{
++	volatile unsigned int *writechunk;
++	int x,y,z;
++	unsigned int tmp;
++	int offset=0;
++	if (!wc->t1e1)
++		offset = 4;
++	if (irq & 1) {
++		/* First part */
++		writechunk = wc->writechunk + 1;
++	} else {
++		writechunk = wc->writechunk + DAHDI_CHUNKSIZE * 32  + 1;
++	}
++	for (y=0;y<wc->numspans;y++) {
++		if (wc->tspans[y]->span.flags & DAHDI_FLAG_RUNNING) 
++			dahdi_transmit(&wc->tspans[y]->span);
++	}
++
++	for (x=0;x<DAHDI_CHUNKSIZE;x++) {
++		/* Once per chunk */
++		for (z=0;z<24;z++) {
++			/* All T1/E1 channels */
++			tmp = (wc->tspans[0]->span.chans[z]->writechunk[x] << 24);
++			writechunk[z+offset] = tmp;
++		}
++		if (wc->t1e1) {
++			for (z=24;z<31;z++) {
++				/* Only E1 channels now */
++				tmp = 0;
++				if (wc->tspans[0]->span.channels > 24)
++					tmp |= (wc->tspans[0]->span.chans[z]->writechunk[x] << 24);
++				writechunk[z] = tmp;
++			}
++		}
++		/* Advance pointer by 4 TDM frame lengths */
++		writechunk += 32;
++	}
++
++}
++#endif
++
++static void t4_check_sigbits(struct t4 *wc, int span)
++{
++	int a,i,rxs;
++	struct t4_span *ts = wc->tspans[span];
++
++	if (debug & DEBUG_RBS)
++		dev_notice(&wc->dev->dev, "Checking sigbits on span %d\n",
++				span + 1);
++
++	if (!(ts->span.flags & DAHDI_FLAG_RUNNING))
++		return;
++	if (ts->spantype == TYPE_E1) {
++		for (i = 0; i < 15; i++) {
++			a = t4_framer_in(wc, span, 0x71 + i);
++			/* Get high channel in low bits */
++			rxs = (a & 0xf);
++			if (!(ts->span.chans[i+16]->sig & DAHDI_SIG_CLEAR)) {
++				if (ts->span.chans[i+16]->rxsig != rxs)
++					dahdi_rbsbits(ts->span.chans[i+16], rxs);
++			}
++			rxs = (a >> 4) & 0xf;
++			if (!(ts->span.chans[i]->sig & DAHDI_SIG_CLEAR)) {
++				if (ts->span.chans[i]->rxsig != rxs)
++					dahdi_rbsbits(ts->span.chans[i], rxs);
++			}
++		}
++	} else if (ts->span.lineconfig & DAHDI_CONFIG_D4) {
++		for (i = 0; i < 24; i+=4) {
++			a = t4_framer_in(wc, span, 0x70 + (i>>2));
++			/* Get high channel in low bits */
++			rxs = (a & 0x3) << 2;
++			if (!(ts->span.chans[i+3]->sig & DAHDI_SIG_CLEAR)) {
++				if (ts->span.chans[i+3]->rxsig != rxs)
++					dahdi_rbsbits(ts->span.chans[i+3], rxs);
++			}
++			rxs = (a & 0xc);
++			if (!(ts->span.chans[i+2]->sig & DAHDI_SIG_CLEAR)) {
++				if (ts->span.chans[i+2]->rxsig != rxs)
++					dahdi_rbsbits(ts->span.chans[i+2], rxs);
++			}
++			rxs = (a >> 2) & 0xc;
++			if (!(ts->span.chans[i+1]->sig & DAHDI_SIG_CLEAR)) {
++				if (ts->span.chans[i+1]->rxsig != rxs)
++					dahdi_rbsbits(ts->span.chans[i+1], rxs);
++			}
++			rxs = (a >> 4) & 0xc;
++			if (!(ts->span.chans[i]->sig & DAHDI_SIG_CLEAR)) {
++				if (ts->span.chans[i]->rxsig != rxs)
++					dahdi_rbsbits(ts->span.chans[i], rxs);
++			}
++		}
++	} else {
++		for (i = 0; i < 24; i+=2) {
++			a = t4_framer_in(wc, span, 0x70 + (i>>1));
++			/* Get high channel in low bits */
++			rxs = (a & 0xf);
++			if (!(ts->span.chans[i+1]->sig & DAHDI_SIG_CLEAR)) {
++				/* XXX Not really reset on every trans! XXX */
++				if (ts->span.chans[i+1]->rxsig != rxs) {
++					dahdi_rbsbits(ts->span.chans[i+1], rxs);
++				}
++			}
++			rxs = (a >> 4) & 0xf;
++			if (!(ts->span.chans[i]->sig & DAHDI_SIG_CLEAR)) {
++				/* XXX Not really reset on every trans! XXX */
++				if (ts->span.chans[i]->rxsig != rxs) {
++					dahdi_rbsbits(ts->span.chans[i], rxs);
++				}
++			}
++		}
++	}
++}
++
++static void t4_check_alarms(struct t4 *wc, int span)
++{
++	unsigned char c, d, e;
++	int alarms;
++	int x,j;
++	struct t4_span *ts = wc->tspans[span];
++	unsigned long flags;
++
++	if (!(ts->span.flags & DAHDI_FLAG_RUNNING))
++		return;
++
++	spin_lock_irqsave(&wc->reglock, flags);
++
++	c = __t4_framer_in(wc, span, 0x4c);
++	d = __t4_framer_in(wc, span, 0x4d);
++
++	/* Assume no alarms */
++	alarms = 0;
++
++	/* And consider only carrier alarms */
++	ts->span.alarms &= (DAHDI_ALARM_RED | DAHDI_ALARM_BLUE | DAHDI_ALARM_NOTOPEN);
++
++	if (ts->spantype == TYPE_E1) {
++		if (c & 0x04) {
++			/* No multiframe found, force RAI high after 400ms only if
++			   we haven't found a multiframe since last loss
++			   of frame */
++			if (!(ts->spanflags & FLAG_NMF)) {
++				__t4_framer_out(wc, span, 0x20, 0x9f | 0x20);	/* LIM0: Force RAI High */
++				ts->spanflags |= FLAG_NMF;
++				dev_notice(&wc->dev->dev,
++					"NMF workaround on!\n");
++			}
++			__t4_framer_out(wc, span, 0x1e, 0xc3);	/* Reset to CRC4 mode */
++			__t4_framer_out(wc, span, 0x1c, 0xf2);	/* Force Resync */
++			__t4_framer_out(wc, span, 0x1c, 0xf0);	/* Force Resync */
++		} else if (!(c & 0x02)) {
++			if ((ts->spanflags & FLAG_NMF)) {
++				__t4_framer_out(wc, span, 0x20, 0x9f);	/* LIM0: Clear forced RAI */
++				ts->spanflags &= ~FLAG_NMF;
++				dev_notice(&wc->dev->dev,
++					"NMF workaround off!\n");
++			}
++		}
++	} else {
++		/* Detect loopup code if we're not sending one */
++		if ((!ts->span.mainttimer) && (d & 0x08)) {
++			/* Loop-up code detected */
++			if ((ts->loopupcnt++ > 80)  && (ts->span.maintstat != DAHDI_MAINT_REMOTELOOP)) {
++				__t4_framer_out(wc, span, 0x36, 0x08);	/* LIM0: Disable any local loop */
++				__t4_framer_out(wc, span, 0x37, 0xf6 );	/* LIM1: Enable remote loop */
++				ts->span.maintstat = DAHDI_MAINT_REMOTELOOP;
++			}
++		} else
++			ts->loopupcnt = 0;
++		/* Same for loopdown code */
++		if ((!ts->span.mainttimer) && (d & 0x10)) {
++			/* Loop-down code detected */
++			if ((ts->loopdowncnt++ > 80)  && (ts->span.maintstat == DAHDI_MAINT_REMOTELOOP)) {
++				__t4_framer_out(wc, span, 0x36, 0x08);	/* LIM0: Disable any local loop */
++				__t4_framer_out(wc, span, 0x37, 0xf0 );	/* LIM1: Disable remote loop */
++				ts->span.maintstat = DAHDI_MAINT_NONE;
++			}
++		} else
++			ts->loopdowncnt = 0;
++	}
++
++	if (ts->span.lineconfig & DAHDI_CONFIG_NOTOPEN) {
++		for (x=0,j=0;x < ts->span.channels;x++)
++			if ((ts->span.chans[x]->flags & DAHDI_FLAG_OPEN)
++#ifdef CONFIG_DAHDI_NET
++					||
++			    (ts->span.chans[x]->flags & DAHDI_FLAG_NETDEV)
++#endif
++			    )
++				j++;
++		if (!j)
++			alarms |= DAHDI_ALARM_NOTOPEN;
++	}
++
++	/* Loss of Frame Alignment */
++	if (c & 0x20) {
++		if (ts->alarmcount >= alarmdebounce) {
++
++			/* Disable Slip Interrupts */
++			e = __t4_framer_in(wc, span, 0x17);
++			__t4_framer_out(wc, span, 0x17, (e|0x03));
++
++			alarms |= DAHDI_ALARM_RED;
++		} else {
++			if (unlikely(debug && !ts->alarmcount)) {
++				/* starting to debounce LOF/LFA */
++				dev_info(&wc->dev->dev, "opvxd115: LOF/LFA "
++					"detected on span %d but debouncing "
++					"for %d ms\n", span + 1,
++					alarmdebounce);
++			}
++			ts->alarmcount++;
++		}
++	} else
++		ts->alarmcount = 0;
++
++	/* Loss of Signal */
++	if (c & 0x80) {
++		if (ts->losalarmcount >= losalarmdebounce) {
++			/* Disable Slip Interrupts */
++			e = __t4_framer_in(wc, span, 0x17);
++			__t4_framer_out(wc, span, 0x17, (e|0x03));
++
++			alarms |= DAHDI_ALARM_RED;
++		} else {
++			if (unlikely(debug && !ts->losalarmcount)) {
++				/* starting to debounce LOS */
++				dev_info(&wc->dev->dev, "opvxd115: LOS "
++					"detected on span %d but debouncing "
++					"for %d ms\n",
++					span + 1, losalarmdebounce);
++			}
++			ts->losalarmcount++;
++		}
++	} else
++		ts->losalarmcount = 0;
++
++	/* Alarm Indication Signal */
++	if (c & 0x40) {
++		if (ts->aisalarmcount >= aisalarmdebounce)
++			alarms |= DAHDI_ALARM_BLUE;
++		else {
++			if (unlikely(debug && !ts->aisalarmcount)) {
++				/* starting to debounce AIS */
++				dev_info(&wc->dev->dev, "opvxd115: AIS "
++					"detected on span %d but debouncing "
++					"for %d ms\n",
++					span + 1, aisalarmdebounce);
++			}
++			ts->aisalarmcount++;
++		}
++	} else
++		ts->aisalarmcount = 0;
++
++#ifdef DAHDI_SPAN_OPS
++	/* Add detailed alarm status information to a red alarm state */
++	if (alarms & DAHDI_ALARM_RED) {
++		if (c & FRS0_LOS)
++			alarms |= DAHDI_ALARM_LOS;
++		if (c & FRS0_LFA)
++			alarms |= DAHDI_ALARM_LFA;
++		if (c & FRS0_LMFA)
++			alarms |= DAHDI_ALARM_LMFA;
++	}
++
++	if (unlikely(debug)) {
++		/* Check to ensure the xmit line isn't shorted */
++		if (unlikely(d & FRS1_XLS)) {
++			dev_info(&wc->dev->dev,
++				"Detected a possible hardware malfunction"\
++				" this card may need servicing\n");
++		}
++	}
++#endif
++
++	if (((!ts->span.alarms) && alarms) || 
++	    (ts->span.alarms && (!alarms))) 
++		set_bit(T4_CHECK_TIMING, &wc->checkflag);
++
++	/* Keep track of recovering */
++	if ((!alarms) && ts->span.alarms) 
++		ts->alarmtimer = DAHDI_ALARMSETTLE_TIME;
++	if (ts->alarmtimer)
++		alarms |= DAHDI_ALARM_RECOVER;
++
++	/* If receiving alarms, go into Yellow alarm state */
++	if (alarms && !(ts->spanflags & FLAG_SENDINGYELLOW)) {
++		/* We manually do yellow alarm to handle RECOVER and NOTOPEN, otherwise it's auto anyway */
++		unsigned char fmr4;
++		fmr4 = __t4_framer_in(wc, span, 0x20);
++		__t4_framer_out(wc, span, 0x20, fmr4 | 0x20);
++		dev_info(&wc->dev->dev, "Setting yellow alarm span %d\n",
++								span+1);
++		ts->spanflags |= FLAG_SENDINGYELLOW;
++	} else if ((!alarms) && (ts->spanflags & FLAG_SENDINGYELLOW)) {
++		unsigned char fmr4;
++		/* We manually do yellow alarm to handle RECOVER  */
++		fmr4 = __t4_framer_in(wc, span, 0x20);
++		__t4_framer_out(wc, span, 0x20, fmr4 & ~0x20);
++		dev_info(&wc->dev->dev, "Clearing yellow alarm span %d\n",
++								span+1);
++
++		/* Re-enable timing slip interrupts */
++		e = __t4_framer_in(wc, span, 0x17);
++
++		__t4_framer_out(wc, span, 0x17, (e & ~(0x03)));
++
++		ts->spanflags &= ~FLAG_SENDINGYELLOW;
++	}
++
++	/* Re-check the timing source when we enter/leave alarm, not withstanding
++	   yellow alarm */
++	if (c & 0x10) { /* receiving yellow (RAI) */
++		if (ts->yelalarmcount >= yelalarmdebounce)
++			alarms |= DAHDI_ALARM_YELLOW;
++		else {
++			if (unlikely(debug && !ts->yelalarmcount)) {
++				/* starting to debounce AIS */
++				dev_info(&wc->dev->dev, "wct%dxxp: yellow "
++					"(RAI) detected on span %d but "
++					"debouncing for %d ms\n",
++					wc->numspans, span + 1,
++					yelalarmdebounce);
++			}
++			ts->yelalarmcount++;
++		}
++	} else
++		ts->yelalarmcount = 0;
++
++	if (ts->span.mainttimer || ts->span.maintstat) 
++		alarms |= DAHDI_ALARM_LOOPBACK;
++	ts->span.alarms = alarms;
++	spin_unlock_irqrestore(&wc->reglock, flags);
++	dahdi_alarm_notify(&ts->span);
++}
++
++static void t4_do_counters(struct t4 *wc)
++{
++	int span;
++	for (span=0;span<wc->numspans;span++) {
++		struct t4_span *ts = wc->tspans[span];
++		int docheck=0;
++
++		spin_lock(&wc->reglock);
++		if (ts->loopupcnt || ts->loopdowncnt || ts->alarmcount
++			|| ts->losalarmcount || ts->aisalarmcount
++			|| ts->yelalarmcount)
++			docheck++;
++
++		if (ts->alarmtimer) {
++			if (!--ts->alarmtimer) {
++				docheck++;
++				ts->span.alarms &= ~(DAHDI_ALARM_RECOVER);
++			}
++		}
++		spin_unlock(&wc->reglock);
++		if (docheck) {
++			t4_check_alarms(wc, span);
++			dahdi_alarm_notify(&ts->span);
++		}
++	}
++}
++
++static inline void __handle_leds(struct t4 *wc)
++{
++	int x;
++
++	wc->blinktimer++;
++	for (x=0;x<wc->numspans;x++) {
++		struct t4_span *ts = wc->tspans[x];
++		if (ts->span.flags & DAHDI_FLAG_RUNNING) {
++			if ((ts->span.alarms & (DAHDI_ALARM_RED | DAHDI_ALARM_BLUE)) || ts->losalarmcount) {
++#ifdef FANCY_ALARM
++				if (wc->blinktimer == (altab[wc->alarmpos] >> 1)) {
++					__t4_set_led(wc, x, WC_RED);
++				}
++				if (wc->blinktimer == 0xf) {
++					__t4_set_led(wc, x, WC_OFF);
++				}
++#else
++				if (wc->blinktimer == 160) {
++					__t4_set_led(wc, x, WC_RED);
++				} else if (wc->blinktimer == 480) {
++					__t4_set_led(wc, x, WC_OFF);
++				}
++#endif
++			} else if (ts->span.alarms & DAHDI_ALARM_YELLOW) {
++				/* Yellow Alarm */
++				__t4_set_led(wc, x, WC_YELLOW);
++			} else if (ts->span.mainttimer || ts->span.maintstat) {
++#ifdef FANCY_ALARM
++				if (wc->blinktimer == (altab[wc->alarmpos] >> 1)) {
++					__t4_set_led(wc, x, WC_GREEN);
++				}
++				if (wc->blinktimer == 0xf) {
++					__t4_set_led(wc, x, WC_OFF);
++				}
++#else
++				if (wc->blinktimer == 160) {
++					__t4_set_led(wc, x, WC_GREEN);
++				} else if (wc->blinktimer == 480) {
++					__t4_set_led(wc, x, WC_OFF);
++				}
++#endif
++			} else {
++				/* No Alarm */
++				__t4_set_led(wc, x, WC_GREEN);
++			}
++		}	else
++				__t4_set_led(wc, x, WC_OFF);
++
++	}
++#ifdef FANCY_ALARM
++	if (wc->blinktimer == 0xf) {
++		wc->blinktimer = -1;
++		wc->alarmpos++;
++		if (wc->alarmpos >= (sizeof(altab) / sizeof(altab[0])))
++			wc->alarmpos = 0;
++	}
++#else
++	if (wc->blinktimer == 480)
++		wc->blinktimer = 0;
++#endif
++}
++
++static inline void t4_framer_interrupt(struct t4 *wc, int span)
++{
++	unsigned char gis, isr0, isr1, isr2, isr3, isr4;
++#if (defined(DAHDI_SPAN_OPS) || defined(DAHDI_SPAN_MODULE) )
++	/* Check interrupts for a given span */
++	unsigned char reg;
++#endif
++	int readsize = -1;
++	struct t4_span *ts = wc->tspans[span];
++	struct dahdi_chan *sigchan;
++	unsigned long flags;
++
++
++	/* 1st gen cards isn't used interrupts */
++	gis = t4_framer_in(wc, span, FRMR_GIS);
++	isr0 = (gis & FRMR_GIS_ISR0) ? t4_framer_in(wc, span, FRMR_ISR0) : 0;
++	isr1 = (gis & FRMR_GIS_ISR1) ? t4_framer_in(wc, span, FRMR_ISR1) : 0;
++	isr2 = (gis & FRMR_GIS_ISR2) ? t4_framer_in(wc, span, FRMR_ISR2) : 0;
++	isr3 = (gis & FRMR_GIS_ISR3) ? t4_framer_in(wc, span, FRMR_ISR3) : 0;
++	isr4 = (gis & FRMR_GIS_ISR4) ? t4_framer_in(wc, span, FRMR_ISR4) : 0;
++
++ 	if ((debug & DEBUG_FRAMER) && !(isr3 & ISR3_SEC)) {
++ 		dev_info(&wc->dev->dev, "gis: %02x, isr0: %02x, isr1: %02x, "\
++ 			"isr2: %02x, isr3: %08x, isr4: %02x, intcount=%u\n",
++ 			gis, isr0, isr1, isr2, isr3, isr4, wc->intcount);
++ 	}
++ 
++#if (defined(DAHDI_SPAN_OPS) || defined(DAHDI_SPAN_MODULE) )
++	/* Collect performance counters once per second */
++ 	if (isr3 & ISR3_SEC) {
++ 		ts->span.count.fe += t4_framer_in(wc, span, FECL_T);
++ 		ts->span.count.crc4 += t4_framer_in(wc, span, CEC1L_T);
++ 		ts->span.count.cv += t4_framer_in(wc, span, CVCL_T);
++ 		ts->span.count.ebit += t4_framer_in(wc, span, EBCL_T);
++ 		ts->span.count.be += t4_framer_in(wc, span, BECL_T);
++ 		ts->span.count.prbs = t4_framer_in(wc, span, FRS1_T);
++ 	}
++ 
++	/* Collect errored second counter once per second */
++ 	if (isr3 & ISR3_ES) {
++ 		ts->span.count.errsec += 1;
++ 	}
++ 
++ 	if (isr3 & 0x08) {
++ 		reg = t4_framer_in(wc, span, FRS1_T);
++		dev_info(&wc->dev->dev, "FRS1: %d\n", reg);
++ 		if (reg & LLBDD) {
++ 			dev_info(&wc->dev->dev, "Line loop-back activation "\
++ 					"signal detected with status: %01d "\
++ 					"for span %d\n", reg & LLBAD, span+1);
++ 		}
++ 	}
++#endif
++
++	if (isr0)
++		t4_check_sigbits(wc, span);
++
++	if (ts->spantype == TYPE_E1) {
++		/* E1 checks */
++		if ((isr3 & 0x38) || isr2 || isr1)
++			t4_check_alarms(wc, span);
++	} else {
++		/* T1 checks */
++		if (isr2 || (isr3 & 0x08))
++			t4_check_alarms(wc, span);
++	}
++	if (!ts->span.alarms) {
++		if ((isr3 & 0x3) || (isr4 & 0xc0))
++			ts->span.count.timingslips++;
++
++		if (debug & DEBUG_MAIN) {
++			if (isr3 & 0x02)
++				dev_notice(&wc->dev->dev, "opvxd115: RECEIVE "
++					"slip NEGATIVE on span %d\n",
++					span + 1);
++			if (isr3 & 0x01)
++				dev_notice(&wc->dev->dev, "opvxd115: RECEIVE "
++					"slip POSITIVE on span %d\n",
++					span + 1);
++			if (isr4 & 0x80)
++				dev_notice(&wc->dev->dev, "opvxd115: TRANSMIT "
++					"slip POSITIVE on span %d\n",
++					span + 1);
++			if (isr4 & 0x40)
++				dev_notice(&wc->dev->dev, "opvxd115: TRANSMIT "
++					"slip NEGATIVE on span %d\n",
++					span + 1);
++		}
++	} else
++		ts->span.count.timingslips = 0;
++
++	spin_lock_irqsave(&wc->reglock, flags);
++	/* HDLC controller checks - receive side */
++	if (!ts->sigchan) {
++		spin_unlock_irqrestore(&wc->reglock, flags);
++		return;
++	}
++
++	sigchan = ts->sigchan;
++	spin_unlock_irqrestore(&wc->reglock, flags);
++
++	if (isr0 & FRMR_ISR0_RME) {
++		readsize = (t4_framer_in(wc, span, FRMR_RBCH) << 8) | t4_framer_in(wc, span, FRMR_RBCL);
++		if (debug & DEBUG_FRAMER)
++			dev_notice(&wc->dev->dev, "Received data length is %d "
++				"(%d)\n", readsize,
++				readsize & FRMR_RBCL_MAX_SIZE);
++		/* RPF isn't set on last part of frame */
++		if ((readsize > 0) && ((readsize &= FRMR_RBCL_MAX_SIZE) == 0))
++			readsize = FRMR_RBCL_MAX_SIZE + 1;
++	} else if (isr0 & FRMR_ISR0_RPF)
++		readsize = FRMR_RBCL_MAX_SIZE + 1;
++
++	if (readsize > 0) {
++		int i;
++		unsigned char readbuf[FRMR_RBCL_MAX_SIZE + 1];
++
++		if (debug & DEBUG_FRAMER)
++			dev_notice(&wc->dev->dev, "Framer %d: Got RPF/RME! "
++				"readsize is %d\n", sigchan->span->offset,
++				readsize);
++
++		for (i = 0; i < readsize; i++)
++			readbuf[i] = t4_framer_in(wc, span, FRMR_RXFIFO);
++
++		/* Tell the framer to clear the RFIFO */
++		t4_framer_cmd_wait(wc, span, FRMR_CMDR_RMC);
++
++		if (debug & DEBUG_FRAMER) {
++			dev_notice(&wc->dev->dev, "RX(");
++			for (i = 0; i < readsize; i++)
++				dev_notice(&wc->dev->dev, "%s%02x",
++					(i ? " " : ""), readbuf[i]);
++			dev_notice(&wc->dev->dev, ")\n");
++		}
++
++		if (isr0 & FRMR_ISR0_RME) {
++			/* Do checks for HDLC problems */
++			unsigned char rsis = readbuf[readsize-1];
++#if 0
++			unsigned int olddebug = debug;
++#endif
++			unsigned char rsis_reg = t4_framer_in(wc, span, FRMR_RSIS);
++
++#if 0
++			if ((rsis != 0xA2) || (rsis != rsis_reg))
++				debug |= DEBUG_FRAMER;
++#endif
++
++			++ts->frames_in;
++			if ((debug & DEBUG_FRAMER) && !(ts->frames_in & 0x0f))
++				dev_notice(&wc->dev->dev, "Received %d frames "
++					"on span %d\n", ts->frames_in, span);
++			if (debug & DEBUG_FRAMER)
++				dev_notice(&wc->dev->dev, "Received HDLC frame"
++					" %d.  RSIS = 0x%x (%x)\n",
++					ts->frames_in, rsis, rsis_reg);
++			if (!(rsis & FRMR_RSIS_CRC16)) {
++				if (debug & DEBUG_FRAMER)
++					dev_notice(&wc->dev->dev, "CRC check "
++							"failed %d\n", span);
++				dahdi_hdlc_abort(sigchan, DAHDI_EVENT_BADFCS);
++			} else if (rsis & FRMR_RSIS_RAB) {
++				if (debug & DEBUG_FRAMER)
++					dev_notice(&wc->dev->dev, "ABORT of "
++						"current frame due to "
++						"overflow %d\n", span);
++				dahdi_hdlc_abort(sigchan, DAHDI_EVENT_ABORT);
++			} else if (rsis & FRMR_RSIS_RDO) {
++				if (debug & DEBUG_FRAMER)
++					dev_notice(&wc->dev->dev, "HDLC "
++						"overflow occured %d\n",
++						span);
++				dahdi_hdlc_abort(sigchan, DAHDI_EVENT_OVERRUN);
++			} else if (!(rsis & FRMR_RSIS_VFR)) {
++				if (debug & DEBUG_FRAMER)
++					dev_notice(&wc->dev->dev, "Valid Frame"
++						" check failed on span %d\n",
++						span);
++				dahdi_hdlc_abort(sigchan, DAHDI_EVENT_ABORT);
++			} else {
++				dahdi_hdlc_putbuf(sigchan, readbuf, readsize - 1);
++				dahdi_hdlc_finish(sigchan);
++				if (debug & DEBUG_FRAMER)
++					dev_notice(&wc->dev->dev, "Received "
++						"valid HDLC frame on span %d"
++						"\n", span);
++			}
++#if 0
++			debug = olddebug;
++#endif
++		} else if (isr0 & FRMR_ISR0_RPF)
++			dahdi_hdlc_putbuf(sigchan, readbuf, readsize);
++	}
++
++	/* Transmit side */
++	if (isr1 & FRMR_ISR1_XDU) {
++		if (debug & DEBUG_FRAMER)
++			dev_notice(&wc->dev->dev, "XDU: Resetting signal "
++					"controller!\n");
++		t4_framer_cmd_wait(wc, span, FRMR_CMDR_SRES);
++	} else if (isr1 & FRMR_ISR1_XPR) {
++		if (debug & DEBUG_FRAMER)
++			dev_notice(&wc->dev->dev, "Sigchan %d is %p\n",
++					sigchan->chanpos, sigchan);
++
++		if (debug & DEBUG_FRAMER)
++			dev_notice(&wc->dev->dev, "Framer %d: Got XPR!\n",
++					sigchan->span->offset);
++		t4_hdlc_xmit_fifo(wc, span, ts);
++	}
++
++	if (isr1 & FRMR_ISR1_ALLS) {
++		if (debug & DEBUG_FRAMER)
++			dev_notice(&wc->dev->dev, "ALLS received\n");
++	}
++}
++
++#ifdef SUPPORT_GEN1
++DAHDI_IRQ_HANDLER(t4_interrupt)
++{
++	struct t4 *wc = dev_id;
++	unsigned long flags;
++	int x;
++	
++	unsigned int status;
++	unsigned int status2;
++
++#if 0
++	if (wc->intcount < 20)
++		dev_notice(&wc->dev->dev, "Pre-interrupt\n");
++#endif
++	
++	/* Make sure it's really for us */
++	status = __t4_pci_in(wc, WC_INTR);
++
++	/* Process framer interrupts */
++	status2 = t4_framer_in(wc, 0, FRMR_CIS);
++	if (status2 & 0x0f) {
++		for (x = 0; x < wc->numspans; ++x) {
++			if (status2 & (1 << x))
++				t4_framer_interrupt(wc, x);
++		}
++	}
++
++	/* Ignore if it's not for us */
++	if (!status)
++		return IRQ_NONE;
++
++	__t4_pci_out(wc, WC_INTR, 0);
++
++	if (!wc->spansstarted) {
++		dev_notice(&wc->dev->dev, "Not prepped yet!\n");
++		return IRQ_NONE;
++	}
++
++	wc->intcount++;
++#if 0
++	if (wc->intcount < 20)
++		dev_notice(&wc->dev->dev, "Got interrupt, status = %08x\n",
++				status);
++#endif		
++
++	if (status & 0x3) {
++		t4_receiveprep(wc, status);
++		t4_transmitprep(wc, status);
++	}
++	
++#if 0
++	if ((wc->intcount < 10) || !(wc->intcount % 1000)) {
++		status2 = t4_framer_in(wc, 0, FRMR_CIS);
++		dev_notice(&wc->dev->dev, "Status2: %04x\n", status2);
++		for (x = 0;x<wc->numspans;x++) {
++			status2 = t4_framer_in(wc, x, FRMR_FRS0);
++			dev_notice(&wc->dev->dev, "FRS0/%d: %04x\n", x,
++					status2);
++		}
++	}
++#endif
++	t4_do_counters(wc);
++
++	x = wc->intcount & 15 /* 63 */;
++	switch(x) {
++	case 0:
++	case 1:
++	case 2:
++	case 3:
++		t4_check_sigbits(wc, x);
++		break;
++	case 4:
++	case 5:
++	case 6:
++	case 7:
++		t4_check_alarms(wc, x - 4);
++		break;
++	}
++
++	spin_lock_irqsave(&wc->reglock, flags);
++
++	__handle_leds(wc);
++
++	if (test_bit(T4_CHECK_TIMING, &wc->checkflag))
++		__t4_set_timing_source_auto(wc);
++
++	spin_unlock_irqrestore(&wc->reglock, flags);
++
++	return IRQ_RETVAL(1);
++}
++#endif
++
++static int t4_allocate_buffers(struct t4 *wc, int numbufs, volatile unsigned int **oldalloc, dma_addr_t *oldwritedma)
++{
++	volatile unsigned int *alloc;
++	dma_addr_t writedma;
++
++	alloc =
++		/* 32 channels, Double-buffer, Read/Write, 4 spans */
++		(unsigned int *)pci_alloc_consistent(wc->dev, numbufs * T4_BASE_SIZE * 2, &writedma);
++
++	if (!alloc) {
++		dev_notice(&wc->dev->dev, "wct%dxxp: Unable to allocate "
++				"DMA-able memory\n", wc->numspans);
++		return -ENOMEM;
++	}
++
++	if (oldwritedma)
++		*oldwritedma = wc->writedma;
++	if (oldalloc)
++		*oldalloc = wc->writechunk;
++
++	wc->writechunk = alloc;
++	wc->writedma = writedma;
++
++	/* Read is after the whole write piece (in words) */
++	wc->readchunk = wc->writechunk + (T4_BASE_SIZE * numbufs) / 4;
++	
++	/* Same thing but in bytes...  */
++	wc->readdma = wc->writedma + (T4_BASE_SIZE * numbufs);
++
++	wc->numbufs = numbufs;
++	
++	/* Initialize Write/Buffers to all blank data */
++	memset((void *)wc->writechunk,0x00, T4_BASE_SIZE * numbufs);
++	memset((void *)wc->readchunk,0xff, T4_BASE_SIZE * numbufs);
++
++	dev_notice(&wc->dev->dev, "DMA memory base of size %d at %p.  Read: "
++		"%p and Write %p\n", numbufs * T4_BASE_SIZE * 2,
++		wc->writechunk, wc->readchunk, wc->writechunk);
++
++	return 0;
++}
++
++static void t4_increase_latency(struct t4 *wc, int newlatency)
++{
++	unsigned long flags;
++	volatile unsigned int *oldalloc;
++	dma_addr_t oldaddr;
++	int oldbufs;
++
++	spin_lock_irqsave(&wc->reglock, flags);
++
++	__t4_pci_out(wc, WC_DMACTRL, 0x00000000);
++	/* Acknowledge any pending interrupts */
++	__t4_pci_out(wc, WC_INTR, 0x00000000);
++
++	__t4_pci_in(wc, WC_VERSION);
++
++	oldbufs = wc->numbufs;
++
++	if (t4_allocate_buffers(wc, newlatency, &oldalloc, &oldaddr)) {
++		dev_info(&wc->dev->dev, "Error allocating latency buffers for "
++				"latency of %d\n", newlatency);
++		__t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++		spin_unlock_irqrestore(&wc->reglock, flags);
++		return;
++	}
++
++	__t4_pci_out(wc, WC_RDADDR, wc->readdma);
++	__t4_pci_out(wc, WC_WRADDR, wc->writedma);
++
++	__t4_pci_in(wc, WC_VERSION);
++
++	__t4_pci_out(wc, 5, (ms_per_irq << 16) | newlatency);
++	__t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++
++	__t4_pci_in(wc, WC_VERSION);
++
++	wc->rxident = 0;
++	wc->lastindex = 0;
++
++	spin_unlock_irqrestore(&wc->reglock, flags);
++
++	pci_free_consistent(wc->dev, T4_BASE_SIZE * oldbufs * 2, (void *)oldalloc, oldaddr);
++
++	dev_info(&wc->dev->dev, "Increased latency to %d\n", newlatency);
++
++}
++
++static void t4_isr_bh(unsigned long data)
++{
++	struct t4 *wc = (struct t4 *)data;
++
++	if (test_bit(T4_CHANGE_LATENCY, &wc->checkflag)) {
++		if (wc->needed_latency != wc->numbufs) {
++			t4_increase_latency(wc, wc->needed_latency);
++			clear_bit(T4_CHANGE_LATENCY, &wc->checkflag);
++		}
++	}
++#ifdef VPM_SUPPORT
++	if (wc->vpm) {
++		if (test_and_clear_bit(T4_CHECK_VPM, &wc->checkflag)) {
++			if (wc->vpm450m) {
++				/* How stupid is it that the octasic can't generate an
++				   interrupt when there's a tone, in spite of what their
++				   documentation says? */
++				t4_check_vpm450(wc);
++			} else
++				t4_check_vpm400(wc, wc->vpm400checkstatus);
++		}
++	}
++#endif
++}
++
++DAHDI_IRQ_HANDLER(t4_interrupt_gen2)
++{
++	struct t4 *wc = dev_id;
++	unsigned int status;
++	unsigned char rxident, expected;
++	
++	/* Check this first in case we get a spurious interrupt */
++	if (unlikely(test_bit(T4_STOP_DMA, &wc->checkflag))) {
++		/* Stop DMA cleanly if requested */
++		wc->dmactrl = 0x0;
++		t4_pci_out(wc, WC_DMACTRL, 0x00000000);
++		/* Acknowledge any pending interrupts */
++		t4_pci_out(wc, WC_INTR, 0x00000000);
++		spin_lock(&wc->reglock);
++		__t4_set_sclk_src(wc, WC_SELF, 0, 0);
++		spin_unlock(&wc->reglock);
++		return IRQ_RETVAL(1);
++	}
++
++	/* Make sure it's really for us */
++	status = __t4_pci_in(wc, WC_INTR);
++
++	/* Ignore if it's not for us */
++	if (!(status & 0x7)) {
++		return IRQ_NONE;
++	}
++
++#ifdef ENABLE_WORKQUEUES
++	__t4_pci_out(wc, WC_INTR, status & 0x00000008);
++#endif
++
++	if (unlikely(!wc->spansstarted)) {
++		dev_info(&wc->dev->dev, "Not prepped yet!\n");
++		return IRQ_NONE;
++	}
++
++	wc->intcount++;
++
++	if ((wc->flags & FLAG_5THGEN) && (status & 0x2)) {
++		rxident = (status >> 16) & 0x7f;
++		expected = (wc->rxident + ms_per_irq) % 128;
++	
++		if ((rxident != expected) && !test_bit(T4_IGNORE_LATENCY, &wc->checkflag)) {
++			int needed_latency;
++			int smallest_max;
++
++			if (debug & DEBUG_MAIN)
++				dev_warn(&wc->dev->dev, "Missed interrupt.  "
++					"Expected ident of %d and got ident "
++					"of %d\n", expected, rxident);
++
++			if (test_bit(T4_IGNORE_LATENCY, &wc->checkflag)) {
++				dev_info(&wc->dev->dev,
++					"Should have ignored latency\n");
++			}
++			if (rxident > wc->rxident) {
++				needed_latency = rxident - wc->rxident;
++			} else {
++				needed_latency = (128 - wc->rxident) + rxident;
++			}
++
++			needed_latency += 1;
++
++			smallest_max = (max_latency >= GEN5_MAX_LATENCY) ? GEN5_MAX_LATENCY : max_latency;
++
++			if (needed_latency > smallest_max) {
++				dev_info(&wc->dev->dev, "Truncating latency "
++					"request to %d instead of %d\n",
++					smallest_max, needed_latency);
++				needed_latency = smallest_max;
++			}
++
++			if (needed_latency > wc->numbufs) {
++				int x;
++
++				dev_info(&wc->dev->dev, "Need to increase "
++					"latency.  Estimated latency should "
++					"be %d\n", needed_latency);
++				for (x = 0; x < wc->numspans; x++)
++					wc->ddev->irqmisses++;
++				wc->needed_latency = needed_latency;
++				__t4_pci_out(wc, WC_DMACTRL, 0x00000000);
++				set_bit(T4_CHANGE_LATENCY, &wc->checkflag);
++				goto out;
++			}
++		}
++	
++		wc->rxident = rxident;
++	}
++
++	if (unlikely((wc->intcount < 20)))
++
++		dev_info(&wc->dev->dev, "2G: Got interrupt, status = %08x, "
++			"CIS = %04x\n", status, t4_framer_in(wc, 0, FRMR_CIS));
++
++	if (likely(status & 0x2)) {
++#ifdef ENABLE_WORKQUEUES
++		int cpus = num_online_cpus();
++		atomic_set(&wc->worklist, wc->numspans);
++		if (wc->tspans[0]->span.flags & DAHDI_FLAG_RUNNING)
++			t4_queue_work(wc->workq, &wc->tspans[0]->swork, 0);
++		else
++			atomic_dec(&wc->worklist);
++#else
++#if 1
++		unsigned int reg5 = __t4_pci_in(wc, 5);
++		if (wc->intcount < 20) {
++
++			dev_info(&wc->dev->dev, "Reg 5 is %08x\n", reg5);
++		}
++#endif
++
++		if (wc->flags & FLAG_5THGEN) {
++			unsigned int current_index = (reg5 >> 8) & 0x7f;
++
++			while (((wc->lastindex + 1) % wc->numbufs) != current_index) {
++				wc->lastindex = (wc->lastindex + 1) % wc->numbufs;
++				setup_chunks(wc, wc->lastindex);
++				t4_prep_gen2(wc);
++			}
++		} else {
++			t4_prep_gen2(wc);
++		}
++
++#endif
++		t4_do_counters(wc);
++		spin_lock(&wc->reglock);
++		__handle_leds(wc);
++		spin_unlock(&wc->reglock);
++
++	}
++
++	if (unlikely(status & 0x1)) {
++		unsigned char cis;
++
++		cis = t4_framer_in(wc, 0, FRMR_CIS);
++		if (cis & FRMR_CIS_GIS1)
++			t4_framer_interrupt(wc, 0);
++		if (cis & FRMR_CIS_GIS2)
++			t4_framer_interrupt(wc, 1);
++		if (cis & FRMR_CIS_GIS3)
++			t4_framer_interrupt(wc, 2);
++		if (cis & FRMR_CIS_GIS4)
++			t4_framer_interrupt(wc, 3);
++	}
++
++	if (wc->vpm && vpmdtmfsupport) {
++		if (wc->vpm450m) {
++			/* How stupid is it that the octasic can't generate an
++			   interrupt when there's a tone, in spite of what their
++			   documentation says? */
++			if (!(wc->intcount & 0xf)) {
++				set_bit(T4_CHECK_VPM, &wc->checkflag);
++			}
++		} else if ((status & 0xff00) != 0xff00) {
++			wc->vpm400checkstatus = (status & 0xff00) >> 8;
++			set_bit(T4_CHECK_VPM, &wc->checkflag);
++		}
++	}
++
++	spin_lock(&wc->reglock);
++
++	if (unlikely(test_bit(T4_CHECK_TIMING, &wc->checkflag))) {
++		__t4_set_timing_source_auto(wc);
++	}
++
++	spin_unlock(&wc->reglock);
++
++out:
++	if (unlikely(test_bit(T4_CHANGE_LATENCY, &wc->checkflag) || test_bit(T4_CHECK_VPM, &wc->checkflag)))
++		tasklet_schedule(&wc->t4_tlet);
++
++#ifndef ENABLE_WORKQUEUES
++	__t4_pci_out(wc, WC_INTR, 0);
++#endif	
++
++	return IRQ_RETVAL(1);
++}
++
++#ifdef SUPPORT_GEN1
++static int t4_reset_dma(struct t4 *wc)
++{
++	/* Turn off DMA and such */
++	wc->dmactrl = 0x0;
++	t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++	t4_pci_out(wc, WC_COUNT, 0);
++	t4_pci_out(wc, WC_RDADDR, 0);
++	t4_pci_out(wc, WC_WRADDR, 0);
++	t4_pci_out(wc, WC_INTR, 0);
++	/* Turn it all back on */
++	t4_pci_out(wc, WC_RDADDR, wc->readdma);
++	t4_pci_out(wc, WC_WRADDR, wc->writedma);
++	t4_pci_out(wc, WC_COUNT, ((DAHDI_MAX_CHUNKSIZE * 2 * 32 - 1) << 18) | ((DAHDI_MAX_CHUNKSIZE * 2 * 32 - 1) << 2));
++	t4_pci_out(wc, WC_INTR, 0);
++#ifdef VPM_SUPPORT
++	wc->dmactrl = 0xc0000000 | (1 << 29) | wc->vpm;
++#else	
++	wc->dmactrl = 0xc0000000 | (1 << 29);
++#endif
++	if (noburst)
++		wc->dmactrl |= (1 << 26);
++	t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++	return 0;
++}
++#endif
++
++#ifdef VPM_SUPPORT
++static void t4_vpm_set_dtmf_threshold(struct t4 *wc, unsigned int threshold)
++{
++	unsigned int x;
++
++	for (x = 0; x < 8; x++) {
++		t4_vpm_out(wc, x, 0xC4, (threshold >> 8) & 0xFF);
++		t4_vpm_out(wc, x, 0xC5, (threshold & 0xFF));
++	}
++	dev_info(&wc->dev->dev, "VPM: DTMF threshold set to %d\n", threshold);
++}
++
++static unsigned int t4_vpm_mask(int chip)
++{
++	unsigned int mask=0;
++	switch(vpmspans) {
++	case 4:
++		mask = 0x55555555 << (chip >> 2);
++		break;
++	case 2:
++		mask = 0x11111111 << (chip >> 1);
++		break;
++	case 1:
++		mask = 0x01010101 << chip;
++		break;
++	}
++	return mask;
++}
++
++static int t4_vpm_spanno(int chip)
++{
++	int spanno = 0;
++	switch(vpmspans) {
++	case 4:
++		spanno = chip & 0x3;
++		break;
++	case 2:
++		spanno = chip & 0x1;
++		break;
++	/* Case 1 is implicit */
++	}
++	return spanno;
++}
++
++static int t4_vpm_echotail(void)
++{
++	int echotail = 0x01ff;
++	switch(vpmspans) {
++	case 4:
++		echotail = 0x007f;
++		break;
++	case 2:
++		echotail = 0x00ff;
++		break;
++	/* Case 1 is implicit */
++	}
++	return echotail;
++}
++
++static void t4_vpm450_init(struct t4 *wc)
++{
++	unsigned int check1, check2;
++	int laws[1] = { 0, };
++	int x;
++	unsigned int vpm_capacity;
++	struct firmware embedded_firmware;
++	const struct firmware *firmware = &embedded_firmware;
++#if !defined(HOTPLUG_FIRMWARE)
++	extern void _binary_dahdi_fw_oct6114_032_bin_size;
++	extern void _binary_dahdi_fw_oct6114_064_bin_size;
++	extern void _binary_dahdi_fw_oct6114_128_bin_size;
++	extern u8 _binary_dahdi_fw_oct6114_032_bin_start[];
++	extern u8 _binary_dahdi_fw_oct6114_064_bin_start[];
++	extern u8 _binary_dahdi_fw_oct6114_128_bin_start[];
++#else
++	static const char oct032_firmware[] = "dahdi-fw-oct6114-032.bin";
++	static const char oct064_firmware[] = "dahdi-fw-oct6114-064.bin";
++	static const char oct128_firmware[] = "dahdi-fw-oct6114-128.bin";
++#endif
++
++	if (!vpmsupport) {
++		dev_info(&wc->dev->dev, "VPM450: Support Disabled\n");
++		return;
++	}
++
++	/* Turn on GPIO/DATA mux if supported */
++	t4_gpio_setdir(wc, (1 << 24), (1 << 24));
++	__t4_raw_oct_out(wc, 0x000a, 0x5678);
++	__t4_raw_oct_out(wc, 0x0004, 0x1234);
++	check1 = __t4_raw_oct_in(wc, 0x0004);
++	check2 = __t4_raw_oct_in(wc, 0x000a);
++	if (debug)
++		dev_notice(&wc->dev->dev, "OCT Result: %04x/%04x\n",
++			__t4_raw_oct_in(wc, 0x0004),
++			__t4_raw_oct_in(wc, 0x000a));
++	if (__t4_raw_oct_in(wc, 0x0004) != 0x1234) {
++		dev_notice(&wc->dev->dev, "VPM450: Not Present\n");
++		return;
++	}
++
++	/* Setup alaw vs ulaw rules */
++	for (x = 0;x < wc->numspans; x++) {
++		if (wc->tspans[x]->span.channels > 24)
++			laws[x] = 1;
++	}
++
++	switch ((vpm_capacity = get_vpm450m_capacity(wc))) {
++	case 32:
++#if defined(HOTPLUG_FIRMWARE)
++		if ((request_firmware(&firmware, oct032_firmware, &wc->dev->dev) != 0) ||
++		    !firmware) {
++			dev_notice(&wc->dev->dev, "VPM450: firmware %s not "
++				"available from userspace\n", oct032_firmware);
++			return;
++		}
++#else
++		embedded_firmware.data = _binary_dahdi_fw_oct6114_032_bin_start;
++		/* Yes... this is weird. objcopy gives us a symbol containing
++		   the size of the firmware, not a pointer a variable containing
++		   the size. The only way we can get the value of the symbol
++		   is to take its address, so we define it as a pointer and
++		   then cast that value to the proper type.
++	      */
++		embedded_firmware.size = (size_t) &_binary_dahdi_fw_oct6114_032_bin_size;
++#endif
++		break;
++	case 64:
++#if defined(HOTPLUG_FIRMWARE)
++		if ((request_firmware(&firmware, oct064_firmware, &wc->dev->dev) != 0) ||
++		    !firmware) {
++			dev_notice(&wc->dev->dev, "VPM450: firmware %s not "
++				"available from userspace\n", oct064_firmware);
++			return;
++		}
++#else
++		embedded_firmware.data = _binary_dahdi_fw_oct6114_064_bin_start;
++		/* Yes... this is weird. objcopy gives us a symbol containing
++		   the size of the firmware, not a pointer a variable containing
++		   the size. The only way we can get the value of the symbol
++		   is to take its address, so we define it as a pointer and
++		   then cast that value to the proper type.
++	      */
++		embedded_firmware.size = (size_t) &_binary_dahdi_fw_oct6114_064_bin_size;
++#endif
++		break;
++	case 128:
++#if defined(HOTPLUG_FIRMWARE)
++		if ((request_firmware(&firmware, oct128_firmware, &wc->dev->dev) != 0) ||
++		    !firmware) {
++			dev_notice(&wc->dev->dev, "VPM450: firmware %s not "
++				"available from userspace\n", oct128_firmware);
++			return;
++		}
++#else
++		embedded_firmware.data = _binary_dahdi_fw_oct6114_128_bin_start;
++		/* Yes... this is weird. objcopy gives us a symbol containing
++		   the size of the firmware, not a pointer a variable containing
++		   the size. The only way we can get the value of the symbol
++		   is to take its address, so we define it as a pointer and
++		   then cast that value to the proper type.
++		*/
++		embedded_firmware.size = (size_t) &_binary_dahdi_fw_oct6114_128_bin_size;
++#endif
++		break;
++	default:
++		dev_notice(&wc->dev->dev, "Unsupported channel capacity found "
++				"on VPM module (%d).\n", vpm_capacity);
++		return;
++	}
++
++	if (!(wc->vpm450m = init_vpm450m(wc, laws, wc->numspans, firmware))) {
++		dev_notice(&wc->dev->dev, "VPM450: Failed to initialize\n");
++		if (firmware != &embedded_firmware)
++			release_firmware(firmware);
++		return;
++	}
++
++	if (firmware != &embedded_firmware)
++		release_firmware(firmware);
++
++	if (vpmdtmfsupport == -1) {
++		dev_notice(&wc->dev->dev, "VPM450: hardware DTMF disabled.\n");
++		vpmdtmfsupport = 0;
++	}
++
++	wc->vpm = T4_VPM_PRESENT;
++	dev_info(&wc->dev->dev, "VPM450: Present and operational servicing %d "
++			"span(s)\n", wc->numspans);
++		
++}
++
++static void t4_vpm400_init(struct t4 *wc)
++{
++	unsigned char reg;
++	unsigned int mask;
++	unsigned int ver;
++	unsigned int i, x, y, gen2vpm=0;
++
++	if (!vpmsupport) {
++		dev_info(&wc->dev->dev, "VPM400: Support Disabled\n");
++		return;
++	}
++
++	switch(vpmspans) {
++	case 4:
++	case 2:
++	case 1:
++		break;
++	default:
++		dev_notice(&wc->dev->dev, "VPM400: %d is not a valid vpmspans "
++				"value, using 4\n", vpmspans);
++		vpmspans = 1;
++	}
++
++	for (x=0;x<8;x++) {
++		int spanno = t4_vpm_spanno(x);
++		struct t4_span *ts = wc->tspans[spanno];
++		int echotail = t4_vpm_echotail();
++
++		ver = t4_vpm_in(wc, x, 0x1a0); /* revision */
++		if ((ver != 0x26) && (ver != 0x33)) {
++			if (x)
++				dev_notice(&wc->dev->dev,
++					"VPM400: Inoperable\n");
++			return;
++		}
++		if (ver == 0x33) {
++			if (x && !gen2vpm) {
++				dev_notice(&wc->dev->dev,
++					"VPM400: Inconsistent\n");
++				return;
++			}
++			ts->spanflags |= FLAG_VPM2GEN;
++			gen2vpm++;
++		} else if (gen2vpm) {
++			dev_notice(&wc->dev->dev,
++				"VPM400: Inconsistent\n");
++			return;
++		}
++
++
++		/* Setup GPIO's */
++		for (y=0;y<4;y++) {
++			t4_vpm_out(wc, x, 0x1a8 + y, 0x00); /* GPIO out */
++			t4_vpm_out(wc, x, 0x1ac + y, 0x00); /* GPIO dir */
++			t4_vpm_out(wc, x, 0x1b0 + y, 0x00); /* GPIO sel */
++		}
++
++		/* Setup TDM path - sets fsync and tdm_clk as inputs */
++		reg = t4_vpm_in(wc, x, 0x1a3); /* misc_con */
++		t4_vpm_out(wc, x, 0x1a3, reg & ~2);
++
++		/* Setup timeslots */
++		t4_vpm_out(wc, x, 0x02f, 0x20 | (spanno << 3)); 
++
++		/* Setup Echo length (128 taps) */
++		t4_vpm_out(wc, x, 0x022, (echotail >> 8));
++		t4_vpm_out(wc, x, 0x023, (echotail & 0xff));
++		
++		/* Setup the tdm channel masks for all chips*/
++		mask = t4_vpm_mask(x);
++		for (i = 0; i < 4; i++)
++			t4_vpm_out(wc, x, 0x30 + i, (mask >> (i << 3)) & 0xff);
++
++		/* Setup convergence rate */
++		reg = t4_vpm_in(wc,x,0x20);
++		reg &= 0xE0;
++		if (ts->spantype == TYPE_E1) {
++			if (x < vpmspans)
++				dev_info(&wc->dev->dev, "VPM400: Span %d "
++						"A-law mode\n", spanno);
++			reg |= 0x01;
++		} else {
++			if (x < vpmspans)
++				dev_info(&wc->dev->dev, "VPM400: Span %d "
++						"U-law mode\n", spanno);
++			reg &= ~0x01;
++		}
++		t4_vpm_out(wc,x,0x20,(reg | 0x20));
++		
++		/* Initialize echo cans */
++		for (i = 0 ; i < MAX_TDM_CHAN; i++) {
++			if (mask & (0x00000001 << i))
++				t4_vpm_out(wc,x,i,0x00);
++		}
++
++		wait_a_little();
++
++		/* Put in bypass mode */
++		for (i = 0 ; i < MAX_TDM_CHAN ; i++) {
++			if (mask & (0x00000001 << i)) {
++				t4_vpm_out(wc,x,i,0x01);
++			}
++		}
++
++		/* Enable bypass */
++		for (i = 0 ; i < MAX_TDM_CHAN ; i++) {
++			if (mask & (0x00000001 << i))
++				t4_vpm_out(wc,x,0x78 + i,0x01);
++		}
++      
++		/* set DTMF detection threshold */
++		t4_vpm_set_dtmf_threshold(wc, dtmfthreshold);
++
++		/* Enable DTMF detectors (always DTMF detect all spans) */
++		for (i = 0; i < MAX_DTMF_DET; i++) {
++			t4_vpm_out(wc, x, 0x98 + i, 0x40 | (i * 2) | ((x < 4) ? 0 : 1));
++		}
++		for (i = 0x34; i < 0x38; i++)
++			t4_vpm_out(wc, x, i, 0x00);
++		for (i = 0x3C; i < 0x40; i++)
++			t4_vpm_out(wc, x, i, 0x00);
++
++		for (i = 0x48; i < 0x4B; i++)
++			t4_vpm_out(wc, x, i, 0x00);
++		for (i = 0x50; i < 0x53; i++)
++			t4_vpm_out(wc, x, i, 0x00);
++		for (i = 0xB8; i < 0xBE; i++)
++			t4_vpm_out(wc, x, i, 0xFF);
++		if (gen2vpm) {
++			for (i = 0xBE; i < 0xC0; i++)
++				t4_vpm_out(wc, x, i, 0xFF);
++		} else {
++			for (i = 0xBE; i < 0xC0; i++)
++				t4_vpm_out(wc, x, i, 0x00);
++		}
++		for (i = 0xC0; i < 0xC4; i++)
++			t4_vpm_out(wc, x, i, (x < 4) ? 0x55 : 0xAA);
++
++	} 
++	if (vpmdtmfsupport == -1) {
++		dev_info(&wc->dev->dev, "VPM400: hardware DTMF enabled.\n");
++		vpmdtmfsupport = 0;
++	}
++	dev_info(&wc->dev->dev, "VPM400%s: Present and operational servicing "
++		"%d span(s)\n", (gen2vpm ? " (2nd Gen)" : ""), wc->numspans);
++	wc->vpm = T4_VPM_PRESENT;
++}
++
++#endif
++
++static void t4_tsi_reset(struct t4 *wc) 
++{
++	int x;
++	for (x=0;x<128;x++) {
++		wc->dmactrl &= ~0x00007fff;
++		wc->dmactrl |= (0x00004000 | (x << 7));
++		t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++	}
++	wc->dmactrl &= ~0x00007fff;
++	t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++}
++
++/* Note that channels here start from 1 */
++static void t4_tsi_assign(struct t4 *wc, int fromspan, int fromchan, int tospan, int tochan)
++{
++	unsigned long flags;
++	int fromts, tots;
++
++	fromts = (fromspan << 5) |(fromchan);
++	tots = (tospan << 5) | (tochan);
++
++	if (!wc->t1e1) {
++		fromts += 4;
++		tots += 4;
++	}
++	spin_lock_irqsave(&wc->reglock, flags);
++	wc->dmactrl &= ~0x00007fff;
++	wc->dmactrl |= (0x00004000 | (tots << 7) | (fromts));
++	__t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++	wc->dmactrl &= ~0x00007fff;
++	__t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++	spin_unlock_irqrestore(&wc->reglock, flags);
++}
++
++static void t4_tsi_unassign(struct t4 *wc, int tospan, int tochan)
++{
++	unsigned long flags;
++	int tots;
++
++	tots = (tospan << 5) | (tochan);
++
++	if (!wc->t1e1) 
++		tots += 4;
++	spin_lock_irqsave(&wc->reglock, flags);
++	wc->dmactrl &= ~0x00007fff;
++	wc->dmactrl |= (0x00004000 | (tots << 7));
++	__t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++	if (debug & DEBUG_TSI)
++		dev_notice(&wc->dev->dev, "Sending '%08x\n", wc->dmactrl);
++	wc->dmactrl &= ~0x00007fff;
++	__t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++	spin_unlock_irqrestore(&wc->reglock, flags);
++}
++#ifdef CONFIG_EXTENDED_RESET
++static void t4_extended_reset(struct t4 *wc)
++{
++	unsigned int oldreg = t4_pci_in(wc, 0x4);
++
++	udelay(1000);
++
++	t4_pci_out(wc, 0x4, 0x42000000);
++	t4_pci_out(wc, 0xa, 0x42000000);
++	t4_pci_out(wc, 0xa, 0x00080000);
++	t4_pci_out(wc, 0xa, 0x00080000);
++	t4_pci_out(wc, 0xa, 0x00080000);
++	t4_pci_out(wc, 0xa, 0x00180000);
++	t4_pci_out(wc, 0xa, 0x00080000);
++	t4_pci_out(wc, 0xa, 0x00180000);
++	t4_pci_out(wc, 0xa, 0x00080000);
++	t4_pci_out(wc, 0xa, 0x00180000);
++	t4_pci_out(wc, 0xa, 0x00080000);
++	t4_pci_out(wc, 0xa, 0x00180000);
++	t4_pci_out(wc, 0xa, 0x00080000);
++	t4_pci_out(wc, 0xa, 0x00180000);
++	t4_pci_out(wc, 0xa, 0x00080000);
++	t4_pci_out(wc, 0xa, 0x00180000);
++	t4_pci_out(wc, 0x4, oldreg);
++
++	udelay(1000);
++}
++#endif
++
++static int t4_hardware_init_1(struct t4 *wc, unsigned int cardflags)
++{
++	unsigned int version;
++
++	version = t4_pci_in(wc, WC_VERSION);
++	dev_info(&wc->dev->dev, "Firmware Version: %08x\n", version);
++	dev_info(&wc->dev->dev, "Burst Mode: %s\n",
++		(!(cardflags & FLAG_BURST) && noburst) ? "Off" : "On");
++#ifdef ENABLE_WORKQUEUES
++	dev_info(&wc->dev->dev, "Work Queues: Enabled\n");
++#endif
++
++#ifdef CONFIG_EXTENDED_RESET
++	t4_extended_reset(wc);
++#endif
++
++	/* Make sure DMA engine is not running and interrupts are acknowledged */
++	wc->dmactrl = 0x0;
++	t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++	/* Reset Framer and friends */
++	t4_pci_out(wc, WC_LEDS, 0x00000000);
++
++	/* Set DMA addresses */
++	t4_pci_out(wc, WC_RDADDR, wc->readdma);
++	t4_pci_out(wc, WC_WRADDR, wc->writedma);
++
++	/* Setup counters, interrupt flags (ignored in Gen2) */
++	if (cardflags & FLAG_2NDGEN) {
++		t4_tsi_reset(wc);
++	} else {
++		t4_pci_out(wc, WC_COUNT, ((DAHDI_MAX_CHUNKSIZE * 2 * 32 - 1) << 18) | ((DAHDI_MAX_CHUNKSIZE * 2 * 32 - 1) << 2));
++	}
++	
++	/* Reset pending interrupts */
++	t4_pci_out(wc, WC_INTR, 0x00000000);
++
++	/* Read T1/E1 status */
++	if (t1e1override > -1)
++		wc->t1e1 = t1e1override;
++	else
++		wc->t1e1 = ((t4_pci_in(wc, WC_LEDS)) & 0x0f00) >> 8;
++	wc->order = ((t4_pci_in(wc, WC_LEDS)) & 0xf0000000) >> 28;
++	order_index[wc->order]++;
++	return 0;
++}
++
++static int t4_hardware_init_2(struct t4 *wc)
++{
++	int x;
++	unsigned int regval;
++
++	if (t4_pci_in(wc, WC_VERSION) >= 0xc01a0165) {
++		wc->tspans[0]->spanflags |= FLAG_OCTOPT;
++		dev_info(&wc->dev->dev, "Octasic Optimizations: Enabled\n");
++	}
++	/* Setup LEDS, take out of reset */
++	t4_pci_out(wc, WC_LEDS, 0x000000ff);
++	t4_activate(wc);
++
++	/* 
++	 * In order to find out the QFALC framer version, we have to temporarily term off compat
++	 * mode and take a peak at VSTR.  We turn compat back on when we are done.
++	 */
++	if (t4_framer_in(wc, 0, 0x4a) != 0x05)
++		dev_info(&wc->dev->dev, "WARNING: FALC framer not intialized "
++				"in compatibility mode.\n");
++	regval = t4_framer_in(wc, 0 ,0xd6);
++	regval |= (1 << 5); /* set COMP_DIS*/
++	t4_framer_out(wc, 0, 0xd6, regval);
++	regval = t4_framer_in(wc, 0, 0x4a);
++	if (regval == 0x05)
++		dev_info(&wc->dev->dev, "FALC Framer Version: 2.1 or "
++				"earlier\n");
++	else if (regval == 0x20) {
++		dev_info(&wc->dev->dev, "FALC Framer Version: 3.1\n");
++		wc->falc31 = 1;
++	} else
++		dev_info(&wc->dev->dev, "FALC Framer Version: Unknown "
++				"(VSTR = 0x%02x)\n", regval);
++	regval = t4_framer_in(wc, 0 ,0xd6);
++	regval &= ~(1 << 5); /* clear COMP_DIS*/
++	t4_framer_out(wc, 0, 0xd6, regval);
++	
++	t4_framer_out(wc, 0, 0x4a, 0xaa);
++	dev_info(&wc->dev->dev, "Board ID: %02x\n", wc->order);
++
++	for (x=0;x< 11;x++)
++		dev_info(&wc->dev->dev, "Reg %d: 0x%08x\n", x,
++				t4_pci_in(wc, x));
++	return 0;
++}
++
++static int __devinit t4_launch(struct t4 *wc)
++{
++	int x;
++	unsigned long flags;
++	if (test_bit(DAHDI_FLAGBIT_REGISTERED, &wc->tspans[0]->span.flags))
++		return 0;
++	dev_info(&wc->dev->dev, "opvxd115: Launching card: %d\n",
++			wc->order);
++
++	/* Setup serial parameters and system interface */
++	for (x=0;x<PORTS_PER_FRAMER;x++)
++		t4_serial_setup(wc, x);
++
++	for (x = 0; x < wc->numspans; ++x) {
++		list_add_tail(&wc->tspans[x]->span.device_node,
++			      &wc->ddev->spans);
++	}
++	if (dahdi_register_device(wc->ddev, &wc->dev->dev)) {
++		dev_err(&wc->dev->dev, "Unable to register span %s\n",
++				wc->tspans[0]->span.name);
++		return -1;
++	}
++	set_bit(T4_CHECK_TIMING, &wc->checkflag);
++	spin_lock_irqsave(&wc->reglock, flags);
++	__t4_set_sclk_src(wc, WC_SELF, 0, 0);
++	spin_unlock_irqrestore(&wc->reglock, flags);
++	tasklet_init(&wc->t4_tlet, t4_isr_bh, (unsigned long)wc);
++	return 0;
++}
++
++static void free_wc(struct t4 *wc)
++{
++	unsigned int x, y;
++
++	for (x = 0; x < sizeof(wc->tspans)/sizeof(wc->tspans[0]); x++) {
++		if (!wc->tspans[x]) {
++			continue;
++		}
++
++		for (y = 0; y < sizeof(wc->tspans[x]->chans)/sizeof(wc->tspans[x]->chans[0]); y++) {
++			if (wc->tspans[x]->chans[y]) {
++				kfree(wc->tspans[x]->chans[y]);
++			}
++			if (wc->tspans[x]->ec[y])
++				kfree(wc->tspans[x]->ec[y]);
++		}
++		kfree(wc->tspans[x]);
++	}
++	kfree(wc);
++}
++
++static int __devinit t4_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
++{
++	struct t4 *wc;
++	struct devtype *dt;
++	unsigned int x, f;
++	int init_latency;
++	
++	if (pci_enable_device(pdev)) {
++		return -EIO;
++	}
++
++	if (!(wc = kmalloc(sizeof(*wc), GFP_KERNEL))) {
++		return -ENOMEM;
++	}
++
++	memset(wc, 0x0, sizeof(*wc));
++	spin_lock_init(&wc->reglock);
++	dt = (struct devtype *) (ent->driver_data);
++
++	wc->flags = dt->flags;
++
++	wc->numspans = 1;
++	
++	wc->variety = dt->desc;
++	
++	wc->memaddr = pci_resource_start(pdev, 0);
++	wc->memlen = pci_resource_len(pdev, 0);
++	wc->membase = ioremap(wc->memaddr, wc->memlen);
++	/* This rids of the Double missed interrupt message after loading */
++	wc->last0 = 1;
++#if 0
++	if (!request_mem_region(wc->memaddr, wc->memlen, wc->variety))
++		dev_info(&wc->dev->dev, "opvxd115: Unable to request memory "
++				"region :(, using anyway...\n");
++#endif
++	if (pci_request_regions(pdev, wc->variety))
++		dev_info(&pdev->dev, "opvxd115: Unable to request regions\n");
++	
++	dev_info(&pdev->dev, "Found opvxd115 at base address %08lx, remapped "
++			"to %p\n", wc->memaddr, wc->membase);
++	
++	wc->dev = pdev;
++	
++	/* Enable bus mastering */
++	pci_set_master(pdev);
++
++	/* Keep track of which device we are */
++	pci_set_drvdata(pdev, wc);
++
++	if (wc->flags & FLAG_5THGEN) {
++		if ((ms_per_irq > 1) && (latency <= ((ms_per_irq) << 1))) {
++			init_latency = ms_per_irq << 1;
++		} else {
++			if (latency > 2)
++				init_latency = latency;
++			else
++				init_latency = 2;
++		}
++		dev_info(&wc->dev->dev, "5th gen card with initial latency of "
++			"%d and %d ms per IRQ\n", init_latency, ms_per_irq);
++	} else {
++		if (wc->flags & FLAG_2NDGEN)
++			init_latency = 1;
++		else
++			init_latency = 2;
++	}
++
++	if (max_latency < init_latency) {
++		printk(KERN_INFO "maxlatency must be set to something greater than %d ms, increasing it to %d\n", init_latency, init_latency);
++		max_latency = init_latency;
++	}
++	
++	if (t4_allocate_buffers(wc, init_latency, NULL, NULL)) {
++		return -ENOMEM;
++	}
++
++	/* Initialize hardware */
++	t4_hardware_init_1(wc, wc->flags);
++	
++	for(x = 0; x < MAX_T4_CARDS; x++) {
++		if (!cards[x])
++			break;
++	}
++	
++	if (x >= MAX_T4_CARDS) {
++		dev_notice(&wc->dev->dev, "No cards[] slot available!!\n");
++		kfree(wc);
++		return -ENOMEM;
++	}
++	
++	wc->num = x;
++	cards[x] = wc;
++	
++#ifdef ENABLE_WORKQUEUES
++	if (wc->flags & FLAG_2NDGEN) {
++		char tmp[20];
++
++		sprintf(tmp, "opvxd115");
++		wc->workq = create_workqueue(tmp);
++	}
++#endif			
++
++	/* Allocate pieces we need here */
++	for (x = 0; x < PORTS_PER_FRAMER; x++) {
++		if (!(wc->tspans[x] = kmalloc(sizeof(*wc->tspans[x]), GFP_KERNEL))) {
++			free_wc(wc);
++			return -ENOMEM;
++		}
++
++		memset(wc->tspans[x], 0, sizeof(*wc->tspans[x]));
++
++		if (wc->t1e1 & (1 << x)) {
++			wc->tspans[x]->spantype = TYPE_E1;
++		} else {
++			if (j1mode)
++				wc->tspans[x]->spantype = TYPE_J1;
++			else
++				wc->tspans[x]->spantype = TYPE_T1;
++		}
++
++		for (f = 0; f < (wc->tspans[x]->spantype == TYPE_E1 ? 31 : 24); f++) {
++			if (!(wc->tspans[x]->chans[f] = kmalloc(sizeof(*wc->tspans[x]->chans[f]), GFP_KERNEL))) {
++				free_wc(wc);
++				return -ENOMEM;
++			}
++			memset(wc->tspans[x]->chans[f], 0, sizeof(*wc->tspans[x]->chans[f]));
++			if (!(wc->tspans[x]->ec[f] = kmalloc(sizeof(*wc->tspans[x]->ec[f]), GFP_KERNEL))) {
++				free_wc(wc);
++				return -ENOMEM;
++			}
++			memset(wc->tspans[x]->ec[f], 0, sizeof(*wc->tspans[x]->ec[f]));
++		}
++
++#ifdef ENABLE_WORKQUEUES
++		INIT_WORK(&wc->tspans[x]->swork, workq_handlespan, wc->tspans[x]);
++#endif				
++		wc->tspans[x]->spanflags |= wc->flags;
++	}
++	
++	/* Continue hardware intiialization */
++	t4_hardware_init_2(wc);
++	
++#ifdef SUPPORT_GEN1
++	if (request_irq(pdev->irq, (wc->flags & FLAG_2NDGEN) ? t4_interrupt_gen2 :t4_interrupt, DAHDI_IRQ_SHARED_DISABLED, "opvxd115", wc)) 
++#else
++		if (!(wc->tspans[0]->spanflags & FLAG_2NDGEN)) {
++			dev_notice(&wc->dev->dev, "This driver does not "
++					"support 1st gen modules\n");
++			free_wc(wc);
++			return -ENODEV;
++		}	
++			if (request_irq(pdev->irq, t4_interrupt_gen2, DAHDI_IRQ_SHARED_DISABLED, "opvxd115", wc)) 
++#endif
++	{
++		dev_notice(&wc->dev->dev, "opvxd115: Unable to request IRQ %d\n",
++				pdev->irq);
++		free_wc(wc);
++		return -EIO;
++	}
++	
++	init_spans(wc);
++	/* get the current number of probed cards and run a slice of a tail
++	 * insertion sort */
++	for (x = 0; x < MAX_T4_CARDS; x++) {
++		if (!cards[x+1])
++			break;
++	}
++	for ( ; x > 0; x--) {
++		if (cards[x]->order < cards[x-1]->order) {
++			struct t4 *tmp = cards[x];
++			cards[x] = cards[x-1];
++			cards[x-1] = tmp;
++		} else {
++			/* if we're not moving it, we won't move any more
++			 * since all cards are sorted on addition */
++			break;
++		}
++	}
++	
++	dev_info(&wc->dev->dev, "Found an OpenVox Card: %s\n", wc->variety);
++	wc->gpio = 0x00000000;
++	t4_pci_out(wc, WC_GPIO, wc->gpio);
++	t4_gpio_setdir(wc, (1 << 17), (1 << 17));
++	t4_gpio_setdir(wc, (0xff), (0xff));
++
++	create_sysfs_files(wc);
++	
++#if 0
++	for (x=0;x<0x10000;x++) {
++		__t4_raw_oct_out(wc, 0x0004, x);
++		__t4_raw_oct_out(wc, 0x000a, x ^ 0xffff);
++		if (__t4_raw_oct_in(wc, 0x0004) != x) 
++			dev_notice(&wc->dev->dev, "Register 4 failed %04x\n",
++					x);
++		if (__t4_raw_oct_in(wc, 0x000a) != (x ^ 0xffff))
++			dev_notice(&wc->dev->dev, "Register 10 failed %04x\n",
++					x);
++	}
++#endif
++
++	return 0;
++}
++
++static int t4_hardware_stop(struct t4 *wc)
++{
++
++	/* Turn off DMA, leave interrupts enabled */
++	set_bit(T4_STOP_DMA, &wc->checkflag);
++
++	/* Wait for interrupts to stop */
++	msleep(25);
++
++	/* Turn off counter, address, etc */
++	if (wc->tspans[0]->spanflags & FLAG_2NDGEN) {
++		t4_tsi_reset(wc);
++	} else {
++		t4_pci_out(wc, WC_COUNT, 0x000000);
++	}
++	t4_pci_out(wc, WC_RDADDR, 0x0000000);
++	t4_pci_out(wc, WC_WRADDR, 0x0000000);
++	wc->gpio = 0x00000000;
++	t4_pci_out(wc, WC_GPIO, wc->gpio);
++	t4_pci_out(wc, WC_LEDS, 0x00000000);
++
++	dev_notice(&wc->dev->dev, "\nStopped opvxd115, Turned off DMA\n");
++	return 0;
++}
++
++static void __devexit t4_remove_one(struct pci_dev *pdev)
++{
++	struct t4 *wc = pci_get_drvdata(pdev);
++	int basesize;
++
++	if (!wc) {
++		return;
++	}
++
++	remove_sysfs_files(wc);
++
++	/* Stop hardware */
++	t4_hardware_stop(wc);
++	
++	/* Release vpm450m */
++	if (wc->vpm450m)
++		release_vpm450m(wc->vpm450m);
++	wc->vpm450m = NULL;
++	/* Unregister spans */
++
++	basesize = DAHDI_MAX_CHUNKSIZE * 32 * 4;
++	if (!(wc->tspans[0]->spanflags & FLAG_2NDGEN))
++		basesize = basesize * 2;
++
++	dahdi_unregister_device(wc->ddev);
++	kfree(wc->ddev->location);
++	kfree(wc->ddev->devicetype);
++	dahdi_free_device(wc->ddev);
++#ifdef ENABLE_WORKQUEUES
++	if (wc->workq) {
++		flush_workqueue(wc->workq);
++		destroy_workqueue(wc->workq);
++	}
++#endif			
++	
++	free_irq(pdev->irq, wc);
++	
++	if (wc->membase)
++		iounmap(wc->membase);
++	
++	pci_release_regions(pdev);		
++	
++	/* Immediately free resources */
++	pci_free_consistent(pdev, T4_BASE_SIZE * wc->numbufs * 2, (void *)wc->writechunk, wc->writedma);
++	
++	order_index[wc->order]--;
++	
++	cards[wc->num] = NULL;
++	pci_set_drvdata(pdev, NULL);
++	free_wc(wc);
++}
++
++
++static struct pci_device_id t4_pci_tbl[] __devinitdata =
++{
++	{ 0x1b74, 0x0115, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&opvxd115 }, /* OpenVox D115P/D115E */
++	{ 0x1b74, 0xd130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&opvxd130 }, /* OpenVox D130P/D130E */
++	{ 0, }
++};
++
++static struct pci_driver t4_driver = {
++	.name = "opvxd115",
++	.probe = t4_init_one,
++	.remove = __devexit_p(t4_remove_one),
++	.id_table = t4_pci_tbl,
++};
++
++static int __init t4_init(void)
++{
++	int res;
++	res = dahdi_pci_module(&t4_driver);
++	if (res)
++		return -ENODEV;
++	/* initialize cards since we have all of them */
++	/* warn for missing zero and duplicate numbers */
++	if (cards[0] && cards[0]->order != 0) {
++		printk(KERN_NOTICE "opvxd115: Ident of first card is not zero (%d)\n",
++			cards[0]->order);
++	}
++	for (res = 0; cards[res]; res++) {
++		/* warn the user of duplicate ident values it is probably
++		 * unintended */
++		if (debug && res < 15 && cards[res+1] &&
++		    cards[res]->order == cards[res+1]->order) {
++			printk(KERN_NOTICE "opvxd115: Duplicate ident value found (%d)\n",
++				cards[res]->order);
++		}
++		t4_launch(cards[res]);
++	}
++	return 0;
++}
++
++static void __exit t4_cleanup(void)
++{
++	pci_unregister_driver(&t4_driver);
++}
++
++
++MODULE_AUTHOR("mark.liu <mark.liu at openvox.cn>");
++MODULE_DESCRIPTION("Unified OpenVox Single T1/E1/J1 Card Driver");
++MODULE_ALIAS("opvxd115");
++MODULE_LICENSE("GPL v2");
++
++module_param(pedanticpci, int, 0600);
++module_param(debug, int, 0600);
++module_param(noburst, int, 0600);
++module_param(timingcable, int, 0600);
++module_param(t1e1override, int, 0600);
++module_param(alarmdebounce, int, 0600);
++module_param(losalarmdebounce, int, 0600);
++module_param(aisalarmdebounce, int, 0600);
++module_param(yelalarmdebounce, int, 0600);
++module_param(max_latency, int, 0600);
++module_param(j1mode, int, 0600);
++module_param(sigmode, int, 0600);
++module_param(latency, int, 0600);
++module_param(ms_per_irq, int, 0600);
++#ifdef VPM_SUPPORT
++module_param(vpmsupport, int, 0600);
++module_param(vpmdtmfsupport, int, 0600);
++module_param(vpmspans, int, 0600);
++module_param(dtmfthreshold, int, 0600);
++#endif
++
++MODULE_DEVICE_TABLE(pci, t4_pci_tbl);
++
++module_init(t4_init);
++module_exit(t4_cleanup);
+diff --git a/drivers/dahdi/opvxd115/opvxd115-diag.c b/drivers/dahdi/opvxd115/opvxd115-diag.c
+new file mode 100644
+index 0000000..7d1dfdb
+--- /dev/null
++++ b/drivers/dahdi/opvxd115/opvxd115-diag.c
+@@ -0,0 +1,427 @@
++/*
++ * See http://www.asterisk.org for more information about
++ * the Asterisk project. Please do not directly contact
++ * any of the maintainers of this project for assistance;
++ * the project provides a web site, mailing lists and IRC
++ * channels for your use.
++ *
++ * This program is free software, distributed under the terms of
++ * the GNU General Public License Version 2 as published by the
++ * Free Software Foundation. See the LICENSE file included with
++ * this program for more details.
++ */
++
++#include <fcntl.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <sys/ioctl.h>
++#include <errno.h>
++#include <string.h>
++#include <dahdi/user.h>
++#include "opvxd115.h"
++
++struct t4_reg_def {
++	int reg;
++	char *name;
++	int global;
++};
++static struct t4_reg_def xreginfo[] = {
++	{ 0x00, "RDADDR" },
++	{ 0x01, "WRADDR" },
++	{ 0x02, "COUNT" },
++	{ 0x03, "DMACTRL" },
++	{ 0x04, "WCINTR" },
++	{ 0x06, "VERSION" },
++	{ 0x07, "LEDS" },
++	{ 0x08, "GPIOCTL" },
++	{ 0x09, "GPIO" }, 
++	{ 0x0A, "LADDR" },
++	{ 0x0b, "LDATA" },
++};
++
++static struct t4_reg_def reginfo[] = {
++	{ 0x00, "XFIFO" },
++	{ 0x01, "XFIFO" },
++	{ 0x02, "CMDR" },
++	{ 0x03, "MODE" },
++	{ 0x04, "RAH1" },
++	{ 0x05, "RAH2" },
++	{ 0x06, "RAL1" },
++	{ 0x07, "RAL2" },
++	{ 0x08, "IPC", 1 },
++	{ 0x09, "CCR1" },
++	{ 0x0a, "CCR2" },
++	{ 0x0c, "RTR1" },
++	{ 0x0d, "RTR2" },
++	{ 0x0e, "RTR3" },
++	{ 0x0f, "RTR4" },
++	{ 0x10, "TTR1" },
++	{ 0x11, "TTR2" },
++	{ 0x12, "TTR3" },
++	{ 0x13, "TTR4" },
++	{ 0x14, "IMR0" },
++	{ 0x15, "IMR1" },
++	{ 0x16, "IMR2" },
++	{ 0x17, "IMR3" },
++	{ 0x18, "IMR4" },
++	{ 0x1b, "IERR" },
++	{ 0x1c, "FMR0" },
++	{ 0x1d, "FMR1" },
++	{ 0x1e, "FMR2" },
++	{ 0x1f, "LOOP" },
++	{ 0x20, "XSW" },
++	{ 0x21, "XSP" },
++	{ 0x22, "XC0" },
++	{ 0x23, "XC1" },
++	{ 0x24, "RC0" },
++	{ 0x25, "RC1" },
++	{ 0x26, "XPM0" },
++	{ 0x27, "XPM1" },
++	{ 0x28, "XPM2" },
++	{ 0x29, "TSWM" },
++	{ 0x2b, "IDLE" },
++	{ 0x2c, "XSA4" },
++	{ 0x2d, "XSA5" },
++	{ 0x2e, "XSA6" },
++	{ 0x2f, "XSA7" },
++	{ 0x30, "XSA8" },
++	{ 0x31, "FMR3" },
++	{ 0x32, "ICB1" },
++	{ 0x33, "ICB2" },
++	{ 0x34, "ICB3" },
++	{ 0x35, "ICB4" },
++	{ 0x36, "LIM0" },
++	{ 0x37, "LIM1" },
++	{ 0x38, "PCD" },
++	{ 0x39, "PCR" },
++	{ 0x3a, "LIM2" },
++	{ 0x3b, "LCR1" },
++	{ 0x3c, "LCR2" },
++	{ 0x3d, "LCR3" },
++	{ 0x3e, "SIC1" },
++	{ 0x3f, "SIC2" },
++	{ 0x40, "SIC3" },
++	{ 0x44, "CMR1" },
++	{ 0x45, "CMR2" },
++	{ 0x46, "GCR" },
++	{ 0x47, "ESM" },
++	{ 0x60, "DEC" },
++	{ 0x70, "XS1" },
++	{ 0x71, "XS2" },
++	{ 0x72, "XS3" },
++	{ 0x73, "XS4" },
++	{ 0x74, "XS5" },
++	{ 0x75, "XS6" },
++	{ 0x76, "XS7" },
++	{ 0x77, "XS8" },
++	{ 0x78, "XS9" },
++	{ 0x79, "XS10" },
++	{ 0x7a, "XS11" },
++	{ 0x7b, "XS12" },
++	{ 0x7c, "XS13" },
++	{ 0x7d, "XS14" },
++	{ 0x7e, "XS15" },
++	{ 0x7f, "XS16" },
++	{ 0x80, "PC1" },
++	{ 0x81, "PC2" },
++	{ 0x82, "PC3" },
++	{ 0x83, "PC4" },
++	{ 0x84, "PC5" },
++	{ 0x85, "GPC1", 1 },
++	{ 0x87, "CMDR2" },
++	{ 0x8d, "CCR5" },
++	{ 0x92, "GCM1", 1 },
++	{ 0x93, "GCM2", 1 },
++	{ 0x94, "GCM3", 1 },
++	{ 0x95, "GCM4", 1 },
++	{ 0x96, "GCM5", 1 },
++	{ 0x97, "GCM6", 1 },
++	{ 0x98, "GCM7", 1 },
++	{ 0x99, "GCM8", 1 },
++	{ 0xa0, "TSEO" },
++	{ 0xa1, "TSBS1" },
++	{ 0xa8, "TPC0" },	
++};
++
++static struct t4_reg_def t1_reginfo[] = {
++	{ 0x00, "XFIFO" },
++	{ 0x01, "XFIFO" },
++	{ 0x02, "CMDR" },
++	{ 0x03, "MODE" },
++	{ 0x04, "RAH1" },
++	{ 0x05, "RAH2" },
++	{ 0x06, "RAL1" },
++	{ 0x07, "RAL2" },
++	{ 0x08, "IPC", 1 },
++	{ 0x09, "CCR1" },
++	{ 0x0a, "CCR2" },
++	{ 0x0c, "RTR1" },
++	{ 0x0d, "RTR2" },
++	{ 0x0e, "RTR3" },
++	{ 0x0f, "RTR4" },
++	{ 0x10, "TTR1" },
++	{ 0x11, "TTR2" },
++	{ 0x12, "TTR3" },
++	{ 0x13, "TTR4" },
++	{ 0x14, "IMR0" },
++	{ 0x15, "IMR1" },
++	{ 0x16, "IMR2" },
++	{ 0x17, "IMR3" },
++	{ 0x18, "IMR4" },
++	{ 0x1b, "IERR" },
++	{ 0x1c, "FMR0" },
++	{ 0x1d, "FMR1" },
++	{ 0x1e, "FMR2" },
++	{ 0x1f, "LOOP" },
++	{ 0x20, "FMR4" },
++	{ 0x21, "FMR5" },
++	{ 0x22, "XC0" },
++	{ 0x23, "XC1" },
++	{ 0x24, "RC0" },
++	{ 0x25, "RC1" },
++	{ 0x26, "XPM0" },
++	{ 0x27, "XPM1" },
++	{ 0x28, "XPM2" },
++	{ 0x2b, "IDLE" },
++	{ 0x2c, "XDL1" },
++	{ 0x2d, "XDL2" },
++	{ 0x2e, "XDL3" },
++	{ 0x2f, "CCB1" },
++	{ 0x30, "CCB2" },
++	{ 0x31, "CCB3" },
++	{ 0x32, "ICB1" },
++	{ 0x33, "ICB2" },
++	{ 0x34, "ICB3" },
++	{ 0x36, "LIM0" },
++	{ 0x37, "LIM1" },
++	{ 0x38, "PCD" },
++	{ 0x39, "PCR" },
++	{ 0x3a, "LIM2" },
++	{ 0x3b, "LCR1" },
++	{ 0x3c, "LCR2" },
++	{ 0x3d, "LCR3" },
++	{ 0x3e, "SIC1" },
++	{ 0x3f, "SIC2" },
++	{ 0x40, "SIC3" },
++	{ 0x44, "CMR1" },
++	{ 0x45, "CMR2" },
++	{ 0x46, "GCR" },
++	{ 0x47, "ESM" },
++	{ 0x60, "DEC" },
++	{ 0x70, "XS1" },
++	{ 0x71, "XS2" },
++	{ 0x72, "XS3" },
++	{ 0x73, "XS4" },
++	{ 0x74, "XS5" },
++	{ 0x75, "XS6" },
++	{ 0x76, "XS7" },
++	{ 0x77, "XS8" },
++	{ 0x78, "XS9" },
++	{ 0x79, "XS10" },
++	{ 0x7a, "XS11" },
++	{ 0x7b, "XS12" },
++	{ 0x80, "PC1" },
++	{ 0x81, "PC2" },
++	{ 0x82, "PC3" },
++	{ 0x83, "PC4" },
++	{ 0x84, "PC5" },
++	{ 0x85, "GPC1", 1 },
++	{ 0x87, "CMDR2" },
++	{ 0x8d, "CCR5" },
++	{ 0x92, "GCM1", 1 },
++	{ 0x93, "GCM2", 1 },
++	{ 0x94, "GCM3", 1 },
++	{ 0x95, "GCM4", 1 },
++	{ 0x96, "GCM5", 1 },
++	{ 0x97, "GCM6", 1 },
++	{ 0x98, "GCM7", 1 },
++	{ 0x99, "GCM8", 1 },
++	{ 0xa0, "TSEO" },
++	{ 0xa1, "TSBS1" },
++	{ 0xa8, "TPC0" },	
++};
++
++static struct t4_reg_def t1_sreginfo[] = {
++	{ 0x00, "RFIFO" },
++	{ 0x01, "RFIFO" },
++	{ 0x49, "RBD" },
++	{ 0x4a, "VSTR", 1 },
++	{ 0x4b, "RES" },
++	{ 0x4c, "FRS0" },
++	{ 0x4d, "FRS1" },
++	{ 0x4e, "FRS2" },
++	{ 0x4f, "Old FRS1" },
++	{ 0x50, "FECL" },
++	{ 0x51, "FECH" },
++	{ 0x52, "CVCL" },
++	{ 0x53, "CVCH" },
++	{ 0x54, "CECL" },
++	{ 0x55, "CECH" },
++	{ 0x56, "EBCL" },
++	{ 0x57, "EBCH" },
++	{ 0x58, "BECL" },
++	{ 0x59, "BECH" },
++	{ 0x5a, "COEC" },
++	{ 0x5c, "RDL1" },
++	{ 0x5d, "RDL2" },
++	{ 0x5e, "RDL3" },
++	{ 0x62, "RSP1" },
++	{ 0x63, "RSP2" },
++	{ 0x64, "SIS" },
++	{ 0x65, "RSIS" },
++	{ 0x66, "RBCL" },
++	{ 0x67, "RBCH" },
++	{ 0x68, "ISR0" },
++	{ 0x69, "ISR1" },
++	{ 0x6a, "ISR2" },
++	{ 0x6b, "ISR3" },
++	{ 0x6c, "ISR4" },
++	{ 0x6e, "GIS" },
++	{ 0x6f, "CIS", 1 },
++	{ 0x70, "RS1" },
++	{ 0x71, "RS2" },
++	{ 0x72, "RS3" },
++	{ 0x73, "RS4" },
++	{ 0x74, "RS5" },
++	{ 0x75, "RS6" },
++	{ 0x76, "RS7" },
++	{ 0x77, "RS8" },
++	{ 0x78, "RS9" },
++	{ 0x79, "RS10" },
++	{ 0x7a, "RS11" },
++	{ 0x7b, "RS12" },
++};
++
++static struct t4_reg_def sreginfo[] = {
++	{ 0x00, "RFIFO" },
++	{ 0x01, "RFIFO" },
++	{ 0x49, "RBD" },
++	{ 0x4a, "VSTR", 1 },
++	{ 0x4b, "RES" },
++	{ 0x4c, "FRS0" },
++	{ 0x4d, "FRS1" },
++	{ 0x4e, "RSW" },
++	{ 0x4f, "RSP" },
++	{ 0x50, "FECL" },
++	{ 0x51, "FECH" },
++	{ 0x52, "CVCL" },
++	{ 0x53, "CVCH" },
++	{ 0x54, "CEC1L" },
++	{ 0x55, "CEC1H" },
++	{ 0x56, "EBCL" },
++	{ 0x57, "EBCH" },
++	{ 0x58, "CEC2L" },
++	{ 0x59, "CEC2H" },
++	{ 0x5a, "CEC3L" },
++	{ 0x5b, "CEC3H" },
++	{ 0x5c, "RSA4" },
++	{ 0x5d, "RSA5" },
++	{ 0x5e, "RSA6" },
++	{ 0x5f, "RSA7" },
++	{ 0x60, "RSA8" },
++	{ 0x61, "RSA6S" },
++	{ 0x62, "RSP1" },
++	{ 0x63, "RSP2" },
++	{ 0x64, "SIS" },
++	{ 0x65, "RSIS" },
++	{ 0x66, "RBCL" },
++	{ 0x67, "RBCH" },
++	{ 0x68, "ISR0" },
++	{ 0x69, "ISR1" },
++	{ 0x6a, "ISR2" },
++	{ 0x6b, "ISR3" },
++	{ 0x6c, "ISR4" },
++	{ 0x6e, "GIS" },
++	{ 0x6f, "CIS", 1 },
++	{ 0x70, "RS1" },
++	{ 0x71, "RS2" },
++	{ 0x72, "RS3" },
++	{ 0x73, "RS4" },
++	{ 0x74, "RS5" },
++	{ 0x75, "RS6" },
++	{ 0x76, "RS7" },
++	{ 0x77, "RS8" },
++	{ 0x78, "RS9" },
++	{ 0x79, "RS10" },
++	{ 0x7a, "RS11" },
++	{ 0x7b, "RS12" },
++	{ 0x7c, "RS13" },
++	{ 0x7d, "RS14" },
++	{ 0x7e, "RS15" },
++	{ 0x7f, "RS16" },
++};
++
++static char *tobin(int x)
++{
++	static char s[9] = "";
++	int y,z=0;
++	for (y=7;y>=0;y--) {
++		if (x & (1 << y))
++			s[z++] = '1';
++		else
++			s[z++] = '0';
++	}
++	s[z] = '\0';
++	return s;
++}
++
++static char *tobin32(unsigned int x)
++{
++	static char s[33] = "";
++	int y,z=0;
++	for (y=31;y>=0;y--) {
++		if (x & (1 << y))
++			s[z++] = '1';
++		else
++			s[z++] = '0';
++	}
++	s[z] = '\0';
++	return s;
++}
++
++int main(int argc, char *argv[])
++{
++	int fd;
++	int x;
++	char fn[256];
++	struct t4_regs regs;
++	if ((argc < 2) || ((*(argv[1]) != '/') && !atoi(argv[1]))) {
++		fprintf(stderr, "Usage: opvxd115-diag <channel>\n");
++		exit(1);
++	}
++	if (*(argv[1]) == '/')
++		dahdi_copy_string(fn, argv[1], sizeof(fn));
++	else
++		snprintf(fn, sizeof(fn), "/dev/dahdi/%d", atoi(argv[1]));
++	fd = open(fn, O_RDWR);
++	if (fd <0) {
++		fprintf(stderr, "Unable to open '%s': %s\n", fn, strerror(errno));
++		exit(1);
++	}
++	if (ioctl(fd, WCT4_GET_REGS, &regs)) {
++		fprintf(stderr, "Unable to get registers: %s\n", strerror(errno));
++		exit(1);
++	}
++	printf("PCI Registers:\n");
++	for (x=0;x<sizeof(xreginfo) / sizeof(xreginfo[0]);x++) {
++		fprintf(stdout, "%s (%02x): %08x (%s)\n", xreginfo[x].name, xreginfo[x].reg, regs.pci[xreginfo[x].reg], tobin32(regs.pci[xreginfo[x].reg]));
++	}
++	printf("\nE1 Control Registers:\n");
++	for (x=0;x<sizeof(reginfo) / sizeof(reginfo[0]);x++) {
++		fprintf(stdout, "%s (%02x): %02x (%s)\n", reginfo[x].name, reginfo[x].reg, regs.regs[reginfo[x].reg], tobin(regs.regs[reginfo[x].reg]));
++	}
++	printf("\nE1 Status Registers:\n");
++	for (x=0;x<sizeof(sreginfo) / sizeof(sreginfo[0]);x++) {
++		fprintf(stdout, "%s (%02x): %02x (%s)\n", sreginfo[x].name, sreginfo[x].reg, regs.regs[sreginfo[x].reg], tobin(regs.regs[sreginfo[x].reg]));
++	}
++	printf("\nT1 Control Registers:\n");
++	for (x=0;x<sizeof(t1_reginfo) / sizeof(t1_reginfo[0]);x++) {
++		fprintf(stdout, "%s (%02x): %02x (%s)\n", t1_reginfo[x].name, t1_reginfo[x].reg, regs.regs[t1_reginfo[x].reg], tobin(regs.regs[t1_reginfo[x].reg]));
++	}
++	printf("\nT1 Status Registers:\n");
++	for (x=0;x<sizeof(t1_sreginfo) / sizeof(t1_sreginfo[0]);x++) {
++		fprintf(stdout, "%s (%02x): %02x (%s)\n", t1_sreginfo[x].name, t1_sreginfo[x].reg, regs.regs[t1_sreginfo[x].reg], tobin(regs.regs[t1_sreginfo[x].reg]));
++	}
++	exit(0);
++}
+diff --git a/drivers/dahdi/opvxd115/opvxd115.h b/drivers/dahdi/opvxd115/opvxd115.h
+new file mode 100644
+index 0000000..d89d5b3
+--- /dev/null
++++ b/drivers/dahdi/opvxd115/opvxd115.h
+@@ -0,0 +1,124 @@
++/*
++ * Wildcard T400P FXS Interface Driver for DAHDI Telephony interface
++ *
++ * Written by Mark Spencer <markster at linux-support.net>
++ *
++ * Copyright (C) 2001-2008, Digium, Inc.
++ *
++ * All rights reserved.
++ *
++ */
++
++/*
++ * See http://www.asterisk.org for more information about
++ * the Asterisk project. Please do not directly contact
++ * any of the maintainers of this project for assistance;
++ * the project provides a web site, mailing lists and IRC
++ * channels for your use.
++ *
++ * This program is free software, distributed under the terms of
++ * the GNU General Public License Version 2 as published by the
++ * Free Software Foundation. See the LICENSE file included with
++ * this program for more details.
++ */
++
++#include <linux/ioctl.h>
++
++#define FRMR_TTR_BASE 0x10
++#define FRMR_RTR_BASE 0x0c
++#define FRMR_TSEO 0xa0
++#define FRMR_TSBS1 0xa1
++#define FRMR_CCR1 0x09
++#define FRMR_CCR1_ITF 0x08
++#define FRMR_CCR1_EITS 0x10
++#define FRMR_CCR2 0x0a
++#define FRMR_CCR2_RCRC 0x04
++#define FRMR_CCR2_RADD 0x10
++#define FRMR_MODE 0x03
++#define FRMR_MODE_NO_ADDR_CMP 0x80
++#define FRMR_MODE_SS7 0x20
++#define FRMR_MODE_HRAC 0x08
++#define FRMR_IMR0 0x14
++#define FRMR_IMR0_RME 0x80
++#define FRMR_IMR0_RPF 0x01
++#define FRMR_IMR1 0x15
++#define FRMR_IMR1_ALLS 0x20
++#define FRMR_IMR1_XDU 0x10
++#define FRMR_IMR1_XPR 0x01
++#define FRMR_XC0 0x22
++#define FRMR_XC1 0x23
++#define FRMR_RC0 0x24
++#define FRMR_RC1 0x25
++#define FRMR_SIC1 0x3e
++#define FRMR_SIC2 0x3f
++#define FRMR_SIC3 0x40
++#define FRMR_CMR1 0x44
++#define FRMR_CMR2 0x45
++#define FRMR_GCR 0x46
++#define FRMR_ISR0 0x68
++#define FRMR_ISR0_RME 0x80
++#define FRMR_ISR0_RPF 0x01
++#define FRMR_ISR1 0x69
++#define FRMR_ISR1_ALLS 0x20
++#define FRMR_ISR1_XDU 0x10
++#define FRMR_ISR1_XPR 0x01
++#define FRMR_ISR2 0x6a
++#define FRMR_ISR3 0x6b
++#define FRMR_ISR4 0x6c
++#define FRMR_GIS  0x6e
++#define FRMR_GIS_ISR0 0x01
++#define FRMR_GIS_ISR1 0x02
++#define FRMR_GIS_ISR2 0x04
++#define FRMR_GIS_ISR3 0x08
++#define FRMR_GIS_ISR4 0x10
++#define FRMR_CIS 0x6f
++#define FRMR_CIS_GIS1 0x01
++#define FRMR_CIS_GIS2 0x02
++#define FRMR_CIS_GIS3 0x04
++#define FRMR_CIS_GIS4 0x08
++#define FRMR_CMDR 0x02
++#define FRMR_CMDR_SRES 0x01
++#define FRMR_CMDR_XRES 0x10
++#define FRMR_CMDR_RMC 0x80
++#define FRMR_CMDR_XTF 0x04
++#define FRMR_CMDR_XHF 0x08
++#define FRMR_CMDR_XME 0x02
++#define FRMR_RSIS 0x65
++#define FRMR_RSIS_VFR 0x80
++#define FRMR_RSIS_RDO 0x40
++#define FRMR_RSIS_CRC16 0x20
++#define FRMR_RSIS_RAB 0x10
++#define FRMR_RBCL 0x66
++#define FRMR_RBCL_MAX_SIZE 0x1f
++#define FRMR_RBCH 0x67
++#define FRMR_RXFIFO 0x00
++#define FRMR_SIS 0x64
++#define FRMR_SIS_XFW 0x40
++#define FRMR_TXFIFO 0x00
++
++#define FRS0 0x4c
++#define FRS0_LOS (1<<7)
++#define FRS0_LFA (1<<5)
++#define FRS0_LMFA (1<<1)
++
++#define FRS1 0x4d
++#define FRS1_XLS (1<<1)
++#define FRS1_XLO (1<<0)
++
++#define NUM_REGS 0xa9
++#define NUM_PCI 12
++
++struct t4_regs {
++	unsigned int pci[NUM_PCI];
++	unsigned char regs[NUM_REGS];
++};
++
++#define T4_CHECK_VPM		0
++#define T4_LOADING_FW		1
++#define T4_STOP_DMA		2
++#define T4_CHECK_TIMING		3
++#define T4_CHANGE_LATENCY	4
++#define T4_IGNORE_LATENCY	5
++
++#define WCT4_GET_REGS	_IOW (DAHDI_CODE, 60, struct t4_regs)
++
+diff --git a/drivers/dahdi/opvxd115/vpm450m.c b/drivers/dahdi/opvxd115/vpm450m.c
+new file mode 100644
+index 0000000..f1ed421
+--- /dev/null
++++ b/drivers/dahdi/opvxd115/vpm450m.c
+@@ -0,0 +1,587 @@
++/*
++ * Copyright (C) 2005-2006 Digium, Inc.
++ *
++ * Mark Spencer <markster at digium.com>
++ * Modified by mark.liu at openvox.cn 06/16/2009
++ 
++ * All Rights Reserved
++ */
++
++/*
++ * See http://www.asterisk.org for more information about
++ * the Asterisk project. Please do not directly contact
++ * any of the maintainers of this project for assistance;
++ * the project provides a web site, mailing lists and IRC
++ * channels for your use.
++ *
++ * This program is free software, distributed under the terms of
++ * the GNU General Public License Version 2 as published by the
++ * Free Software Foundation. See the LICENSE file included with
++ * this program for more details.
++ */
++
++#include <linux/slab.h>
++#include <linux/vmalloc.h>
++#include <linux/string.h>
++#include <linux/time.h>
++#include <linux/version.h>
++
++#include "vpm450m.h"
++#include "oct6100api/oct6100_api.h"
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
++#include <linux/config.h>
++#endif
++
++/* API for Octasic access */
++UINT32 Oct6100UserGetTime(tPOCT6100_GET_TIME f_pTime)
++{
++	/* Why couldn't they just take a timeval like everyone else? */
++	struct timeval tv;
++	unsigned long long total_usecs;
++	unsigned int mask = ~0;
++	
++	do_gettimeofday(&tv);
++	total_usecs = (((unsigned long long)(tv.tv_sec)) * 1000000) + 
++				  (((unsigned long long)(tv.tv_usec)));
++	f_pTime->aulWallTimeUs[0] = (total_usecs & mask);
++	f_pTime->aulWallTimeUs[1] = (total_usecs >> 32);
++	return cOCT6100_ERR_OK;
++}
++
++UINT32 Oct6100UserMemSet(PVOID f_pAddress, UINT32 f_ulPattern, UINT32 f_ulLength)
++{
++	memset(f_pAddress, f_ulPattern, f_ulLength);
++	return cOCT6100_ERR_OK;
++}
++
++UINT32 Oct6100UserMemCopy(PVOID f_pDestination, const void *f_pSource, UINT32 f_ulLength)
++{
++	memcpy(f_pDestination, f_pSource, f_ulLength);
++	return cOCT6100_ERR_OK;
++}
++
++UINT32 Oct6100UserCreateSerializeObject(tPOCT6100_CREATE_SERIALIZE_OBJECT f_pCreate)
++{
++	return cOCT6100_ERR_OK;
++}
++
++UINT32 Oct6100UserDestroySerializeObject(tPOCT6100_DESTROY_SERIALIZE_OBJECT f_pDestroy)
++{
++#ifdef OCTASIC_DEBUG
++	printk(KERN_DEBUG "I should never be called! (destroy serialize object)\n");
++#endif
++	return cOCT6100_ERR_OK;
++}
++
++UINT32 Oct6100UserSeizeSerializeObject(tPOCT6100_SEIZE_SERIALIZE_OBJECT f_pSeize)
++{
++	/* Not needed */
++	return cOCT6100_ERR_OK;
++}
++
++UINT32 Oct6100UserReleaseSerializeObject(tPOCT6100_RELEASE_SERIALIZE_OBJECT f_pRelease)
++{
++	/* Not needed */
++	return cOCT6100_ERR_OK;
++}
++
++UINT32 Oct6100UserDriverWriteApi(tPOCT6100_WRITE_PARAMS f_pWriteParams)
++{
++	oct_set_reg(f_pWriteParams->pProcessContext, f_pWriteParams->ulWriteAddress, f_pWriteParams->usWriteData);
++	return cOCT6100_ERR_OK;
++}
++
++UINT32 Oct6100UserDriverWriteSmearApi(tPOCT6100_WRITE_SMEAR_PARAMS f_pSmearParams)
++{
++	unsigned int x;
++	for (x=0;x<f_pSmearParams->ulWriteLength;x++) {
++		oct_set_reg(f_pSmearParams->pProcessContext, f_pSmearParams->ulWriteAddress + (x << 1), f_pSmearParams->usWriteData);
++	}
++	return cOCT6100_ERR_OK;
++}
++
++UINT32 Oct6100UserDriverWriteBurstApi(tPOCT6100_WRITE_BURST_PARAMS f_pBurstParams)
++{
++	unsigned int x;
++	for (x=0;x<f_pBurstParams->ulWriteLength;x++) {
++		oct_set_reg(f_pBurstParams->pProcessContext, f_pBurstParams->ulWriteAddress + (x << 1), f_pBurstParams->pusWriteData[x]);
++	}
++	return cOCT6100_ERR_OK;
++}
++
++UINT32 Oct6100UserDriverReadApi(tPOCT6100_READ_PARAMS f_pReadParams)
++{
++	*(f_pReadParams->pusReadData) = oct_get_reg(f_pReadParams->pProcessContext, f_pReadParams->ulReadAddress);
++	return cOCT6100_ERR_OK;
++}
++
++UINT32 Oct6100UserDriverReadBurstApi(tPOCT6100_READ_BURST_PARAMS f_pBurstParams)
++{
++	unsigned int x;
++	for (x=0;x<f_pBurstParams->ulReadLength;x++) {
++		f_pBurstParams->pusReadData[x] = oct_get_reg(f_pBurstParams->pProcessContext, f_pBurstParams->ulReadAddress + (x << 1));
++	}
++	return cOCT6100_ERR_OK;
++}
++
++#define SOUT_G168_1100GB_ON 0x40000004
++#define SOUT_DTMF_1 0x40000011
++#define SOUT_DTMF_2 0x40000012
++#define SOUT_DTMF_3 0x40000013
++#define SOUT_DTMF_A 0x4000001A
++#define SOUT_DTMF_4 0x40000014
++#define SOUT_DTMF_5 0x40000015
++#define SOUT_DTMF_6 0x40000016
++#define SOUT_DTMF_B 0x4000001B
++#define SOUT_DTMF_7 0x40000017
++#define SOUT_DTMF_8 0x40000018
++#define SOUT_DTMF_9 0x40000019
++#define SOUT_DTMF_C 0x4000001C
++#define SOUT_DTMF_STAR 0x4000001E
++#define SOUT_DTMF_0 0x40000010
++#define SOUT_DTMF_POUND 0x4000001F
++#define SOUT_DTMF_D 0x4000001D
++
++#define ROUT_G168_2100GB_ON 0x10000000
++#define ROUT_G168_2100GB_WSPR 0x10000002
++#define ROUT_SOUT_G168_2100HB_END 0x50000003
++#define ROUT_G168_1100GB_ON 0x10000004
++
++#define ROUT_DTMF_1 0x10000011
++#define ROUT_DTMF_2 0x10000012
++#define ROUT_DTMF_3 0x10000013
++#define ROUT_DTMF_A 0x1000001A
++#define ROUT_DTMF_4 0x10000014
++#define ROUT_DTMF_5 0x10000015
++#define ROUT_DTMF_6 0x10000016
++#define ROUT_DTMF_B 0x1000001B
++#define ROUT_DTMF_7 0x10000017
++#define ROUT_DTMF_8 0x10000018
++#define ROUT_DTMF_9 0x10000019
++#define ROUT_DTMF_C 0x1000001C
++#define ROUT_DTMF_STAR 0x1000001E
++#define ROUT_DTMF_0 0x10000010
++#define ROUT_DTMF_POUND 0x1000001F
++#define ROUT_DTMF_D 0x1000001D
++
++#if 0 
++#define cOCT6100_ECHO_OP_MODE_DIGITAL cOCT6100_ECHO_OP_MODE_HT_FREEZE
++#else
++#define cOCT6100_ECHO_OP_MODE_DIGITAL cOCT6100_ECHO_OP_MODE_POWER_DOWN
++#endif
++
++struct vpm450m {
++	tPOCT6100_INSTANCE_API pApiInstance;
++	UINT32 aulEchoChanHndl[ 128 ];
++	int chanflags[128];
++	int ecmode[128];
++	int numchans;
++};
++
++#define FLAG_DTMF	 (1 << 0)
++#define FLAG_MUTE	 (1 << 1)
++#define FLAG_ECHO	 (1 << 2)
++
++static unsigned int tones[] = {
++	SOUT_DTMF_1,
++	SOUT_DTMF_2,
++	SOUT_DTMF_3,
++	SOUT_DTMF_A,
++	SOUT_DTMF_4,
++	SOUT_DTMF_5,
++	SOUT_DTMF_6,
++	SOUT_DTMF_B,
++	SOUT_DTMF_7,
++	SOUT_DTMF_8,
++	SOUT_DTMF_9,
++	SOUT_DTMF_C,
++	SOUT_DTMF_STAR,
++	SOUT_DTMF_0,
++	SOUT_DTMF_POUND,
++	SOUT_DTMF_D,
++	SOUT_G168_1100GB_ON,
++
++	ROUT_DTMF_1,
++	ROUT_DTMF_2,
++	ROUT_DTMF_3,
++	ROUT_DTMF_A,
++	ROUT_DTMF_4,
++	ROUT_DTMF_5,
++	ROUT_DTMF_6,
++	ROUT_DTMF_B,
++	ROUT_DTMF_7,
++	ROUT_DTMF_8,
++	ROUT_DTMF_9,
++	ROUT_DTMF_C,
++	ROUT_DTMF_STAR,
++	ROUT_DTMF_0,
++	ROUT_DTMF_POUND,
++	ROUT_DTMF_D,
++	ROUT_G168_1100GB_ON,
++};
++
++static void vpm450m_setecmode(struct vpm450m *vpm450m, int channel, int mode)
++{
++	tOCT6100_CHANNEL_MODIFY *modify;
++	UINT32 ulResult;
++
++	if (vpm450m->ecmode[channel] == mode)
++		return;
++	modify = kmalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_ATOMIC);
++	if (!modify) {
++		printk(KERN_NOTICE "opvxd115: Unable to allocate memory for setec!\n");
++		return;
++	}
++	Oct6100ChannelModifyDef(modify);
++	modify->ulEchoOperationMode = mode;
++	modify->ulChannelHndl = vpm450m->aulEchoChanHndl[channel];
++	ulResult = Oct6100ChannelModify(vpm450m->pApiInstance, modify);
++	if (ulResult != GENERIC_OK) {
++		printk(KERN_NOTICE "Failed to apply echo can changes on channel %d!\n", channel);
++	} else {
++#ifdef OCTASIC_DEBUG
++		printk(KERN_DEBUG "Echo can on channel %d set to %d\n", channel, mode);
++#endif
++		vpm450m->ecmode[channel] = mode;
++	}
++	kfree(modify);
++}
++
++void vpm450m_setdtmf(struct vpm450m *vpm450m, int channel, int detect, int mute)
++{
++	tOCT6100_CHANNEL_MODIFY *modify;
++	UINT32 ulResult;
++
++	modify = kmalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_KERNEL);
++	if (!modify) {
++		printk(KERN_NOTICE "opvxd115: Unable to allocate memory for setdtmf!\n");
++		return;
++	}
++	Oct6100ChannelModifyDef(modify);
++	modify->ulChannelHndl = vpm450m->aulEchoChanHndl[channel];
++	if (mute) {
++		vpm450m->chanflags[channel] |= FLAG_MUTE;
++		modify->VqeConfig.fDtmfToneRemoval = TRUE;
++	} else {
++		vpm450m->chanflags[channel] &= ~FLAG_MUTE;
++		modify->VqeConfig.fDtmfToneRemoval = FALSE;
++	}
++	if (detect)
++		vpm450m->chanflags[channel] |= FLAG_DTMF;
++	else
++		vpm450m->chanflags[channel] &= ~FLAG_DTMF;
++	if (vpm450m->chanflags[channel] & (FLAG_DTMF|FLAG_MUTE)) {
++		if (!(vpm450m->chanflags[channel] & FLAG_ECHO)) {
++			vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_RESET);
++			vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_FREEZE);
++		}
++	} else {
++		if (!(vpm450m->chanflags[channel] & FLAG_ECHO))
++			vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_DIGITAL);
++	}
++
++	ulResult = Oct6100ChannelModify(vpm450m->pApiInstance, modify);
++	if (ulResult != GENERIC_OK) {
++		printk(KERN_NOTICE "Failed to apply dtmf mute changes on channel %d!\n", channel);
++	}
++/*	printk(KERN_DEBUG "VPM450m: Setting DTMF on channel %d: %s / %s\n", channel, (detect ? "DETECT" : "NO DETECT"), (mute ? "MUTE" : "NO MUTE")); */
++	kfree(modify);
++}
++
++void vpm450m_setec(struct vpm450m *vpm450m, int channel, int eclen)
++{
++	if (eclen) {
++		vpm450m->chanflags[channel] |= FLAG_ECHO;
++		vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_RESET);
++		vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_NORMAL);
++	} else {
++		vpm450m->chanflags[channel] &= ~FLAG_ECHO;
++		if (vpm450m->chanflags[channel] & (FLAG_DTMF | FLAG_MUTE)) {
++			vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_RESET);
++			vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_FREEZE);
++		} else
++			vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_DIGITAL);
++	}
++/*	printk(KERN_DEBUG "VPM450m: Setting EC on channel %d to %d\n", channel, eclen); */
++}
++
++int vpm450m_checkirq(struct vpm450m *vpm450m)
++{
++	tOCT6100_INTERRUPT_FLAGS InterruptFlags;
++	
++	Oct6100InterruptServiceRoutineDef(&InterruptFlags);
++	Oct6100InterruptServiceRoutine(vpm450m->pApiInstance, &InterruptFlags);
++
++	return InterruptFlags.fToneEventsPending ? 1 : 0;
++}
++
++int vpm450m_getdtmf(struct vpm450m *vpm450m, int *channel, int *tone, int *start)
++{
++	tOCT6100_TONE_EVENT tonefound;
++	tOCT6100_EVENT_GET_TONE tonesearch;
++	UINT32 ulResult;
++	
++	Oct6100EventGetToneDef(&tonesearch);
++	tonesearch.pToneEvent = &tonefound;
++	tonesearch.ulMaxToneEvent = 1;
++	ulResult = Oct6100EventGetTone(vpm450m->pApiInstance, &tonesearch);
++	if (tonesearch.ulNumValidToneEvent) {
++		if (channel)
++			*channel = tonefound.ulUserChanId;
++		if (tone) {
++			switch(tonefound.ulToneDetected) {
++			case SOUT_DTMF_1:
++				*tone = '1';
++				break;
++			case SOUT_DTMF_2:
++				*tone = '2';
++				break;
++			case SOUT_DTMF_3:
++				*tone = '3';
++				break;
++			case SOUT_DTMF_A:
++				*tone = 'A';
++				break;
++			case SOUT_DTMF_4:
++				*tone = '4';
++				break;
++			case SOUT_DTMF_5:
++				*tone = '5';
++				break;
++			case SOUT_DTMF_6:
++				*tone = '6';
++				break;
++			case SOUT_DTMF_B:
++				*tone = 'B';
++				break;
++			case SOUT_DTMF_7:
++				*tone = '7';
++				break;
++			case SOUT_DTMF_8:
++				*tone = '8';
++				break;
++			case SOUT_DTMF_9:
++				*tone = '9';
++				break;
++			case SOUT_DTMF_C:
++				*tone = 'C';
++				break;
++			case SOUT_DTMF_STAR:
++				*tone = '*';
++				break;
++			case SOUT_DTMF_0:
++				*tone = '0';
++				break;
++			case SOUT_DTMF_POUND:
++				*tone = '#';
++				break;
++			case SOUT_DTMF_D:
++				*tone = 'D';
++				break;
++			case SOUT_G168_1100GB_ON:
++				*tone = 'f';
++				break;
++			default:
++#ifdef OCTASIC_DEBUG
++				printk(KERN_DEBUG "Unknown tone value %08x\n", tonefound.ulToneDetected);
++#endif
++				*tone = 'u';
++				break;
++			}
++		}
++		if (start)
++			*start = (tonefound.ulEventType == cOCT6100_TONE_PRESENT);
++		return 1;
++	}
++	return 0;
++}
++
++unsigned int get_vpm450m_capacity(void *wc)
++{
++	UINT32 ulResult;
++
++	tOCT6100_API_GET_CAPACITY_PINS CapacityPins;
++
++	Oct6100ApiGetCapacityPinsDef(&CapacityPins);
++	CapacityPins.pProcessContext = wc;
++	CapacityPins.ulMemoryType = cOCT6100_MEM_TYPE_DDR;
++	CapacityPins.fEnableMemClkOut = TRUE;
++	CapacityPins.ulMemClkFreq = cOCT6100_MCLK_FREQ_133_MHZ;
++
++	ulResult = Oct6100ApiGetCapacityPins(&CapacityPins);
++	if (ulResult != cOCT6100_ERR_OK) {
++		printk(KERN_DEBUG "Failed to get chip capacity, code %08x!\n", ulResult);
++		return 0;
++	}
++
++	return CapacityPins.ulCapacityValue;
++}
++
++struct vpm450m *init_vpm450m(void *wc, int *isalaw, int numspans, const struct firmware *firmware)
++{
++	tOCT6100_CHIP_OPEN *ChipOpen;
++	tOCT6100_GET_INSTANCE_SIZE InstanceSize;
++	tOCT6100_CHANNEL_OPEN *ChannelOpen;
++	UINT32 ulResult;
++	struct vpm450m *vpm450m;
++	int x,y,law;
++#ifdef CONFIG_4KSTACKS
++	unsigned long flags;
++#endif
++	
++	if (!(vpm450m = kmalloc(sizeof(struct vpm450m), GFP_KERNEL)))
++		return NULL;
++
++	memset(vpm450m, 0, sizeof(struct vpm450m));
++
++	if (!(ChipOpen = kmalloc(sizeof(tOCT6100_CHIP_OPEN), GFP_KERNEL))) {
++		kfree(vpm450m);
++		return NULL;
++	}
++
++	memset(ChipOpen, 0, sizeof(tOCT6100_CHIP_OPEN));
++
++	if (!(ChannelOpen = kmalloc(sizeof(tOCT6100_CHANNEL_OPEN), GFP_KERNEL))) {
++		kfree(vpm450m);
++		kfree(ChipOpen);
++		return NULL;
++	}
++
++	memset(ChannelOpen, 0, sizeof(tOCT6100_CHANNEL_OPEN));
++
++	for (x=0;x<128;x++)
++		vpm450m->ecmode[x] = -1;
++
++	vpm450m->numchans = numspans * 32;
++	printk(KERN_INFO "VPM450: echo cancellation for %d channels\n", vpm450m->numchans);
++		
++	Oct6100ChipOpenDef(ChipOpen);
++
++	/* Setup Chip Open Parameters */
++	ChipOpen->ulUpclkFreq = cOCT6100_UPCLK_FREQ_33_33_MHZ;
++	Oct6100GetInstanceSizeDef(&InstanceSize);
++
++	ChipOpen->pProcessContext = wc;
++
++	ChipOpen->pbyImageFile = firmware->data;
++	ChipOpen->ulImageSize = firmware->size;
++	ChipOpen->fEnableMemClkOut = TRUE;
++	ChipOpen->ulMemClkFreq = cOCT6100_MCLK_FREQ_133_MHZ;
++	ChipOpen->ulMaxChannels = vpm450m->numchans;
++	ChipOpen->ulMemoryType = cOCT6100_MEM_TYPE_DDR;
++	ChipOpen->ulMemoryChipSize = cOCT6100_MEMORY_CHIP_SIZE_32MB;
++	ChipOpen->ulNumMemoryChips = 1;
++	ChipOpen->ulMaxTdmStreams = 4;
++	ChipOpen->aulTdmStreamFreqs[0] = cOCT6100_TDM_STREAM_FREQ_8MHZ;
++	ChipOpen->ulTdmSampling = cOCT6100_TDM_SAMPLE_AT_FALLING_EDGE;
++#if 0
++	ChipOpen->fEnableAcousticEcho = TRUE;
++#endif		
++
++	ulResult = Oct6100GetInstanceSize(ChipOpen, &InstanceSize);
++	if (ulResult != cOCT6100_ERR_OK) {
++		printk(KERN_NOTICE "Failed to get instance size, code %08x!\n", ulResult);
++		kfree(vpm450m);
++		kfree(ChipOpen);
++		kfree(ChannelOpen);
++		return NULL;
++	}
++	
++	
++	vpm450m->pApiInstance = vmalloc(InstanceSize.ulApiInstanceSize);
++	if (!vpm450m->pApiInstance) {
++		printk(KERN_NOTICE "Out of memory (can't allocate %d bytes)!\n", InstanceSize.ulApiInstanceSize);
++		kfree(vpm450m);
++		kfree(ChipOpen);
++		kfree(ChannelOpen);
++		return NULL;
++	}
++
++	/* I don't know what to curse more in this comment, the problems caused by
++	 * the 4K kernel stack limit change or the octasic API for being so darn
++	 * stack unfriendly.  Stupid, stupid, stupid.  So we disable IRQs so we
++	 * don't run the risk of overflowing the stack while we initialize the
++	 * octasic. */
++#ifdef CONFIG_4KSTACKS
++	local_irq_save(flags);
++#endif
++	ulResult = Oct6100ChipOpen(vpm450m->pApiInstance, ChipOpen);
++	if (ulResult != cOCT6100_ERR_OK) {
++		printk(KERN_NOTICE "Failed to open chip, code %08x!\n", ulResult);
++#ifdef CONFIG_4KSTACKS
++		local_irq_restore(flags);
++#endif
++		kfree(vpm450m);
++		kfree(ChipOpen);
++		kfree(ChannelOpen);
++		return NULL;
++	}
++	for (x=0;x<128;x++) {
++		if ((x & 0x03) < numspans) {
++			/* span timeslots are interleaved 12341234... 
++		 	*  therefore, the lower 2 bits tell us which span this 
++			*  timeslot/channel
++		 	*/
++			if (isalaw[x & 0x03]) 
++				law = cOCT6100_PCM_A_LAW;
++			else
++				law = cOCT6100_PCM_U_LAW;
++			Oct6100ChannelOpenDef(ChannelOpen);
++			ChannelOpen->pulChannelHndl = &vpm450m->aulEchoChanHndl[x];
++			ChannelOpen->ulUserChanId = x;
++			ChannelOpen->TdmConfig.ulRinPcmLaw = law;
++			ChannelOpen->TdmConfig.ulRinStream = 0;
++			ChannelOpen->TdmConfig.ulRinTimeslot = x;
++			ChannelOpen->TdmConfig.ulSinPcmLaw = law;
++			ChannelOpen->TdmConfig.ulSinStream = 1;
++			ChannelOpen->TdmConfig.ulSinTimeslot = x;
++			ChannelOpen->TdmConfig.ulSoutPcmLaw = law;
++			ChannelOpen->TdmConfig.ulSoutStream = 2;
++			ChannelOpen->TdmConfig.ulSoutTimeslot = x;
++			ChannelOpen->TdmConfig.ulRoutPcmLaw = law;
++			ChannelOpen->TdmConfig.ulRoutStream = 3;
++			ChannelOpen->TdmConfig.ulRoutTimeslot = x;
++			ChannelOpen->VqeConfig.fEnableNlp = TRUE;
++			ChannelOpen->VqeConfig.fRinDcOffsetRemoval = TRUE;
++			ChannelOpen->VqeConfig.fSinDcOffsetRemoval = TRUE;
++			
++			ChannelOpen->fEnableToneDisabler = TRUE;
++			ChannelOpen->ulEchoOperationMode = cOCT6100_ECHO_OP_MODE_DIGITAL;
++			
++			ulResult = Oct6100ChannelOpen(vpm450m->pApiInstance, ChannelOpen);
++			if (ulResult != GENERIC_OK) {
++				printk(KERN_NOTICE "Failed to open channel %d!\n", x);
++			}
++			for (y=0;y<sizeof(tones) / sizeof(tones[0]); y++) {
++				tOCT6100_TONE_DETECTION_ENABLE enable;
++				Oct6100ToneDetectionEnableDef(&enable);
++				enable.ulChannelHndl = vpm450m->aulEchoChanHndl[x];
++				enable.ulToneNumber = tones[y];
++				if (Oct6100ToneDetectionEnable(vpm450m->pApiInstance, &enable) != GENERIC_OK) 
++					printk(KERN_NOTICE "Failed to enable tone detection on channel %d for tone %d!\n", x, y);
++			}
++		}
++	}
++
++#ifdef CONFIG_4KSTACKS
++	local_irq_restore(flags);
++#endif
++	kfree(ChipOpen);
++	kfree(ChannelOpen);
++	return vpm450m;
++}
++
++void release_vpm450m(struct vpm450m *vpm450m)
++{
++	UINT32 ulResult;
++	tOCT6100_CHIP_CLOSE ChipClose;
++
++	Oct6100ChipCloseDef(&ChipClose);
++	ulResult = Oct6100ChipClose(vpm450m->pApiInstance, &ChipClose);
++	if (ulResult != cOCT6100_ERR_OK) {
++		printk(KERN_NOTICE "Failed to close chip, code %08x!\n", ulResult);
++	}
++	vfree(vpm450m->pApiInstance);
++	kfree(vpm450m);
++}
+diff --git a/drivers/dahdi/opvxd115/vpm450m.h b/drivers/dahdi/opvxd115/vpm450m.h
+new file mode 100644
+index 0000000..735a2cc
+--- /dev/null
++++ b/drivers/dahdi/opvxd115/vpm450m.h
+@@ -0,0 +1,43 @@
++/*
++ * Copyright (C) 2005-2006 Digium, Inc.
++ *
++ * Mark Spencer <markster at digium.com>
++ *
++ * All Rights Reserved
++ *
++ */
++
++/*
++ * See http://www.asterisk.org for more information about
++ * the Asterisk project. Please do not directly contact
++ * any of the maintainers of this project for assistance;
++ * the project provides a web site, mailing lists and IRC
++ * channels for your use.
++ *
++ * This program is free software, distributed under the terms of
++ * the GNU General Public License Version 2 as published by the
++ * Free Software Foundation. See the LICENSE file included with
++ * this program for more details.
++ */
++
++#ifndef _VPM450M_H
++#define _VPM450M_H
++
++#include <linux/firmware.h>
++
++struct vpm450m;
++
++/* From driver */
++unsigned int oct_get_reg(void *data, unsigned int reg);
++void oct_set_reg(void *data, unsigned int reg, unsigned int val);
++
++/* From vpm450m */
++struct vpm450m *init_vpm450m(void *wc, int *isalaw, int numspans, const struct firmware *firmware);
++unsigned int get_vpm450m_capacity(void *wc);
++void vpm450m_setec(struct vpm450m *instance, int channel, int eclen);
++void vpm450m_setdtmf(struct vpm450m *instance, int channel, int dtmfdetect, int dtmfmute);
++int vpm450m_checkirq(struct vpm450m *vpm450m);
++int vpm450m_getdtmf(struct vpm450m *vpm450m, int *channel, int *tone, int *start);
++void release_vpm450m(struct vpm450m *instance);
++
++#endif
+diff --git a/drivers/dahdi/wcopenpci.c b/drivers/dahdi/wcopenpci.c
+new file mode 100644
+index 0000000..d7fbc2d
+--- /dev/null
++++ b/drivers/dahdi/wcopenpci.c
+@@ -0,0 +1,1784 @@
++/*
++ * Voicetronix OpenPCI Interface Driver for Zapata Telephony interface
++ *
++ * Written by Mark Spencer <markster at linux-support.net>
++ *            Matthew Fredrickson <creslin at linux-support.net>
++ *            Ben Kramer <ben at voicetronix.com.au>
++ *            Ron Lee <ron at voicetronix.com.au>
++ *
++ * 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 <markster at digium.com> "\
++			"Voicetronix <support at voicetronix.com.au>"
++
++#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 <linux/init.h>
++#include <linux/module.h>
++#include <linux/pci.h>
++#include <linux/delay.h>
++#include <linux/sched.h>
++
++#include <dahdi/kernel.h>
++#include <dahdi/version.h>
++#include "proslic.h"
++#include <dahdi/wctdm_user.h>
++#include <linux/interrupt.h>
++#include <linux/mutex.h>
++
++#include "fxo_modes.h"
++
++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)
++
++
++static 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_device *ddev;
++	struct dahdi_span span;
++} *cards[MAX_CARDS];
++
++/* You must hold this lock anytime you access or modify the cards[] array. */
++static 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; x < DAHDI_CHUNKSIZE; ++x) {
++		/* Send a sample, as a 32-bit word */
++#ifdef __BIG_ENDIAN
++#warning No big endian support (yet)
++#else
++		/* transmit second 4 ports */
++		writechunk[y] = 0;
++		if (wc->porttype[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; x < DAHDI_CHUNKSIZE; ++x) {
++#ifdef __BIG_ENDIAN
++#warning No big endian support (yet)
++#else
++		/* Receive first 4 ports */
++
++		if (wc->porttype[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);
++}
++
++DAHDI_IRQ_HANDLER(openpci_isr)
++{
++	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 __user *)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 __user *)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((void __user *)data, &stats, sizeof(stats)))
++			return -EFAULT;
++		break;
++	case WCTDM_GET_REGS:
++		if (wc->porttype[port] == VT_PORT_PROSLIC) {
++			for (x = 0; x < NUM_INDIRECT_REGS; x++)
++				if (!read_indreg_fxs(wc, port, x,
++							&regs.indirect[x]))
++					return -EIO;
++			for (x = 0; x < NUM_REGS; x++)
++				if (!read_reg_fxs(wc, port, x, &regs.direct[x]))
++					return -EIO;
++		} else {
++			memset(&regs, 0, sizeof(regs));
++			for (x = 0; x < NUM_FXO_REGS; x++) {
++				if (!read_reg_fxo(wc, port, x, &regs.direct[x]))
++					return -EIO;
++			}
++		}
++		if (copy_to_user((void __user *)data, &regs, sizeof(regs)))
++			return -EFAULT;
++		break;
++	case WCTDM_SET_REG:
++		if (copy_from_user(&regop, (void __user *)data, sizeof(regop)))
++			return -EFAULT;
++		if (regop.indirect) {
++			if (wc->porttype[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, (void __user *)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 inline struct openpci* openpci_from_span(struct dahdi_span *span) {
++	return container_of(span, struct openpci, span);
++}
++
++static int openpci_watchdog(struct dahdi_span *span, int event)
++{
++	info("TDM: Restarting DMA");
++	restart_dma(openpci_from_span(span));
++	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 const struct dahdi_span_ops openpci_span_ops = {
++	.owner = THIS_MODULE,
++	.hooksig = openpci_hooksig,
++	.open = openpci_open,
++	.close = openpci_close,
++	.ioctl = openpci_ioctl,
++	.watchdog = openpci_watchdog
++};
++
++static int span_initialize(struct openpci *wc)
++{
++	int x;
++
++	wc->ddev = dahdi_create_device();
++	//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);
++	wc->ddev->location = kasprintf(GFP_KERNEL, "PCI Bus %02d Slot %02d",
++				      wc->dev->bus->number,
++				      PCI_SLOT(wc->dev->devfn) + 1);
++	if (!wc->ddev->location)
++		return -ENOMEM;
++	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->ddev->manufacturer = "Voicetronix";
++	wc->ddev->devicetype = wc->variety;
++	wc->span.deflaw = DAHDI_LAW_MULAW;
++	wc->span.chans = wc->chans;
++	wc->span.channels = MAX_PORTS;
++	wc->span.flags = DAHDI_FLAG_RBS;
++	wc->span.ops = &openpci_span_ops;
++
++	list_add_tail(&wc->span.device_node, &wc->ddev->spans);
++	if (dahdi_register_device(wc->ddev, &wc->dev->dev)) {
++		cardcrit(wc->boardnum, "Unable to register device 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_device(wc->ddev);
++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->ddev->location);
++	dahdi_free_device(wc->ddev);
++	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_device(wc->ddev);
++	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->ddev->location);
++	dahdi_free_device(wc->ddev);
++	kfree(wc);
++	cardinfo(wc->boardnum, "Removed OpenPCI card.");
++}
++
++static DEFINE_PCI_DEVICE_TABLE(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),
++	.id_table = openpci_pci_tbl,
++};
++
++static int __init openpci_init(void)
++{
++#ifdef __BIG_ENDIAN
++	warn("No big endian support (yet)");
++	return -ENODEV;
++#endif
++
++	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 --git a/drivers/dahdi/zaphfc/Kbuild b/drivers/dahdi/zaphfc/Kbuild
+new file mode 100644
+index 0000000..960fb3a
+--- /dev/null
++++ b/drivers/dahdi/zaphfc/Kbuild
+@@ -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 --git a/drivers/dahdi/zaphfc/base.c b/drivers/dahdi/zaphfc/base.c
+new file mode 100644
+index 0000000..fb35ba7
+--- /dev/null
++++ b/drivers/dahdi/zaphfc/base.c
+@@ -0,0 +1,1720 @@
++/*
++ * zaphfc.c - Dahdi driver for HFC-S PCI A based ISDN BRI cards
++ *
++ * Dahdi rewrite in hardhdlc mode
++ * Jose A. Deniz <odicha at hotmail.com>
++ *
++ * 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 <jw_vzaphfc at headissue.com>
++ *
++ * Original author of this code is
++ * Daniele "Vihai" Orlandi <daniele at orlandi.com>
++ *
++ * Major rewrite of the driver made by
++ * Klaus-Peter Junghanns <kpj at junghanns.net>
++ *
++ * This program is free software and may be modified and
++ * distributed under the terms of the GNU General Public License.
++ *
++ * Please read the README file for important infos.
++ */
++
++#include <linux/spinlock.h>
++#include <linux/init.h>
++#include <linux/pci.h>
++#include <linux/interrupt.h>
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/sched.h>
++#include <linux/proc_fs.h>
++#include <linux/if_arp.h>
++
++#include <dahdi/kernel.h>
++
++#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 file *file, struct dahdi_span *span)
++{
++	struct dahdi_hfc *zthfc = dahdi_hfc_from_span(span);
++	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 file *file, 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 file *file, struct dahdi_span *span,
++		struct dahdi_lineconfig *lc)
++{
++	span->lineconfig = lc->lineconfig;
++
++	return 0;
++}
++
++static const struct dahdi_span_ops hfc_zap_span_ops = {
++	.owner = THIS_MODULE,
++	.chanconfig = hfc_zap_chanconfig,
++	.spanconfig = hfc_zap_spanconfig,
++	.startup = hfc_zap_startup,
++	.shutdown = hfc_zap_shutdown,
++	.maint = hfc_zap_maint,
++	.rbsbits = hfc_zap_rbsbits,
++	.open = hfc_zap_open,
++	.close = hfc_zap_close,
++	.ioctl = hfc_zap_ioctl,
++	.hdlc_hard_xmit = hfc_hdlc_hard_xmit
++};
++
++static int hfc_zap_initialize(struct dahdi_hfc *hfccard)
++{
++	 struct hfc_card *hfctmp = hfccard->card;
++	int i;
++
++	hfccard->ddev = dahdi_create_device();
++	hfccard->ddev->manufacturer = "Cologne Chips";
++	hfccard->ddev->devicetype = "HFC-S PCI-A ISDN";
++	hfccard->ddev->location = kasprintf(GFP_KERNEL,
++			"PCI Bus %02d Slot %02d",
++			hfctmp->pcidev->bus->number,
++			PCI_SLOT(hfctmp->pcidev->devfn) + 1);
++	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 ? SPANTYPE_DIGITAL_BRI_NT : 
++		SPANTYPE_DIGITAL_BRI_TE;
++	hfccard->span.flags = 0;
++	hfccard->span.ops = &hfc_zap_span_ops;
++	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;
++
++	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[DAHDI_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];
++
++	list_add_tail(&hfccard->span.device_node, &hfccard->ddev->spans);
++	if (dahdi_register_device(hfccard->ddev, &hfctmp->pcidev->dev)) {
++		printk(KERN_ERR "unable to register DAHDI device %s!\n",
++				hfccard->span.name);
++		return -1; /* FIXME: release resources? */
++	}
++
++	return 0;
++}
++
++static void hfc_dahdi_free(struct dahdi_hfc *hfccard)
++{
++	kfree(hfccard->ddev->location);
++	dahdi_free_device(hfccard->ddev);
++}
++
++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_device(card->ztdev->ddev);
++	hfc_dahdi_free(card->ztdev);
++
++
++	/*
++	 * disable memio and busmaster
++	 */
++	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 <jw_vzaphfc at headissue.com>, "
++		"Daniele (Vihai) Orlandi <daniele at orlandi.com>, "
++		"Jose A. Deniz <odicha at hotmail.com>");
++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 --git a/drivers/dahdi/zaphfc/fifo.c b/drivers/dahdi/zaphfc/fifo.c
+new file mode 100644
+index 0000000..aab46c0
+--- /dev/null
++++ b/drivers/dahdi/zaphfc/fifo.c
+@@ -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 <daniele at orlandi.com>
++ *
++ * This program is free software and may be modified and
++ * distributed under the terms of the GNU General Public License.
++ *
++ */
++
++#include <linux/kernel.h>
++
++#include <dahdi/kernel.h>
++
++#include "fifo.h"
++
++static void hfc_fifo_mem_read(struct hfc_chan_simplex *chan,
++	int z_start,
++	void *data, int size)
++{
++	int bytes_to_boundary = chan->z_max - z_start + 1;
++	if (bytes_to_boundary >= size) {
++		memcpy(data,
++			chan->z_base + z_start,
++			size);
++	} else {
++		/*
++		 * Buffer wrap
++		 */
++		memcpy(data,
++			chan->z_base + z_start,
++			bytes_to_boundary);
++
++		memcpy(data + bytes_to_boundary,
++			chan->fifo_base,
++			size - bytes_to_boundary);
++	}
++}
++
++static void hfc_fifo_mem_write(struct hfc_chan_simplex *chan,
++	void *data, int size)
++{
++	int bytes_to_boundary = chan->z_max - *Z1_F1(chan) + 1;
++	if (bytes_to_boundary >= size) {
++		memcpy(chan->z_base + *Z1_F1(chan),
++			data,
++			size);
++	} else {
++		/*
++		 * FIFO wrap
++		 */
++
++		memcpy(chan->z_base + *Z1_F1(chan),
++			data,
++			bytes_to_boundary);
++
++		memcpy(chan->fifo_base,
++			data + bytes_to_boundary,
++			size - bytes_to_boundary);
++	}
++}
++
++int hfc_fifo_get(struct hfc_chan_simplex *chan,
++		void *data, int size)
++{
++	int available_bytes;
++
++	/*
++	 * Some useless statistic
++	 */
++	chan->bytes += size;
++
++	available_bytes = hfc_fifo_used_rx(chan);
++
++	if (available_bytes < size && !chan->fifo_underrun++) {
++		/*
++		 * print the warning only once
++		 */
++		printk(KERN_WARNING hfc_DRIVER_PREFIX
++			"card %d: "
++			"chan %s: "
++			"RX FIFO not enough (%d) bytes to receive!\n",
++			chan->chan->card->cardnum,
++			chan->chan->name,
++			available_bytes);
++		return -1;
++	}
++
++	hfc_fifo_mem_read(chan, *Z2_F2(chan), data, size);
++	*Z2_F2(chan) = Z_inc(chan, *Z2_F2(chan), size);
++	return available_bytes - size;
++}
++
++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 --git a/drivers/dahdi/zaphfc/fifo.h b/drivers/dahdi/zaphfc/fifo.h
+new file mode 100644
+index 0000000..e2ae524
+--- /dev/null
++++ b/drivers/dahdi/zaphfc/fifo.h
+@@ -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 <daniele at orlandi.com>
++ *
++ * Major rewrite of the driver made by
++ * Klaus-Peter Junghanns <kpj at junghanns.net>
++ *
++ * This program is free software and may be modified and
++ * distributed under the terms of the GNU General 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 --git a/drivers/dahdi/zaphfc/zaphfc.h b/drivers/dahdi/zaphfc/zaphfc.h
+new file mode 100644
+index 0000000..69671a7
+--- /dev/null
++++ b/drivers/dahdi/zaphfc/zaphfc.h
+@@ -0,0 +1,419 @@
++/*
++ * zaphfc.h - Dahdi driver for HFC-S PCI A based ISDN BRI cards
++ *
++ * Dahdi port by Jose A. Deniz <odicha at hotmail.com>
++ *
++ * 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 <jw_vzaphfc at headissue.com>
++ *
++ * Orginal author of this code is
++ * Daniele "Vihai" Orlandi <daniele at orlandi.com>
++ *
++ * Major rewrite of the driver made by
++ * Klaus-Peter Junghanns <kpj at junghanns.net>
++ *
++ * This program is free software and may be modified and
++ * distributed under the terms of the GNU General Public License.
++ *
++ */
++
++#ifndef _HFC_ZAPHFC_H
++#define _HFC_ZAPHFC_H
++
++#include <asm/io.h>
++
++#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_device *ddev;
++	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 struct dahdi_hfc *dahdi_hfc_from_span(struct dahdi_span *span)
++{
++	return container_of(span, struct dahdi_hfc, span);
++}
++
++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 --git a/drivers/staging/echo/Kbuild b/drivers/staging/echo/Kbuild
+new file mode 100644
+index 0000000..f813006
+--- /dev/null
++++ b/drivers/staging/echo/Kbuild
+@@ -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 --git a/drivers/staging/echo/echo.c b/drivers/staging/echo/echo.c
+new file mode 100644
+index 0000000..4cc4f2e
+--- /dev/null
++++ b/drivers/staging/echo/echo.c
+@@ -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 <steveu at coppice.org>
++ *         and David Rowe <david_at_rowetel_dot_com>
++ *
++ * 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 <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/slab.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 */
++
++
++/* 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 --git a/drivers/staging/echo/echo.h b/drivers/staging/echo/echo.h
+new file mode 100644
+index 0000000..754e66d
+--- /dev/null
++++ b/drivers/staging/echo/echo.h
+@@ -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 <steveu at coppice.org>
++ *         and David Rowe <david_at_rowetel_dot_com>
++ *
++ * 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 --git a/drivers/staging/echo/fir.h b/drivers/staging/echo/fir.h
+new file mode 100644
+index 0000000..288bffc
+--- /dev/null
++++ b/drivers/staging/echo/fir.h
+@@ -0,0 +1,286 @@
++/*
++ * SpanDSP - a series of DSP components for telephony
++ *
++ * fir.h - General telephony FIR routines
++ *
++ * Written by Steve Underwood <steveu at coppice.org>
++ *
++ * 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 --git a/drivers/staging/echo/mmx.h b/drivers/staging/echo/mmx.h
+new file mode 100644
+index 0000000..c030cdf
+--- /dev/null
++++ b/drivers/staging/echo/mmx.h
+@@ -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 --git a/drivers/staging/echo/oslec.h b/drivers/staging/echo/oslec.h
+new file mode 100644
+index 0000000..f417536
+--- /dev/null
++++ b/drivers/staging/echo/oslec.h
+@@ -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 <steveu at coppice.org>
++ *         and David Rowe <david_at_rowetel_dot_com>
++ *
++ * 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 */
diff --git a/debian/patches/hotplug_mod_params b/debian/patches/hotplug_mod_params
new file mode 100644
index 0000000..58fb991
--- /dev/null
+++ b/debian/patches/hotplug_mod_params
@@ -0,0 +1,17 @@
+Author: Tzafrir Cohen <tzafrir.cohen at xorcom.com>
+Description: use hotplug mode ("pinned spans") by default
+
+Disable automatic span assignment. Span assignment should be done by
+userspace udev hooks.
+
+--- a/drivers/dahdi/dahdi-base.c
++++ b/drivers/dahdi/dahdi-base.c
+@@ -7369,7 +7369,7 @@ int dahdi_assign_device_spans(struct dah
+ 	return 0;
+ }
+ 
+-static int auto_assign_spans = 1;
++static int auto_assign_spans = 0;
+ static const char *UNKNOWN = "";
+ 
+ /**
diff --git a/debian/patches/no_firmware_download b/debian/patches/no_firmware_download
new file mode 100644
index 0000000..599740e
--- /dev/null
+++ b/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
+@@ -80,7 +80,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
+ 	objdump -d drivers/dahdi/*.ko drivers/dahdi/*/*.ko | $(CHECKSTACK)
diff --git a/debian/patches/notest b/debian/patches/notest
new file mode 100644
index 0000000..f61a37a
--- /dev/null
+++ b/debian/patches/notest
@@ -0,0 +1,15 @@
+Description: Remove a bodus "test" target
+Author: Tzafrir Cohen <tzafrir.cohen at xorcom.com>
+
+--- a/Makefile
++++ b/Makefile
+@@ -190,9 +190,6 @@ dist-clean: clean
+ firmware-download:
+ 	@$(MAKE) -C drivers/dahdi/firmware all
+ 
+-test:
+-	./test-script $(DESTDIR)/lib/modules/$(KVERS) dahdi
+-
+ docs: $(GENERATED_DOCS)
+ 
+ README.html: README
diff --git a/debian/patches/pciradio-request_firmware b/debian/patches/pciradio-request_firmware
new file mode 100644
index 0000000..082e28d
--- /dev/null
+++ b/debian/patches/pciradio-request_firmware
@@ -0,0 +1,135 @@
+Author: Ben Hutchings <ben at decadent.org.uk>
+Description: pciradio: Use request_firmware() to load bitfile
+Bug-Debian: http://bugs.debian.org/693666
+
+Non-free bits belong in separate (source & binary) packages.
+This driver must use request_firmware() to load the FPGA bitfile.
+
+---
+--- a/drivers/dahdi/pciradio.c
++++ b/drivers/dahdi/pciradio.c
+@@ -51,6 +51,7 @@ With driver:	303826  (1.5 %)
+ #include <linux/moduleparam.h>
+ #include <linux/sched.h>
+ #include <linux/slab.h>
++#include <linux/firmware.h>
+ #include <linux/delay.h>
+ #include <asm/io.h>
+ #include <asm/delay.h> 
+@@ -220,8 +221,6 @@ struct tonedef {
+ 	unsigned char b2;
+ } ;
+ 
+-#include "radfw.h"
+-
+ static struct tonedef cttable_tx [] = {
+ {0,0,0},
+ {670,0xE,0xB1},
+@@ -1505,9 +1504,18 @@ static void wait_just_a_bit(int foo)
+ 
+ static int pciradio_hardware_init(struct pciradio *rad)
+ {
++const struct firmware *bitfile;
+ unsigned char byte1,byte2;
+ int	x;
+ unsigned long endjif;
++int res;
++
++	res = request_firmware(&bitfile, "dahdi-fw-pciradio.bin",
++			       &rad->dev->dev);
++	if (res)
++		return res;
++
++	res = -EIO;
+ 
+ 	/* Signal Reset */
+ 	outb(0x01, rad->ioaddr + RAD_CNTL);
+@@ -1539,7 +1547,7 @@ unsigned long endjif;
+ 	while (inb(rad->ioaddr + RAD_AUXR) & (XINIT | XDONE) && (jiffies <= endjif));
+ 	if (endjif < jiffies) {
+ 		printk(KERN_DEBUG "Timeout waiting for INIT and DONE to go low\n");
+-		return -1;
++		goto out;
+ 	}
+ 	if (debug) printk(KERN_DEBUG "fwload: Init and done gone to low\n");
+ 	/* De-assert PGM */
+@@ -1550,16 +1558,16 @@ unsigned long endjif;
+ 	while (!(inb(rad->ioaddr + RAD_AUXR) & XINIT) && (jiffies <= endjif));
+ 	if (endjif < jiffies) {
+ 		printk(KERN_DEBUG "Timeout waiting for INIT to go high\n");
+-		return -1;
++		goto out;
+ 	}
+ 	if (debug) printk(KERN_DEBUG "fwload: Init went high (clearing done)\nNow loading...\n");
+ 	/* Assert CS+Write */
+ 	rad->ios &= ~XCS;
+ 	outb(rad->ios, rad->ioaddr + RAD_AUXD);
+-	for (x = 0; x < sizeof(radfw); x++)
++	for (x = 0; x < bitfile->size; x++)
+ 	   {
+ 		  /* write the byte */
+-		outb(radfw[x],rad->ioaddr + RAD_REGBASE);
++		outb(bitfile->data[x], rad->ioaddr + RAD_REGBASE);
+ 		  /* if DONE signal, we're done, exit */
+ 		if (inb(rad->ioaddr + RAD_AUXR) & XDONE) break;
+ 		  /* if INIT drops, we're screwed, exit */
+@@ -1580,12 +1588,12 @@ unsigned long endjif;
+ 	if (!(inb(rad->ioaddr + RAD_AUXR) & XINIT))
+ 	   {
+ 		printk(KERN_NOTICE "Drove Init low!! CRC Error!!!\n");
+-		return -1;
++		goto out;
+ 	   }
+ 	if (!(inb(rad->ioaddr + RAD_AUXR) & XDONE))
+ 	   {
+ 		printk(KERN_INFO "Did not get DONE signal. Short file maybe??\n");
+-		return -1;
++		goto out;
+ 	   }
+ 	wait_just_a_bit(2);
+ 	/* get the thingy started */
+@@ -1646,7 +1654,10 @@ unsigned long endjif;
+ 	/* Wait 1/4 of a sec */
+ 	wait_just_a_bit(HZ/4);
+ 
+-	return 0;
++	res = 0;
++out:
++	release_firmware(bitfile);
++	return res;
+ }
+ 
+ static void pciradio_enable_interrupts(struct pciradio *rad)
+@@ -1765,7 +1776,8 @@ static int __devinit pciradio_init_one(s
+ 			/* Keep track of which device we are */
+ 			pci_set_drvdata(pdev, rad);
+ 
+-			if (pciradio_hardware_init(rad)) {
++			res = pciradio_hardware_init(rad);
++			if (res) {
+ 				/* Set Reset Low */
+ 				x=inb(rad->ioaddr + RAD_CNTL);
+ 				outb((~0x1)&x, rad->ioaddr + RAD_CNTL);
+@@ -1780,7 +1792,7 @@ static int __devinit pciradio_init_one(s
+ 				pci_set_drvdata(pdev, NULL);
+ 				dahdi_free_device(rad->ddev);
+ 				kfree(rad);
+-				return -EIO;
++				return res;
+ 
+ 			}
+ 
+--- a/drivers/dahdi/Kbuild
++++ b/drivers/dahdi/Kbuild
+@@ -166,11 +166,4 @@ ifeq ($(HPEC_PRESENT),yes)
+ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_HPEC)	+= dahdi_echocan_hpec.o
+ endif
+ 
+-$(obj)/pciradio.o: $(obj)/radfw.h
+-
+ hostprogs-y := makefw
+-
+-$(obj)/radfw.h: $(src)/pciradio.rbt $(obj)/makefw
+-	$(obj)/makefw $< radfw > $@
+-
+-clean-files	:= radfw.h
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..0d1c5d2
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1,8 @@
+# Mega-patch of extra drivers:
+dahdi_linux_extra
+no_firmware_download
+chanmute
+notest
+tor2-request_firmware
+pciradio-request_firmware
+hotplug_mod_params
diff --git a/debian/patches/tor2-request_firmware b/debian/patches/tor2-request_firmware
new file mode 100644
index 0000000..170dfc0
--- /dev/null
+++ b/debian/patches/tor2-request_firmware
@@ -0,0 +1,87 @@
+Author: Ben Hutchings <ben at decadent.org.uk>
+Description: tor2: Use request_firmware() to load bitfile
+Bug-Debian: http://bugs.debian.org/693666
+
+Non-free bits belong in separate (source & binary) packages.
+This driver must use request_firmware() to load the FPGA bitfile.
+
+---
+--- a/drivers/dahdi/tor2.c
++++ b/drivers/dahdi/tor2.c
+@@ -31,11 +31,11 @@
+ #include <linux/interrupt.h>
+ #include <linux/moduleparam.h>
+ #include <linux/slab.h>
++#include <linux/firmware.h>
+ 
+ #include <dahdi/kernel.h>
+ #define NEED_PCI_IDS
+ #include "tor2-hw.h"
+-#include "tor2fw.h"
+ 
+ /*
+  * Tasklets provide better system interactive response at the cost of the
+@@ -379,6 +379,7 @@ static int __devinit tor2_probe(struct p
+ 	unsigned long endjif;
+ 	__iomem volatile unsigned long *gpdata_io, *lasdata_io;
+ 	unsigned long gpdata,lasdata;
++	const struct firmware *bitfile;
+ 
+ 	res = pci_enable_device(pdev);
+ 	if (res)
+@@ -439,6 +440,11 @@ static int __devinit tor2_probe(struct p
+ 		       tor->xilinx8_len, tor->xilinx8_region);
+ 		goto err_out_release_plx_region;
+ 	}
++	res = request_firmware(&bitfile, "dahdi-fw-tor2.bin", &pdev->dev);
++	if (res) {
++		ret = res;
++		goto err_out_release_all_regions;
++	}
+ 	pci_set_drvdata(pdev, tor);
+ 	printk(KERN_INFO "Detected %s at 0x%lx/0x%lx irq %d\n", tor->type, 
+ 		tor->xilinx32_region, tor->xilinx8_region,tor->irq);
+@@ -490,10 +496,10 @@ static int __devinit tor2_probe(struct p
+ 	/* assert WRITE signal */
+ 	gpdata &= ~GPIO_WRITE;
+ 	writel(gpdata, gpdata_io);
+-	for (x = 0; x < sizeof(tor2fw); x++)
++	for (x = 0; x < bitfile->size; x++)
+ 	   {
+ 		  /* write the byte */
+-		writeb(tor2fw[x], tor->mem8);
++		writeb(bitfile->data[x], tor->mem8);
+ 		  /* if DONE signal, we're done, exit */
+ 		if (readl(gpdata_io) & GPIO_DONE)
+ 			break;
+@@ -612,9 +618,12 @@ static int __devinit tor2_probe(struct p
+ 			break;
+ 	}
+ 
++	release_firmware(bitfile);
+ 	return 0;
+ 
+ err_out_release_all:
++	release_firmware(bitfile);
++err_out_release_all_regions:
+ 	release_mem_region(tor->xilinx32_region, tor->xilinx32_len);
+ 	release_mem_region(tor->xilinx8_region, tor->xilinx8_len);
+ err_out_release_plx_region:
+--- a/drivers/dahdi/Kbuild
++++ b/drivers/dahdi/Kbuild
+@@ -167,14 +167,10 @@ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECH
+ endif
+ 
+ $(obj)/pciradio.o: $(obj)/radfw.h
+-$(obj)/tor2.o: $(obj)/tor2fw.h
+ 
+ hostprogs-y := makefw
+ 
+-$(obj)/tor2fw.h: $(src)/tormenta2.rbt $(obj)/makefw 
+-	$(obj)/makefw $< tor2fw > $@
+-
+ $(obj)/radfw.h: $(src)/pciradio.rbt $(obj)/makefw
+ 	$(obj)/makefw $< radfw > $@
+ 
+-clean-files	:= radfw.h tor2fw.h
++clean-files	:= radfw.h
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..c1fd5e6
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,131 @@
+#!/usr/bin/make -f
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+export HOTPLUG_FIRMWARE=1
+
+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[0-9]*\(~\|$$\)/\1/' -e 's/~\(rc\|beta\)/-\1/')
+
+UPFILENAME := $(PACKAGE_SRC)_$(UPVERSION).orig.tar.gz
+FILENAME := $(PACKAGE_SRC)_$(DEB_BASE_VERSION).orig.tar.gz
+URL := http://downloads.asterisk.org/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
+
+%:
+	dh $@
+
+kdist_clean: clean
+
+kdist_config: prep-deb-files
+
+binary-modules: prep-deb-files
+	dh_testdir
+	dh_testroot
+	dh_prep
+	#cp -a $(CURDIR)/debian/generated/* .
+	make modules KERNEL_SOURCES=$(KSRC) MODVERSIONS=detect KERNEL=linux-$(KVERS) 
+	make install-modules KERNELRELEASE=$(KVERS) DESTDIR=$(CURDIR)/debian/$(PKGNAME)
+	# The modules are way too big. This is only in kernel 2.6
+	# (Removed for now: breaks cross-building)
+	#find debian/$(PKGNAME)/lib/modules -name '*.ko' |xargs strip -g
+	$(RM) -f debian/$(PKGNAME)/lib/modules/$(KVERS)/modules.*
+	dh_installmodules
+	dh_installdebconf
+	dh_installdocs
+	dh_installchangelogs
+	dh_compress
+	dh_fixperms
+	dh_installdeb
+	dh_gencontrol -- -v$(VERSION)
+	dh_md5sums
+	dh_builddeb --destdir=$(DEB_DESTDIR)
+
+## END OF M-A SECTION
+
+# FIXME: this should be done by Files-Exclude in debian/copyright:
+override_dh_auto_build:
+ifneq ($(wildcard drivers/dahdi/xpp/firmwares/USB_FW.hex),)
+	@echo "Tarball is not DFSG-compliant. Use './debian/rules get-orig-source'"
+	exit 1
+endif
+	$(MAKE) docs
+	$(MAKE) $(GENERATED_SOURCES)
+
+override_dh_auto_clean:
+	rm -f $(GENERATED_SOURCES)
+	rm -f dahdi/include/version.h
+	[ ! -f Makefile ] || $(MAKE) dist-clean || true
+
+TARPARDIR=$(CURDIR)/debian/tmp
+TARDIR=$(TARPARDIR)/modules/$(PREFIX)
+
+override_dh_auto_install:
+	$(MAKE) install-include DESTDIR=$(CURDIR)/debian/$(SKPG)
+
+override_dh_install:
+	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
+
+print-version:
+	@@echo "Debian version:          $(DEBVERSION)"
+	@@echo "Upstream version:        $(UPVERSION)"
+
+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
+	@@rm -f $(TARBALL_DIR)/$(PACKAGE_SRC)-$(UPVERSION)/drivers/dahdi/*.rbt
+	@@cd $(TARBALL_DIR) ; \
+	tar cfz ../$(FILENAME) *
+	@@echo Cleaning up...
+	@@$(RM) -rf $(TARBALL_DIR)/
+	@@$(RM) -f ../tarballs/$(UPFILENAME)
+
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..163aaf8
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/debian/watch b/debian/watch
new file mode 100644
index 0000000..468130a
--- /dev/null
+++ b/debian/watch
@@ -0,0 +1,6 @@
+version=3
+opts=uversionmangle=s/\-rc/~rc/,dversionmangle=s/\~dfsg//; \
+	http://downloads.asterisk.org/pub/telephony/dahdi-linux/releases/dahdi-linux-(.*)\.tar\.gz\
+	debian svn-upgrade
+
+

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-voip/dahdi-linux.git



More information about the Pkg-voip-commits mailing list