summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaik Broemme2015-06-30 18:32:32 +0200
committerMaik Broemme2015-06-30 18:32:32 +0200
commit5d364f149da51e486ded92a4ea8ac5f4169ca12c (patch)
treec4b93642cd8912f4a52b2b101ad37e66e3a226a3
downloadaur-5d364f149da51e486ded92a4ea8ac5f4169ca12c.tar.gz
Initial import
-rw-r--r--.SRCINFO35
-rw-r--r--PKGBUILD71
-rw-r--r--dahdi-linux-2.10.1-allo.patch7300
-rw-r--r--dahdi-linux-2.10.1-openvox-1.patch6228
-rw-r--r--dahdi-linux-2.10.1-openvox-2.patch5851
-rw-r--r--dahdi-linux-2.10.1-openvox-3.patch7272
-rw-r--r--dahdi-linux-2.10.1-yeastar.patch6269
-rw-r--r--dahdi-linux.install15
8 files changed, 33041 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO
new file mode 100644
index 000000000000..fd21a929a0e7
--- /dev/null
+++ b/.SRCINFO
@@ -0,0 +1,35 @@
+pkgbase = dahdi-linux
+ pkgdesc = DAHDI drivers for Asterisk (Digium, OpenVox, Allo and Yeastar cards)
+ pkgver = 2.10.1
+ pkgrel = 2
+ url = http://www.asterisk.org/
+ install = dahdi-linux.install
+ arch = i686
+ arch = x86_64
+ license = LGPLv2
+ makedepends = linux-headers>=4.0
+ makedepends = linux-headers<4.1
+ depends = linux>=4.0
+ depends = linux<4.1
+ conflicts = dahdi
+ source = http://downloads.asterisk.org/pub/telephony/dahdi-linux/dahdi-linux-2.10.1.tar.gz
+ source = oslec.h::http://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/plain/drivers/misc/echo/oslec.h?id=refs/tags/v4.0
+ source = dahdi-linux-2.10.1-allo.patch
+ source = dahdi-linux-2.10.1-openvox-1.patch
+ source = dahdi-linux-2.10.1-openvox-2.patch
+ source = dahdi-linux-2.10.1-openvox-3.patch
+ source = dahdi-linux-2.10.1-yeastar.patch
+ source = dahdi-kernel-4.0-1.patch::https://github.com/asterisk/dahdi-linux/commit/1cc0ad510acd404e63923ed3062b9302d53580da.diff
+ source = dahdi-kernel-4.0-2.patch::https://github.com/asterisk/dahdi-linux/commit/1559db9d1ae03780788788c07334ca54cdd1253a.diff
+ sha256sums = 94c532e190fc6372f9731df71f8c590fc6f6f184d5394339ce892ac6788843aa
+ sha256sums = dc8b86ded9963ad825ea7a18bdbcfb0a4758bbc9dcac5fc9cd9bcf58074a0748
+ sha256sums = 626c63d2baad6d7236e98904002d447eefc055b051a87264c4e0175446be7625
+ sha256sums = 5e1e109039395a09412a6639fd25c5e70cc353ebc1c7b1ef26709bec1f22c6cb
+ sha256sums = 941c8fba7516b1f8fe961fe31087894a556243f2e28fdcbbf25ae0ea962bfd13
+ sha256sums = 1e06d18f7a995ac2534fc0109f6f170981cd4be9ebc6e69779552a0b2ebd5634
+ sha256sums = 1830ed6696f6c28f61943d5f037e2f6306237a4a7ba0b6b1c2d5b801378413b8
+ sha256sums = ac261ef988e16ef348f9b67e0ccf9cded32808c90b487cc13d8422252516e808
+ sha256sums = 4cf5aaa0c288661ade991122fbcd5c5dca7f2a3fa4cfad32d98b807b6641d9f1
+
+pkgname = dahdi-linux
+
diff --git a/PKGBUILD b/PKGBUILD
new file mode 100644
index 000000000000..5ca56e66707f
--- /dev/null
+++ b/PKGBUILD
@@ -0,0 +1,71 @@
+# Maintainer: Maik Broemme <mbroemme@libmpq.org>
+pkgname="dahdi-linux"
+pkgdesc="DAHDI drivers for Asterisk (Digium, OpenVox, Allo and Yeastar cards)"
+pkgver=2.10.1
+pkgrel=2
+arch=("i686" "x86_64")
+url="http://www.asterisk.org/"
+license=("LGPLv2")
+depends=("linux>=4.0" "linux<4.1")
+makedepends=("linux-headers>=4.0" "linux-headers<4.1")
+conflicts=("dahdi")
+install="${pkgname}.install"
+source=(
+ "http://downloads.asterisk.org/pub/telephony/${pkgname}/${pkgname}-${pkgver}.tar.gz"
+ "oslec.h::http://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/plain/drivers/misc/echo/oslec.h?id=refs/tags/v4.0"
+ "dahdi-linux-2.10.1-allo.patch"
+ "dahdi-linux-2.10.1-openvox-1.patch"
+ "dahdi-linux-2.10.1-openvox-2.patch"
+ "dahdi-linux-2.10.1-openvox-3.patch"
+ "dahdi-linux-2.10.1-yeastar.patch"
+ "dahdi-kernel-4.0-1.patch::https://github.com/asterisk/dahdi-linux/commit/1cc0ad510acd404e63923ed3062b9302d53580da.diff"
+ "dahdi-kernel-4.0-2.patch::https://github.com/asterisk/dahdi-linux/commit/1559db9d1ae03780788788c07334ca54cdd1253a.diff"
+)
+sha256sums=(
+ "94c532e190fc6372f9731df71f8c590fc6f6f184d5394339ce892ac6788843aa"
+ "dc8b86ded9963ad825ea7a18bdbcfb0a4758bbc9dcac5fc9cd9bcf58074a0748"
+ "626c63d2baad6d7236e98904002d447eefc055b051a87264c4e0175446be7625"
+ "5e1e109039395a09412a6639fd25c5e70cc353ebc1c7b1ef26709bec1f22c6cb"
+ "941c8fba7516b1f8fe961fe31087894a556243f2e28fdcbbf25ae0ea962bfd13"
+ "1e06d18f7a995ac2534fc0109f6f170981cd4be9ebc6e69779552a0b2ebd5634"
+ "1830ed6696f6c28f61943d5f037e2f6306237a4a7ba0b6b1c2d5b801378413b8"
+ "ac261ef988e16ef348f9b67e0ccf9cded32808c90b487cc13d8422252516e808"
+ "4cf5aaa0c288661ade991122fbcd5c5dca7f2a3fa4cfad32d98b807b6641d9f1"
+)
+
+build() {
+ cd "${srcdir}/${pkgname}-${pkgver}"
+
+ # enable additional drivers.
+ patch -Np1 -i "${srcdir}/dahdi-linux-2.10.1-allo.patch"
+ patch -Np1 -i "${srcdir}/dahdi-linux-2.10.1-openvox-1.patch"
+ patch -Np1 -i "${srcdir}/dahdi-linux-2.10.1-openvox-2.patch"
+ patch -Np1 -i "${srcdir}/dahdi-linux-2.10.1-openvox-3.patch"
+ patch -Np1 -i "${srcdir}/dahdi-linux-2.10.1-yeastar.patch"
+
+ # linux >= 4.0 patches.
+ patch -Np1 -i "${srcdir}/dahdi-kernel-4.0-1.patch"
+ patch -Np1 -i "${srcdir}/dahdi-kernel-4.0-2.patch"
+
+ # enable dahdi oslec with upstream echo module.
+ mkdir -p drivers/staging/echo
+ cp "${srcdir}/oslec.h" drivers/staging/echo/oslec.h
+ sed 's|ifneq (,$(wildcard $(src)/../staging/echo/echo.c))|ifneq (,$(wildcard $(src)/../staging/echo/oslec.h))|' -i drivers/dahdi/Kbuild
+ sed '\|obj-m += ../staging/echo/echo.o|d' -i drivers/dahdi/Kbuild
+
+ # fix wrong installation paths.
+ sed 's,$(DESTDIR)/lib/firmware,$(DESTDIR)/usr/lib/firmware,g' -i drivers/dahdi/firmware/Makefile
+ sed 's,$DESTDIR/lib/firmware,$DESTDIR/usr/lib/firmware,g' -i build_tools/install_firmware
+
+ # compile.
+ make -j1 DESTDIR="${pkgdir}" all
+}
+
+package() {
+ cd "${srcdir}/${pkgname}-${pkgver}"
+ make DESTDIR="${pkgdir}" install-firmware
+ make DESTDIR="${pkgdir}" install-include
+ make DESTDIR="${pkgdir}" install-xpp-firm
+ cd drivers
+ find . -name "*.ko" -exec gzip "{}" \; -exec install -D -m 0644 "{}.gz" "${pkgdir}/usr/lib/modules/extramodules-4.0-ARCH/{}.gz" \;
+}
diff --git a/dahdi-linux-2.10.1-allo.patch b/dahdi-linux-2.10.1-allo.patch
new file mode 100644
index 000000000000..99a1ed2d21f0
--- /dev/null
+++ b/dahdi-linux-2.10.1-allo.patch
@@ -0,0 +1,7300 @@
+diff -Nur dahdi-linux-2.10.0.1/drivers/dahdi/Kbuild dahdi-linux-2.10.0.1-allo/drivers/dahdi/Kbuild
+--- dahdi-linux-2.10.0.1/drivers/dahdi/Kbuild 2014-09-22 20:40:19.000000000 +0200
++++ dahdi-linux-2.10.0.1-allo/drivers/dahdi/Kbuild 2015-02-10 15:12:17.829888792 +0100
+@@ -14,6 +14,8 @@
+ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTE12XP) += wcte12xp/
+ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTE13XP) += wcte13xp.o
+
++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ALLO4XXP) += allo4xxp/
++
+ wcte13xp-objs := wcte13xp-base.o wcxb_spi.o wcxb.o wcxb_flash.o
+ CFLAGS_wcte13xp-base.o += -I$(src)/oct612x -I$(src)/oct612x/include -I$(src)/oct612x/octdeviceapi -I$(src)/oct612x/octdeviceapi/oct6100api
+ ifeq ($(HOTPLUG_FIRMWARE),yes)
+diff -Nur dahdi-linux-2.10.0.1/drivers/dahdi/Kconfig dahdi-linux-2.10.0.1-allo/drivers/dahdi/Kconfig
+--- dahdi-linux-2.10.0.1/drivers/dahdi/Kconfig 2014-09-22 20:40:19.000000000 +0200
++++ dahdi-linux-2.10.0.1-allo/drivers/dahdi/Kconfig 2015-02-10 15:01:15.886129051 +0100
+@@ -291,4 +291,14 @@
+
+ If unsure, say Y.
+
++config DAHDI_ALLO4XXP
++ tristate "Allo TE410P Quad-T1/E1 PCI"
++ depends on DAHDI && PCI
++ default DAHDI
++ ---help---
++ To compile this driver as a module, choose M here: the
++ module will be called wcte11xp.
++
++ If unsure, say Y.
++
+ source "drivers/dahdi/xpp/Kconfig"
+diff -Nur dahdi-linux-2.10.0.1/drivers/dahdi/allo4xxp/Kbuild dahdi-linux-2.10.0.1-allo/drivers/dahdi/allo4xxp/Kbuild
+--- dahdi-linux-2.10.0.1/drivers/dahdi/allo4xxp/Kbuild 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-allo/drivers/dahdi/allo4xxp/Kbuild 2015-02-10 15:01:15.886129051 +0100
+@@ -0,0 +1,31 @@
++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ALLO4XXP) += allo4xxp.o
++
++FIRM_DIR := ../firmware
++
++EXTRA_CFLAGS += -I$(src)/.. -I$(src)/../oct612x/ $(shell $(src)/../oct612x/octasic-helper cflags $(src)/../oct612x) -Wno-undef
++
++# The OCT612X source files are from a vendor drop and we do not want to edit
++# them to make this warning go away. Therefore, turn off the
++# unused-but-set-variable warning for this driver.
++
++EXTRA_CFLAGS += $(call cc-option, -Wno-unused-but-set-variable)
++
++ifeq ($(HOTPLUG_FIRMWARE),yes)
++ EXTRA_CFLAGS+=-DHOTPLUG_FIRMWARE
++endif
++
++allo4xxp-objs := base.o vpm450m.o
++
++ifneq ($(HOTPLUG_FIRMWARE),yes)
++allo4xxp-objs += $(FIRM_DIR)/dahdi-fw-oct6114-064.o $(FIRM_DIR)/dahdi-fw-oct6114-128.o $(FIRM_DIR)/dahdi-fw-oct6114-256.o
++$(warning WARNING: You are compiling firmware into allo4xxp.ko which is not available under the terms of the GPL. It may be a violation of the GPL to distribute the resulting image since it combines both GPL and non-GPL work. You should consult a lawyer of your own before distributing such an image.)
++endif
++
++$(obj)/$(FIRM_DIR)/dahdi-fw-oct6114-064.o: $(obj)/base.o
++ $(MAKE) -C $(obj)/$(FIRM_DIR) dahdi-fw-oct6114-064.o
++
++$(obj)/$(FIRM_DIR)/dahdi-fw-oct6114-128.o: $(obj)/base.o
++ $(MAKE) -C $(obj)/$(FIRM_DIR) dahdi-fw-oct6114-128.o
++
++$(obj)/$(FIRM_DIR)/dahdi-fw-oct6114-256.o: $(obj)/base.o
++ $(MAKE) -C $(obj)/$(FIRM_DIR) dahdi-fw-oct6114-256.o
+diff -Nur dahdi-linux-2.10.0.1/drivers/dahdi/allo4xxp/allo4xxp.h dahdi-linux-2.10.0.1-allo/drivers/dahdi/allo4xxp/allo4xxp.h
+--- dahdi-linux-2.10.0.1/drivers/dahdi/allo4xxp/allo4xxp.h 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-allo/drivers/dahdi/allo4xxp/allo4xxp.h 2015-02-10 15:01:15.886129051 +0100
+@@ -0,0 +1,144 @@
++/*
++ * Wildcard T400P FXS Interface Driver for DAHDI Telephony interface
++ *
++ * Written by Mark Spencer <markster@linux-support.net>
++ *
++ * 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/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
++/* OctalFALC Only */
++#define FRMR_CMR4 0x41
++#define FRMR_CMR5 0x42
++#define FRMR_CMR6 0x43
++#define FRMR_GPC2 0x8a
++/* End Octal */
++#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
++
++/* CIS - Octal falc bits */
++#define FRMR_CIS_GIS5 0x10
++#define FRMR_CIS_GIS6 0x20
++#define FRMR_CIS_GIS7 0x40
++#define FRMR_CIS_GIS8 0x80
++
++#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];
++};
++
++struct t4_reg {
++ unsigned int reg;
++ unsigned int val;
++};
++
++#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)
++#define WCT4_GET_REG _IOW(DAHDI_CODE, 61, struct t4_reg)
++#define WCT4_SET_REG _IOW(DAHDI_CODE, 62, struct t4_reg)
++
+diff -Nur dahdi-linux-2.10.0.1/drivers/dahdi/allo4xxp/base.c dahdi-linux-2.10.0.1-allo/drivers/dahdi/allo4xxp/base.c
+--- dahdi-linux-2.10.0.1/drivers/dahdi/allo4xxp/base.c 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-allo/drivers/dahdi/allo4xxp/base.c 2015-02-10 15:01:15.889462151 +0100
+@@ -0,0 +1,5677 @@
++/*
++ * TE410P Quad-T1/E1 PCI Driver version 0.1, 12/16/02
++ *
++ * Written by Mark Spencer <markster@digium.com>
++ * Based on previous works, designs, and archetectures conceived and
++ * written by Jim Dixon <jim@lambdatel.com>.
++ * Further modified, optimized, and maintained by
++ * Matthew Fredrickson <creslin@digium.com> and
++ * Russ Meyerriecks <rmeyerriecks@digium.com>
++ *
++ * Copyright (C) 2001 Jim Dixon / Zapata Telephony.
++ * Copyright (C) 2001-2012, 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 <linux/crc32.h>
++
++#include <stdbool.h>
++#include <dahdi/kernel.h>
++
++#include "allo4xxp.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
++
++/* Support first generation cards? */
++#define SUPPORT_GEN1
++
++/* Define to get more attention-grabbing but slightly more I/O using
++ alarm status */
++#undef 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
++
++#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 */
++ struct cpu_workqueue_struct *cpu_wq;
++ 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_FORCE_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
++ */
++/* #define CONFIG_FORCE_EXTENDED_RESET */
++/* #define CONFIG_NOEXTENDED_RESET */
++
++/*
++ * Uncomment the following definition in order to disable Active-State Power
++ * Management on the PCIe bridge for PCIe cards. This has been known to work
++ * around issues where the BIOS enables it on the cards even though the
++ * platform does not support it.
++ *
++ */
++/* #define CONFIG_WCT4XXP_DISABLE_ASPM */
++
++#if defined(CONFIG_FORCE_EXTENDED_RESET) && defined(CONFIG_NOEXTENDED_RESET)
++#error "You cannot define both CONFIG_FORCE_EXTENDED_RESET and " \
++ "CONFIG_NOEXTENDED_RESET."
++#endif
++
++int debug = 0;
++static int timingcable = 0;
++static int t1e1override = -1; /* deprecated */
++static char *default_linemode = "auto";
++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*/
++#endif /* VPM_SUPPORT */
++
++/* 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;
++static int ignore_rotary;
++
++#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 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_EXPRESS (1 << 9)
++#define FLAG_5THGEN (1 << 10)
++#define FLAG_8PORT (1 << 11)
++#define FLAG_1PORT (1 << 12)
++
++#define CANARY 0xc0de
++
++/* names of available HWEC modules */
++#ifdef VPM_SUPPORT
++#define T4_VPM_PRESENT (1 << 28)
++static const char *vpmoct032_name = "VPMOCT032"; //cem:1port change
++static const char *vpmoct064_name = "VPMOCT064";
++static const char *vpmoct128_name = "VPMOCT128";
++static const char *vpmoct256_name = "VPMOCT256";
++#endif
++
++struct devtype {
++ char *desc;
++ unsigned int flags;
++};
++/*ALLO:*/
++static struct devtype allo1280p2 = { "Allocard 2aCP8e (2nd Gen)", FLAG_8PORT | FLAG_5THGEN | FLAG_BURST | FLAG_2NDGEN | FLAG_3RDGEN | FLAG_EXPRESS };
++static struct devtype allo1240p2 = { "Allocard 2aCP4e (2nd Gen)", FLAG_5THGEN | FLAG_BURST | FLAG_2NDGEN | FLAG_3RDGEN | FLAG_EXPRESS };
++static struct devtype allo1241p2 = { "Allocard 2aCP4 (2nd Gen)", FLAG_5THGEN | FLAG_BURST | FLAG_2NDGEN | FLAG_3RDGEN };
++static struct devtype allo1220p2 = { "Allocard 2aCP2e (2nd Gen)", FLAG_2PORT | FLAG_5THGEN | FLAG_BURST | FLAG_2NDGEN | FLAG_3RDGEN | FLAG_EXPRESS };
++static struct devtype allo1210p2 = { "Allocard 2aCP1e (2nd Gen)", FLAG_5THGEN | FLAG_BURST | FLAG_2NDGEN | FLAG_3RDGEN | FLAG_1PORT };
++
++
++struct t4;
++
++enum linemode {T1, E1, J1};
++
++struct spi_state {
++ int wrreg;
++ int rdreg;
++};
++
++struct t4_span {
++ struct t4 *owner;
++ u32 *writechunk; /* Double-word aligned write memory */
++ u32 *readchunk; /* Double-word aligned read memory */
++ enum linemode linemode;
++ int sync;
++ int alarmtimer;
++ int notclear;
++ unsigned long alarm_time;
++ unsigned long losalarm_time;
++ unsigned long aisalarm_time;
++ unsigned long yelalarm_time;
++ unsigned long alarmcheck_time;
++ 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
++ /* 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;
++#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 */
++ unsigned int intcount;
++ int num; /* Which card we are */
++ int syncsrc; /* active sync source */
++ struct dahdi_device *ddev;
++ struct t4_span *tspans[8]; /* 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 */
++ const struct devtype *devtype;
++ unsigned int reset_required:1; /* If reset needed in serial_setup */
++ unsigned int falc31:1; /* are we falc v3.1 (atomic not necessary) */
++ unsigned int t1e1:8; /* T1 / E1 select pins */
++ int ledreg; /* LED Register */
++ int ledreg2; /* LED Register2 */
++ unsigned int gpio;
++ unsigned int gpioctl;
++ int e1recover; /* E1 recovery timer */
++ spinlock_t reglock; /* lock register access */
++ int spansstarted; /* number of spans started */
++ u32 *writechunk; /* Double-word aligned write memory */
++ u32 *readchunk; /* Double-word aligned read memory */
++#ifdef ENABLE_WORKQUEUES
++ atomic_t worklist;
++ struct workqueue_struct *workq;
++#endif
++ int last0; /* for detecting double-missed IRQ */
++
++ /* DMA related fields */
++ unsigned int dmactrl;
++ dma_addr_t readdma;
++ dma_addr_t writedma;
++ void __iomem *membase; /* Base address of card */
++
++ /* Flags for our bottom half */
++ unsigned long checkflag;
++ struct tasklet_struct t4_tlet;
++ /* Latency related additions */
++ unsigned char rxident;
++ unsigned char lastindex;
++ int numbufs;
++ int needed_latency;
++
++#ifdef VPM_SUPPORT
++ struct vpm450m *vpm;
++#endif
++ struct spi_state st;
++};
++
++static inline bool is_pcie(const struct t4 *wc)
++{
++ return (wc->devtype->flags & FLAG_EXPRESS) > 0;
++}
++
++static inline bool has_e1_span(const struct t4 *wc)
++{
++ return (wc->t1e1 > 0);
++}
++
++static inline bool is_octal(const struct t4 *wc)
++{
++ return (wc->devtype->flags & FLAG_8PORT) > 0;
++}
++
++static inline int T4_BASE_SIZE(struct t4 *wc)
++{
++ if (is_octal(wc))
++ return DAHDI_MAX_CHUNKSIZE * 32 * 8;
++ else
++ return DAHDI_MAX_CHUNKSIZE * 32 * 4;
++}
++
++/**
++ * ports_on_framer - The number of ports on the framers.
++ * @wc: Board to check.
++ *
++ * The framer ports could be different the the number of ports on the card
++ * since the dual spans have four ports internally but two ports extenally.
++ *
++ */
++static inline unsigned int ports_on_framer(const struct t4 *wc)
++{
++ return (is_octal(wc)) ? 8 : 4;
++}
++
++#ifdef VPM_SUPPORT
++static void t4_vpm_init(struct t4 *wc);
++
++static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec);
++
++static const struct dahdi_echocan_features vpm_ec_features = {
++ .NLP_automatic = 1,
++ .CED_tx_detect = 1,
++ .CED_rx_detect = 1,
++};
++
++static const struct dahdi_echocan_ops vpm_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_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);
++static int t4_reset_counters(struct dahdi_span *span);
++#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_LEDS2 12
++
++#define WC_SET_AUTH (1 << 20)
++#define WC_GET_AUTH (1 << 12)
++
++#define WC_LFRMR_CS (1 << 10) /* Framer's ChipSelect signal */
++#define WC_LCS (1 << 11)
++#define WC_LCS2 (1 << 12)
++#define WC_LALE (1 << 13)
++#define WC_LFRMR_CS2 (1 << 14) /* Framer's ChipSelect signal 2 */
++#define WC_LREAD (1 << 15)
++#define WC_LWRITE (1 << 16)
++
++#define WC_ACTIVATE (1 << 12)
++
++#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 FMR5 0x21 /* Framer Mode Register 5 */
++#define FMR5_XLU (1 << 4) /* Transmit loopup code */
++#define FMR5_XLD (1 << 5) /* Transmit loopdown code */
++#define FMR5_EIBR (1 << 6) /* Internal Bit Robbing Access */
++#define DEC_T 0x60 /* Diable Error Counter */
++#define IERR_T 0x1B /* Single Bit Defect Insertion Register */
++#define IBV (1 << 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];
++
++struct t8_firm_header {
++ u8 header[6];
++ __le32 chksum;
++ u8 pad[18];
++ __le32 version;
++} __packed;
++
++#define MAX_TDM_CHAN 32
++#define MAX_DTMF_DET 16
++
++#define HDLC_IMR0_MASK (FRMR_IMR0_RME | FRMR_IMR0_RPF)
++#define HDLC_IMR1_MASK (FRMR_IMR1_XDU | FRMR_IMR1_XPR)
++
++static inline unsigned int __t4_pci_in(struct t4 *wc, const unsigned int addr)
++{
++ unsigned int res = readl(wc->membase + (addr * sizeof(u32)));
++ return res;
++}
++
++static inline void __t4_pci_out(struct t4 *wc, const unsigned int addr, const unsigned int value)
++{
++#ifdef DEBUG
++ unsigned int tmp;
++#endif
++ writel(value, wc->membase + (addr * sizeof(u32)));
++#ifdef DEBUG
++ tmp = __t4_pci_in(wc, WC_VERSION);
++ if ((tmp & 0xffff0000) != 0xc01a0000)
++ dev_notice(&wc->dev->dev,
++ "Version Synchronization Error!\n");
++#else
++ __t4_pci_in(wc, WC_VERSION);
++#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)
++{
++ if (span <= 3) {
++ 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);
++ } else {
++ int oldreg = wc->ledreg2;
++
++ span &= 3;
++ wc->ledreg2 &= ~(0x3 << (span << 1));
++ wc->ledreg2 |= (color << (span << 1));
++ if (oldreg != wc->ledreg2)
++ __t4_pci_out(wc, WC_LEDS2, wc->ledreg2);
++ }
++}
++
++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 unsigned int __t4_framer_in(const struct t4 *wc, int unit,
++ const unsigned int addr)
++{
++ unsigned int ret;
++ register u32 val;
++ void __iomem *const wc_laddr = wc->membase + (WC_LADDR*sizeof(u32));
++ void __iomem *const wc_version = wc->membase + (WC_VERSION*sizeof(u32));
++ void __iomem *const wc_ldata = wc->membase + (WC_LDATA*sizeof(u32));
++ int haddr = (((unit & 4) ? 0 : WC_LFRMR_CS2));
++ unit &= 0x3;
++
++ val = ((unit & 0x3) << 8) | (addr & 0xff) | haddr;
++ writel(val, wc_laddr);
++ readl(wc_version);
++ writel(val | WC_LFRMR_CS | WC_LREAD, wc_laddr);
++ readl(wc_version);
++ ret = readb(wc_ldata);
++ writel(val, wc_laddr);
++ readl(wc_version);
++ return ret;
++}
++
++static 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 void __t4_framer_out(const struct t4 *wc, int unit, const u8 addr,
++ const unsigned int value)
++{
++ register u32 val;
++ void __iomem *const wc_laddr = wc->membase + (WC_LADDR*sizeof(u32));
++ void __iomem *const wc_version = wc->membase + (WC_VERSION*sizeof(u32));
++ void __iomem *const wc_ldata = wc->membase + (WC_LDATA*sizeof(u32));
++ int haddr = (((unit & 4) ? 0 : WC_LFRMR_CS2));
++
++ val = ((unit & 0x3) << 8) | (addr & 0xff) | haddr;
++ writel(val, wc_laddr);
++ readl(wc_version);
++ writel(value, wc_ldata);
++ readl(wc_version);
++ writel(val | WC_LFRMR_CS | WC_LWRITE, wc_laddr);
++ readl(wc_version);
++ writel(val, wc_laddr);
++ readl(wc_version);
++}
++
++static 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 __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 (!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));
++ __t4_pci_out(wc, WC_LADDR, (0));
++}
++
++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));
++ __t4_pci_out(wc, WC_LADDR, (WC_LWRITE | WC_LALE));
++ __t4_pci_out(wc, WC_LADDR, (WC_LALE));
++ 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 (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 (!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 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 void t4_check_vpm(struct t4 *wc)
++{
++ int channel, tone, start, span;
++
++ if (vpm450m_checkirq(wc->vpm)) {
++ while(vpm450m_getdtmf(wc->vpm, &channel, &tone, &start)) {
++ span = channel & 0x3;
++ channel >>= 2;
++ if (!has_e1_span(wc))
++ 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));
++ }
++ }
++ }
++ }
++}
++
++#endif /* VPM_SUPPORT */
++
++static void hdlc_stop(struct t4 *wc, unsigned int span)
++{
++ struct t4_span *t = wc->tspans[span];
++ unsigned char imr0, imr1, mode;
++ unsigned long flags;
++ 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);
++ }
++
++ spin_lock_irqsave(&wc->reglock, flags);
++ 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);
++ spin_unlock_irqrestore(&wc->reglock, flags);
++
++ 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;
++
++ spin_lock_irqsave(&wc->reglock, flags);
++
++ /* 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);
++
++ spin_unlock_irqrestore(&wc->reglock, flags);
++
++ /* 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 (E1 != ts->linemode) {
++ 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);
++ }
++}
++
++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 const char *__t4_echocan_name(struct t4 *wc)
++{
++ if (wc->vpm) {
++ if (wc->numspans == 2)
++ return vpmoct064_name;
++ else if (wc->numspans == 4)
++ return vpmoct128_name;
++ else if (wc->numspans == 1) //cem:1port change
++ return vpmoct032_name; //cem:1port change
++ else if (wc->numspans == 8)
++ return vpmoct256_name;
++ }
++ return NULL;
++}
++
++static const char *t4_echocan_name(const struct dahdi_chan *chan)
++{
++ struct t4 *wc = chan->pvt;
++ return __t4_echocan_name(wc);
++}
++
++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 bool alaw = (chan->span->deflaw == 2);
++
++ if (!vpmsupport || !wc->vpm)
++ return -ENODEV;
++
++ if (ecp->param_count > 0) {
++ dev_warn(&wc->dev->dev, "%s echo canceller does not support "
++ "parameters; failing request\n",
++ chan->ec_factory->get_name(chan));
++ return -EINVAL;
++ }
++
++ *ec = tspan->ec[chan->chanpos - 1];
++ (*ec)->ops = &vpm_ec_ops;
++ (*ec)->features = vpm_ec_features;
++
++ channel = has_e1_span(wc) ? chan->chanpos : chan->chanpos + 4;
++
++ if (is_octal(wc))
++ channel = channel << 3;
++ else
++ 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_set_alaw_companding(wc->vpm, channel, alaw);
++ vpm450m_setec(wc->vpm, channel, ecp->tap_length);
++ return 0;
++}
++
++static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec)
++{
++ struct t4 *wc = chan->pvt;
++ int channel;
++
++ if (!wc->vpm)
++ return;
++
++ memset(ec, 0, sizeof(*ec));
++ channel = has_e1_span(wc) ? chan->chanpos : chan->chanpos + 4;
++
++ if (is_octal(wc))
++ channel = channel << 3;
++ else
++ 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->vpm, channel, 0);
++}
++#endif
++
++static int t4_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data)
++{
++ struct t4_regs regs;
++ struct t4_reg reg;
++ int x;
++ struct t4 *wc = chan->pvt;
++#ifdef VPM_SUPPORT
++ int j;
++ int channel;
++ struct t4_span *ts = wc->tspans[chan->span->offset];
++#endif
++
++ switch(cmd) {
++ case WCT4_SET_REG:
++ if (copy_from_user(&reg, (struct t4_reg __user *)data,
++ sizeof(reg)))
++ return -EFAULT;
++ t4_pci_out(wc, reg.reg, reg.val);
++ break;
++ case WCT4_GET_REG:
++ if (copy_from_user(&reg, (struct t4_reg __user *)data,
++ sizeof(reg)))
++ return -EFAULT;
++ reg.val = t4_pci_in(wc, reg.reg);
++ if (copy_to_user((struct t4_reg __user *)data,
++ &reg, sizeof(reg)))
++ return -EFAULT;
++ break;
++ 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((void __user *) 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);
++
++ channel = has_e1_span(wc) ? chan->chanpos : chan->chanpos + 4;
++ if (is_octal(wc))
++ channel = channel << 3;
++ else
++ channel = channel << 2;
++ channel |= chan->span->offset;
++ vpm450m_setdtmf(wc->vpm, 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);
++ ++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);
++}
++
++/**
++ * t4_set_framer_bits - Atomically set bits in a framer register.
++ */
++static void t4_set_framer_bits(struct t4 *wc, unsigned int spanno,
++ unsigned int const addr, u16 bits)
++{
++ unsigned long flags;
++ unsigned int reg;
++
++ spin_lock_irqsave(&wc->reglock, flags);
++ reg = __t4_framer_in(wc, spanno, addr);
++ __t4_framer_out(wc, spanno, addr, (reg | bits));
++ spin_unlock_irqrestore(&wc->reglock, flags);
++}
++
++static int t4_maint(struct dahdi_span *span, int cmd)
++{
++ struct t4_span *ts = container_of(span, struct t4_span, span);
++ struct t4 *wc = ts->owner;
++ unsigned int reg;
++ unsigned long flags;
++
++ if (E1 == ts->linemode) {
++ 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);
++ t4_set_framer_bits(wc, span->offset, LIM0_T, LIM0_LL);
++ break;
++ case DAHDI_MAINT_NETWORKLINELOOP:
++ dev_info(&wc->dev->dev,
++ "Turning on network line loopback\n");
++ t4_clear_maint(span);
++ t4_set_framer_bits(wc, span->offset, LIM1_T, LIM1_RL);
++ break;
++ case DAHDI_MAINT_NETWORKPAYLOADLOOP:
++ dev_info(&wc->dev->dev,
++ "Turning on network payload loopback\n");
++ t4_clear_maint(span);
++ t4_set_framer_bits(wc, span->offset, FMR2_T, FMR2_PLB);
++ break;
++ case DAHDI_MAINT_LOOPUP:
++ case DAHDI_MAINT_LOOPDOWN:
++ dev_info(&wc->dev->dev,
++ "Loopup & loopdown not supported in E1 mode\n");
++ return -ENOSYS;
++ 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");
++ t4_set_framer_bits(wc, span->offset, FMR0, FMR0_SIM);
++ break;
++ 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);
++ t4_set_framer_bits(wc, span->offset, LIM0_T, LIM0_LL);
++ break;
++ case DAHDI_MAINT_NETWORKLINELOOP:
++ dev_info(&wc->dev->dev,
++ "Turning on network line loopback\n");
++ t4_clear_maint(span);
++ t4_set_framer_bits(wc, span->offset, LIM1_T, LIM1_RL);
++ break;
++ case DAHDI_MAINT_NETWORKPAYLOADLOOP:
++ dev_info(&wc->dev->dev,
++ "Turning on network payload loopback\n");
++ t4_clear_maint(span);
++ t4_set_framer_bits(wc, span->offset, FMR2_T, FMR2_PLB);
++ break;
++ case DAHDI_MAINT_LOOPUP:
++ dev_info(&wc->dev->dev, "Transmitting loopup code\n");
++ t4_clear_maint(span);
++ t4_set_framer_bits(wc, span->offset, FMR5, FMR5_XLU);
++ ts->span.maintstat = DAHDI_MAINT_REMOTELOOP;
++ break;
++ case DAHDI_MAINT_LOOPDOWN:
++ dev_info(&wc->dev->dev, "Transmitting loopdown code\n");
++ t4_clear_maint(span);
++ t4_set_framer_bits(wc, span->offset, FMR5, FMR5_XLD);
++ break;
++ 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");
++ return -ENOSYS;
++ case DAHDI_RESET_COUNTERS:
++ t4_reset_counters(span);
++ break;
++ case DAHDI_MAINT_ALARM_SIM:
++ spin_lock_irqsave(&wc->reglock, flags);
++ 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
++ */
++ __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;
++ 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 = container_of(span, struct t4_span, span);
++ struct t4 *wc = ts->owner;
++ unsigned int reg;
++ unsigned long flags;
++
++ spin_lock_irqsave(&wc->reglock, flags);
++
++ /* 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)));
++
++ /* Clear loopup/loopdown signals on the line */
++ reg = __t4_framer_in(wc, span->offset, FMR5);
++ __t4_framer_out(wc, span->offset, FMR5, (reg & ~(FMR5_XLU | FMR5_XLD)));
++
++ spin_unlock_irqrestore(&wc->reglock, flags);
++ span->mainttimer = 0;
++
++ return 0;
++}
++
++static int t4_reset_counters(struct dahdi_span *span)
++{
++ struct t4_span *ts = container_of(span, struct t4_span, span);
++ memset(&ts->span.count, 0, sizeof(ts->span.count));
++ return 0;
++}
++
++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 (E1 == ts->linemode) {
++ 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 = container_of(span, struct t4_span, span);
++ struct t4 *wc = ts->owner;
++
++ tspan = span->offset + 1;
++ if (tspan < 0) {
++ dev_notice(&wc->dev->dev, "T%dXXP: Span '%d' isn't us?\n",
++ wc->numspans, 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 == 8) &&
++ (!(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->tspans[4]->span.flags & DAHDI_FLAG_RUNNING)) &&
++ (!(wc->tspans[5]->span.flags & DAHDI_FLAG_RUNNING)) &&
++ (!(wc->tspans[6]->span.flags & DAHDI_FLAG_RUNNING)) &&
++ (!(wc->tspans[7]->span.flags & DAHDI_FLAG_RUNNING)))
++ ||
++ ((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 == 1) && //cem:1port change
++ (!(wc->tspans[0]->span.flags & DAHDI_FLAG_RUNNING))) //cem:1port change
++ ||
++ ((wc->numspans == 2) &&
++ (!(wc->tspans[0]->span.flags & DAHDI_FLAG_RUNNING)) &&
++ (!(wc->tspans[1]->span.flags & DAHDI_FLAG_RUNNING)))) {
++ /* No longer in use, disable interrupts */
++ dev_info(&wc->dev->dev, "ALLO%dXXP: Disabling interrupts since "
++ "there are no active spans\n", wc->numspans);
++ 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 (E1 == wc->linemode) {
++ /* 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 = container_of(span, struct t4_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, "ALLO%dXXP: Configuring span %d\n",
++ wc->numspans, span->spanno);
++
++ if (lc->sync < 0)
++ lc->sync = 0;
++ if (lc->sync > wc->numspans) {
++ dev_warn(&wc->dev->dev, "WARNING: Cannot set priority on span %d to %d. Please set to a number between 1 and %d\n",
++ span->spanno, 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[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;
++
++ 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_spanconfig(struct file *file, struct dahdi_span *span,
++ struct dahdi_lineconfig *lc)
++{
++ int ret;
++ struct dahdi_device *const ddev = span->parent;
++ struct dahdi_span *s;
++
++ ret = _t4_spanconfig(file, span, lc);
++
++ /* Make sure all the spans have a basic configuration in case they are
++ * not all specified in the configuration files. */
++ lc->sync = 0;
++ list_for_each_entry(s, &ddev->spans, device_node) {
++ WARN_ON(!s->channels);
++ if (!s->channels)
++ continue;
++ if (!s->chans[0]->sigcap)
++ _t4_spanconfig(file, s, lc);
++ }
++ return ret;
++}
++
++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, "ALLO%dXXP: Reconfigured "
++ "channel %d (%s) sigtype %d\n", wc->numspans,
++ chan->channo, chan->name, sigtype);
++ else
++ dev_notice(&wc->dev->dev, "ALLO%dXXP: Configured channel"
++ " %d (%s) sigtype %d\n", wc->numspans,
++ 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 int set_span_devicetype(struct t4 *wc)
++{
++#ifdef VPM_SUPPORT
++ const char *vpmstring = __t4_echocan_name(wc);
++
++ if (vpmstring) {
++ wc->ddev->devicetype = kasprintf(GFP_KERNEL, "%s (%s)",
++ wc->devtype->desc, vpmstring);
++ } else {
++ wc->ddev->devicetype = kasprintf(GFP_KERNEL, wc->devtype->desc);
++ }
++#else
++ wc->ddev->devicetype = kasprintf(GFP_KERNEL, wc->devtype->desc);
++#endif
++
++ if (!wc->ddev->devicetype)
++ return -ENOMEM;
++ return 0;
++}
++
++/* 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;
++ int basesize = T4_BASE_SIZE(wc) >> 2;
++
++ if (!has_e1_span(wc))
++ 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 * (basesize)));
++ ts->readchunk = (void *)(wc->readchunk + (x * 32 * 2) + (which * (basesize)));
++ 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 * (basesize)));
++ mychans->readchunk = (void *)(wc->readchunk + ((x * 32 + y + offset) * 2) + (which * (basesize)));
++ }
++ }
++ }
++}
++
++static int __t4_hardware_init_1(struct t4 *wc, unsigned int cardflags,
++ bool first_time);
++static int __t4_hardware_init_2(struct t4 *wc, bool first_time);
++
++static int t4_hardware_stop(struct t4 *wc);
++
++static void t4_framer_reset(struct t4 *wc)
++{
++ const bool first_time = false;
++ bool have_vpm = wc->vpm != NULL;
++ if (have_vpm) {
++ release_vpm450m(wc->vpm);
++ wc->vpm = NULL;
++ }
++ t4_hardware_stop(wc);
++ __t4_set_sclk_src(wc, WC_SELF, 0, 0);
++ __t4_hardware_init_1(wc, wc->devtype->flags, first_time);
++ __t4_hardware_init_2(wc, first_time);
++ if (have_vpm) {
++ t4_vpm_init(wc);
++ wc->dmactrl |= (wc->vpm) ? T4_VPM_PRESENT : 0;
++ t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++ }
++ setup_chunks(wc, 0);
++ wc->lastindex = 0;
++}
++
++/**
++ * t4_serial_setup - Setup serial parameters and system interface.
++ * @wc: The card to configure.
++ *
++ */
++static void t4_serial_setup(struct t4 *wc)
++{
++ unsigned long flags;
++ unsigned int unit;
++ bool reset_required = false;
++
++ if (debug) {
++ dev_info(&wc->dev->dev,
++ "ALLO%dXXP: Setting up global serial parameters\n",
++ wc->numspans);//cEM:
++ }
++
++ spin_lock_irqsave(&wc->reglock, flags);
++ reset_required = wc->reset_required > 0;
++ wc->reset_required = 0;
++ spin_unlock_irqrestore(&wc->reglock, flags);
++
++ if (reset_required)
++ t4_framer_reset(wc);
++
++ spin_lock_irqsave(&wc->reglock, flags);
++ /* GPC1: Multiplex mode enabled, FSC is output, active low, RCLK from
++ * channel 0 */
++ __t4_framer_out(wc, 0, 0x85, 0xe0);
++ if (is_octal(wc))
++ __t4_framer_out(wc, 0, FRMR_GPC2, 0x00);
++
++ /* IPC: Interrupt push/pull active low */
++ __t4_framer_out(wc, 0, 0x08, 0x01);
++
++ if (is_octal(wc)) {
++ /* Global clocks (16.384 Mhz CLK) */
++ __t4_framer_out(wc, 0, 0x92, 0x00); /* GCM1 */
++ __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, 0x01);
++ __t4_framer_out(wc, 0, 0x97, 0x0b);
++ __t4_framer_out(wc, 0, 0x98, 0xdb);
++ __t4_framer_out(wc, 0, 0x99, 0xdf);
++ } else {
++ /* 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);
++ }
++ spin_unlock_irqrestore(&wc->reglock, flags);
++
++ for (unit = 0; unit < ports_on_framer(wc); ++unit) {
++ spin_lock_irqsave(&wc->reglock, flags);
++
++ /* Configure interrupts */
++ /* GCR: Interrupt on Activation/Deactivation of each */
++ __t4_framer_out(wc, unit, FRMR_GCR, 0x00);
++
++ /* Configure system interface */
++ if (is_octal(wc)) {
++ /* SIC1: 16.384 Mhz clock/bus, double buffer receive /
++ * transmit, byte interleaved */
++ __t4_framer_out(wc, unit, FRMR_SIC1, 0xc2 | 0x08);
++ } else {
++ /* SIC1: 8.192 Mhz clock/bus, double buffer receive /
++ * transmit, byte interleaved */
++ __t4_framer_out(wc, unit, FRMR_SIC1, 0xc2);
++ }
++ /* SIC2: No FFS, no center receive eliastic buffer, phase */
++ __t4_framer_out(wc, unit, FRMR_SIC2, 0x20 | (unit << 1));
++ /* SIC3: Edges for capture */
++ if (is_octal(wc)) {
++ __t4_framer_out(wc, unit, FRMR_SIC3, 0x04 | (1 << 4));
++ } else {
++ __t4_framer_out(wc, unit, FRMR_SIC3, 0x04);
++ }
++ /* CMR2: We provide sync and clock for tx and rx. */
++ __t4_framer_out(wc, unit, FRMR_CMR2, 0x00);
++
++ if (is_octal(wc)) {
++ /* Set RCLK to 16 MHz */
++ __t4_framer_out(wc, unit, FRMR_CMR4, 0x5);
++
++ if (!has_e1_span(wc)) { /* T1/J1 mode */
++ __t4_framer_out(wc, unit, FRMR_XC0, 0x07);
++ __t4_framer_out(wc, unit, FRMR_XC1, 0x04);
++ if (wc->tspans[unit]->linemode == J1)
++ __t4_framer_out(wc, unit, FRMR_RC0, 0x87);
++ else
++ __t4_framer_out(wc, unit, FRMR_RC0, 0x07);
++ __t4_framer_out(wc, unit, FRMR_RC1, 0x04);
++ } else { /* E1 mode */
++ __t4_framer_out(wc, unit, FRMR_XC0, 0x00);
++ __t4_framer_out(wc, unit, FRMR_XC1, 0x04);
++ __t4_framer_out(wc, unit, FRMR_RC0, 0x00);
++ __t4_framer_out(wc, unit, FRMR_RC1, 0x04);
++ }
++
++ } else {
++ if (!has_e1_span(wc)) { /* T1/J1 mode */
++ __t4_framer_out(wc, unit, FRMR_XC0, 0x03);
++ __t4_framer_out(wc, unit, FRMR_XC1, 0x84);
++ if (J1 == wc->tspans[unit]->linemode)
++ __t4_framer_out(wc, unit, FRMR_RC0, 0x83);
++ else
++ __t4_framer_out(wc, unit, FRMR_RC0, 0x03);
++ __t4_framer_out(wc, unit, FRMR_RC1, 0x84);
++ } else { /* E1 mode */
++ __t4_framer_out(wc, unit, FRMR_XC0, 0x00);
++ __t4_framer_out(wc, unit, FRMR_XC1, 0x04);
++ __t4_framer_out(wc, unit, FRMR_RC0, 0x04);
++ __t4_framer_out(wc, unit, FRMR_RC1, 0x04);
++ }
++ }
++
++ /* Configure ports */
++
++ /* PC1: SPYR/SPYX input on RPA/XPA */
++ __t4_framer_out(wc, unit, 0x80, 0x00);
++
++ /* PC2: RMFB/XSIG output/input on RPB/XPB */
++ /* PC3: Some unused stuff */
++ /* PC4: Some more unused stuff */
++ if (is_octal(wc)) {
++ __t4_framer_out(wc, unit, 0x81, 0xBB);
++ __t4_framer_out(wc, unit, 0x82, 0xf5);
++ __t4_framer_out(wc, unit, 0x83, 0x35);
++ } else if (wc->falc31) {
++ __t4_framer_out(wc, unit, 0x81, 0xBB);
++ __t4_framer_out(wc, unit, 0x82, 0xBB);
++ __t4_framer_out(wc, unit, 0x83, 0xBB);
++ } else {
++ __t4_framer_out(wc, unit, 0x81, 0x22);
++ __t4_framer_out(wc, unit, 0x82, 0x65);
++ __t4_framer_out(wc, unit, 0x83, 0x35);
++ }
++
++ /* PC5: XMFS active low, SCLKR is input, RCLK is output */
++ __t4_framer_out(wc, unit, 0x84, 0x01);
++
++ if (debug & DEBUG_MAIN) {
++ dev_notice(&wc->dev->dev,
++ "Successfully initialized serial bus "
++ "for unit %d\n", unit);
++ }
++
++ spin_unlock_irqrestore(&wc->reglock, flags);
++ }
++}
++
++/**
++ * t4_span_assigned - Called when the span is assigned by DAHDI.
++ * @span: Span that has been assigned.
++ *
++ * When this function is called, the span has a valid spanno and all the
++ * channels on the span have valid channel numbers assigned.
++ *
++ * This function is necessary because a device may be registered, and
++ * then user space may then later decide to assign span numbers and the
++ * channel numbers.
++ *
++ */
++static void t4_span_assigned(struct dahdi_span *span)
++{
++ struct t4_span *tspan = container_of(span, struct t4_span, span);
++ struct t4 *wc = tspan->owner;
++ struct dahdi_span *pos;
++ unsigned int unassigned_spans = 0;
++ unsigned long flags;
++
++ /* We use this to make sure all the spans are assigned before
++ * running the serial setup. */
++ list_for_each_entry(pos, &wc->ddev->spans, device_node) {
++ if (!test_bit(DAHDI_FLAGBIT_REGISTERED, &pos->flags))
++ ++unassigned_spans;
++ }
++
++ if (0 == unassigned_spans) {
++ t4_serial_setup(wc);
++
++ 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);
++ }
++}
++
++static void free_wc(struct t4 *wc)
++{
++ unsigned int x, y;
++
++ for (x = 0; x < ARRAY_SIZE(wc->tspans); x++) {
++ if (!wc->tspans[x])
++ continue;
++ for (y = 0; y < ARRAY_SIZE(wc->tspans[x]->chans); y++) {
++ kfree(wc->tspans[x]->chans[y]);
++ kfree(wc->tspans[x]->ec[y]);
++ }
++ kfree(wc->tspans[x]);
++ }
++
++ kfree(wc->ddev->devicetype);
++ kfree(wc->ddev->location);
++ kfree(wc->ddev->hardware_id);
++ dahdi_free_device(wc->ddev);
++ kfree(wc);
++}
++
++/**
++ * t4_alloc_channels - Allocate the channels on a span.
++ * @wc: The board we're allocating for.
++ * @ts: The span we're allocating for.
++ * @linemode: Which mode (T1/E1/J1) to use for this span.
++ *
++ * This function must only be called before the span is assigned it's
++ * possible for user processes to have an open reference to the
++ * channels.
++ *
++ */
++static int t4_alloc_channels(struct t4 *wc, struct t4_span *ts,
++ enum linemode linemode)
++{
++ int i;
++
++ if (test_bit(DAHDI_FLAGBIT_REGISTERED, &ts->span.flags)) {
++ dev_dbg(&wc->dev->dev,
++ "Cannot allocate channels on a span that is already "
++ "assigned.\n");
++ return -EINVAL;
++ }
++
++ /* Cleanup any previously allocated channels. */
++ for (i = 0; i < ARRAY_SIZE(ts->chans); ++i) {
++ kfree(ts->chans[i]);
++ kfree(ts->ec[i]);
++ ts->chans[i] = NULL;
++ ts->ec[i] = NULL;
++ }
++
++ ts->linemode = linemode;
++ for (i = 0; i < ((E1 == ts->linemode) ? 31 : 24); i++) {
++ struct dahdi_chan *chan;
++ struct dahdi_echocan_state *ec;
++
++ chan = kzalloc(sizeof(*chan), GFP_KERNEL);
++ if (!chan) {
++ free_wc(wc);
++ return -ENOMEM;
++ }
++ ts->chans[i] = chan;
++
++ ec = kzalloc(sizeof(*ec), GFP_KERNEL);
++ if (!ec) {
++ free_wc(wc);
++ return -ENOMEM;
++ }
++ ts->ec[i] = ec;
++ }
++
++ return 0;
++}
++
++static void t4_init_one_span(struct t4 *wc, struct t4_span *ts)
++{
++ unsigned long flags;
++ unsigned int reg;
++ int i;
++ if(wc->numspans != 1) {
++ snprintf(ts->span.name, sizeof(ts->span.name) - 1,
++ "ALLO%d/%d/%d", wc->numspans, wc->num, ts->span.offset + 1);
++ snprintf(ts->span.desc, sizeof(ts->span.desc) - 1,
++ "T%dXXP (PCI) Card %d Span %d", wc->numspans, wc->num,
++ ts->span.offset + 1);
++ }
++ else {
++ snprintf(ts->span.name, sizeof(ts->span.name) - 1,
++ "ALLO4/%d/%d", wc->num, ts->span.offset + 1); //cem:1port change
++ snprintf(ts->span.desc, sizeof(ts->span.desc) - 1,"T4XXP (PCI) Card %d Span %d",
++ wc->num, ts->span.offset + 1);
++ }
++ switch (ts->linemode) {
++ case T1:
++ ts->span.spantype = SPANTYPE_DIGITAL_T1;
++ break;
++ case E1:
++ ts->span.spantype = SPANTYPE_DIGITAL_E1;
++ break;
++ case J1:
++ ts->span.spantype = SPANTYPE_DIGITAL_J1;
++ break;
++ }
++
++ /* HDLC Specific init */
++ ts->sigchan = NULL;
++ ts->sigmode = sigmode;
++ ts->sigactive = 0;
++
++ if (E1 != ts->linemode) {
++ 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;
++
++ for (i = 0; i < ts->span.channels; i++) {
++ struct dahdi_chan *const chan = ts->chans[i];
++ chan->pvt = wc;
++ snprintf(chan->name, sizeof(chan->name) - 1,
++ "%s/%d", ts->span.name, i + 1);
++ t4_chan_set_sigcap(&ts->span, i);
++ chan->chanpos = i + 1;
++ }
++
++ /* Enable 1sec timer interrupt */
++ spin_lock_irqsave(&wc->reglock, flags);
++ reg = __t4_framer_in(wc, ts->span.offset, FMR1_T);
++ __t4_framer_out(wc, ts->span.offset, FMR1_T, (reg | FMR1_ECM));
++
++ /* Enable Errored Second interrupt */
++ __t4_framer_out(wc, ts->span.offset, ESM, 0);
++ spin_unlock_irqrestore(&wc->reglock, flags);
++
++ t4_reset_counters(&ts->span);
++}
++
++/**
++ * t4_set_linemode - Allows user space to change the linemode before spans are assigned.
++ * @span: span on which to change the linemode.
++ * @linemode: A value from enumerated spantypes
++ *
++ * This callback is used to override the E1/T1 mode jumper settings and set
++ * the linemode on for each span. Called when the "spantype" attribute
++ * is written in sysfs under the dahdi_device.
++ *
++ */
++static int t4_set_linemode(struct dahdi_span *span, enum spantypes linemode)
++{
++ struct t4_span *ts = container_of(span, struct t4_span, span);
++ struct t4 *wc = ts->owner;
++ int res = 0;
++ enum linemode mode;
++ const char *old_name;
++ static DEFINE_MUTEX(linemode_lock);
++ unsigned long flags;
++
++ dev_dbg(&wc->dev->dev, "Setting '%s' to '%s'\n", span->name,
++ dahdi_spantype2str(linemode));
++
++ if (span->spantype == linemode)
++ return 0;
++
++ spin_lock_irqsave(&wc->reglock, flags);
++ wc->reset_required = 1;
++ spin_unlock_irqrestore(&wc->reglock, flags);
++
++ /* Do not allow the t1e1 member to be changed by multiple threads. */
++ mutex_lock(&linemode_lock);
++ old_name = dahdi_spantype2str(span->spantype);
++ switch (linemode) {
++ case SPANTYPE_DIGITAL_T1:
++ dev_info(&wc->dev->dev,
++ "Changing from %s to T1 line mode.\n", old_name);
++ mode = T1;
++ wc->t1e1 &= ~(1 << span->offset);
++ break;
++ case SPANTYPE_DIGITAL_E1:
++ dev_info(&wc->dev->dev,
++ "Changing from %s to E1 line mode.\n", old_name);
++ mode = E1;
++ wc->t1e1 |= (1 << span->offset);
++ break;
++ case SPANTYPE_DIGITAL_J1:
++ dev_info(&wc->dev->dev,
++ "Changing from %s to J1 line mode.\n", old_name);
++ mode = J1;
++ wc->t1e1 &= ~(1 << span->offset);
++ break;
++ default:
++ dev_err(&wc->dev->dev,
++ "Got invalid linemode %d from dahdi\n", linemode);
++ res = -EINVAL;
++ }
++
++ if (!res) {
++ t4_alloc_channels(wc, ts, mode);
++ t4_init_one_span(wc, ts);
++ dahdi_init_span(span);
++ }
++
++ mutex_unlock(&linemode_lock);
++ return res;
++}
++
++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,
++ .assigned = t4_span_assigned,
++ .set_spantype = t4_set_linemode,
++};
++
++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,
++ .assigned = t4_span_assigned,
++ .set_spantype = t4_set_linemode,
++#ifdef VPM_SUPPORT
++ .echocan_create = t4_echocan_create,
++ .echocan_name = t4_echocan_name,
++#endif
++};
++
++/**
++ * init_spans - Do first initialization on all the spans
++ * @wc: Card to initialize the spans on.
++ *
++ * This function is called *before* the dahdi_device is first registered
++ * with the system. What happens in t4_init_one_span can happen between
++ * when the device is registered and when the spans are assigned via
++ * sysfs (or automatically).
++ *
++ */
++static void init_spans(struct t4 *wc)
++{
++ int x, y;
++ int gen2;
++ struct t4_span *ts;
++ unsigned int reg;
++ unsigned long flags;
++
++ gen2 = (wc->tspans[0]->spanflags & FLAG_2NDGEN);
++ for (x = 0; x < wc->numspans; x++) {
++ ts = wc->tspans[x];
++ if(wc->numspans != 1) {
++ sprintf(ts->span.name, "ALLO%d/%d/%d", wc->numspans, wc->num, x + 1);
++ snprintf(ts->span.desc, sizeof(ts->span.desc) - 1,
++ "T%dXXP (PCI) Card %d Span %d", wc->numspans, wc->num, x+1);
++ }
++ else {
++ sprintf(ts->span.name, "ALLO4/%d/%d", wc->num, x + 1); //cem:1port change
++ snprintf(ts->span.desc, sizeof(ts->span.desc) - 1,
++ "T4XXP (PCI) Card %d Span %d", wc->num, x+1);
++ }
++ switch (ts->linemode) {
++ case T1:
++ ts->span.spantype = SPANTYPE_DIGITAL_T1;
++ break;
++ case E1:
++ ts->span.spantype = SPANTYPE_DIGITAL_E1;
++ break;
++ case J1:
++ ts->span.spantype = SPANTYPE_DIGITAL_J1;
++ break;
++ }
++
++ /* HDLC Specific init */
++ ts->sigchan = NULL;
++ ts->sigmode = sigmode;
++ ts->sigactive = 0;
++
++ if (E1 != ts->linemode) {
++ 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);
++
++ if (gen2) {
++ ts->span.ops = &t4_gen2_span_ops;
++ } else {
++ ts->span.ops = &t4_gen1_span_ops;
++ }
++
++ for (y=0;y<wc->tspans[x]->span.channels;y++) {
++ struct dahdi_chan *mychans = ts->chans[y];
++ sprintf(mychans->name, "ALLO4/%d/%d/%d", wc->num, x + 1, y + 1);//cem:1port change, v002
++ t4_chan_set_sigcap(&ts->span, x);
++ mychans->pvt = wc;
++ mychans->chanpos = y + 1;
++ }
++
++ /* Start checking for alarms in 250 ms */
++ ts->alarmcheck_time = jiffies + msecs_to_jiffies(250);
++
++ /* Enable 1sec timer interrupt */
++ spin_lock_irqsave(&wc->reglock, flags);
++ 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);
++ spin_unlock_irqrestore(&wc->reglock, flags);
++
++ t4_reset_counters(&ts->span);
++
++ }
++
++ set_span_devicetype(wc);
++ setup_chunks(wc, 0);
++ wc->lastindex = 0;
++}
++
++static int syncsrc = 0;
++static int syncnum = 0 /* -1 */;
++static int syncspan = 0;
++static DEFINE_SPINLOCK(synclock);
++
++static void __t4_set_rclk_src(struct t4 *wc, int span)
++{
++ if (is_octal(wc)) {
++ int cmr5 = 0x00 | (span << 5);
++ int cmr1 = 0x38; /* Clock Mode: RCLK sourced by DCO-R1
++ by default, Disable Clock-Switching */
++
++ __t4_framer_out(wc, 0, 0x44, cmr1);
++ __t4_framer_out(wc, 0, FRMR_CMR5, cmr5);
++ } else {
++ 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);
++}
++
++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);
++}
++
++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; /* allo4xxp card number */
++ int newsyncspan = 0; /* span on given allo4xxp card */
++ spin_lock_irqsave(&synclock, flags);
++ 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 < cards[x]->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);
++ spin_unlock_irqrestore(&synclock, flags);
++ return 0;
++}
++
++static void __t4_set_timing_source_auto(struct t4 *wc)
++{
++ int x, i;
++ 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);
++ }
++
++ /* Propagate sync selection to dahdi_span struct
++ * this is read by dahdi_tool to display the span's
++ * master/slave sync information */
++ for (i = 0; i < wc->numspans; i++) {
++ wc->tspans[i]->span.syncsrc = wc->syncsrc + 1;
++ }
++ }
++}
++
++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;
++
++ if (is_octal(wc))
++ fmr1 = 0x9c | 0x02; /* FMR1: Mode 1, T1 mode, CRC on for ESF, 8.192 Mhz system data rate, no XAIS */
++ else
++ 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, FMR5, FMR5_EIBR); /* 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;
++ if (is_octal(wc)) {
++ /* 16 MHz */
++ fmr1 = 0x44 | 0x02; /* FMR1: E1 mode, Automatic force resync, PCM30 mode, 8.192 Mhz backplane, no XAIS */
++ } else {
++ /* 8 MHz */
++ 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 */
++
++ __t4_framer_out(wc, unit, 0x2f, 0x00);
++ __t4_framer_out(wc, unit, 0x30, 0x00);
++ __t4_framer_out(wc, unit, 0x31, 0x00);
++
++ dev_info(&wc->dev->dev, "ALLO%dXXP: Span %d configured for %s/%s%s\n",
++ wc->numspans, unit + 1, framing, line, crc4);
++}
++
++/**
++ * t4_check_for_interrupts - Return 0 if the card is generating interrupts.
++ * @wc: The card to check.
++ *
++ * If the card is not generating interrupts, this function will also place all
++ * the spans on the card into red alarm.
++ *
++ */
++static int t4_check_for_interrupts(struct t4 *wc)
++{
++ unsigned int starting_intcount = wc->intcount;
++ unsigned long stop_time = jiffies + HZ*2;
++ unsigned long flags;
++ int x;
++
++ msleep(20);
++ spin_lock_irqsave(&wc->reglock, flags);
++ while (starting_intcount == wc->intcount) {
++ spin_unlock_irqrestore(&wc->reglock, flags);
++ if (time_after(jiffies, stop_time)) {
++ for (x = 0; x < wc->numspans; x++)
++ wc->tspans[x]->span.alarms = DAHDI_ALARM_RED;
++ dev_err(&wc->dev->dev, "Interrupts not detected.\n");
++ return -EIO;
++ }
++ msleep(100);
++ spin_lock_irqsave(&wc->reglock, flags);
++ }
++ spin_unlock_irqrestore(&wc->reglock, flags);
++
++ return 0;
++}
++
++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 = container_of(span, struct t4_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, "ALLO%dXXP: Span '%d' isn't us?\n",
++ wc->numspans, 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 (E1 == ts->linemode)
++ __t4_configure_e1(wc, span->offset, span->lineconfig);
++ else
++ __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->devtype->flags & FLAG_5THGEN)
++ __t4_pci_out(wc, 5, (ms_per_irq << 16) | wc->numbufs);
++ else
++ __t4_pci_out(wc, 5, (1 << 16) | 1);
++ /* enable interrupts */
++ /* Start DMA, enabling DMA interrupts on read only */
++ wc->dmactrl |= (ts->spanflags & FLAG_2NDGEN) ? 0xc0000000 : 0xc0000003;
++#ifdef VPM_SUPPORT
++ wc->dmactrl |= (wc->vpm) ? T4_VPM_PRESENT : 0;
++#endif
++ /* Seed interrupt register */
++ __t4_pci_out(wc, WC_INTR, 0x0c);
++ if (noburst || !(ts->spanflags & FLAG_BURST))
++ wc->dmactrl |= (1 << 26);
++ else
++ 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);
++
++ local_irq_save(flags);
++ t4_check_alarms(wc, span->offset);
++ t4_check_sigbits(wc, span->offset);
++ local_irq_restore(flags);
++ if (wc->numspans >= 1) { /*cem:1port change, v002*/
++ if (wc->tspans[0]->sync == span->spanno)
++ dev_info(&wc->dev->dev, "SPAN %d: Primary Sync Source\n",
++ span->spanno);
++ }
++ if (wc->tspans[1]->sync == span->spanno)
++ dev_info(&wc->dev->dev, "SPAN %d: Secondary Sync Source\n",
++ span->spanno);
++ if (wc->numspans >= 4) {
++ if (wc->tspans[2]->sync == span->spanno)
++ dev_info(&wc->dev->dev, "SPAN %d: Tertiary Sync Source"
++ "\n", span->spanno);
++ if (wc->tspans[3]->sync == span->spanno)
++ dev_info(&wc->dev->dev, "SPAN %d: Quaternary Sync "
++ "Source\n", span->spanno);
++ }
++ if (wc->numspans == 8) {
++ if (wc->tspans[4]->sync == span->spanno)
++ dev_info(&wc->dev->dev, "SPAN %d: Quinary Sync "
++ "Source\n", span->spanno);
++ if (wc->tspans[5]->sync == span->spanno)
++ dev_info(&wc->dev->dev, "SPAN %d: Senary Sync "
++ "Source\n", span->spanno);
++ if (wc->tspans[6]->sync == span->spanno)
++ dev_info(&wc->dev->dev, "SPAN %d: Septenary Sync "
++ "Source\n", span->spanno);
++ if (wc->tspans[7]->sync == span->spanno)
++ dev_info(&wc->dev->dev, "SPAN %d: Octonary Sync "
++ "Source\n", span->spanno);
++ }
++
++ if (!alreadyrunning) {
++ if (t4_check_for_interrupts(wc))
++ return -EIO;
++ }
++
++ if (debug)
++ dev_info(&wc->dev->dev, "Completed startup!\n");
++ clear_bit(T4_IGNORE_LATENCY, &wc->checkflag);
++ return 0;
++}
++
++static int t4_startup(struct file *file, struct dahdi_span *span)
++{
++ int ret;
++ struct dahdi_device *const ddev = span->parent;
++ struct dahdi_span *s;
++
++ ret = _t4_startup(file, span);
++ list_for_each_entry(s, &ddev->spans, device_node) {
++ if (!test_bit(DAHDI_FLAGBIT_RUNNING, &s->flags)) {
++ _t4_startup(file, s);
++ }
++ }
++ return ret;
++}
++
++#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 = wc->tspans[1]->e1check = 0;
++ if (wc->numspans == 4)
++ wc->tspans[2]->e1check = wc->tspans[3]->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)
++{
++ unsigned int *readchunk;
++ int dbl = 0;
++ int x,y,z;
++ unsigned int tmp;
++ int offset=0;
++ if (!has_e1_span(wc))
++ 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 (unlikely(dbl && (debug & DEBUG_MAIN)))
++ dev_notice(&wc->dev->dev, "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];
++ if (wc->numspans == 4) {
++ wc->tspans[3]->span.chans[z]->readchunk[x] = tmp & 0xff;
++ wc->tspans[2]->span.chans[z]->readchunk[x] = (tmp & 0xff00) >> 8;
++ }
++ if (wc->numspans == 2) { /*cem:1port change, v002*/
++ wc->tspans[1]->span.chans[z]->readchunk[x] = (tmp & 0xff0000) >> 16;
++ }
++ wc->tspans[0]->span.chans[z]->readchunk[x] = tmp >> 24;
++ }
++ if (has_e1_span(wc)) {
++ if (wc->e1recover > 0)
++ wc->e1recover--;
++ tmp = readchunk[0];
++ if (wc->numspans == 4) {
++ e1_check(wc, 3, (tmp & 0x7f));
++ e1_check(wc, 2, (tmp & 0x7f00) >> 8);
++ }
++ if (wc->numspans >=2) //cem:1port change
++ e1_check(wc, 1, (tmp & 0x7f0000) >> 16);
++ e1_check(wc, 0, (tmp & 0x7f000000) >> 24);
++ for (z=24;z<31;z++) {
++ /* Only E1 channels now */
++ tmp = readchunk[z+1];
++ if (wc->numspans == 4) {
++ if (wc->tspans[3]->span.channels > 24)
++ wc->tspans[3]->span.chans[z]->readchunk[x] = tmp & 0xff;
++ if (wc->tspans[2]->span.channels > 24)
++ wc->tspans[2]->span.chans[z]->readchunk[x] = (tmp & 0xff00) >> 8;
++ }
++ if (wc->numspans == 2) {/*cem:1port change, v002*/
++ if (wc->tspans[1]->span.channels > 24)
++ wc->tspans[1]->span.chans[z]->readchunk[x] = (tmp & 0xff0000) >> 16;
++ }
++ 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 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
++ _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)
++{
++ u32 *writechunk;
++ int x, y, z;
++ unsigned int tmp;
++ int offset = 0;
++ if (!has_e1_span(wc))
++ 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[3]->span.chans[z]->writechunk[x]) |
++ (wc->tspans[2]->span.chans[z]->writechunk[x] << 8) |
++ (wc->tspans[1]->span.chans[z]->writechunk[x] << 16) |
++ (wc->tspans[0]->span.chans[z]->writechunk[x] << 24);
++ writechunk[z+offset] = tmp;
++ }
++ if (has_e1_span(wc)) {
++ for (z=24;z<31;z++) {
++ /* Only E1 channels now */
++ tmp = 0;
++ if (wc->numspans == 4) {
++ if (wc->tspans[3]->span.channels > 24)
++ tmp |= wc->tspans[3]->span.chans[z]->writechunk[x];
++ if (wc->tspans[2]->span.channels > 24)
++ tmp |= (wc->tspans[2]->span.chans[z]->writechunk[x] << 8);
++ }
++ if (wc->numspans == 2) { /*cem:1port change, v002*/
++ if (wc->tspans[1]->span.channels > 24)
++ tmp |= (wc->tspans[1]->span.chans[z]->writechunk[x] << 16);
++ }
++ 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_dahdi_rbsbits(struct dahdi_chan *const chan, int rxs)
++{
++ if ((debug & DEBUG_RBS) && printk_ratelimit()) {
++ const struct t4_span *tspan = container_of(chan->span,
++ struct t4_span,
++ span);
++ const struct t4 *const wc = tspan->owner;
++ dev_notice(&wc->dev->dev, "Detected sigbits change on " \
++ "channel %s to %04x\n", chan->name, rxs);
++ }
++ dahdi_rbsbits(chan, rxs);
++}
++
++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 (E1 == ts->linemode) {
++ 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)
++ t4_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)
++ t4_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)
++ t4_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)
++ t4_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)
++ t4_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)
++ t4_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) {
++ t4_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) {
++ t4_dahdi_rbsbits(ts->span.chans[i], rxs);
++ }
++ }
++ }
++ }
++}
++
++/* Must be called from within hardirq context. */
++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];
++
++ if (time_before(jiffies, ts->alarmcheck_time))
++ return;
++
++ if (!(ts->span.flags & DAHDI_FLAG_RUNNING))
++ return;
++
++ spin_lock(&wc->reglock);
++
++ 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 (E1 == ts->linemode) {
++ 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,
++ "Lost crc4-multiframe alignment\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,
++ "Obtained crc4-multiframe alignment\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)) {
++ dev_notice(&wc->dev->dev,
++ "span %d: Loopup detected,"\
++ " enabling remote loop\n",
++ span+1);
++ __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)) {
++ dev_notice(&wc->dev->dev,
++ "span %d: Loopdown detected,"\
++ " disabling remote loop\n",
++ span+1);
++ __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) ||
++ dahdi_have_netdev(ts->span.chans[x]))
++ j++;
++ if (!j)
++ alarms |= DAHDI_ALARM_NOTOPEN;
++ }
++
++ /* Loss of Frame Alignment */
++ if (c & 0x20) {
++ if (!ts->alarm_time) {
++ if (unlikely(debug)) {
++ /* starting to debounce LOF/LFA */
++ dev_info(&wc->dev->dev, "allo%dxxp: LOF/LFA "
++ "detected on span %d but debouncing "
++ "for %d ms\n", wc->numspans, span + 1,
++ alarmdebounce);
++ }
++ ts->alarm_time = jiffies +
++ msecs_to_jiffies(alarmdebounce);
++ } else if (time_after(jiffies, ts->alarm_time)) {
++ /* Disable Slip Interrupts */
++ e = __t4_framer_in(wc, span, 0x17);
++ __t4_framer_out(wc, span, 0x17, (e|0x03));
++
++ alarms |= DAHDI_ALARM_RED;
++ }
++ } else {
++ ts->alarm_time = 0;
++ }
++
++ /* Loss of Signal */
++ if (c & 0x80) {
++ if (!ts->losalarm_time) {
++ if (unlikely(debug)) {
++ /* starting to debounce LOS */
++ dev_info(&wc->dev->dev, "allo%dxxp: LOS "
++ "detected on span %d but debouncing "
++ "for %d ms\n", wc->numspans,
++ span + 1, losalarmdebounce);
++ }
++ ts->losalarm_time = jiffies +
++ msecs_to_jiffies(losalarmdebounce);
++ } else if (time_after(jiffies, ts->losalarm_time)) {
++ /* Disable Slip Interrupts */
++ e = __t4_framer_in(wc, span, 0x17);
++ __t4_framer_out(wc, span, 0x17, (e|0x03));
++
++ alarms |= DAHDI_ALARM_RED;
++ }
++ } else {
++ ts->losalarm_time = 0;
++ }
++
++ /* Alarm Indication Signal */
++ if (c & 0x40) {
++ if (!ts->aisalarm_time) {
++ if (unlikely(debug)) {
++ /* starting to debounce AIS */
++ dev_info(&wc->dev->dev, "allo%dxxp: AIS "
++ "detected on span %d but debouncing "
++ "for %d ms\n", wc->numspans,
++ span + 1, aisalarmdebounce);
++ }
++ ts->aisalarm_time = jiffies +
++ msecs_to_jiffies(aisalarmdebounce);
++ } else if (time_after(jiffies, ts->aisalarm_time)) {
++ alarms |= DAHDI_ALARM_BLUE;
++ }
++ } else {
++ ts->aisalarm_time = 0;
++ }
++
++ /* 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");
++ }
++ }
++
++ 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->yelalarm_time) {
++ if (unlikely(debug)) {
++ /* starting to debounce AIS */
++ dev_info(&wc->dev->dev, "allo%dxxp: yellow "
++ "(RAI) detected on span %d but "
++ "debouncing for %d ms\n",
++ wc->numspans, span + 1,
++ yelalarmdebounce);
++ }
++ ts->yelalarm_time = jiffies +
++ msecs_to_jiffies(yelalarmdebounce);
++ } else if (time_after(jiffies, ts->yelalarm_time)) {
++ alarms |= DAHDI_ALARM_YELLOW;
++ }
++ } else {
++ ts->yelalarm_time = 0;
++ }
++
++ if (alarms)
++ ts->alarmcheck_time = jiffies + msecs_to_jiffies(100);
++ else
++ ts->alarmcheck_time = jiffies + msecs_to_jiffies(50);
++
++ if (ts->span.mainttimer || ts->span.maintstat)
++ alarms |= DAHDI_ALARM_LOOPBACK;
++ ts->span.alarms = alarms;
++
++ spin_unlock(&wc->reglock);
++ 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];
++
++ spin_lock(&wc->reglock);
++ if (ts->alarmtimer && (0 == (--ts->alarmtimer)))
++ ts->span.alarms &= ~(DAHDI_ALARM_RECOVER);
++ spin_unlock(&wc->reglock);
++
++ t4_check_alarms(wc, 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->losalarm_time) {
++#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 >= ARRAY_SIZE(altab))
++ wc->alarmpos = 0;
++ }
++#else
++ if (wc->blinktimer == 480)
++ wc->blinktimer = 0;
++#endif
++}
++
++static inline void t4_framer_interrupt(struct t4 *wc, int span)
++{
++ /* Check interrupts for a given span */
++ unsigned char gis, isr0, isr1, isr2, isr3, isr4;
++ int readsize = -1;
++ struct t4_span *ts = wc->tspans[span];
++ struct dahdi_chan *sigchan;
++ unsigned long flags;
++
++
++ /* 1st gen cards isn't used interrupts */
++ spin_lock_irqsave(&wc->reglock, flags);
++ 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);
++ }
++
++ /* 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);
++ }
++ spin_unlock_irqrestore(&wc->reglock, flags);
++
++ /* Collect errored second counter once per second */
++ if (isr3 & ISR3_ES) {
++ ts->span.count.errsec += 1;
++ }
++
++ if (isr0)
++ t4_check_sigbits(wc, span);
++
++ if (E1 == ts->linemode) {
++ /* 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, "ALLO%d10P: RECEIVE "
++ "slip NEGATIVE on span %d\n",
++ wc->numspans, span + 1);
++ if (isr3 & 0x01)
++ dev_notice(&wc->dev->dev, "ALLO%d10P: RECEIVE "
++ "slip POSITIVE on span %d\n",
++ wc->numspans, span + 1);
++ if (isr4 & 0x80)
++ dev_notice(&wc->dev->dev, "ALLO%dXXP: TRANSMIT "
++ "slip POSITIVE on span %d\n",
++ wc->numspans, span + 1);
++ if (isr4 & 0x40)
++ dev_notice(&wc->dev->dev, "ALLO%d10P: TRANSMIT "
++ "slip NEGATIVE on span %d\n",
++ wc->numspans, 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];
++ unsigned char rsis_reg = t4_framer_in(wc, span, FRMR_RSIS);
++
++ ++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);
++ }
++ } 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
++static irqreturn_t _t4_interrupt(int irq, void *dev_id)
++{
++ struct t4 *wc = dev_id;
++ unsigned long flags;
++ int x;
++
++ unsigned int status;
++ unsigned int status2;
++
++ /* 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 (status & 0x3) {
++ t4_receiveprep(wc, status);
++ t4_transmitprep(wc, status);
++ }
++
++ 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);
++}
++
++DAHDI_IRQ_HANDLER(t4_interrupt)
++{
++ irqreturn_t ret;
++ unsigned long flags;
++ local_irq_save(flags);
++ ret = _t4_interrupt(irq, dev_id);
++ local_irq_restore(flags);
++ return ret;
++}
++#endif
++
++static int t4_allocate_buffers(struct t4 *wc, int numbufs,
++ void **oldalloc, dma_addr_t *oldwritedma)
++{
++ void *alloc;
++ dma_addr_t writedma;
++
++ /* 32 channels, Double-buffer, Read/Write, 4 spans */
++ alloc = pci_alloc_consistent(wc->dev, numbufs * T4_BASE_SIZE(wc) * 2,
++ &writedma);
++
++ if (!alloc) {
++ dev_notice(&wc->dev->dev, "allo%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(wc) * numbufs) / 4;
++
++ /* Same thing but in bytes... */
++ wc->readdma = wc->writedma + (T4_BASE_SIZE(wc) * numbufs);
++
++ wc->numbufs = numbufs;
++
++ /* Initialize Write/Buffers to all blank data */
++ memset(wc->writechunk, 0x00, T4_BASE_SIZE(wc) * numbufs);
++ memset(wc->readchunk, 0xff, T4_BASE_SIZE(wc) * numbufs);
++
++ if (debug) {
++ dev_notice(&wc->dev->dev, "DMA memory base of size %d at " \
++ "%p. Read: %p and Write %p\n",
++ numbufs * T4_BASE_SIZE(wc) * 2, wc->writechunk,
++ wc->readchunk, wc->writechunk);
++ }
++
++ return 0;
++}
++
++static void t4_increase_latency(struct t4 *wc, int newlatency)
++{
++ unsigned long flags;
++ void *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(wc) * oldbufs * 2,
++ 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)) {
++ /* 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_vpm(wc);
++ }
++ }
++#endif
++}
++
++static irqreturn_t _t4_interrupt_gen2(int irq, void *dev_id)
++{
++ 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->devtype->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) {
++ dev_info(&wc->dev->dev, "Need to increase "
++ "latency. Estimated latency should "
++ "be %d\n", needed_latency);
++ 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;
++ }
++
++#ifdef DEBUG
++ if (unlikely((wc->intcount < 20)))
++ dev_dbg(&wc->dev->dev, "2G: Got interrupt, status = %08x, "
++ "CIS = %04x\n", status, t4_framer_in(wc, 0, FRMR_CIS));
++#endif
++
++ 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);
++ if (wc->numspans >= 2) { //cem:1port change
++ if (wc->tspans[1]->span.flags & DAHDI_FLAG_RUNNING)
++ t4_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)
++ t4_queue_work(wc->workq, &wc->tspans[2]->swork, 2 % cpus);
++ else
++ atomic_dec(&wc->worklist);
++ if (wc->tspans[3]->span.flags & DAHDI_FLAG_RUNNING)
++ t4_queue_work(wc->workq, &wc->tspans[3]->swork, 3 % cpus);
++ else
++ atomic_dec(&wc->worklist);
++ }
++#else
++ unsigned int reg5 = __t4_pci_in(wc, 5);
++
++#ifdef DEBUG
++ if (wc->intcount < 20)
++ dev_info(&wc->dev->dev, "Reg 5 is %08x\n", reg5);
++#endif
++
++ if (wc->devtype->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 (is_octal(wc)) {
++ if (cis & FRMR_CIS_GIS5)
++ t4_framer_interrupt(wc, 4);
++ if (cis & FRMR_CIS_GIS6)
++ t4_framer_interrupt(wc, 5);
++ if (cis & FRMR_CIS_GIS7)
++ t4_framer_interrupt(wc, 6);
++ if (cis & FRMR_CIS_GIS8)
++ t4_framer_interrupt(wc, 7);
++ }
++ }
++
++#ifdef VPM_SUPPORT
++ if (wc->vpm && vpmdtmfsupport) {
++ /* 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);
++ }
++#endif
++
++ 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);
++}
++
++DAHDI_IRQ_HANDLER(t4_interrupt_gen2)
++{
++ irqreturn_t ret;
++ unsigned long flags;
++ local_irq_save(flags);
++ ret = _t4_interrupt_gen2(irq, dev_id);
++ local_irq_restore(flags);
++ return ret;
++}
++
++#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) ? T4_VPM_PRESENT : 0);
++#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_init(struct t4 *wc)
++{
++ int laws[8] = { 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; //cem:1port change
++ extern void _binary_dahdi_fw_oct6114_064_bin_size;
++ extern void _binary_dahdi_fw_oct6114_128_bin_size;
++ extern void _binary_dahdi_fw_oct6114_256_bin_size;
++ extern u8 _binary_dahdi_fw_oct6114_032_bin_start[]; //cem:1port change
++ extern u8 _binary_dahdi_fw_oct6114_064_bin_start[];
++ extern u8 _binary_dahdi_fw_oct6114_128_bin_start[];
++ extern u8 _binary_dahdi_fw_oct6114_256_bin_start[];
++#else
++ static const char oct032_firmware[] = "dahdi-fw-oct6114-032.bin"; //cem:1port change
++ static const char oct064_firmware[] = "dahdi-fw-oct6114-064.bin";
++ static const char oct128_firmware[] = "dahdi-fw-oct6114-128.bin";
++ static const char oct256_firmware[] = "dahdi-fw-oct6114-256.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);
++ __t4_raw_oct_in(wc, 0x0004);
++ __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;
++ }
++
++ vpm_capacity = get_vpm450m_capacity(&wc->dev->dev);
++ if (vpm_capacity != wc->numspans * 32) {
++ dev_info(&wc->dev->dev, "Disabling VPMOCT%03d. ALLO%dXXP"\
++ " requires a VPMOCT%03d", vpm_capacity,
++ wc->numspans, wc->numspans*32);
++ return;
++ }
++
++ switch (vpm_capacity) {
++ case 32:/*cem:1port change, v002*/
++#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;
++ case 256:
++#if defined(HOTPLUG_FIRMWARE)
++ if ((request_firmware(&firmware, oct256_firmware, &wc->dev->dev) != 0) ||
++ !firmware) {
++ dev_notice(&wc->dev->dev, "VPM450: firmware %s not "
++ "available from userspace\n", oct256_firmware);
++ return;
++ }
++#else
++ embedded_firmware.data = _binary_dahdi_fw_oct6114_256_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_256_bin_size;
++#endif
++ break;
++ default:
++ dev_notice(&wc->dev->dev, "Unsupported channel capacity found "
++ "on VPM module (%d).\n", vpm_capacity);
++ return;
++ }
++
++ wc->vpm = init_vpm450m(&wc->dev->dev, laws, wc->numspans, firmware);
++ if (!wc->vpm) {
++ 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_info(&wc->dev->dev, "VPM450: hardware DTMF disabled.\n");
++ vpmdtmfsupport = 0;
++ }
++
++ dev_info(&wc->dev->dev, "VPM450: Present and operational servicing %d "
++ "span(s)\n", wc->numspans);
++
++}
++#endif /* VPM_SUPPORT */
++
++static void t4_tsi_reset(struct t4 *wc)
++{
++ int x;
++ if (is_octal(wc)) {
++ for (x = 0; x < 256; x++) {
++ wc->dmactrl &= ~0x0001ffff;
++ wc->dmactrl |= (0x00004000 | ((x & 0x7f) << 7) | ((x >> 7) << 15));
++ t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++ }
++ wc->dmactrl &= ~0x0001ffff;
++ } else {
++ 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 (!has_e1_span(wc)) {
++ fromts += 4;
++ tots += 4;
++ }
++ spin_lock_irqsave(&wc->reglock, flags);
++ if (is_octal(wc)) {
++ int fromts_b = fromts & 0x7f;
++ int fromts_t = fromts >> 7;
++ int tots_b = tots & 0x7f;
++ int tots_t = tots >> 7;
++
++ wc->dmactrl &= ~0x0001ffff;
++ wc->dmactrl |= ((fromts_t << 16) | (tots_t << 15) | 0x00004000 | (tots_b << 7) | (fromts_b));
++ __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++ wc->dmactrl &= ~0x0001ffff;
++ __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++ } else {
++ 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 (!has_e1_span(wc))
++ tots += 4;
++ spin_lock_irqsave(&wc->reglock, flags);
++ if (is_octal(wc)) {
++ int tots_b = tots & 0x7f;
++ int tots_t = tots >> 7;
++
++ wc->dmactrl &= ~0x0001ffff;
++ wc->dmactrl |= ((tots_t << 15) | 0x00004000 | (tots_b << 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 &= ~0x0001ffff;
++ __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++ } else {
++ 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);
++}
++
++#ifndef CONFIG_NOEXTENDED_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
++
++#define SPI_CS (0)
++#define SPI_CLK (1)
++#define SPI_IO0 (2)
++#define SPI_IO1 (3)
++#define SPI_IO3 (4)
++#define SPI_IO2 (5)
++#define SPI_IN (0)
++#define SPI_OUT (1)
++#define ESPI_REG 13
++
++static void t8_clear_bit(struct t4 *wc, int whichb)
++{
++ wc->st.wrreg &= ~(1 << whichb);
++}
++
++static void t8_set_bit(struct t4 *wc, int whichb, int val)
++{
++ t8_clear_bit(wc, whichb);
++ wc->st.wrreg |= (val << whichb);
++}
++
++static int t8_get_bit(struct t4 *wc, int whichb)
++{
++ return (wc->st.rdreg >> whichb) & 1;
++}
++
++static void set_iodir(struct t4 *wc, int whichb, int dir)
++{
++ whichb += 16;
++ t8_clear_bit(wc, whichb);
++ t8_set_bit(wc, whichb, dir);
++}
++
++static void write_hwreg(struct t4 *wc)
++{
++ t4_pci_out(wc, ESPI_REG, wc->st.wrreg);
++}
++
++static void read_hwreg(struct t4 *wc)
++{
++ wc->st.rdreg = t4_pci_in(wc, ESPI_REG);
++}
++
++static void set_cs(struct t4 *wc, int state)
++{
++ t8_set_bit(wc, SPI_CS, state);
++ write_hwreg(wc);
++}
++
++static void set_clk(struct t4 *wc, int clk)
++{
++ t8_set_bit(wc, SPI_CLK, clk);
++ write_hwreg(wc);
++}
++
++static void clk_bit_out(struct t4 *wc, int val)
++{
++ t8_set_bit(wc, SPI_IO0, val & 1);
++ set_clk(wc, 0);
++ set_clk(wc, 1);
++}
++
++static void shift_out(struct t4 *wc, int val)
++{
++ int i;
++ for (i = 7; i >= 0; i--)
++ clk_bit_out(wc, (val >> i) & 1);
++}
++
++static int clk_bit_in(struct t4 *wc)
++{
++ int ret;
++ set_clk(wc, 0);
++ read_hwreg(wc);
++ ret = t8_get_bit(wc, SPI_IO1);
++ set_clk(wc, 1);
++ return ret;
++}
++
++static int shift_in(struct t4 *wc)
++{
++ int ret = 0;
++ int i;
++ int bit;
++
++ for (i = 7; i >= 0; i--) {
++ bit = clk_bit_in(wc);
++ ret |= ((bit & 1) << i);
++ }
++ return ret;
++}
++
++static void write_enable(struct t4 *wc)
++{
++ int cmd = 0x06;
++ set_cs(wc, 0);
++ shift_out(wc, cmd);
++ set_cs(wc, 1);
++}
++
++static int read_sr1(struct t4 *wc)
++{
++ int cmd = 0x05;
++ int ret;
++ set_cs(wc, 0);
++ shift_out(wc, cmd);
++ ret = shift_in(wc);
++ set_cs(wc, 1);
++ return ret;
++}
++
++static void clear_busy(struct t4 *wc)
++{
++ static const int SR1_BUSY = (1 << 0);
++ unsigned long stop;
++
++ stop = jiffies + 2*HZ;
++ while (read_sr1(wc) & SR1_BUSY) {
++ if (time_after(jiffies, stop)) {
++ if (printk_ratelimit()) {
++ dev_err(&wc->dev->dev,
++ "Lockup in %s\n", __func__);
++ }
++ break;
++ }
++ cond_resched();
++ }
++}
++
++static void sector_erase(struct t4 *wc, uint32_t addr)
++{
++ int cmd = 0x20;
++ write_enable(wc);
++ set_cs(wc, 0);
++ shift_out(wc, cmd);
++ shift_out(wc, (addr >> 16) & 0xff);
++ shift_out(wc, (addr >> 8) & 0xff);
++ shift_out(wc, (addr >> 0) & 0xff);
++ set_cs(wc, 1);
++ clear_busy(wc);
++}
++
++static void erase_half(struct t4 *wc)
++{
++ uint32_t addr = 0x00010000;
++ uint32_t primary_firmware = 0x00250000;
++ uint32_t i;
++
++ if(wc->devtype->flags & FLAG_2PORT | wc->devtype->flags & FLAG_1PORT)
++ primary_firmware = 0x000E0000;
++ dev_info(&wc->dev->dev, "Erasing fpag primary firmware from Address:%x\n",primary_firmware);
++
++ for (i = addr; i < (addr + primary_firmware); i += 4096)
++ sector_erase(wc, i);
++}
++
++
++#define T8_FLASH_PAGE_SIZE 256UL
++
++static void t8_update_firmware_page(struct t4 *wc, u32 address,
++ const u8 *page_data, size_t size)
++{
++ int i;
++
++ write_enable(wc);
++ set_cs(wc, 0);
++ shift_out(wc, 0x02);
++ shift_out(wc, (address >> 16) & 0xff);
++ shift_out(wc, (address >> 8) & 0xff);
++ shift_out(wc, (address >> 0) & 0xff);
++
++ for (i = 0; i < size; ++i)
++ shift_out(wc, page_data[i]);
++
++ set_cs(wc, 1);
++ clear_busy(wc);
++}
++
++static int t8_update_firmware(struct t4 *wc, const struct firmware *fw,
++ const char *t8_firmware)
++{
++ int res;
++ size_t offset = 0;
++ const u32 BASE_ADDRESS = 0x00010000;
++ const u8 *data, *end;
++ size_t size = 0;
++
++ /* Erase flash */
++ erase_half(wc);
++
++ dev_info(&wc->dev->dev,
++ "Uploading %s. This can take up to 30 seconds.\n", t8_firmware);
++
++ data = &fw->data[sizeof(struct t8_firm_header)];
++ end = &fw->data[fw->size];
++
++ while (data < end) {
++ /* Calculate the tail end of data that's shorter than a page */
++ size = min(T8_FLASH_PAGE_SIZE, (unsigned long)(end - data));
++
++ t8_update_firmware_page(wc, BASE_ADDRESS + offset,
++ data, size);
++ data += T8_FLASH_PAGE_SIZE;
++ offset += T8_FLASH_PAGE_SIZE;
++
++ cond_resched();
++ }
++
++ /* Reset te820 fpga after loading firmware */
++#if 0
++ dev_info(&wc->dev->dev, "Firmware load complete. Reseting device.\n");
++/*allo:*/
++ res = pci_save_state(wc->dev);
++ if (res)
++ goto error_exit;
++ /* Set the fpga reset bits and clobber the remainder of the
++ * register, device will be reset anyway */
++ t4_pci_out(wc, WC_LEDS, 0xe0000000);
++ msleep(1000);
++
++ pci_restore_state(wc->dev);
++#endif
++
++ /* Signal the driver to restart initialization.
++ * This will back out all initialization so far and
++ * restart the driver load process */
++ return -EAGAIN;
++
++error_exit:
++ return res;
++}
++
++static char read_flash_byte(struct t4 *wc, unsigned int addr)
++{
++ int cmd = 0x03;
++ char data;
++
++ set_cs(wc, 0);
++
++ shift_out(wc, cmd);
++
++ shift_out(wc, (addr >> 16) & 0xff);
++ shift_out(wc, (addr >> 8) & 0xff);
++ shift_out(wc, (addr >> 0) & 0xff);
++
++ data = shift_in(wc) & 0xff;
++
++ set_cs(wc, 1);
++
++ return data;
++}
++
++/**
++ * t8_read_serial - Returns the serial number of the board.
++ * @wc: The board whos serial number we are reading.
++ *
++ * The buffer returned is dynamically allocated and must be kfree'd by the
++ * caller. If memory could not be allocated, NULL is returned.
++ *
++ * Must be called in process context.
++ *
++ */
++static char *t8_read_serial(struct t4 *wc)
++{
++ int i;
++ static const int MAX_SERIAL = 16;
++ static const u32 base_addr = 0x00000000;
++ unsigned char c;
++ unsigned char *serial = kzalloc(MAX_SERIAL + 1, GFP_KERNEL);
++
++ if (!serial)
++ return NULL;
++
++ for (i = 0; i < MAX_SERIAL; ++i) {
++ c = read_flash_byte(wc, base_addr+i);
++ if ((c >= 'a' && c <= 'z') ||
++ (c >= 'A' && c <= 'Z') ||
++ (c >= '0' && c <= '9'))
++ serial[i] = c;
++ else
++ break;
++
++ }
++
++ if (!i) {
++ kfree(serial);
++ serial = NULL;
++ }
++
++ return serial;
++}
++
++
++static void setup_spi(struct t4 *wc)
++{
++ wc->st.rdreg = wc->st.wrreg = 0;
++
++ set_iodir(wc, SPI_IO0, SPI_OUT);
++ set_iodir(wc, SPI_IO1, SPI_IN);
++ set_iodir(wc, SPI_CS, SPI_OUT);
++ set_iodir(wc, SPI_CLK, SPI_OUT);
++
++ t8_set_bit(wc, SPI_CS, 1);
++ t8_set_bit(wc, SPI_CLK, 1);
++
++ write_hwreg(wc);
++}
++
++static int t8_check_firmware(struct t4 *wc, unsigned int version)
++{
++ int res = 0;
++ u32 crc;
++ const struct t8_firm_header *header;
++ const struct firmware *fw;
++ static const char *t8_firmware;
++ static char t8_firm[128] = "allo-dahdi-fw-2aCP4.bin";
++
++ if (is_octal(wc)){
++ strcpy(t8_firm,"allo-dahdi-fw-2aCP8e.bin");
++ }
++ else if(wc->devtype->flags & FLAG_2PORT){
++ strcpy(t8_firm, "allo-dahdi-fw-2aCP2e.bin");
++ }
++ else if(wc->devtype->flags & FLAG_1PORT){
++ strcpy(t8_firm, "allo-dahdi-fw-2aCP1e.bin");
++ }
++ else if (wc->devtype->flags & FLAG_EXPRESS){
++ strcpy(t8_firm,"allo-dahdi-fw-2aCP4e.bin");
++ }
++
++
++ t8_firmware = (const char *)t8_firm;
++
++ res = request_firmware(&fw, t8_firmware, &wc->dev->dev);
++ if (res) {
++ dev_info(&wc->dev->dev, "firmware %s not "
++ "available from userspace\n", t8_firmware);
++ goto cleanup;
++ }
++
++ header = (const struct t8_firm_header *)fw->data;
++
++/* Check the crc before anything else */
++ crc = crc32(~0, &fw->data[10], fw->size - 10) ^ ~0;
++ if (memcmp("CEMBLR", header->header, sizeof(header->header)) ||
++ (le32_to_cpu(header->chksum) != crc)) {
++ dev_info(&wc->dev->dev,
++ "%s is invalid. Please reinstall.:%08X %08X\n", t8_firmware, le32_to_cpu(header->chksum), crc);
++ goto cleanup;
++ }
++
++ /* Spi struct must be setup before any access to flash memory */
++ setup_spi(wc);
++
++ /* Check the two firmware versions */
++ if (le32_to_cpu(header->version) == version)
++ goto cleanup;
++
++ dev_info(&wc->dev->dev, "%s Version: %08x available for flash\n",
++ t8_firmware, header->version);
++
++ res = t8_update_firmware(wc, fw, t8_firmware);
++ if (res && res != -EAGAIN) {
++ dev_info(&wc->dev->dev, "Failed to load firmware %s\n",
++ t8_firmware);
++ }
++/*allo:*/
++#if 1
++ release_firmware(fw);
++ return 0;
++#endif
++cleanup:
++ release_firmware(fw);
++ return res;
++}
++
++static int
++__t4_hardware_init_1(struct t4 *wc, unsigned int cardflags, bool first_time)
++{
++ unsigned int version;
++ int res;
++
++ version = t4_pci_in(wc, WC_VERSION);
++ if (is_octal(wc) && first_time) {
++ dev_info(&wc->dev->dev, "Firmware Version: %01x.%02x\n",
++ (version & 0xf00) >> 8,
++ version & 0xff);
++ } else if (first_time) {
++ dev_info(&wc->dev->dev, "Firmware Version: %08x\n", version);
++ }
++ if (debug) {
++ 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
++ }
++
++ /* Check the field updatable firmware for the wcte820 */
++ res = t8_check_firmware(wc, version);
++ if (res)
++ return res;
++ wc->ddev->hardware_id = t8_read_serial(wc);
++
++#if defined(CONFIG_FORCE_EXTENDED_RESET)
++ t4_extended_reset(wc);
++#elif !defined(CONFIG_NOEXTENDED_RESET)
++ if (wc->devtype->flags & FLAG_EXPRESS)
++ 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 (first_time) {
++ if (!strcasecmp("auto", default_linemode)) {
++ if (-1 == t1e1override) {
++ wc->t1e1 = (((t4_pci_in(wc, WC_LEDS)) &
++ 0x0f00) >> 8);
++ wc->t1e1 &= 0xf;
++ if (is_octal(wc)) {
++ wc->t1e1 |= ((t4_pci_in(wc, WC_LEDS2)) &
++ 0x0f00) >> 4;
++ }
++ } else {
++ dev_warn(&wc->dev->dev,
++ "'t1e1override' is deprecated. Please use 'default_linemode'.\n");
++ wc->t1e1 = t1e1override & 0xf;
++ }
++ } else if (!strcasecmp("t1", default_linemode)) {
++ wc->t1e1 = 0;
++ } else if (!strcasecmp("e1", default_linemode)) {
++ wc->t1e1 = 0xff;
++ } else if (!strcasecmp("j1", default_linemode)) {
++ wc->t1e1 = 0;
++ j1mode = 1;
++ } else {
++ dev_err(&wc->dev->dev, "'%s' is an unknown linemode.\n",
++ default_linemode);
++ wc->t1e1 = 0;
++ }
++ }
++
++ wc->order = ((t4_pci_in(wc, WC_LEDS)) & 0xf0000000) >> 28;
++ order_index[wc->order]++;
++
++ /* TE820 Auth Check */
++ if (is_octal(wc)) {
++ unsigned long stop = jiffies + HZ;
++ uint32_t donebit;
++
++ t4_pci_out(wc, WC_LEDS2, WC_SET_AUTH);
++ donebit = t4_pci_in(wc, WC_LEDS2);
++ while (!(donebit & WC_GET_AUTH)) {
++ if (time_after(jiffies, stop)) {
++ /* Encryption check failed, stop operation */
++ dev_info(&wc->dev->dev,
++ "Failed encryption check. "
++ "Unloading driver.\n");
++ return -EIO;
++ }
++ msleep(20);
++ donebit = t4_pci_in(wc, WC_LEDS2);
++ }
++ }
++
++ return 0;
++}
++
++static int t4_hardware_init_1(struct t4 *wc, unsigned int cardflags)
++{
++ return __t4_hardware_init_1(wc, cardflags, true);
++}
++
++static int __t4_hardware_init_2(struct t4 *wc, bool first_time)
++{
++ int x;
++ unsigned int regval;
++ unsigned long flags;
++
++ if (t4_pci_in(wc, WC_VERSION) >= 0xc01a0165) {
++ wc->tspans[0]->spanflags |= FLAG_OCTOPT;
++ }
++ /* Setup LEDS, take out of reset */
++ t4_pci_out(wc, WC_LEDS, 0x000000ff);
++ udelay(100);
++ t4_activate(wc);
++ udelay(100);
++
++ /* In order to find out the QFALC framer version, we have to
++ * temporarily turn off compat mode and take a peak at VSTR. We turn
++ * compat back on when we are done.
++ *
++ */
++ spin_lock_irqsave(&wc->reglock, flags);
++ regval = __t4_framer_in(wc, 0, 0xd6);
++ if (is_octal(wc))
++ regval |= (1 << 4); /* SSI16 - For 16 MHz multiplex mode with comp = 1 */
++ else
++ regval |= (1 << 5); /* set COMP_DIS*/
++
++ __t4_framer_out(wc, 0, 0xd6, regval);
++ spin_unlock_irqrestore(&wc->reglock, flags);
++
++ if (!is_octal(wc)) {
++ regval = t4_framer_in(wc, 0, 0x4a);
++ if (first_time && regval == 0x05) {
++ dev_info(&wc->dev->dev, "FALC Framer Version: 2.1 or "
++ "earlier\n");
++ } else if (regval == 0x20) {
++ if (first_time)
++ dev_info(&wc->dev->dev, "FALC Framer Version: 3.1\n");
++ wc->falc31 = 1;
++ } else if (first_time) {
++ dev_info(&wc->dev->dev, "FALC Framer Version: Unknown "
++ "(VSTR = 0x%02x)\n", regval);
++ }
++ }
++
++ spin_lock_irqsave(&wc->reglock, flags);
++ 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);
++ spin_unlock_irqrestore(&wc->reglock, flags);
++
++ if (debug) {
++ 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));
++ }
++ }
++
++ 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));
++
++ return 0;
++}
++
++static int t4_hardware_init_2(struct t4 *wc)
++{
++ return __t4_hardware_init_2(wc, true);
++}
++
++static int __devinit t4_launch(struct t4 *wc)
++{
++ int x;
++ int res;
++
++ if (test_bit(DAHDI_FLAGBIT_REGISTERED, &wc->tspans[0]->span.flags))
++ return 0;
++
++ if (debug) {
++ dev_info(&wc->dev->dev,
++ "ALLO%dXXP: Launching card: %d\n", wc->numspans,
++ wc->order);
++ }
++
++ wc->ddev->manufacturer = "Allo";
++ if (!ignore_rotary && (1 == order_index[wc->order])) {
++ wc->ddev->location = kasprintf(GFP_KERNEL,
++ "Board ID Switch %d", wc->order);
++ } else {
++ bool express = ((wc->tspans[0]->spanflags & FLAG_EXPRESS) > 0);
++ wc->ddev->location = kasprintf(GFP_KERNEL,
++ "PCI%s Bus %02d Slot %02d",
++ (express) ? " Express" : "",
++ wc->dev->bus->number,
++ PCI_SLOT(wc->dev->devfn) + 1);
++ }
++
++ if (!wc->ddev->location)
++ return -ENOMEM;
++
++ for (x = 0; x < wc->numspans; ++x) {
++ list_add_tail(&wc->tspans[x]->span.device_node,
++ &wc->ddev->spans);
++ }
++
++ tasklet_init(&wc->t4_tlet, t4_isr_bh, (unsigned long)wc);
++
++ res = dahdi_register_device(wc->ddev, &wc->dev->dev);
++ if (res) {
++ dev_err(&wc->dev->dev, "Failed to register with DAHDI.\n");
++ return res;
++ }
++
++ return 0;
++}
++
++/**
++ * allo4xxp_sort_cards - Sort the cards in card array by rotary switch settings.
++ *
++ */
++static void allo4xxp_sort_cards(void)
++{
++ int x;
++
++ /* 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;
++ }
++ }
++}
++
++static int __devinit
++t4_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
++{
++ int res;
++ struct t4 *wc;
++ unsigned int x;
++ int init_latency;
++
++ if (pci_enable_device(pdev)) {
++ return -EIO;
++ }
++
++ wc = kzalloc(sizeof(*wc), GFP_KERNEL);
++ if (!wc)
++ return -ENOMEM;
++
++ wc->ddev = dahdi_create_device();
++ if (!wc->ddev) {
++ kfree(wc);
++ return -ENOMEM;
++ }
++
++ spin_lock_init(&wc->reglock);
++ wc->devtype = (const struct devtype *)(ent->driver_data);
++
++#ifdef CONFIG_WCT4XXP_DISABLE_ASPM
++ if (is_pcie(wc)) {
++ pci_disable_link_state(pdev->bus->self, PCIE_LINK_STATE_L0S |
++ PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM);
++ };
++#endif
++
++ if (is_octal(wc))
++ wc->numspans = 8;
++ else if (wc->devtype->flags & FLAG_2PORT)
++ wc->numspans = 2;
++ else if (wc->devtype->flags & FLAG_1PORT)/*cem:1port change, v002*/
++ wc->numspans = 1;
++ else
++ wc->numspans = 4;
++
++ wc->membase = pci_iomap(pdev, 0, 0);
++ /* This rids of the Double missed interrupt message after loading */
++ wc->last0 = 1;
++ if (pci_request_regions(pdev, wc->devtype->desc))
++ dev_info(&pdev->dev, "allo%dxxp: Unable to request regions\n",
++ wc->numspans);
++
++ if (debug)
++ dev_info(&pdev->dev, "Found ALLO%dXXP\n", wc->numspans);
++
++ wc->dev = pdev;
++
++ /* Enable bus mastering */
++ pci_set_master(pdev);
++
++ /* Keep track of which device we are */
++ pci_set_drvdata(pdev, wc);
++
++ if (wc->devtype->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;
++ }
++/*ALLO:*/
++ dev_info(&wc->dev->dev, "2nd gen card with initial latency of "
++ "%d and %d ms per IRQ\n", init_latency, ms_per_irq);
++ } else {
++ if (wc->devtype->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 */
++ res = t4_hardware_init_1(wc, wc->devtype->flags);
++ if (res) {
++ /* If this function returns -EAGAIN, we expect
++ * to attempt another driver load. Clean everything
++ * up first */
++ pci_iounmap(wc->dev, wc->membase);
++ pci_release_regions(wc->dev);
++ pci_free_consistent(wc->dev, T4_BASE_SIZE(wc) * wc->numbufs * 2,
++ wc->writechunk, wc->writedma);
++ pci_set_drvdata(wc->dev, NULL);
++ free_wc(wc);
++ return res;
++ }
++
++ 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->devtype->flags & FLAG_2NDGEN) {
++ char tmp[20];
++
++ sprintf(tmp, "te%dxxp[%d]", wc->numspans, wc->num);
++ wc->workq = create_workqueue(tmp);
++ }
++#endif
++
++ /* Allocate pieces we need here */
++ for (x = 0; x < ports_on_framer(wc); x++) {
++ struct t4_span *ts;
++ enum linemode linemode;
++
++ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
++ if (!ts) {
++ free_wc(wc);
++ return -ENOMEM;
++ }
++ wc->tspans[x] = ts;
++
++#ifdef ENABLE_WORKQUEUES
++ INIT_WORK(&ts->swork, workq_handlespan, ts);
++#endif
++ ts->spanflags |= wc->devtype->flags;
++ linemode = (wc->t1e1 & (1 << x)) ? E1 : ((j1mode) ? J1 : T1);
++ t4_alloc_channels(wc, wc->tspans[x], linemode);
++ }
++
++ /* Continue hardware intiialization */
++ t4_hardware_init_2(wc);
++
++#ifdef SUPPORT_GEN1
++ if (request_irq(pdev->irq, (wc->devtype->flags & FLAG_2NDGEN) ?
++ t4_interrupt_gen2 : t4_interrupt,
++ DAHDI_IRQ_SHARED,
++ (wc->numspans == 8) ? "allo8xxp" :
++ (wc->numspans == 2) ? "allo2xxp" :
++ (wc->numspans == 1)? "allo1xxp" :
++ "allo4xxp",
++ 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, "t4xxp", wc)) {
++#endif
++ dev_notice(&wc->dev->dev, "Unable to request IRQ %d\n",
++ pdev->irq);
++ free_wc(wc);
++ return -EIO;
++ }
++
++ init_spans(wc);
++
++ if (!ignore_rotary)
++ allo4xxp_sort_cards();
++
++ if (wc->ddev->hardware_id) {
++ dev_info(&wc->dev->dev,
++ "Found a Allocard: %s (SN: %s)\n", wc->devtype->desc,
++ wc->ddev->hardware_id);
++ } else {
++ dev_info(&wc->dev->dev,
++ "Found a Allocard: %s\n", wc->devtype->desc);
++ }
++
++#ifdef VPM_SUPPORT
++ if (!wc->vpm) {
++ t4_vpm_init(wc);
++ wc->dmactrl |= (wc->vpm) ? T4_VPM_PRESENT : 0;
++ t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++ if (wc->vpm)
++ set_span_devicetype(wc);
++ }
++#endif
++
++ create_sysfs_files(wc);
++
++ res = 0;
++ if (ignore_rotary)
++ res = t4_launch(wc);
++
++ return res;
++}
++
++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);
++
++ if (debug) {
++ dev_notice(&wc->dev->dev, "Stopped ALLO%dXXP, Turned off DMA\n",
++ wc->numspans);
++ }
++ return 0;
++}
++
++static int __devinit
++t4_init_one_retry(struct pci_dev *pdev, const struct pci_device_id *ent)
++{
++ int res;
++ res = t4_init_one(pdev, ent);
++
++ /* If the driver was reset by a firmware load,
++ * try to load once again */
++ if (-EAGAIN == res) {
++ res = t4_init_one(pdev, ent);
++ if (-EAGAIN == res) {
++ dev_err(&pdev->dev, "Failed to update firmware.\n");
++ res = -EIO;
++ }
++ }
++
++ return res;
++}
++
++static void _t4_remove_one(struct t4 *wc)
++{
++ int basesize;
++
++ if (!wc)
++ return;
++
++ dahdi_unregister_device(wc->ddev);
++
++ remove_sysfs_files(wc);
++
++ /* Stop hardware */
++ t4_hardware_stop(wc);
++
++#ifdef VPM_SUPPORT
++ /* Release vpm */
++ if (wc->vpm)
++ release_vpm450m(wc->vpm);
++ wc->vpm = NULL;
++#endif
++ /* Unregister spans */
++
++ basesize = DAHDI_MAX_CHUNKSIZE * 32 * 4;
++ if (!(wc->tspans[0]->spanflags & FLAG_2NDGEN))
++ basesize = basesize * 2;
++
++#ifdef ENABLE_WORKQUEUES
++ if (wc->workq) {
++ flush_workqueue(wc->workq);
++ destroy_workqueue(wc->workq);
++ }
++#endif
++
++ free_irq(wc->dev->irq, wc);
++
++ if (wc->membase)
++ pci_iounmap(wc->dev, wc->membase);
++
++ pci_release_regions(wc->dev);
++
++ /* Immediately free resources */
++ pci_free_consistent(wc->dev, T4_BASE_SIZE(wc) * wc->numbufs * 2,
++ wc->writechunk, wc->writedma);
++
++ order_index[wc->order]--;
++
++ cards[wc->num] = NULL;
++ pci_set_drvdata(wc->dev, NULL);
++ free_wc(wc);
++}
++
++static void __devexit t4_remove_one(struct pci_dev *pdev)
++{
++ struct t4 *wc = pci_get_drvdata(pdev);
++ if (!wc)
++ return;
++
++ _t4_remove_one(wc);
++}
++
++
++static DEFINE_PCI_DEVICE_TABLE(t4_pci_tbl) =
++{
++
++/*ALLO:*/
++ { 0x1d21, 0x1280, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&allo1280p2 },
++ { 0x1d21, 0x1240, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&allo1240p2 },
++ { 0x1d21, 0x1241, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&allo1241p2 },
++ { 0x1d21, 0x1220, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&allo1220p2 },
++ { 0x1d21, 0x1210, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&allo1210p2 },
++ { 0, }
++};
++
++static void _t4_shutdown(struct pci_dev *pdev)
++{
++ struct t4 *wc = pci_get_drvdata(pdev);
++ t4_hardware_stop(wc);
++}
++
++static int t4_suspend(struct pci_dev *pdev, pm_message_t state)
++{
++ return -ENOSYS;
++}
++
++static struct pci_driver t4_driver = {
++ .name = "allo4xxp",
++ .probe = t4_init_one_retry,
++ .remove = __devexit_p(t4_remove_one),
++ .shutdown = _t4_shutdown,
++ .suspend = t4_suspend,
++ .id_table = t4_pci_tbl,
++};
++
++static int __init t4_init(void)
++{
++ int i;
++ int res;
++
++ if (-1 != t1e1override) {
++ pr_info("'t1e1override' module parameter is deprecated. "
++ "Please use 'default_linemode' instead.\n");
++ }
++
++ res = dahdi_pci_module(&t4_driver);
++ if (res)
++ return -ENODEV;
++
++ /* If we're ignoring the rotary switch settings, then we've already
++ * registered in the context of .probe */
++ if (!ignore_rotary) {
++
++ /* 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 "allo4xxp: Ident of first card is not zero (%d)\n",
++ cards[0]->order);
++ }
++
++ for (i = 0; cards[i]; i++) {
++ /* warn the user of duplicate ident values it is
++ * probably unintended */
++ if (debug && res < 15 && cards[i+1] &&
++ cards[res]->order == cards[i+1]->order) {
++ printk(KERN_NOTICE "allo4xxp: Duplicate ident "
++ "value found (%d)\n", cards[i]->order);
++ }
++ res = t4_launch(cards[i]);
++ if (res) {
++ int j;
++ for (j = 0; j < i; ++j)
++ _t4_remove_one(cards[j]);
++ break;
++ }
++ }
++ }
++ return res;
++}
++
++static void __exit t4_cleanup(void)
++{
++ pci_unregister_driver(&t4_driver);
++}
++
++MODULE_AUTHOR("Allo Incorporated <support@allo.com>");
++MODULE_DESCRIPTION("Wildcard Dual/Quad/Octal-port Digital Card Driver");
++MODULE_ALIAS("allo2xxp");
++MODULE_LICENSE("GPL v2");
++
++module_param(debug, int, 0600);
++module_param(noburst, int, 0600);
++module_param(timingcable, int, 0600);
++module_param(t1e1override, int, 0400);
++module_param(default_linemode, charp, S_IRUGO);
++MODULE_PARM_DESC(default_linemode, "\"auto\"(default), \"e1\", \"t1\", "
++ "or \"j1\". \"auto\" will use the value from any hardware "
++ "jumpers.");
++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);
++module_param(ignore_rotary, int, 0400);
++MODULE_PARM_DESC(ignore_rotary, "Set to > 0 to ignore the rotary switch when " \
++ "registering with DAHDI.");
++
++#ifdef VPM_SUPPORT
++module_param(vpmsupport, int, 0600);
++module_param(vpmdtmfsupport, int, 0600);
++#endif
++
++MODULE_DEVICE_TABLE(pci, t4_pci_tbl);
++
++module_init(t4_init);
++module_exit(t4_cleanup);
+diff -Nur dahdi-linux-2.10.0.1/drivers/dahdi/allo4xxp/vpm450m.c dahdi-linux-2.10.0.1-allo/drivers/dahdi/allo4xxp/vpm450m.c
+--- dahdi-linux-2.10.0.1/drivers/dahdi/allo4xxp/vpm450m.c 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-allo/drivers/dahdi/allo4xxp/vpm450m.c 2015-02-10 15:01:15.889462151 +0100
+@@ -0,0 +1,624 @@
++/*
++ * Copyright (C) 2005-2012 Digium, Inc.
++ *
++ * Mark Spencer <markster@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.
++ */
++
++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
++
++#include <linux/slab.h>
++#include <linux/vmalloc.h>
++#include <linux/string.h>
++#include <linux/time.h>
++#include <linux/version.h>
++
++#include <dahdi/kernel.h>
++#include <stdbool.h>
++
++#include "vpm450m.h"
++#include <oct612x.h>
++
++static int wct4xxp_oct612x_write(struct oct612x_context *context,
++ u32 address, u16 value)
++{
++ struct t4 *wc = dev_get_drvdata(context->dev);
++ oct_set_reg(wc, address, value);
++ return 0;
++}
++
++static int wct4xxp_oct612x_read(struct oct612x_context *context, u32 address,
++ u16 *value)
++{
++ struct t4 *wc = dev_get_drvdata(context->dev);
++ *value = (u16)oct_get_reg(wc, address);
++ return 0;
++}
++
++static int wct4xxp_oct612x_write_smear(struct oct612x_context *context,
++ u32 address, u16 value, size_t count)
++{
++ struct t4 *wc = dev_get_drvdata(context->dev);
++ int i;
++ for (i = 0; i < count; ++i)
++ oct_set_reg(wc, address + (i << 1), value);
++ return 0;
++}
++
++static int wct4xxp_oct612x_write_burst(struct oct612x_context *context,
++ u32 address, const u16 *buffer,
++ size_t count)
++{
++ struct t4 *wc = dev_get_drvdata(context->dev);
++ int i;
++ for (i = 0; i < count; ++i)
++ oct_set_reg(wc, address + (i << 1), buffer[i]);
++ return 0;
++}
++
++static int wct4xxp_oct612x_read_burst(struct oct612x_context *context,
++ u32 address, u16 *buffer, size_t count)
++{
++ struct t4 *wc = dev_get_drvdata(context->dev);
++ int i;
++ for (i = 0; i < count; ++i)
++ buffer[i] = oct_get_reg(wc, address + (i << 1));
++ return 0;
++}
++
++static const struct oct612x_ops wct4xxp_oct612x_ops = {
++ .write = wct4xxp_oct612x_write,
++ .read = wct4xxp_oct612x_read,
++ .write_smear = wct4xxp_oct612x_write_smear,
++ .write_burst = wct4xxp_oct612x_write_burst,
++ .read_burst = wct4xxp_oct612x_read_burst,
++};
++
++#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;
++ struct oct612x_context context;
++ UINT32 aulEchoChanHndl[256];
++ int chanflags[256];
++ int ecmode[256];
++ int numchans;
++};
++
++#define FLAG_DTMF (1 << 0)
++#define FLAG_MUTE (1 << 1)
++#define FLAG_ECHO (1 << 2)
++#define FLAG_ALAW (1 << 3)
++
++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,
++};
++
++void vpm450m_set_alaw_companding(struct vpm450m *vpm450m, int channel,
++ bool alaw)
++{
++ tOCT6100_CHANNEL_MODIFY *modify;
++ UINT32 ulResult;
++ UINT32 law_to_use = (alaw) ? cOCT6100_PCM_A_LAW :
++ cOCT6100_PCM_U_LAW;
++
++ if (channel >= ARRAY_SIZE(vpm450m->chanflags)) {
++ pr_err("Channel out of bounds in %s\n", __func__);
++ return;
++ }
++ /* If we're already in this companding mode, no need to do anything. */
++ if (alaw == ((vpm450m->chanflags[channel] & FLAG_ALAW) > 0))
++ return;
++
++ modify = kzalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_ATOMIC);
++ if (!modify) {
++ pr_notice("Unable to allocate memory for setec!\n");
++ return;
++ }
++
++ Oct6100ChannelModifyDef(modify);
++ modify->ulChannelHndl = vpm450m->aulEchoChanHndl[channel];
++ modify->fTdmConfigModified = TRUE;
++ modify->TdmConfig.ulSinPcmLaw = law_to_use;
++ modify->TdmConfig.ulRinPcmLaw = law_to_use;
++ modify->TdmConfig.ulSoutPcmLaw = law_to_use;
++ modify->TdmConfig.ulRoutPcmLaw = law_to_use;
++ ulResult = Oct6100ChannelModify(vpm450m->pApiInstance, modify);
++ if (ulResult != GENERIC_OK) {
++ pr_notice("Failed to apply echo can changes on channel %d %d %08x!\n",
++ vpm450m->aulEchoChanHndl[channel], channel, ulResult);
++ } else {
++ if (debug) {
++ pr_info("Changed companding on channel %d to %s.\n",
++ channel, (alaw) ? "alaw" : "ulaw");
++ }
++ if (alaw)
++ vpm450m->chanflags[channel] |= FLAG_ALAW;
++ else
++ vpm450m->chanflags[channel] &= ~(FLAG_ALAW);
++ }
++ kfree(modify);
++}
++
++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 "wct4xxp: 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 %08x!\n", channel, ulResult);
++ } 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;
++
++ if (channel >= ARRAY_SIZE(vpm450m->chanflags)) {
++ pr_err("Channel out of bounds in %s\n", __func__);
++ return;
++ }
++
++ modify = kmalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_KERNEL);
++ if (!modify) {
++ printk(KERN_NOTICE "wct4xxp: 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 (channel >= ARRAY_SIZE(vpm450m->chanflags)) {
++ pr_err("Channel out of bounds in %s\n", __func__);
++ return;
++ }
++
++ 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;
++
++ Oct6100EventGetToneDef(&tonesearch);
++ tonesearch.pToneEvent = &tonefound;
++ tonesearch.ulMaxToneEvent = 1;
++ 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(struct device *device)
++{
++ struct oct612x_context context;
++ UINT32 ulResult;
++
++ tOCT6100_API_GET_CAPACITY_PINS CapacityPins;
++
++ context.dev = device;
++ context.ops = &wct4xxp_oct612x_ops;
++
++ Oct6100ApiGetCapacityPinsDef(&CapacityPins);
++ CapacityPins.pProcessContext = &context;
++ 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(struct device *device, int *isalaw,
++ int numspans, const struct firmware *firmware)
++{
++ tOCT6100_CHIP_OPEN *ChipOpen;
++ tOCT6100_GET_INSTANCE_SIZE InstanceSize;
++ tOCT6100_CHANNEL_OPEN *ChannelOpen;
++ UINT32 ulResult;
++ const unsigned int mask = (8 == numspans) ? 0x7 : 0x3;
++ unsigned int sout_stream, rout_stream;
++ struct vpm450m *vpm450m;
++ int x,y,law;
++
++ if (!(vpm450m = kmalloc(sizeof(struct vpm450m), GFP_KERNEL)))
++ return NULL;
++
++ memset(vpm450m, 0, sizeof(struct vpm450m));
++ vpm450m->context.dev = device;
++ vpm450m->context.ops = &wct4xxp_oct612x_ops;
++
++ 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 < ARRAY_SIZE(vpm450m->ecmode); 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 = &vpm450m->context;
++
++ 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->aulTdmStreamFreqs[0] = cOCT6100_TDM_STREAM_FREQ_8MHZ;
++ ChipOpen->ulMaxFlexibleConfParticipants = 0;
++ ChipOpen->ulMaxConfBridges = 0;
++ ChipOpen->ulMaxRemoteDebugSessions = 0;
++ ChipOpen->fEnableChannelRecording = FALSE;
++ ChipOpen->ulSoftToneEventsBufSize = 64;
++
++ if (vpm450m->numchans <= 128) {
++ ChipOpen->ulMaxTdmStreams = 4;
++ ChipOpen->ulTdmSampling = cOCT6100_TDM_SAMPLE_AT_FALLING_EDGE;
++ } else {
++ ChipOpen->ulMaxTdmStreams = 32;
++ ChipOpen->fEnableFastH100Mode = TRUE;
++ ChipOpen->ulTdmSampling = cOCT6100_TDM_SAMPLE_AT_RISING_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;
++ }
++
++ ulResult = Oct6100ChipOpen(vpm450m->pApiInstance, ChipOpen);
++ if (ulResult != cOCT6100_ERR_OK) {
++ printk(KERN_NOTICE "Failed to open chip, code %08x!\n", ulResult);
++ vfree(vpm450m->pApiInstance);
++ kfree(vpm450m);
++ kfree(ChipOpen);
++ kfree(ChannelOpen);
++ return NULL;
++ }
++
++ sout_stream = (8 == numspans) ? 29 : 2;
++ rout_stream = (8 == numspans) ? 24 : 3;
++
++ for (x = 0; x < ((8 == numspans) ? 256 : 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 (( numspans > 2) || ((x & 0x03) <numspans)) { /*allo: 1port support*/
++ /* span timeslots are interleaved 12341234...
++ * therefore, the lower 2 bits tell us which span this
++ * timeslot/channel
++ */
++ if (isalaw[x & mask]) {
++ law = cOCT6100_PCM_A_LAW;
++ vpm450m->chanflags[x] |= FLAG_ALAW;
++ } else {
++ law = cOCT6100_PCM_U_LAW;
++ vpm450m->chanflags[x] &= ~(FLAG_ALAW);
++ }
++ 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 = sout_stream;
++ ChannelOpen->TdmConfig.ulSoutTimeslot = x;
++#if 1
++ ChannelOpen->TdmConfig.ulRoutPcmLaw = law;
++ ChannelOpen->TdmConfig.ulRoutStream = rout_stream;
++ ChannelOpen->TdmConfig.ulRoutTimeslot = x;
++#endif
++ 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 %x!\n", x, ulResult);
++ continue;
++ }
++ for (y = 0; y < ARRAY_SIZE(tones); 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);
++ }
++ }
++ }
++
++ 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 -Nur dahdi-linux-2.10.0.1/drivers/dahdi/allo4xxp/vpm450m.h dahdi-linux-2.10.0.1-allo/drivers/dahdi/allo4xxp/vpm450m.h
+--- dahdi-linux-2.10.0.1/drivers/dahdi/allo4xxp/vpm450m.h 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-allo/drivers/dahdi/allo4xxp/vpm450m.h 2015-02-10 15:01:15.889462151 +0100
+@@ -0,0 +1,49 @@
++/*
++ * Copyright (C) 2005-2006 Digium, Inc.
++ *
++ * Mark Spencer <markster@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 t4;
++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(struct device *device, int *isalaw,
++ int numspans, const struct firmware *firmware);
++unsigned int get_vpm450m_capacity(struct device *device);
++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);
++void vpm450m_set_alaw_companding(struct vpm450m *vpm450m,
++ int channel, bool alaw);
++
++extern int debug;
++
++#endif
+diff -Nur dahdi-linux-2.10.0.1/drivers/dahdi/allo4xxp/wct4xxp-diag.c dahdi-linux-2.10.0.1-allo/drivers/dahdi/allo4xxp/wct4xxp-diag.c
+--- dahdi-linux-2.10.0.1/drivers/dahdi/allo4xxp/wct4xxp-diag.c 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-allo/drivers/dahdi/allo4xxp/wct4xxp-diag.c 2015-02-10 15:01:15.892795252 +0100
+@@ -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 "wct4xxp.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: wct4xxp-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 -Nur dahdi-linux-2.10.0.1/drivers/dahdi/allo4xxp/wct4xxp.h dahdi-linux-2.10.0.1-allo/drivers/dahdi/allo4xxp/wct4xxp.h
+--- dahdi-linux-2.10.0.1/drivers/dahdi/allo4xxp/wct4xxp.h 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-allo/drivers/dahdi/allo4xxp/wct4xxp.h 2015-02-10 15:01:15.892795252 +0100
+@@ -0,0 +1,144 @@
++/*
++ * Wildcard T400P FXS Interface Driver for DAHDI Telephony interface
++ *
++ * Written by Mark Spencer <markster@linux-support.net>
++ *
++ * 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/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
++/* OctalFALC Only */
++#define FRMR_CMR4 0x41
++#define FRMR_CMR5 0x42
++#define FRMR_CMR6 0x43
++#define FRMR_GPC2 0x8a
++/* End Octal */
++#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
++
++/* CIS - Octal falc bits */
++#define FRMR_CIS_GIS5 0x10
++#define FRMR_CIS_GIS6 0x20
++#define FRMR_CIS_GIS7 0x40
++#define FRMR_CIS_GIS8 0x80
++
++#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];
++};
++
++struct t4_reg {
++ unsigned int reg;
++ unsigned int val;
++};
++
++#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)
++#define WCT4_GET_REG _IOW(DAHDI_CODE, 61, struct t4_reg)
++#define WCT4_SET_REG _IOW(DAHDI_CODE, 62, struct t4_reg)
++
+diff -Nur dahdi-linux-2.10.0.1/drivers/dahdi/firmware/Makefile dahdi-linux-2.10.0.1-allo/drivers/dahdi/firmware/Makefile
+--- dahdi-linux-2.10.0.1/drivers/dahdi/firmware/Makefile 2014-09-22 20:40:19.000000000 +0200
++++ dahdi-linux-2.10.0.1-allo/drivers/dahdi/firmware/Makefile 2015-02-10 15:01:15.892795252 +0100
+@@ -41,10 +41,11 @@
+ A4B_VERSION:=b0019
+
+ FIRMWARE_URL:=http://downloads.digium.com/pub/telephony/firmware/releases
++FIRMWARE_URL_ALLO:=http://www.allo.com/firmware/pri-card-second-gen
+
+ ALL_FIRMWARE=FIRMWARE-OCT6114-032 FIRMWARE-OCT6114-064 FIRMWARE-OCT6114-128 FIRMWARE-OCT6114-256
+ ALL_FIRMWARE+=FIRMWARE-TC400M FIRMWARE-HX8 FIRMWARE-VPMOCT032 FIRMWARE-TE820 FIRMWARE-TE133 FIRMWARE-TE134
+-ALL_FIRMWARE+=FIRMWARE-A8A FIRMWARE-A8B FIRMWARE-A4A FIRMWARE-A4B FIRMWARE-TE435 FIRMWARE-TE436
++ALL_FIRMWARE+=FIRMWARE-A8A FIRMWARE-A8B FIRMWARE-A4A FIRMWARE-A4B FIRMWARE-TE435 FIRMWARE-TE436 FIRMWARE-allo-1PPCIe FIRMWARE-allo-2PPCIe FIRMWARE-allo-4PPCI FIRMWARE-allo-4PPCIe FIRMWARE-allo-8PPCIe
+
+ # Firmware files should use the naming convention: dahdi-fw-<base name>-<sub name>-<version> or dahdi-fw-<base name>-<version>
+ # First example: dahdi-fw-oct6114-064-1.05.01
+@@ -69,6 +70,12 @@
+ FIRMWARE:=$(FIRMWARE:FIRMWARE-A8B=dahdi-fw-a8a-$(A8A_VERSION).tar.gz)
+ FIRMWARE:=$(FIRMWARE:FIRMWARE-A4A=dahdi-fw-a4b-$(A4B_VERSION).tar.gz)
+ FIRMWARE:=$(FIRMWARE:FIRMWARE-A4B=dahdi-fw-a4a-$(A4A_VERSION).tar.gz)
++FIRMWARE:=$(FIRMWARE:FIRMWARE-allo-1PPCIe=allo-dahdi-fw-2aCP1e.tar.gz)
++FIRMWARE:=$(FIRMWARE:FIRMWARE-allo-2PPCIe=allo-dahdi-fw-2aCP2e.tar.gz)
++FIRMWARE:=$(FIRMWARE:FIRMWARE-allo-4PPCI=allo-dahdi-fw-2aCP4.tar.gz)
++FIRMWARE:=$(FIRMWARE:FIRMWARE-allo-4PPCIe=allo-dahdi-fw-2aCP4e.tar.gz)
++FIRMWARE:=$(FIRMWARE:FIRMWARE-allo-8PPCIe=allo-dahdi-fw-2aCP8e.tar.gz)
++
+
+ FWLOADERS:=dahdi-fwload-vpmadt032-$(VPMADT032_VERSION).tar.gz
+
+@@ -100,6 +107,9 @@
+ rm -f dahdi-fw-*.tar.gz
+ rm -f dahdi-fwload-*.tar.gz
+ rm -f make_firmware_object
++ rm -f allo-dahdi-fw-*.bin
++ rm -f allo-dahdi-fw-*.tar.gz
++
+
+ # Clean up anything we built
+ clean:
+@@ -113,6 +123,15 @@
+ if test ! -f $@; then exit 1; fi; \
+ fi
+
++allo-dahdi-fw%.tar.gz:
++ @if ( [ "$(HOTPLUG_FIRMWARE)" = "no" ] ) || ( [ -d $(DESTDIR)/usr/lib/hotplug/firmware ] && ! [ -f $(DESTDIR)/usr/lib/hotplug/firmware/.$(subst .tar.gz,,$*) ] ) || ( [ -d $(DESTDIR)/lib/firmware ] && ! [ -f $(DESTDIR)/lib/firmware/.$(subst .tar.gz,,$*) ] ); then \
++ echo "Attempting to download $@"; \
++ if test ! -f $@; then $(DOWNLOAD) $(WGET_ARGS) $(FIRMWARE_URL_ALLO)/$@; fi; \
++ if test ! -f $@; then exit 1; fi; \
++ (cat $@ | gzip -d | tar --no-same-owner -xf -) \
++ fi
++
++
+ firmware-loaders: $(FWLOADERS)
+
+ .PHONY: dahdi-fwload-vpmadt032-$(VPMADT032_VERSION).tar.gz
+@@ -145,16 +164,90 @@
+ @$(call RUN_INST,dahdi-fw-a8b,$(A8B_VERSION))
+ @$(call RUN_INST,dahdi-fw-a4a,$(A4A_VERSION))
+ @$(call RUN_INST,dahdi-fw-a4b,$(A4B_VERSION))
++######################ALLO#################################
++ifeq ($(shell if ( [ -f $(DESTDIR)/usr/lib/hotplug/firmware/.allo-dahdi-fw-2aCP1e ] ) && ( [ -f $(DESTDIR)/lib/firmware/.allo-dahdi-fw-2aCP1e ] ); then echo "no"; else echo "yes"; fi),yes)
++
++ @echo "Installing ALLO firmwares to hotplug firmware directories"
++ @install -m 644 allo-dahdi-fw-2aCP1e.bin $(DESTDIR)/usr/lib/hotplug/firmware
++ @rm -rf $(DESTDIR)/usr/lib/hotplug/firmware/.allo-dahdi-fw-2aCP1e
++ @touch $(DESTDIR)/usr/lib/hotplug/firmware/.allo-dahdi-fw-2aCP1e
++ @install -m 644 allo-dahdi-fw-2aCP1e.bin $(DESTDIR)/lib/firmware
++ @rm -rf $(DESTDIR)/lib/firmware/.allo-dahdi-fw-2aCP1e
++ @touch $(DESTDIR)/lib/firmware/.allo-dahdi-fw-2aCP1e
++else
++ @echo "Firmware of ALLO card allo-dahdi-fw-2aCP1e.bin is already installed with required version"
++endif
++
++
++ifeq ($(shell if ( [ -f $(DESTDIR)/usr/lib/hotplug/firmware/.allo-dahdi-fw-2aCP2e ] ) && ( [ -f $(DESTDIR)/lib/firmware/.allo-dahdi-fw-2aCP2e ] ); then echo "no"; else echo "yes"; fi),yes)
++
++ @echo "Installing ALLO firmwares to hotplug firmware directories"
++ @install -m 644 allo-dahdi-fw-2aCP2e.bin $(DESTDIR)/usr/lib/hotplug/firmware
++ @rm -rf $(DESTDIR)/usr/lib/hotplug/firmware/.allo-dahdi-fw-2aCP2e
++ @touch $(DESTDIR)/usr/lib/hotplug/firmware/.allo-dahdi-fw-2aCP2e
++ @install -m 644 allo-dahdi-fw-2aCP2e.bin $(DESTDIR)/lib/firmware
++ @rm -rf $(DESTDIR)/lib/firmware/.allo-dahdi-fw-2aCP2e
++ @touch $(DESTDIR)/lib/firmware/.allo-dahdi-fw-2aCP2e
++else
++ @echo "Firmware of ALLO cards allo-dahdi-fw-2aCP2e.bin is already installed with required version"
++endif
++
++
++ifeq ($(shell if ( [ -f $(DESTDIR)/usr/lib/hotplug/firmware/.allo-dahdi-fw-2aCP4 ] ) && ( [ -f $(DESTDIR)/lib/firmware/.allo-dahdi-fw-2aCP4 ] ); then echo "no"; else echo "yes"; fi),yes)
++
++ @echo "Installing ALLO firmwares to hotplug firmware directories"
++ @install -m 644 allo-dahdi-fw-2aCP4.bin $(DESTDIR)/usr/lib/hotplug/firmware
++ @rm -rf $(DESTDIR)/usr/lib/hotplug/firmware/.allo-dahdi-fw-2aCP4
++ @touch $(DESTDIR)/usr/lib/hotplug/firmware/.allo-dahdi-fw-2aCP4
++ @install -m 644 allo-dahdi-fw-2aCP4.bin $(DESTDIR)/lib/firmware
++ @rm -rf $(DESTDIR)/lib/firmware/.allo-dahdi-fw-2aCP4
++ @touch $(DESTDIR)/lib/firmware/.allo-dahdi-fw-2aCP4
++else
++ @echo "Firmware of ALLO cards allo-dahdi-fw-2aCP4.bin is already installed with required version"
++endif
++
++ifeq ($(shell if ( [ -f $(DESTDIR)/usr/lib/hotplug/firmware/.allo-dahdi-fw-2aCP4e ] ) && ( [ -f $(DESTDIR)/lib/firmware/.allo-dahdi-fw-2aCP4e ] ); then echo "no"; else echo "yes"; fi),yes)
++
++ @echo "Installing ALLO firmwares to hotplug firmware directories"
++ @install -m 644 allo-dahdi-fw-2aCP4e.bin $(DESTDIR)/usr/lib/hotplug/firmware
++ @rm -rf $(DESTDIR)/usr/lib/hotplug/firmware/.allo-dahdi-fw-2aCP4e
++ @touch $(DESTDIR)/usr/lib/hotplug/firmware/.allo-dahdi-fw-2aCP4e
++ @install -m 644 allo-dahdi-fw-2aCP4e.bin $(DESTDIR)/lib/firmware
++ @rm -rf $(DESTDIR)/lib/firmware/.allo-dahdi-fw-2aCP4e
++ @touch $(DESTDIR)/lib/firmware/.allo-dahdi-fw-2aCP4e
++else
++ @echo "Firmware of ALLO cards allo-dahdi-fw-2aCP4e.bin is already installed with required version"
++endif
++
++
++ifeq ($(shell if ( [ -f $(DESTDIR)/usr/lib/hotplug/firmware/.allo-dahdi-fw-2aCP8e ] ) && ( [ -f $(DESTDIR)/lib/firmware/.allo-dahdi-fw-2aCP8e ] ); then echo "no"; else echo "yes"; fi),yes)
++
++ @echo "Installing ALLO firmwares to hotplug firmware directories"
++ @install -m 644 allo-dahdi-fw-2aCP8e.bin $(DESTDIR)/usr/lib/hotplug/firmware
++ @rm -rf $(DESTDIR)/usr/lib/hotplug/firmware/.allo-dahdi-fw-2aCP8e*
++ @touch $(DESTDIR)/usr/lib/hotplug/firmware/.allo-dahdi-fw-2aCP8e
++ @install -m 644 allo-dahdi-fw-2aCP8e.bin $(DESTDIR)/lib/firmware
++ @rm -rf $(DESTDIR)/lib/firmware/.allo-dahdi-fw-2aCP8e*
++ @touch $(DESTDIR)/lib/firmware/.allo-dahdi-fw-2aCP8e
++else
++ @echo "Firmware of ALLO cards allo-dahdi-fw-2aCP8e.bin is already installed with required version"
++endif
++
++#########################END######################################
+
+ # Uninstall any installed dahdi firmware images from hotplug firmware directories
+ hotplug-uninstall:
+ if [ -d $(DESTDIR)/usr/lib/hotplug/firmware ]; then \
+ rm -f $(DESTDIR)/usr/lib/hotplug/firmware/dahdi-fw-*.bin; \
+ rm -f $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw*; \
++ rm -f $(DESTDIR)/usr/lib/hotplug/firmware/allo-dahdi-fw-*.bin; \
++ rm -f $(DESTDIR)/usr/lib/hotplug/firmware/.allo-dahdi-fw*; \
+ fi
+ if [ -d $(DESTDIR)/lib/firmware ]; then \
+ rm -f $(DESTDIR)/lib/firmware/dahdi-fw-*.bin; \
+ rm -f $(DESTDIR)/lib/firmware/.dahdi-fw*; \
++ rm -f $(DESTDIR)/lib/firmware/allo-dahdi-fw-*.bin; \
++ rm -f $(DESTDIR)/lib/firmware/.allo-dahdi-fw*; \
+ fi
+
+ make_firmware_object: make_firmware_object.in ../dahdi-base.o
diff --git a/dahdi-linux-2.10.1-openvox-1.patch b/dahdi-linux-2.10.1-openvox-1.patch
new file mode 100644
index 000000000000..4a8fcf98f892
--- /dev/null
+++ b/dahdi-linux-2.10.1-openvox-1.patch
@@ -0,0 +1,6228 @@
+--- dahdi-linux-2.10.0.1/drivers/dahdi/Kbuild 2014-09-22 20:40:19.000000000 +0200
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/Kbuild 2015-02-10 15:12:07.233961480 +0100
+@@ -14,6 +14,10 @@
+ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTE12XP) += wcte12xp/
+ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTE13XP) += wcte13xp.o
+
++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OPVXD115) += opvxd115/
++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OPVXA1200) += opvxa1200/
++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OPVXA24XX) += opvxa24xx/
++
+ wcte13xp-objs := wcte13xp-base.o wcxb_spi.o wcxb.o wcxb_flash.o
+ CFLAGS_wcte13xp-base.o += -I$(src)/oct612x -I$(src)/oct612x/include -I$(src)/oct612x/octdeviceapi -I$(src)/oct612x/octdeviceapi/oct6100api
+ ifeq ($(HOTPLUG_FIRMWARE),yes)
+--- dahdi-linux-2.10.0.1/drivers/dahdi/Kconfig 2014-09-22 20:40:19.000000000 +0200
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/Kconfig 2015-02-10 15:03:42.355884929 +0100
+@@ -291,4 +291,34 @@
+
+ If unsure, say Y.
+
++config DAHDI_OPVXD115
++ tristate "OpenVox TE410P Quad-T1/E1 PCI"
++ depends on DAHDI && PCI
++ default DAHDI
++ ---help---
++ To compile this driver as a module, choose M here: the
++ module will be called opvxd115.
++
++ If unsure, say Y.
++
++config DAHDI_OPVXA1200
++ tristate "OpenVox A1200P FXS/FXO Interface Driver"
++ depends on DAHDI && PCI
++ default DAHDI
++ ---help---
++ To compile this driver as a module, choose M here: the
++ module will be called opvxa1200.
++
++ If unsure, say Y.
++
++config DAHDI_OPVXA24XX
++ tristate "OpenVox A24xx FXS/FXO Interface Driver"
++ depends on DAHDI && PCI
++ default DAHDI
++ ---help---
++ To compile this driver as a module, choose M here: the
++ module will be called opvxa24xx.
++
++ If unsure, say Y.
++
+ source "drivers/dahdi/xpp/Kconfig"
+--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa1200/Kbuild 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa1200/Kbuild 2015-02-10 14:19:03.000000000 +0100
+@@ -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
+--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa1200/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa1200/Makefile 2015-02-10 14:19:03.000000000 +0100
+@@ -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
+--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa1200/base.c 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa1200/base.c 2015-02-10 14:19:03.000000000 +0100
+@@ -0,0 +1,3117 @@
++/*
++ * OpenVox A1200P FXS/FXO Interface Driver for DAHDI Telephony interface
++ *
++ * Written by MiaoLin<miaolin@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
++ *
++ * Rev 1.4.12.6 5/15/2011 Miaolin
++ * use write dma to generate irq.
++ * add parameter watchdma allow reset dma when it is not correctly started.
++ * add delay after reset
++ * change reset time to 1 sec.
++ */
++
++#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 int watchdma=0;
++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)
++ if (ints & 0x01)
++ /* 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*/)
++ if(ints & 0x01)
++ /* 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;
++ }
++
++ if(watchdma) {
++ if( (x!=0) && (x!=3) ) {
++ printk("Bad re-sync %d, resetting...\n", x);
++ outb(0x0f, wc->ioaddr + WC_CNTL);
++ for(x=0; x<1000*1000*1000; x++);
++ outb(0x01, wc->ioaddr + WC_CNTL);
++ outb(0x01, wc->ioaddr + WC_OPER);
++ wc->lastchan=-1;
++ return;
++ }
++ }
++
++ 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;
++ unsigned int tsi;
++ 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);
++
++ if((wc->fwversion&0x0f)==6)
++ {
++ tsi = (3-(card%4))*8 + (card/4) *128;
++ wctdm_setreg(wc, card, 34, tsi&0xff);
++ wctdm_setreg(wc, card, 35, (tsi>>8)&0x3);
++ wctdm_setreg(wc, card, 36, (tsi+1)&0xff);
++ wctdm_setreg(wc, card, 37, ((tsi+1)>>8)&0x3);
++ }
++ else
++ {
++ /* 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, tsi;
++ int fxsmode=0;
++// int tmpcard;
++
++ /* 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);
++ if((wc->fwversion&0x0f)==6)
++ {
++ tsi = (3-(card%4))* 8 + (card/4) *128;
++ wctdm_setreg(wc, card, 2, (tsi)&0xff); // Tx Start count low byte 0
++ wctdm_setreg(wc, card, 3, ((tsi)>>8)&0x3); // Tx Start count high byte 0
++ wctdm_setreg(wc, card, 4, (tsi+1)&0xff); // Rx Start count low byte 0
++ wctdm_setreg(wc, card, 5, ((tsi+1)>>8)&0x3); // Rx Start count high byte 0
++ }
++ else
++ {
++ // 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;
++
++ wc->ddev = dahdi_create_device(); //Dennis
++ if (!wc->ddev)
++ return -ENOMEM;
++
++ /* 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, //Dennis
++ "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"; //Dennis
++ 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;
++// init_waitqueue_head(&wc->span.maintq);
++ 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 span with Dahdi\n");
++ 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(0x00, wc->ioaddr + WC_CNTL);
++ /* Wait for 1 second */
++ origjiffies = jiffies;
++ while(1)
++ {
++ if ((jiffies-origjiffies) >= (HZ))
++ break;;
++ }
++ outb(0x01, wc->ioaddr + WC_CNTL);
++ origjiffies = jiffies;
++ while(1)
++ {
++ if ((jiffies-origjiffies) >= (HZ/2))
++ 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);
++ outb(0x33, 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); //Dennis
++ 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->ddev->location); //Dennis
++ dahdi_free_device(wc->ddev);
++ 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_param(watchdma, int, 0600);
++
++MODULE_DESCRIPTION("OpenVox A1200 Driver");
++MODULE_AUTHOR("MiaoLin <miaolin@openvox.com.cn>");
++MODULE_LICENSE("GPL v2");
++
++module_init(wctdm_init);
++module_exit(wctdm_cleanup);
++
+--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa24xx/Kbuild 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa24xx/Kbuild 2015-02-10 14:19:03.000000000 +0100
+@@ -0,0 +1,40 @@
++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OPVXA24XX) += opvxa24xx.o
++
++FIRM_DIR := ../firmware
++
++EXTRA_CFLAGS += -I$(src)/.. -I$(src)/../oct612x/ $(shell $(src)/../oct612x/octasic-helper cflags $(src)/../oct612x) -Wno-undef
++
++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
++
++ifeq ($(HOTPLUG_FIRMWARE),yes)
++ EXTRA_CFLAGS+=-DHOTPLUG_FIRMWARE
++endif
++
++opvxa24xx-objs := private.o a24xx.o si321x.o si3050.o ec3000.o callerid.o busydetect.o base.o $(shell $(src)/../oct612x/octasic-helper objects ../oct612x)
++
++ifneq ($(HOTPLUG_FIRMWARE),yes)
++opvxa24xx-objs += $(FIRM_DIR)/dahdi-fw-oct6114-032.o $(FIRM_DIR)/dahdi-fw-oct6114-064.o $(FIRM_DIR)/dahdi-fw-oct6114-128.o
++endif
++
++
++$(obj)/$(FIRM_DIR)/dahdi-fw-oct6114-032.o: $(obj)/base.o
++ $(MAKE) -C $(obj)/$(FIRM_DIR) dahdi-fw-oct6114-032.o
++
++$(obj)/$(FIRM_DIR)/dahdi-fw-oct6114-064.o: $(obj)/base.o
++ $(MAKE) -C $(obj)/$(FIRM_DIR) dahdi-fw-oct6114-064.o
++
++$(obj)/$(FIRM_DIR)/dahdi-fw-oct6114-128.o: $(obj)/base.o
++ $(MAKE) -C $(obj)/$(FIRM_DIR) dahdi-fw-oct6114-128.o
++
+--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa24xx/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa24xx/Makefile 2015-02-10 14:19:03.000000000 +0100
+@@ -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
+--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa24xx/a24xx.c 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa24xx/a24xx.c 2015-02-10 14:19:03.000000000 +0100
+@@ -0,0 +1,286 @@
++/*
++ * OpenVox A24xx FXS/FXO Interface Driver for Zapata Telephony interface
++ *
++ * Written by MiaoLin<miaolin@openvox.cn>
++ * $Id: a24xx.c 185 2010-12-14 07:58:51Z yangshugang $
++
++ * 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.
++ *
++ */
++
++#include <linux/spinlock.h>
++#include <linux/jiffies.h>
++
++#include <dahdi/kernel.h>
++
++#include "base.h"
++#include "proslic.h"
++
++#define BIT_EC_PRESENT (1<<0)
++
++#define VPM_DEFAULT_DTMFTHRESHOLD 1000
++
++/* indirect_resg */
++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",0x0ff4},
++{33,20,"PWR_ALARM_Q3Q4",0x6e7e},
++{34,21,"PWR_ALARM_Q5Q6",0x0ff4},
++{35,22,"LOOP_CLOSURE_FILTER",0x8000},
++{36,23,"RING_TRIP_FILTER",0x0320},
++{37,24,"TERM_LP_POLE_Q1Q2",0x0012},
++{38,25,"TERM_LP_POLE_Q3Q4",0x0012},
++{39,26,"TERM_LP_POLE_Q5Q6",0x0012},
++{40,27,"CM_BIAS_RINGING",0x0C00},
++{41,64,"DCDC_MIN_V",0x0C00},
++{42,255,"DCDC_XTRA",0x1000},
++{43,66,"LOOP_CLOSE_TRES_LOW",0x1000},
++};
++
++void __a24xx_wait_just_a_bit(int foo)
++{
++ long newjiffies;
++ newjiffies = jiffies + foo;
++ while(jiffies < newjiffies);
++}
++
++void __a24xx_setcard(void *wc_dev, int card)
++{
++ struct a24xx_dev *dev = (struct a24xx_dev *)(wc_dev);
++ if (dev->curcard != card) {
++ __opvx_a24xx_setcard(dev->mem32, card);
++ dev->curcard = card;
++ }
++}
++
++inline void __a24xx_spi_setreg(struct a24xx_dev *wc_dev, int card, unsigned char reg, unsigned char value)
++{
++ __opvx_a24xx_spi_setreg((void *)wc_dev, wc_dev->mem32, card, wc_dev->modtype[card], reg, value, __a24xx_setcard);
++}
++
++inline unsigned char __a24xx_spi_getreg(struct a24xx_dev *wc_dev, int card, unsigned char reg)
++{
++ return __opvx_a24xx_spi_getreg((void *)wc_dev, wc_dev->mem32, card, wc_dev->modtype[card], reg, __a24xx_setcard);
++}
++
++int __a24xx_malloc_chunk(struct a24xx_dev *wc_dev,unsigned int frq)
++{
++ __opvx_a24xx_set_chunk(&(wc_dev->readchunk), &(wc_dev->writechunk),frq);
++ wc_dev->readdma = wc_dev->writedma + frq * DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS) * 2;
++
++ 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;
++}
++
++int __a24xx_wait_access(struct a24xx_dev *wc_dev, 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 = __a24xx_spi_getreg(wc_dev, card, I_STATUS);
++ if (!data) {
++ return 0;
++ }
++ }
++
++ if(count > (MAX-1)) {
++ printk(" ##### Loop error (%02x) #####\n", data);
++ }
++
++ return 0;
++}
++
++int __a24xx_proslic_setreg_indirect(struct a24xx_dev *wc_dev, int card, unsigned char address, unsigned short data)
++{
++ int res = -1;
++ /* Translate 3215 addresses */
++ if (wc_dev->flags[card] & FLAG_3215) {
++ address = __translate_3215(address);
++ if (address == 255) {
++ return 0;
++ }
++ }
++ if(!__a24xx_wait_access(wc_dev, card)) {
++ __a24xx_spi_setreg(wc_dev, card, IDA_LO,(unsigned char)(data & 0xFF));
++ __a24xx_spi_setreg(wc_dev, card, IDA_HI,(unsigned char)((data & 0xFF00)>>8));
++ __a24xx_spi_setreg(wc_dev, card, IAA,address);
++ res = 0;
++ }
++
++ return res;
++}
++
++int __a24xx_proslic_getreg_indirect(struct a24xx_dev *wc_dev, int card, unsigned char address)
++{
++ int res = -1;
++ char *p=NULL;
++ /* Translate 3215 addresses */
++ if (wc_dev->flags[card] & FLAG_3215) {
++ address = __translate_3215(address);
++ if (address == 255) {
++ return 0;
++ }
++ }
++ if (!__a24xx_wait_access(wc_dev, card)) {
++ __a24xx_spi_setreg(wc_dev, card, IAA, address);
++ if (!__a24xx_wait_access(wc_dev, card)) {
++ unsigned char data1, data2;
++ data1 = __a24xx_spi_getreg(wc_dev, card, IDA_LO);
++ data2 = __a24xx_spi_getreg(wc_dev, card, IDA_HI);
++ res = data1 | (data2 << 8);
++ } else {
++ p = "Failed to wait inside\n";
++ }
++ } else {
++ p = "failed to wait\n";
++ }
++ if (p) {
++ printk("%s\n", p);
++ }
++ return res;
++}
++
++void __a24xx_vpm_setpresent(struct a24xx_dev *wc_dev)
++{
++ wc_dev->vpm = BIT_EC_PRESENT;
++ if ( (wc_dev->fwversion&0xffff) > 0x3)
++ __opvx_a24xx_vpm_setpresent_v2(wc_dev->mem32);
++ else
++ __opvx_a24xx_vpm_setpresent(wc_dev->mem32);
++ printk("OpenVox VPM: Present and operational servicing %d span(s)\n", 1/*wc_dev->numspans*/);
++}
++
++void a24xx_spi_setreg(struct a24xx_dev *wc_dev, int card, unsigned char reg, unsigned char value)
++{
++ unsigned long flags;
++ spin_lock_irqsave(&wc_dev->lock, flags);
++ __a24xx_spi_setreg(wc_dev, card, reg, value);
++ spin_unlock_irqrestore(&wc_dev->lock, flags);
++}
++
++unsigned char a24xx_spi_getreg(struct a24xx_dev *wc_dev, int card, unsigned char reg)
++{
++ unsigned long flags;
++ unsigned char ret;
++ spin_lock_irqsave(&wc_dev->lock, flags);
++ ret = __a24xx_spi_getreg(wc_dev, card, reg);
++ spin_unlock_irqrestore(&wc_dev->lock, flags);
++ return ret;
++}
++
++static inline unsigned int a24xx_oct_in(struct a24xx_dev *wc_dev, const unsigned int addr)
++{
++ unsigned long flags;
++ unsigned int ret;
++
++ spin_lock_irqsave(&wc_dev->lock, flags);
++ if ( (wc_dev->fwversion&0xffff) > 0x3)
++ ret = __opvx_a24xx_oct_in_v2(wc_dev->mem32, addr);
++ else
++ ret = __opvx_a24xx_oct_in(wc_dev->mem32, addr);
++ spin_unlock_irqrestore(&wc_dev->lock, flags);
++
++ return ret;
++}
++
++static inline void a24xx_oct_out(struct a24xx_dev *wc_dev, const unsigned int addr, const unsigned int value)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&wc_dev->lock, flags);
++ if ( (wc_dev->fwversion&0xffff) > 0x3)
++ __opvx_a24xx_oct_out_v2(wc_dev->mem32, addr, value);
++ else
++ __opvx_a24xx_oct_out(wc_dev->mem32, addr, value);
++ spin_unlock_irqrestore(&wc_dev->lock, flags);
++}
++
++void oct_set_reg(void *data, unsigned int reg, unsigned int val)
++{
++ struct a24xx_dev *wc_dev = data;
++ a24xx_oct_out(wc_dev, reg, val);
++}
++
++unsigned int oct_get_reg(void *data, unsigned int reg)
++{
++ struct a24xx_dev *wc_dev = data;
++ unsigned int ret;
++ ret = a24xx_oct_in(wc_dev, reg);
++ return ret;
++}
++
++void a24xx_reset_spi(struct a24xx_dev *wc_dev, int card)
++{
++ unsigned long flags;
++ spin_lock_irqsave(&wc_dev->lock, flags);
++ __opvx_a24xx_reset_spi(wc_dev, card, __a24xx_setcard);
++ spin_unlock_irqrestore(&wc_dev->lock, flags);
++}
++
+--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa24xx/base.c 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa24xx/base.c 2015-02-10 14:19:03.000000000 +0100
+@@ -0,0 +1,2679 @@
++/*
++ * OpenVox A24xx FXS/FXO Interface Driver for Zapata Telephony interface
++ *
++ * Written by MiaoLin<miaolin@openvox.cn>
++ * $Id: base.c 359 2011-04-06 06:11:39Z yangshugang $
++
++ * 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, modified from opvxa1200.c
++ * Rev 0.20 add octasic echo canceller support.
++ *
++ */
++
++#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 <asm/io.h>
++#include <linux/delay.h>
++#include <linux/moduleparam.h>
++#include <linux/sched.h>
++
++#ifdef TEST_LOG_INCOME_VOICE
++#include <linux/string.h>
++#include <asm/uaccess.h> /* get_fs(), set_fs(), KERNEL_DS */
++#include <linux/file.h> /* fput() */
++#endif
++
++#include <dahdi/kernel.h>
++#include <dahdi/wctdm_user.h>
++#include "fxo_modes.h"
++
++#include "proslic.h"
++#include "base.h"
++#include "ec3000.h"
++#include "busydetect.h"
++
++/* module parameters */
++int debug;
++int spi_cmd=0x223;
++
++int reversepolarity = 0;
++int alawoverride = 0;
++int fxotxgain = 0;
++int fxorxgain = 0;
++int fastpickup = 0;
++int fxofullscale = 0; /* fxo full scale tx/rx, register 30, acim */
++int fwringdetect = 0;
++int _opermode = 0;
++int cidsupport = 1; /* default support caller id analysis */
++int cidbuflen = 3000; /* in msec, default 3000 */
++int cidtimeout = 6*1000; /* in msec, default 6000 */
++int fxstxgain = 0;
++int fxsrxgain = 0;
++int fxshonormode = 0;
++int boostringer = 0;
++int fastringer = 0;
++int lowpower = 0;
++int loopcurrent = 20;
++int timingonly = 0;
++int fixedtimepolarity=0; /* time delay in ms when send polarity after rise edge of 1st ring.*/
++int ringdebounce = DEFAULT_RING_DEBOUNCE;
++unsigned int battdebounce;
++unsigned int battalarm;
++unsigned int battthresh;
++int robust = 0;
++#ifdef VPM_SUPPORT
++/* ec debug */
++int ec_debug = 0;
++int vpmsupport = 1;
++#endif
++
++#define MS_PER_HOOKCHECK (1)
++
++int ec_spans = 4;
++
++static char* A2410P_Name = "A2410P";
++static struct a24xx_desc a2410a = { "OpenVox A2410", 0 };
++static char* A1610P_Name = "A1610P";
++static struct a24xx_desc a1610a = { "OpenVox A1610", 0 };
++static char* A810P_Name = "A810P";
++static struct a24xx_desc a810a = { "OpenVox A810", 0 };
++static struct a24xx *ifaces[WC_MAX_IFACES];
++static char *opermode = "FCC";
++
++/* If set to auto, vpmdtmfsupport is enabled for VPM400M and disabled for VPM450M */
++static int vpmdtmfsupport = -1; /* -1=auto, 0=disabled, 1=enabled*/
++static unsigned int ms_per_irq = 1; // 1/2/4/8/16 allowed
++static unsigned int max_iface_index = 0;
++static unsigned int irq_stub = 0;
++
++#define POLARITY_XOR(card) ( \
++ (reversepolarity != 0) ^ (wc_dev->mod[(card)].fxs.reversepolarity != 0) ^ \
++ (wc_dev->mod[(card)].fxs.vmwi_lrev != 0) ^\
++ ((wc_dev->mod[(card)].fxs.vmwisetting.vmwi_type & DAHDI_VMWI_HVAC) != 0)\
++ )
++
++static const struct dahdi_echocan_features vpm_ec_features = {
++ .NLP_automatic = 1,
++ .CED_tx_detect = 1,
++ .CED_rx_detect = 1,
++};
++
++static void a24xx_release(struct a24xx *wc);
++static void a24xx_echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec);
++
++static const struct dahdi_echocan_ops vpm_ec_ops = {
++// .name = "OPENVOX VPM",
++ .echocan_free = a24xx_echocan_free,
++};
++
++static int a24xx_echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp,
++ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec)
++{
++ struct a24xx *wc = chan->pvt;
++ struct a24xx_dev *wc_dev = &wc->dev;
++
++ int channel;
++ const struct dahdi_echocan_ops *ops;
++ const struct dahdi_echocan_features *features;
++
++
++ if (!wc_dev->vpm) {
++ return -ENODEV;
++ }
++
++ if (chan->span->offset >= ec_spans) {
++ return -ENODEV;
++ }
++
++ if (wc_dev->vpm_ec) {
++ ops = &vpm_ec_ops;
++ features = &vpm_ec_features;
++ }
++
++ if (ecp->param_count > 0) {
++ printk(KERN_WARNING "OpenVox VPM echo canceller does not support parameters; failing request\n");
++ return -EINVAL;
++ }
++
++ *ec = wc->ec[chan->chanpos - 1];
++ (*ec)->ops = ops;
++ (*ec)->features = *features;
++
++ channel = chan->chanpos;
++
++ if (wc_dev->vpm_ec) {
++ channel -= 1;
++ if(ec_debug) {
++ printk(KERN_DEBUG "echocan: Card is %d, Channel is %d, offset is %d, length %d\n",
++ wc_dev->pos, chan->chanpos, channel, ecp->tap_length);
++ }
++ opvx_vpm_setec(wc_dev->vpm_ec, channel, ecp->tap_length);
++ msleep(10);
++ }
++
++ return 0;
++}
++
++static void a24xx_echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec)
++{
++ struct a24xx *wc = chan->pvt;
++ struct a24xx_dev *wc_dev = &wc->dev;
++ int channel;
++
++ memset(ec, 0, sizeof(*ec));
++
++ channel = chan->chanpos;
++
++ if (wc_dev->vpm_ec) {
++ channel -= 1;
++ if (ec_debug)
++ printk(KERN_DEBUG "echocan: Card is %d, Channel is %d, Span is %d, offset is %d length 0\n",
++ wc_dev->pos, chan->chanpos, chan->span->offset, channel);
++ opvx_vpm_setec(wc_dev->vpm_ec, channel, 0);
++ }
++}
++
++static void free_wc(struct a24xx *wc)
++{
++ int i;
++ struct a24xx_dev *wc_dev = &wc->dev;
++
++ for (i = 0; i < wc_dev->max_cards; i++) {
++ if (wc->ec[i]) {
++ kfree(wc->ec[i]);
++ wc->ec[i] = NULL;
++ }
++ }
++ kfree(wc);
++ wc = NULL;
++}
++
++static void a24xx_vpm_init(struct a24xx *wc)
++{
++ int x;
++ struct a24xx_dev *wc_dev = &wc->dev;
++ int laws[4] = { 0, };
++ int res;
++
++ 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 ( (wc_dev->fwversion&0xffff) > 0x3)
++ res = __opvx_a24xx_check_vpm_v2(wc_dev->mem32);
++ else
++ res = __opvx_a24xx_check_vpm(wc_dev->mem32);
++
++ if (res < 0) {
++ if (-1 == res) {
++ printk("OpenVox VPM: Support Disabled\n");
++ } else if (-2 == res) {
++ printk("OpenVox VPM: Not Present\n");
++ }
++ return;
++ }
++
++ /* Setup alaw vs ulaw rules */
++ for (x = 0;x < 1; x++) {
++ if(wc->span.deflaw == DAHDI_LAW_ALAW) {
++ laws[x] = 1;
++ }
++ else {
++ laws[x] = 0;
++ }
++ }
++
++ vpm_capacity = opvx_vpm_getcapacity(wc_dev);
++ printk("OpenVox VPM: echo cancellation supports %d channels\n", vpm_capacity);
++
++ switch (vpm_capacity) {
++ case 32:
++#if defined(HOTPLUG_FIRMWARE)
++ if ((request_firmware(&firmware, oct032_firmware, &wc_dev->dev->dev) != 0) ||
++ !firmware) {
++ printk("OpenVox VPM: 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_zaptel_fw_oct6114_032_bin_size;
++#endif
++ break;
++ case 64:
++#if defined(HOTPLUG_FIRMWARE)
++ if ((request_firmware(&firmware, oct064_firmware, &wc_dev->dev->dev) != 0) ||
++ !firmware) {
++ printk("OpenVox VPM: 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->dev) != 0) ||
++ !firmware) {
++ printk("OpenVox VPM: 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:
++ printk("Unsupported channel capacity found on VPM module (%d).\n", vpm_capacity);
++ return;
++ }
++
++ if (!(wc_dev->vpm_ec = opvx_vpm_init(wc_dev, laws, /*wc_dev->numspans*/1, firmware))) {
++ printk("OpenVox VPM: Failed to initialize\n");
++ if (firmware != &embedded_firmware) {
++ release_firmware(firmware);
++ }
++ return;
++ }
++
++ if (firmware != &embedded_firmware) {
++ release_firmware(firmware);
++ }
++
++ if (vpmdtmfsupport == -1) {
++ printk("OpenVox VPM: hardware DTMF disabled.\n");
++ vpmdtmfsupport = 0;
++ }
++
++ __a24xx_vpm_setpresent(wc_dev);
++
++}
++
++#ifdef AUDIO_RINGCHECK
++static inline void ring_check(struct a24xx *wc, int card)
++{
++ int x;
++ short sample;
++ struct a24xx_dev *wc_dev = &wc->dev;
++
++ if (wc_dev->modtype[card] != MOD_TYPE_FXO) {
++ return;
++ }
++ wc_dev->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_dev->mod[card].fxo.peg != 1)) {
++ if (debug > 1) {
++ printk(KERN_DEBUG "High peg!\n");
++ }
++ if ((wc_dev->mod[card].fxo.pegtimer < PEGTIME) && (wc_dev->mod[card].fxo.pegtimer > MINPEGTIME)) {
++ wc_dev->mod[card].fxo.pegcount++;
++ }
++ wc_dev->mod[card].fxo.pegtimer = 0;
++ wc_dev->mod[card].fxo.peg = 1;
++ } else if ((sample < -10000) && (wc_dev->mod[card].fxo.peg != -1)) {
++ if (debug > 1) {
++ printk(KERN_DEBUG "Low peg!\n");
++ }
++ if ((wc_dev->mod[card].fxo.pegtimer < (PEGTIME >> 2)) && (wc_dev->mod[card].fxo.pegtimer > (MINPEGTIME >> 2))) {
++ wc_dev->mod[card].fxo.pegcount++;
++ }
++ wc_dev->mod[card].fxo.pegtimer = 0;
++ wc_dev->mod[card].fxo.peg = -1;
++ }
++ }
++ if (wc_dev->mod[card].fxo.pegtimer > PEGTIME) {
++ /* Reset pegcount if our timer expires */
++ wc_dev->mod[card].fxo.pegcount = 0;
++ }
++ /* Decrement debouncer if appropriate */
++ if (wc_dev->mod[card].fxo.ringdebounce) {
++ wc_dev->mod[card].fxo.ringdebounce--;
++ }
++ if (!wc_dev->mod[card].fxo.offhook && !wc_dev->mod[card].fxo.ringdebounce) {
++ if (!wc_dev->mod[card].fxo.ring && (wc_dev->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_dev->mod[card].fxo.offhook) {
++ dahdi_hooksig(&wc->chans[card], DAHDI_RXSIG_RING);
++ }
++ wc_dev->mod[card].fxo.ring = 1;
++ }
++ if (wc_dev->mod[card].fxo.ring && !wc_dev->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_dev->mod[card].fxo.ring = 0;
++ }
++ }
++}
++#endif
++
++#if 0
++static int a24xx_hardware_init(struct a24xx_dev *wc_dev, char *sigcap)
++{
++ unsigned int x;
++ unsigned char ch;
++
++ /* Signal Reset */
++ if ( (wc_dev->fwversion&0xffff) > 0x3)
++ __opvx_a24xx_reset_modules_v2(wc_dev->mem32, __a24xx_wait_just_a_bit, HZ); // reset again;
++ else
++ __opvx_a24xx_reset_modules(wc_dev->mem32, __a24xx_wait_just_a_bit, HZ); // reset again;
++
++ /* Check OpenVox version */
++ printk("OpenVox %s version: %01x.%01x\n", wc_dev->card_name, wc_dev->fwversion>>16, wc_dev->fwversion&0xffff);
++
++ /* Clear interrupts */
++ __opvx_a24xx_clear_irqs(wc_dev->mem32);
++
++ /* Wait 1/4 of a second more */
++ //opvx_wait_just_a_bit(HZ/4);
++
++ /* test cards exist and type at first */
++ for(x=0; x<wc_dev->max_cards; x+=CARDS_PER_MODULE) {
++ __a24xx_setcard(wc_dev, x);
++ wc_dev->modtype[x] = MOD_TYPE_FXO;
++ ch = __a24xx_spi_getreg(wc_dev, x, 2); /* read register 2, 3050 return 0x3, 3210 return 0x0 */
++ if(0x03 == ch) {
++ wc_dev->modtype[x+1] = MOD_TYPE_FXO;
++ wc_dev->modtype[x+2] = MOD_TYPE_FXO;
++ wc_dev->modtype[x+3] = MOD_TYPE_FXO;
++ if(debug) {
++ printk("module %d is a FXO\n", x);
++ }
++ } else {
++ wc_dev->modtype[x] = MOD_TYPE_FXS;
++ wc_dev->modtype[x+1] = MOD_TYPE_FXS;
++ wc_dev->modtype[x+2] = MOD_TYPE_FXS;
++ wc_dev->modtype[x+3] = MOD_TYPE_FXS;
++ if(debug) {
++ printk("module %d is a FXS or Not Installed\n", x);
++ }
++ }
++ }
++
++ if ( (wc_dev->fwversion&0xffff) > 0x3)
++ __opvx_a24xx_reset_modules_v2(wc_dev->mem32, __a24xx_wait_just_a_bit, HZ); // reset again;
++ else
++ __opvx_a24xx_reset_modules(wc_dev->mem32, __a24xx_wait_just_a_bit, HZ); // reset again;
++
++ for (x = 0; x < wc_dev->max_cards/*MAX_NUM_CARDS*/; x++) {
++ int sane=0,ret=0,readi=0;
++
++ if( (x%4) == 0 && wc_dev->modtype[x]==MOD_TYPE_FXS) { /* set 3215 to daisy chain mode */
++ __a24xx_setcard(wc_dev, x);
++ __opvx_a24xx_write_8bits(wc_dev->mem32, 0x00);
++ __opvx_a24xx_write_8bits(wc_dev->mem32, 0x80);
++ }
++
++#if 1
++ touch_softlockup_watchdog(); // avoid showing CPU softlock message
++
++ /* Init with Auto Calibration */
++ if (!(ret=si321x_init_proslic(wc_dev, x, 0, 0, sane))) {
++ wc_dev->cardflag |= (1 << x);
++ if (debug) {
++ readi = a24xx_spi_getreg(wc_dev,x,LOOP_I_LIMIT);
++ printk("Proslic module %d loop current is %dmA\n",x,
++ ((readi*3)+20));
++ }
++ printk("Module %d: Installed -- AUTO FXS/DPO\n",x);
++ } else {
++ if(ret!=-2) {
++ sane=1;
++
++ printk("Init ProSlic with Manual Calibration \n");
++ /* Init with Manual Calibration */
++ if (!si321x_init_proslic(wc_dev, x, 0, 1, sane)) {
++ wc_dev->cardflag |= (1 << x);
++ if (debug) {
++ readi = a24xx_spi_getreg(wc_dev,x,LOOP_I_LIMIT);
++ printk("Proslic module %d loop current is %dmA\n",x,
++ ((readi*3)+20));
++ }
++ printk("Module %d: Installed -- MANUAL FXS\n",x);
++ } else {
++ printk("Module %d: FAILED FXS (%s)\n", x, fxshonormode ? fxo_modes[_opermode].name : "FCC");
++ sigcap[x] = 1;
++ }
++ } else if (!(ret = si3050_init_voicedaa(wc_dev, x, 0, 0, sane))) {
++ wc_dev->cardflag |= (1 << x);
++ printk("Module %d: Installed -- AUTO FXO (%s mode)\n",x, fxo_modes[_opermode].name);
++ } else
++ printk("Module %d: Not installed\n", x);
++ }
++#endif
++ }
++
++ /* Return error if nothing initialized okay. */
++ if (!wc_dev->cardflag && !timingonly) {
++ return -1;
++ }
++
++ return 0;
++}
++#endif
++
++static int a24xx_hardware_init_all(struct a24xx_dev *wc_dev, char *sigcap)
++{
++ unsigned int x;
++ unsigned char ch;
++ int sane=0,ret=0;
++ int flag=0,tmp_flag=0,blank_flag=0;
++
++ /* Signal Reset */
++ if ( (wc_dev->fwversion&0xffff) > 0x3)
++ __opvx_a24xx_reset_modules_v2(wc_dev->mem32, __a24xx_wait_just_a_bit, HZ); // reset again;
++ else
++ __opvx_a24xx_reset_modules(wc_dev->mem32, __a24xx_wait_just_a_bit, HZ); // reset again;
++
++ /* Check OpenVox version */
++ printk("OpenVox %s version: %01x.%01x\n", wc_dev->card_name, wc_dev->fwversion>>16, wc_dev->fwversion&0xffff);
++
++ /* Clear interrupts */
++ __opvx_a24xx_clear_irqs(wc_dev->mem32);
++
++ /* Wait 1/4 of a second more */
++ //opvx_wait_just_a_bit(HZ/4);
++
++ /* test cards exist and type at first */
++ for(x=0; x<wc_dev->max_cards; x+=CARDS_PER_MODULE) {
++ __a24xx_setcard(wc_dev, x);
++ wc_dev->modtype[x] = MOD_TYPE_FXO;
++ ch = __a24xx_spi_getreg(wc_dev, x, 2); /* read register 2, 3050 return 0x3, 3210 return 0x0 */
++ if(0x03 == ch) {
++ wc_dev->modtype[x+1] = MOD_TYPE_FXO;
++ wc_dev->modtype[x+2] = MOD_TYPE_FXO;
++ wc_dev->modtype[x+3] = MOD_TYPE_FXO;
++ if(debug) {
++ printk("module %d is a FXO\n", x);
++ }
++ } else {
++ wc_dev->modtype[x] = MOD_TYPE_FXS;
++ wc_dev->modtype[x+1] = MOD_TYPE_FXS;
++ wc_dev->modtype[x+2] = MOD_TYPE_FXS;
++ wc_dev->modtype[x+3] = MOD_TYPE_FXS;
++ if(debug) {
++ printk("module %d is a FXS or Not Installed\n", x);
++ }
++ }
++ }
++
++ if ( (wc_dev->fwversion&0xffff) > 0x3)
++ __opvx_a24xx_reset_modules_v2(wc_dev->mem32, __a24xx_wait_just_a_bit, HZ); // reset again;
++ else
++ __opvx_a24xx_reset_modules(wc_dev->mem32, __a24xx_wait_just_a_bit, HZ); // reset again;
++
++ for (x = 0; x < wc_dev->max_cards/*MAX_NUM_CARDS*/; x++) {
++ sane=1,ret=0;
++ touch_softlockup_watchdog(); // avoid showing CPU softlock message
++
++ /* Init with Auto Calibration */
++ if(wc_dev->modtype[x] == MOD_TYPE_FXO){
++ if (!(ret = si3050_init_voicedaa(wc_dev, x, 0, 0, sane))) {
++ wc_dev->cardflag |= (1 << x);
++ }
++ }
++ }
++
++ /*initial FXS modules*/
++ for (x = 0; x < wc_dev->max_cards/*MAX_NUM_CARDS*/; x++) {
++ if(wc_dev->modtype[x] == MOD_TYPE_FXS){
++ flag |=(1 << x);
++ }
++ }
++
++ touch_softlockup_watchdog();
++ if((tmp_flag=si321x_init_proslic_all(wc_dev,flag,0,0,0,&blank_flag))>0)
++ {
++ tmp_flag=si321x_init_proslic_all(wc_dev,tmp_flag,0,1,1,NULL);
++ }
++ flag &=~blank_flag;
++ flag &=~tmp_flag;
++ wc_dev->cardflag |=flag;
++
++ for (x = 0; x < wc_dev->max_cards; x++){
++ if(wc_dev->cardflag & (1<<x)){
++ spin_lock_init(&wc_dev->mod[x].fxs.lasttxhooklock);
++ if(wc_dev->modtype[x] == MOD_TYPE_FXS){
++ printk("Module %d: Installed -- AUTO FXS/DPO\n",x);
++ }else
++ printk("Module %d: Installed -- AUTO FXO (%s mode)\n",x, fxo_modes[_opermode].name);
++ }else{
++ if(tmp_flag & (1 << x)){
++ printk("Module %d: FAILED FXS (%s)\n", x, fxshonormode ? fxo_modes[_opermode].name : "FCC");
++ sigcap[x] = 1; //************
++ }
++ else
++ printk("Module %d: Not installed\n", x);
++ }
++ }
++
++ /* Return error if nothing initialized okay. */
++ if (!wc_dev->cardflag && !timingonly) {
++ return -1;
++ }
++
++ return 0;
++}
++
++static void callerid_ring_on_deal(struct a24xx *wc, int card)
++{
++ struct a24xx_dev *wc_dev = &wc->dev;
++
++ if(wc_dev->cid_state[card] == CID_STATE_IDLE) {
++ reset_parser_variable_from_chan_num(wc->span.spanno, wc->chans[card]->channo);
++ if (is_ring_delay_operation(wc->span.spanno, wc->chans[card]->channo)) {
++ wc_dev->cid_state[card] = CID_STATE_RING_DELAY;
++ } else {
++ wc_dev->cid_state[card] = CID_STATE_RING_ON;
++ wc_dev->cid_history_clone_cnt[card] = cidbuflen;
++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING);
++ }
++ } else if(wc_dev->cid_state[card] == CID_STATE_RING_DELAY) {
++ wc_dev->cid_state[card] = CID_STATE_RING_ON;
++ wc_dev->cid_history_clone_cnt[card] = cidbuflen;
++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING);
++ } else {
++ if (wc_dev->cid_state[card] != CID_STATE_WAIT_RING_FINISH) {
++ set_signal_unknown_from_chan_num(wc->span.spanno, wc->chans[card]->channo);
++ wc_dev->cid_state[card] = CID_STATE_WAIT_RING_FINISH;
++ }
++ wc_dev->cid_history_clone_cnt[card] = cidtimeout;
++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING);
++ }
++}
++
++static void callerid_ring_off_deal(struct a24xx *wc, int card)
++{
++ struct a24xx_dev *wc_dev = &wc->dev;
++
++ if(wc_dev->cid_state[card] == CID_STATE_RING_ON) {
++ wc_dev->cid_state[card] = CID_STATE_RING_OFF;
++ }
++
++ if(wc_dev->cid_state[card] != CID_STATE_RING_DELAY) {
++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK);
++ }
++}
++
++static void a24xx_voicedaa_check_hook(struct a24xx *wc, int card)
++{
++#define MS_PER_CHECK_HOOK 16
++#ifndef AUDIO_RINGCHECK
++ unsigned char res;
++#endif
++ signed char b;
++ int errors = 0;
++ struct a24xx_dev *wc_dev = &wc->dev;
++ struct fxo *fxo = &wc_dev->mod[card].fxo;
++
++ /* Try to track issues that plague slot one FXO's */
++ b = wc_dev->reg0shadow[card];
++ if ((b & 0x2) || !(b & 0x8)) {
++ /* Not good -- don't look at anything else */
++ if (debug) {
++ printk(KERN_DEBUG "Errors (%02x) on card %d!\n", b, card + 1);
++ }
++ errors++;
++ }
++ b &= 0x9b;
++ if (fxo->offhook) {
++ if (b != 0x9) {
++ a24xx_spi_setreg(wc_dev, card, 5, 0x9);
++ }
++ } else {
++ if (b != 0x8) {
++ a24xx_spi_setreg(wc_dev, card, 5, 0x8);
++ }
++ }
++ if (errors) {
++ return;
++ }
++
++ if (!fxo->offhook) {
++ if (fwringdetect) {
++ res = wc_dev->reg0shadow[card] & 0x60;
++ if (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(cidsupport) {
++ callerid_ring_on_deal(wc, card);
++ } 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(cidsupport) {
++ callerid_ring_off_deal(wc, card);
++ } 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_dev->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(cidsupport) {
++ callerid_ring_on_deal(wc, card);
++ } 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(cidsupport) {
++ callerid_ring_off_deal(wc, card);
++ } 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_dev->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_dev->ohdebounce && wc_dev->offhook) {
++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK);
++ if (debug) {
++ printk(KERN_DEBUG "Signalled On Hook\n");
++ }
++#ifdef ZERO_BATT_RING
++ wc_dev->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_dev->onhook) {
++ wc_dev->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) {
++ if (cidsupport) {
++ set_cidstart_desc_from_chan_num(wc->span.spanno, wc->chans[card]->channo, wc_dev->cid_state[card]);
++ if (is_callerid_disable(wc->span.spanno, wc->chans[card]->channo)) {
++ dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY);
++ }
++ } else {
++ dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY);
++ }
++ }
++ fxo->polarity = fxo->lastpol;
++ }
++ }
++ }
++#undef MS_PER_CHECK_HOOK
++}
++
++
++static void a24xx_post_initialize(struct a24xx *wc)
++{
++ int x;
++ struct a24xx_dev *wc_dev = &wc->dev;
++
++ /* Finalize signalling */
++ for (x = 0; x < wc_dev->max_cards/*MAX_NUM_CARDS*/; x++) {
++ if (wc_dev->cardflag & (1 << x)) {
++ if (wc_dev->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 void a24xx_proslic_check_hook(struct a24xx *wc, int card)
++{
++ char res;
++ int hook;
++ struct a24xx_dev *wc_dev = &wc->dev;
++
++ /* For some reason we have to debounce the
++ hook detector. */
++
++ res = wc_dev->reg0shadow[card];
++ hook = (res & 1);
++ if (hook != wc_dev->mod[card].fxs.lastrxhook) {
++ /* Reset the debounce (must be multiple of 4ms) */
++ wc_dev->mod[card].fxs.debounce = 8 * (4 * 8);
++#if 0
++ printk(KERN_DEBUG "Resetting debounce card %d hook %d, %d\n", card, hook, wc_dev->mod[card].fxs.debounce);
++#endif
++ } else {
++ if (wc_dev->mod[card].fxs.debounce > 0) {
++ wc_dev->mod[card].fxs.debounce-= 16 * DAHDI_CHUNKSIZE;
++#if 0
++ printk(KERN_DEBUG "Sustaining hook %d, %d\n", hook, wc_dev->mod[card].fxs.debounce);
++#endif
++ if (!wc_dev->mod[card].fxs.debounce) {
++#if 0
++ printk(KERN_DEBUG "Counted down debounce, newhook: %d...\n", hook);
++#endif
++ wc_dev->mod[card].fxs.debouncehook = hook;
++ }
++ if (!wc_dev->mod[card].fxs.oldrxhook && wc_dev->mod[card].fxs.debouncehook) {
++ /* Off hook */
++ if (debug) {
++ printk(KERN_DEBUG "opvxa24xx: Card %d Going off hook\n", card);
++ }
++ switch (wc_dev->mod[card].fxs.lasttxhook) {
++ case SLIC_LF_RINGING: /* Ringing */
++ case SLIC_LF_OHTRAN_FWD: /* Forward On Hook Transfer */
++ case SLIC_LF_OHTRAN_REV: /* Reverse On Hook Transfer */
++ /* just detected OffHook, during Ringing or OnHookTransfer */
++ wc_dev->mod[card].fxs.idletxhookstate = POLARITY_XOR(card) ?
++ SLIC_LF_ACTIVE_REV :
++ SLIC_LF_ACTIVE_FWD;
++ break;
++ }
++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK);
++ if (robust) {
++ si321x_init_proslic(wc_dev, card, 1, 0, 1);
++ }
++ wc_dev->mod[card].fxs.oldrxhook = 1;
++
++ } else if (wc_dev->mod[card].fxs.oldrxhook && !wc_dev->mod[card].fxs.debouncehook) {
++ /* On hook */
++ if (debug) {
++ printk(KERN_DEBUG "opvxa24xx: Card %d Going on hook\n", card);
++ }
++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK);
++ wc_dev->mod[card].fxs.oldrxhook = 0;
++ }
++ }
++ }
++ wc_dev->mod[card].fxs.lastrxhook = hook;
++}
++
++static void a24xx_transmit(struct a24xx *wc,unsigned int order)
++{
++ int x, y, pos;
++ volatile unsigned char *txbuf;
++ struct a24xx_dev *wc_dev = &wc->dev;
++
++ __opvx_a24xx_transmit(wc_dev->mem32, wc_dev->writechunk, &txbuf,ms_per_irq,order);
++ //printk("ints is %d\n", ints);
++ /* Calculate Transmission */
++
++ for (y=0;y<DAHDI_CHUNKSIZE;y++) {
++#ifdef __BIG_ENDIAN
++ // operation pending...
++#else
++ for (x=0;x<wc_dev->max_cards;x++) {
++ pos = y * MAX_NUM_CARDS + x;
++ txbuf[pos] = wc->chans[x]->writechunk[y];
++ }
++#endif
++ }
++}
++
++static void a24xx_receive(struct a24xx *wc,unsigned int order)
++{
++ int x, y;
++ volatile unsigned char *rxbuf;
++ struct a24xx_dev *wc_dev = &wc->dev;
++
++ __opvx_a24xx_receive(wc_dev->mem32, wc_dev->readchunk, &rxbuf ,ms_per_irq,order);
++ for (x=0;x<DAHDI_CHUNKSIZE;x++) {
++#ifdef __BIG_ENDIAN
++ // operation pending...
++#else
++ for (y=0;y<wc_dev->max_cards/*MAX_NUM_CARDS*/;y++) {
++ if (wc_dev->cardflag & (1 << y)) {
++ wc->chans[y]->readchunk[x] = rxbuf[MAX_NUM_CARDS * x + y];
++ }
++#ifdef TEST_LOG_INCOME_VOICE
++ wc_dev->voc_buf[y][wc_dev->voc_ptr[y]] = rxbuf[MAX_NUM_CARDS * x + y];
++ wc_dev->voc_ptr[y]++;
++ if(wc_dev->voc_ptr[y] >= voc_buffer_size) {
++ wc_dev->voc_ptr[y] = 0;
++ }
++#endif
++ }
++#endif
++ }
++
++#if 0
++ if( (ints&0x0f) == 0x0f) { // dump buffer every 16 times;
++ printk("dump 0x%x\n", ints);
++ for(x=0; x<48; x++) {
++ printk("0x%02x, ", (int)rxbuf[x]);
++ //if( 15== (x%15) )
++ }
++ printk("\n");
++ }
++#endif
++
++ if(cidsupport) {
++ parser_callerid_process(wc, cidbuflen, cidtimeout);
++ }
++
++#ifdef AUDIO_RINGCHECK
++ for (x=0;x<wc_dev->max_cards;x++) {
++ ring_check(wc, x);
++ }
++#endif
++ /* XXX We're wasting 8 taps. We should get closer :( */
++ for (x = 0; x < wc_dev->max_cards/*MAX_NUM_CARDS*/; x++) {
++ if (wc_dev->cardflag & (1 << x)) {
++ dahdi_ec_chunk(wc->chans[x], wc->chans[x]->readchunk, wc->chans[x]->writechunk);
++ }
++ }
++}
++
++#if 0
++DAHDI_IRQ_HANDLER(a24xx_interrupt)
++{
++ unsigned int ints;
++ int x, y, z,order;
++ int mode;
++ struct a24xx *wc = dev_id;
++ struct a24xx_dev *wc_dev = &wc->dev;
++
++ struct fxs * fxs ;
++
++ ints = __opvx_a24xx_get_irqstatus(wc_dev->mem32);
++
++ if (!ints)
++ return IRQ_NONE;
++
++ __opvx_a24xx_set_irqstatus(wc_dev->mem32, ints); // clear interrupt register.
++
++ for (x=0;x<wc_dev->max_cards; x++) {
++ if (wc_dev->cardflag & (1 << x) && (wc_dev->modtype[x] == MOD_TYPE_FXS)) {
++
++ fxs = &wc_dev->mod[x].fxs;
++
++ if (SLIC_LF_RINGING == fxs->lasttxhook && !fxs->neonringing) {
++ /* RINGing, prepare for OHT */
++ fxs->ohttimer = OHT_TIMER << 3;
++ /* OHT mode when idle */
++ fxs->idletxhookstate = POLARITY_XOR(x) ? SLIC_LF_OHTRAN_REV :
++ SLIC_LF_OHTRAN_FWD;
++ } else if (fxs->ohttimer) {
++ /* check if still OnHook */
++ if (!fxs->oldrxhook) {
++ fxs->ohttimer -= DAHDI_CHUNKSIZE;
++ if (fxs->ohttimer)
++ continue;
++
++ /* Switch to active */
++ fxs->idletxhookstate = POLARITY_XOR(x) ? SLIC_LF_ACTIVE_REV :
++ SLIC_LF_ACTIVE_FWD;
++ /* if currently OHT */
++ if ((fxs->lasttxhook == SLIC_LF_OHTRAN_FWD) || (fxs->lasttxhook == SLIC_LF_OHTRAN_REV)) {
++ if (fxs->vmwi_hvac) {
++ /* force idle polarity Forward if ringing */
++ fxs->idletxhookstate = SLIC_LF_ACTIVE_FWD;
++ /* Set ring generator for neon */
++ si321x_set_ring_generator_mode(wc_dev, x, 1);
++ fxs->lasttxhook = SLIC_LF_RINGING;
++ } else {
++ fxs->lasttxhook = fxs->idletxhookstate;
++ }
++ /* Apply the change as appropriate */
++ a24xx_spi_setreg(wc_dev, x, LINE_STATE, fxs->lasttxhook);
++ }
++ } else {
++ fxs->ohttimer = 0;
++ /* Switch to active */
++ fxs->idletxhookstate = POLARITY_XOR(x) ? SLIC_LF_ACTIVE_REV : SLIC_LF_ACTIVE_FWD;
++ printk("Channel %d OnHookTransfer abort\n",x);
++ }
++ }
++ }
++ }
++
++ if (ints & (1<<16)) { /* it is our interrupts */
++ wc_dev->intcount++;
++ switch(ms_per_irq){
++ case 1:
++ z = wc_dev->intcount & 0x3;
++ mode = wc_dev->intcount & 0xc;
++ for(y = 0; y < wc_dev->max_cards / 4; y++) { /* do some detect operate every 4ms */
++ x = z + y*4;
++ if (wc_dev->cardflag & (1 << x ) ) {
++ switch(mode) {
++ case 0:
++ /* Rest */
++ break;
++ case 4:
++ /* Read first shadow reg */
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ wc_dev->reg0shadow[x] = a24xx_spi_getreg(wc_dev, x, 68);
++ //if(x==0)
++ //printk("reg 68 of %x is 0x%x\n", x, wc_dev->reg0shadow[x]);
++ }
++ else if (wc_dev->modtype[x] == MOD_TYPE_FXO)
++ wc_dev->reg0shadow[x] = a24xx_spi_getreg(wc_dev, x, 5);
++ break;
++ case 8:
++ /* Read second shadow reg */
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ wc_dev->reg1shadow[x] = a24xx_spi_getreg(wc_dev, x, 64);
++ //if(x==1)
++ // printk("reg 64 of %x is 0x%x\n", x, wc_dev->reg1shadow[x]);
++ }
++ else if (wc_dev->modtype[x] == MOD_TYPE_FXO)
++ wc_dev->reg1shadow[x] = a24xx_spi_getreg(wc_dev, x, 29);
++ break;
++ case 12:
++ /* Perform processing */
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ a24xx_proslic_check_hook(wc, x);
++ if (!(wc_dev->intcount & 0xf0)) {
++ si321x_proslic_recheck_sanity(wc_dev, x);
++ }
++ } else if (wc_dev->modtype[x] == MOD_TYPE_FXO) {
++ a24xx_voicedaa_check_hook(wc, x);
++ }
++ break;
++ }
++ }
++ }
++ break;
++ case 2:
++ z = wc_dev->intcount & 0x1;
++ mode = wc_dev->intcount & 0x6;
++ for(y = 0; y < wc_dev->max_cards / 2; y++) { /* do some detect operate every 4ms */
++ x = z + y*2;
++ if (wc_dev->cardflag & (1 << x ) ) {
++ switch(mode) {
++ case 0:
++ /* Rest */
++ break;
++ case 2:
++ /* Read first shadow reg */
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ wc_dev->reg0shadow[x] = a24xx_spi_getreg(wc_dev, x, 68);
++ //if(x==0)
++ //printk("reg 68 of %x is 0x%x\n", x, wc_dev->reg0shadow[x]);
++ }
++ else if (wc_dev->modtype[x] == MOD_TYPE_FXO)
++ wc_dev->reg0shadow[x] = a24xx_spi_getreg(wc_dev, x, 5);
++ break;
++ case 4:
++ /* Read second shadow reg */
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ wc_dev->reg1shadow[x] = a24xx_spi_getreg(wc_dev, x, 64);
++ //if(x==1)
++ // printk("reg 64 of %x is 0x%x\n", x, wc_dev->reg1shadow[x]);
++ }
++ else if (wc_dev->modtype[x] == MOD_TYPE_FXO)
++ wc_dev->reg1shadow[x] = a24xx_spi_getreg(wc_dev, x, 29);
++ break;
++ case 6:
++ /* Perform processing */
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ a24xx_proslic_check_hook(wc, x);
++ if (!(wc_dev->intcount & (0xf0>>1))) {
++ si321x_proslic_recheck_sanity(wc_dev, x);
++ }
++ } else if (wc_dev->modtype[x] == MOD_TYPE_FXO) {
++ a24xx_voicedaa_check_hook(wc, x);
++ }
++ break;
++ }
++ }
++ }
++ break;
++ case 4:
++ mode = wc_dev->intcount & 0x3;
++ for(x=0;x<wc_dev->max_cards;x++){
++ if (wc_dev->cardflag & (1 << x ) ) {
++ switch(mode) {
++ case 0:
++ /* Rest */
++ break;
++ case 1:
++ /* Read first shadow reg */
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ wc_dev->reg0shadow[x] = a24xx_spi_getreg(wc_dev, x, 68);
++ //if(x==0)
++ //printk("reg 68 of %x is 0x%x\n", x, wc_dev->reg0shadow[x]);
++ }
++ else if (wc_dev->modtype[x] == MOD_TYPE_FXO)
++ wc_dev->reg0shadow[x] = a24xx_spi_getreg(wc_dev, x, 5);
++ break;
++ case 2:
++ /* Read second shadow reg */
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ wc_dev->reg1shadow[x] = a24xx_spi_getreg(wc_dev, x, 64);
++ //if(x==1)
++ // printk("reg 64 of %x is 0x%x\n", x, wc_dev->reg1shadow[x]);
++ }
++ else if (wc_dev->modtype[x] == MOD_TYPE_FXO)
++ wc_dev->reg1shadow[x] = a24xx_spi_getreg(wc_dev, x, 29);
++ break;
++ case 3:
++ /* Perform processing */
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ a24xx_proslic_check_hook(wc, x);
++ if (!(wc_dev->intcount & (0xf0>>2))) {
++ si321x_proslic_recheck_sanity(wc_dev, x);
++ }
++ }
++ else if (wc_dev->modtype[x] == MOD_TYPE_FXO) {
++ a24xx_voicedaa_check_hook(wc, x);
++ }
++ break;
++ }
++ }
++ }
++ break;
++ case 8:
++ mode = wc_dev->intcount & 0x1;
++ for(x=0;x<wc_dev->max_cards;x++){
++ if (wc_dev->cardflag & (1 << x ) ) {
++ switch(mode) {
++ case 1:
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ wc_dev->reg0shadow[x] = a24xx_spi_getreg(wc_dev, x, 68);
++ wc_dev->reg1shadow[x] = a24xx_spi_getreg(wc_dev, x, 64);
++ }
++ else if (wc_dev->modtype[x] == MOD_TYPE_FXO){
++ wc_dev->reg0shadow[x] = a24xx_spi_getreg(wc_dev, x, 5);
++ wc_dev->reg1shadow[x] = a24xx_spi_getreg(wc_dev, x, 29);
++ }
++ break;
++ case 0:
++ /* Perform processing */
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ a24xx_proslic_check_hook(wc, x);
++ if (!(wc_dev->intcount & (0xf0>>3))) {
++ si321x_proslic_recheck_sanity(wc_dev, x);
++ }
++ }
++ else if (wc_dev->modtype[x] == MOD_TYPE_FXO) {
++ a24xx_voicedaa_check_hook(wc, x);
++ }
++ break;
++ }
++ }
++ }
++ break;
++ case 16:
++ for(x=0;x<wc_dev->max_cards;x++){
++ if (wc_dev->cardflag & (1 << x ) ) {
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ wc_dev->reg0shadow[x] = a24xx_spi_getreg(wc_dev, x, 68);
++ wc_dev->reg1shadow[x] = a24xx_spi_getreg(wc_dev, x, 64);
++ a24xx_proslic_check_hook(wc, x);
++ if (!(wc_dev->intcount & (0xf0>>4))) {
++ si321x_proslic_recheck_sanity(wc_dev, x);
++ }
++ }
++ else if (wc_dev->modtype[x] == MOD_TYPE_FXO){
++ wc_dev->reg0shadow[x] = a24xx_spi_getreg(wc_dev, x, 5);
++ wc_dev->reg1shadow[x] = a24xx_spi_getreg(wc_dev, x, 29);
++ a24xx_voicedaa_check_hook(wc, x);
++ }
++ }
++ }
++ break;
++ }
++
++ if (!(wc_dev->intcount % ( 10000/ms_per_irq ))) {
++ /* Accept an alarm once per 10 seconds */
++ for (x=0;x<wc_dev->max_cards;x++)
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ if (wc_dev->mod[x].fxs.palarms) {
++ wc_dev->mod[x].fxs.palarms--;
++ }
++ }
++ }
++ /**/
++ for(order=0;order < ms_per_irq;order++){
++ a24xx_receive(wc, order);
++ dahdi_receive(&wc->span);
++
++ dahdi_transmit(&wc->span);
++ a24xx_transmit(wc, order);
++ }
++ }
++
++ return IRQ_RETVAL(1);
++}
++#endif
++
++#if 1
++static int interrupt_onecard_handler(struct a24xx *wc)
++{
++ unsigned int ints=0;
++ int x, y, z,order;
++ int mode;
++ struct a24xx_dev *wc_dev = &wc->dev;
++
++ struct fxs * fxs ;
++
++ if((wc_dev->fwversion < ((1 << 16)|3)) || (wc_dev->master)){
++ ints = __opvx_a24xx_get_irqstatus(wc_dev->mem32);
++ if (!ints)
++ return IRQ_NONE;
++ }
++
++ __opvx_a24xx_set_irqstatus(wc_dev->mem32, ints); // clear interrupt register.
++
++ for (x=0;x<wc_dev->max_cards; x++) {
++ if (wc_dev->cardflag & (1 << x) && (wc_dev->modtype[x] == MOD_TYPE_FXS)) {
++ fxs = &wc_dev->mod[x].fxs;
++
++ if (SLIC_LF_RINGING == fxs->lasttxhook && !fxs->neonringing) {
++ /* RINGing, prepare for OHT */
++ fxs->ohttimer = OHT_TIMER << 3;
++ /* OHT mode when idle */
++ fxs->idletxhookstate = POLARITY_XOR(x) ? SLIC_LF_OHTRAN_REV :
++ SLIC_LF_OHTRAN_FWD;
++ } else if (fxs->ohttimer) {
++ /* check if still OnHook */
++ if (!fxs->oldrxhook) {
++ fxs->ohttimer -= DAHDI_CHUNKSIZE;
++ if (fxs->ohttimer)
++ continue;
++
++ /* Switch to active */
++ fxs->idletxhookstate = POLARITY_XOR(x) ? SLIC_LF_ACTIVE_REV :
++ SLIC_LF_ACTIVE_FWD;
++ /* if currently OHT */
++ if ((fxs->lasttxhook == SLIC_LF_OHTRAN_FWD) || (fxs->lasttxhook == SLIC_LF_OHTRAN_REV)) {
++ if (fxs->vmwi_hvac) {
++ /* force idle polarity Forward if ringing */
++ fxs->idletxhookstate = SLIC_LF_ACTIVE_FWD;
++ /* Set ring generator for neon */
++ si321x_set_ring_generator_mode(wc_dev, x, 1);
++ fxs->lasttxhook = SLIC_LF_RINGING;
++ } else {
++ fxs->lasttxhook = fxs->idletxhookstate;
++ }
++ /* Apply the change as appropriate */
++ a24xx_spi_setreg(wc_dev, x, LINE_STATE, fxs->lasttxhook);
++ }
++ } else {
++ fxs->ohttimer = 0;
++ /* Switch to active */
++ fxs->idletxhookstate = POLARITY_XOR(x) ? SLIC_LF_ACTIVE_REV : SLIC_LF_ACTIVE_FWD;
++ printk("Channel %d OnHookTransfer abort\n",x);
++ }
++ }
++ }
++ }
++
++ if ( ((ints & (1<<16)) && wc_dev->master) || (!wc_dev->master)) { /* it is our interrupts */
++ wc_dev->intcount++;
++ switch(ms_per_irq){
++ case 1:
++ z = wc_dev->intcount & 0x3;
++ mode = wc_dev->intcount & 0xc;
++ for(y = 0; y < wc_dev->max_cards / 4; y++) { /* do some detect operate every 4ms */
++ x = z + y*4;
++ if (wc_dev->cardflag & (1 << x ) ) {
++ switch(mode) {
++ case 0:
++ /* Rest */
++ break;
++ case 4:
++ /* Read first shadow reg */
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ wc_dev->reg0shadow[x] = a24xx_spi_getreg(wc_dev, x, 68);
++ //if(x==0)
++ //printk("reg 68 of %x is 0x%x\n", x, wc_dev->reg0shadow[x]);
++ }
++ else if (wc_dev->modtype[x] == MOD_TYPE_FXO)
++ wc_dev->reg0shadow[x] = a24xx_spi_getreg(wc_dev, x, 5);
++ break;
++ case 8:
++ /* Read second shadow reg */
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ wc_dev->reg1shadow[x] = a24xx_spi_getreg(wc_dev, x, 64);
++ //if(x==1)
++ // printk("reg 64 of %x is 0x%x\n", x, wc_dev->reg1shadow[x]);
++ }
++ else if (wc_dev->modtype[x] == MOD_TYPE_FXO)
++ wc_dev->reg1shadow[x] = a24xx_spi_getreg(wc_dev, x, 29);
++ break;
++ case 12:
++ /* Perform processing */
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ a24xx_proslic_check_hook(wc, x);
++ if (!(wc_dev->intcount & 0xf0)) {
++ si321x_proslic_recheck_sanity(wc_dev, x);
++ }
++ } else if (wc_dev->modtype[x] == MOD_TYPE_FXO) {
++ a24xx_voicedaa_check_hook(wc, x);
++ }
++ break;
++ }
++ }
++ }
++ break;
++ case 2:
++ z = wc_dev->intcount & 0x1;
++ mode = wc_dev->intcount & 0x6;
++ for(y = 0; y < wc_dev->max_cards / 2; y++) { /* do some detect operate every 4ms */
++ x = z + y*2;
++ if (wc_dev->cardflag & (1 << x ) ) {
++ switch(mode) {
++ case 0:
++ /* Rest */
++ break;
++ case 2:
++ /* Read first shadow reg */
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ wc_dev->reg0shadow[x] = a24xx_spi_getreg(wc_dev, x, 68);
++ //if(x==0)
++ //printk("reg 68 of %x is 0x%x\n", x, wc_dev->reg0shadow[x]);
++ }
++ else if (wc_dev->modtype[x] == MOD_TYPE_FXO)
++ wc_dev->reg0shadow[x] = a24xx_spi_getreg(wc_dev, x, 5);
++ break;
++ case 4:
++ /* Read second shadow reg */
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ wc_dev->reg1shadow[x] = a24xx_spi_getreg(wc_dev, x, 64);
++ //if(x==1)
++ // printk("reg 64 of %x is 0x%x\n", x, wc_dev->reg1shadow[x]);
++ }
++ else if (wc_dev->modtype[x] == MOD_TYPE_FXO)
++ wc_dev->reg1shadow[x] = a24xx_spi_getreg(wc_dev, x, 29);
++ break;
++ case 6:
++ /* Perform processing */
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ a24xx_proslic_check_hook(wc, x);
++ if (!(wc_dev->intcount & (0xf0>>1))) {
++ si321x_proslic_recheck_sanity(wc_dev, x);
++ }
++ } else if (wc_dev->modtype[x] == MOD_TYPE_FXO) {
++ a24xx_voicedaa_check_hook(wc, x);
++ }
++ break;
++ }
++ }
++ }
++ break;
++ case 4:
++ mode = wc_dev->intcount & 0x3;
++ for(x=0;x<wc_dev->max_cards;x++){
++ if (wc_dev->cardflag & (1 << x ) ) {
++ switch(mode) {
++ case 0:
++ /* Rest */
++ break;
++ case 1:
++ /* Read first shadow reg */
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ wc_dev->reg0shadow[x] = a24xx_spi_getreg(wc_dev, x, 68);
++ //if(x==0)
++ //printk("reg 68 of %x is 0x%x\n", x, wc_dev->reg0shadow[x]);
++ }
++ else if (wc_dev->modtype[x] == MOD_TYPE_FXO)
++ wc_dev->reg0shadow[x] = a24xx_spi_getreg(wc_dev, x, 5);
++ break;
++ case 2:
++ /* Read second shadow reg */
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ wc_dev->reg1shadow[x] = a24xx_spi_getreg(wc_dev, x, 64);
++ //if(x==1)
++ // printk("reg 64 of %x is 0x%x\n", x, wc_dev->reg1shadow[x]);
++ }
++ else if (wc_dev->modtype[x] == MOD_TYPE_FXO)
++ wc_dev->reg1shadow[x] = a24xx_spi_getreg(wc_dev, x, 29);
++ break;
++ case 3:
++ /* Perform processing */
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ a24xx_proslic_check_hook(wc, x);
++ if (!(wc_dev->intcount & (0xf0>>2))) {
++ si321x_proslic_recheck_sanity(wc_dev, x);
++ }
++ }
++ else if (wc_dev->modtype[x] == MOD_TYPE_FXO) {
++ a24xx_voicedaa_check_hook(wc, x);
++ }
++ break;
++ }
++ }
++ }
++ break;
++ case 8:
++ mode = wc_dev->intcount & 0x1;
++ for(x=0;x<wc_dev->max_cards;x++){
++ if (wc_dev->cardflag & (1 << x ) ) {
++ switch(mode) {
++ case 1:
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ wc_dev->reg0shadow[x] = a24xx_spi_getreg(wc_dev, x, 68);
++ wc_dev->reg1shadow[x] = a24xx_spi_getreg(wc_dev, x, 64);
++ }
++ else if (wc_dev->modtype[x] == MOD_TYPE_FXO){
++ wc_dev->reg0shadow[x] = a24xx_spi_getreg(wc_dev, x, 5);
++ wc_dev->reg1shadow[x] = a24xx_spi_getreg(wc_dev, x, 29);
++ }
++ break;
++ case 0:
++ /* Perform processing */
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ a24xx_proslic_check_hook(wc, x);
++ if (!(wc_dev->intcount & (0xf0>>3))) {
++ si321x_proslic_recheck_sanity(wc_dev, x);
++ }
++ }
++ else if (wc_dev->modtype[x] == MOD_TYPE_FXO) {
++ a24xx_voicedaa_check_hook(wc, x);
++ }
++ break;
++ }
++ }
++ }
++ break;
++ case 16:
++ for(x=0;x<wc_dev->max_cards;x++){
++ if (wc_dev->cardflag & (1 << x ) ) {
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ wc_dev->reg0shadow[x] = a24xx_spi_getreg(wc_dev, x, 68);
++ wc_dev->reg1shadow[x] = a24xx_spi_getreg(wc_dev, x, 64);
++ a24xx_proslic_check_hook(wc, x);
++ if (!(wc_dev->intcount & (0xf0>>4))) {
++ si321x_proslic_recheck_sanity(wc_dev, x);
++ }
++ }
++ else if (wc_dev->modtype[x] == MOD_TYPE_FXO){
++ wc_dev->reg0shadow[x] = a24xx_spi_getreg(wc_dev, x, 5);
++ wc_dev->reg1shadow[x] = a24xx_spi_getreg(wc_dev, x, 29);
++ a24xx_voicedaa_check_hook(wc, x);
++ }
++ }
++ }
++ break;
++ }
++
++ if (!(wc_dev->intcount % ( 10000/ms_per_irq ))) {
++ /* Accept an alarm once per 10 seconds */
++ for (x=0;x<wc_dev->max_cards;x++)
++ if (wc_dev->modtype[x] == MOD_TYPE_FXS) {
++ if (wc_dev->mod[x].fxs.palarms) {
++ wc_dev->mod[x].fxs.palarms--;
++ }
++ }
++ }
++ /**/
++ for(order=0;order < ms_per_irq;order++){
++ dahdi_transmit(&wc->span);
++ parser_busy_silent_process(wc, 1);
++ a24xx_transmit(wc, order);
++
++ a24xx_receive(wc, order);
++ parser_busy_silent_process(wc, 0);
++ dahdi_receive(&wc->span);
++ }
++ }
++ return 0;
++}
++
++DAHDI_IRQ_HANDLER(a24xx_interrupt)
++{
++ /**/
++ struct a24xx *wc = dev_id;
++ struct a24xx_dev *wc_dev = &wc->dev;
++ int i;
++
++ if(!wc_dev->master){
++ interrupt_onecard_handler(wc);
++ }else{
++ if(irq_stub){
++ for(i=0;i<max_iface_index;i++){
++ wc = ifaces[i];
++ if(wc){
++ wc_dev = &wc->dev;
++ if( wc_dev->fwversion >= ((1 << 16)|2) ){
++ interrupt_onecard_handler(wc);
++ }
++ }
++ }
++ }else
++ interrupt_onecard_handler(wc);
++ }
++
++
++ return IRQ_RETVAL(1);
++}
++#endif
++/* Must be called from within an interruptible context */
++static int set_vmwi(struct a24xx *wc, int chan_idx)
++{
++ struct a24xx_dev *wc_dev = &wc->dev;
++ struct fxs *const fxs = &wc_dev->mod[chan_idx].fxs;
++
++ if (fxs->vmwi_active_messages) {
++ fxs->vmwi_lrev =
++ (fxs->vmwisetting.vmwi_type & DAHDI_VMWI_LREV) ? 1 : 0;
++ fxs->vmwi_hvdc =
++ (fxs->vmwisetting.vmwi_type & DAHDI_VMWI_HVDC) ? 1 : 0;
++ fxs->vmwi_hvac =
++ (fxs->vmwisetting.vmwi_type & DAHDI_VMWI_HVAC) ? 1 : 0;
++ } else {
++ fxs->vmwi_lrev = 0;
++ fxs->vmwi_hvdc = 0;
++ fxs->vmwi_hvac = 0;
++ }
++
++ if (debug) {
++ printk(KERN_DEBUG "Setting VMWI on channel %d, messages=%d, "
++ "lrev=%d, hvdc=%d, hvac=%d\n",
++ chan_idx,
++ fxs->vmwi_active_messages,
++ fxs->vmwi_lrev,
++ fxs->vmwi_hvdc,
++ fxs->vmwi_hvac
++ );
++ }
++
++ if (fxs->vmwi_hvac) {
++ /* Can't change ring generator while in On Hook Transfer mode*/
++ if (!fxs->ohttimer) {
++ if (POLARITY_XOR(chan_idx))
++ fxs->idletxhookstate |= SLIC_LF_REVMASK;
++ else
++ fxs->idletxhookstate &= ~SLIC_LF_REVMASK;
++ /* Set ring generator for neon */
++ si321x_set_ring_generator_mode(wc_dev, chan_idx, 1);
++ /* Activate ring to send neon pulses */
++ fxs->lasttxhook = SLIC_LF_RINGING;
++ a24xx_spi_setreg(wc_dev, chan_idx, LINE_STATE, fxs->lasttxhook);
++ }
++ } else {
++ if (fxs->neonringing) {
++ /* Set ring generator for normal ringer */
++ si321x_set_ring_generator_mode(wc_dev, chan_idx, 0);
++ /* ACTIVE, polarity determined later */
++ fxs->lasttxhook = SLIC_LF_ACTIVE_FWD;
++ } else if ((fxs->lasttxhook == SLIC_LF_RINGING) ||
++ (fxs->lasttxhook == SLIC_LF_OPEN)) {
++ /* Can't change polarity while ringing or when open,
++ set idlehookstate instead */
++ if (POLARITY_XOR(chan_idx))
++ fxs->idletxhookstate |= SLIC_LF_REVMASK;
++ else
++ fxs->idletxhookstate &= ~SLIC_LF_REVMASK;
++ if(debug)
++ printk(KERN_DEBUG "Unable to change polarity on channel"
++ "%d, lasttxhook=0x%X\n",
++ chan_idx,
++ fxs->lasttxhook
++ );
++ return 0;
++ }
++ if (POLARITY_XOR(chan_idx)) {
++ fxs->idletxhookstate |= SLIC_LF_REVMASK;
++ fxs->lasttxhook |= SLIC_LF_REVMASK;
++ } else {
++ fxs->idletxhookstate &= ~SLIC_LF_REVMASK;
++ fxs->lasttxhook &= ~SLIC_LF_REVMASK;
++ }
++ a24xx_spi_setreg(wc_dev, chan_idx, LINE_STATE, fxs->lasttxhook);
++ }
++ return 0;
++}
++
++static int a24xx_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 a24xx *wc = chan->pvt;
++ struct a24xx_dev *wc_dev = &wc->dev;
++ int x;
++
++ struct fxs *const fxs = &wc_dev->mod[chan->chanpos - 1].fxs;
++
++ switch (cmd) {
++ case DAHDI_ONHOOKTRANSFER:
++ if (wc_dev->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) {
++ return -EINVAL;
++ }
++ if (get_user(x, (__user int *)data)) {
++ return -EFAULT;
++ }
++#if 0
++ /**/
++ wc_dev->mod[chan->chanpos - 1].fxs.ohttimer = x << 3;
++ if (reversepolarity) {
++ wc_dev->mod[chan->chanpos - 1].fxs.idletxhookstate = 0x6; /* OHT mode when idle */
++ } else {
++ wc_dev->mod[chan->chanpos - 1].fxs.idletxhookstate = 0x2;
++ }
++ if (wc_dev->mod[chan->chanpos - 1].fxs.lasttxhook == 0x1 || wc_dev->mod[chan->chanpos - 1].fxs.lasttxhook == 0x5) {
++ /* Apply the change if appropriate */
++ if (reversepolarity) {
++ wc_dev->mod[chan->chanpos - 1].fxs.lasttxhook = 0x6;
++ } else {
++ wc_dev->mod[chan->chanpos - 1].fxs.lasttxhook = 0x2;
++ }
++ a24xx_spi_setreg(wc_dev, chan->chanpos - 1, 64, wc_dev->mod[chan->chanpos - 1].fxs.lasttxhook);
++ }
++#endif
++#if 1
++ /* Active mode when idle */
++ wc_dev->mod[chan->chanpos - 1].fxs.idletxhookstate = POLARITY_XOR(chan->chanpos - 1) ?
++ SLIC_LF_ACTIVE_REV : SLIC_LF_ACTIVE_FWD;
++ if (wc_dev->mod[chan->chanpos - 1].fxs.neonringing) {
++ /* keep same Forward polarity */
++ wc_dev->mod[chan->chanpos - 1].fxs.lasttxhook = SLIC_LF_OHTRAN_FWD;
++ //printk(KERN_INFO "ioctl: Start OnHookTrans, card %d\n",chan->chanpos - 1);
++ a24xx_spi_setreg(wc_dev, chan->chanpos - 1,
++ LINE_STATE, wc_dev->mod[chan->chanpos - 1].fxs.lasttxhook);
++ } else if (wc_dev->mod[chan->chanpos - 1].fxs.lasttxhook == SLIC_LF_ACTIVE_FWD ||
++ wc_dev->mod[chan->chanpos - 1].fxs.lasttxhook == SLIC_LF_ACTIVE_REV) {
++ /* Apply the change if appropriate */
++ wc_dev->mod[chan->chanpos - 1].fxs.lasttxhook = POLARITY_XOR(chan->chanpos - 1) ?
++ SLIC_LF_OHTRAN_REV : SLIC_LF_OHTRAN_FWD;
++ //printk(KERN_INFO "ioctl: Start OnHookTrans, card %d\n",chan->chanpos - 1);
++ a24xx_spi_setreg(wc_dev, chan->chanpos - 1,
++ LINE_STATE, wc_dev->mod[chan->chanpos - 1].fxs.lasttxhook);
++ }
++#endif
++ break;
++#if 1
++ case DAHDI_VMWI_CONFIG:
++ if (wc_dev->modtype[chan->chanpos - 1] != MOD_TYPE_FXS)
++ return -EINVAL;
++ if (copy_from_user(&(fxs->vmwisetting),
++ (__user void *)data,
++ sizeof(fxs->vmwisetting)))
++ return -EFAULT;
++ //printk("--opvxa24xx:DAHDI_VMWI_CONFIG,card=%d--\n",chan->chanpos - 1);
++ set_vmwi(wc, chan->chanpos - 1);
++ break;
++ case DAHDI_VMWI:
++ if (wc_dev->modtype[chan->chanpos - 1] != MOD_TYPE_FXS)
++ return -EINVAL;
++ if (get_user(x, (__user int *) data))
++ return -EFAULT;
++ if (0 > x)
++ return -EFAULT;
++ fxs->vmwi_active_messages = x;
++ set_vmwi(wc, chan->chanpos - 1);
++ //printk("-----opvxa24xx,DAHDI_VMWI,card=%d,set_vmwi------\n",chan->chanpos - 1);
++ break;
++#endif
++ case DAHDI_SETPOLARITY:
++ if (get_user(x, (__user int *)data)) {
++ return -EFAULT;
++ }
++ if (wc_dev->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) {
++ return -EINVAL;
++ }
++ /* Can't change polarity while ringing or when open */
++ if ((wc_dev->mod[chan->chanpos -1 ].fxs.lasttxhook == SLIC_LF_RINGING) ||
++ (wc_dev->mod[chan->chanpos -1 ].fxs.lasttxhook == SLIC_LF_OPEN)) {
++ return -EINVAL;
++ }
++ wc_dev->mod[chan->chanpos -1 ].fxs.reversepolarity = x;
++#if 0
++ if ((x && !reversepolarity) || (!x && reversepolarity)) {
++ wc_dev->mod[chan->chanpos - 1].fxs.lasttxhook |= 0x04;
++ } else {
++ wc_dev->mod[chan->chanpos - 1].fxs.lasttxhook &= ~0x04;
++ }
++#endif
++ if (POLARITY_XOR(chan->chanpos - 1)) {
++ wc_dev->mod[chan->chanpos -1 ].fxs.lasttxhook |= SLIC_LF_REVMASK;
++ printk(KERN_INFO "ioctl: Reverse Polarity, card %d\n",
++ chan->chanpos - 1);
++ } else {
++ wc_dev->mod[chan->chanpos -1 ].fxs.lasttxhook &= ~SLIC_LF_REVMASK;
++ printk(KERN_INFO "ioctl: Normal Polarity, card %d\n",
++ chan->chanpos - 1);
++ }
++ a24xx_spi_setreg(wc_dev, chan->chanpos - 1, LINE_STATE, wc_dev->mod[chan->chanpos - 1].fxs.lasttxhook);
++ break;
++ case WCTDM_GET_STATS:
++ if (wc_dev->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) {
++ stats.tipvolt = a24xx_spi_getreg(wc_dev, chan->chanpos - 1, 80) * -376;
++ stats.ringvolt = a24xx_spi_getreg(wc_dev, chan->chanpos - 1, 81) * -376;
++ stats.batvolt = a24xx_spi_getreg(wc_dev, chan->chanpos - 1, 82) * -376;
++ } else if (wc_dev->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) {
++ stats.tipvolt = (signed char)a24xx_spi_getreg(wc_dev, chan->chanpos - 1, 29) * 1000;
++ stats.ringvolt = (signed char)a24xx_spi_getreg(wc_dev, chan->chanpos - 1, 29) * 1000;
++ stats.batvolt = (signed char)a24xx_spi_getreg(wc_dev, 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_dev->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) {
++ for (x=0;x<NUM_INDIRECT_REGS;x++) {
++ regs.indirect[x] = si321x_proslic_getreg_indirect(wc_dev, chan->chanpos -1, x);
++ }
++ for (x=0;x<NUM_REGS;x++) {
++ regs.direct[x] = a24xx_spi_getreg(wc_dev, chan->chanpos - 1, x);
++ }
++ } else {
++ memset(&regs, 0, sizeof(regs));
++ for (x=0;x<NUM_FXO_REGS;x++)
++ regs.direct[x] = a24xx_spi_getreg(wc_dev, 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_dev->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);
++ si321x_proslic_setreg_indirect(wc_dev, 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);
++ a24xx_spi_setreg(wc_dev, 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_dev->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) {
++ /* Set the ACIM register */
++ a24xx_spi_setreg(wc_dev, chan->chanpos - 1, 30, (fxofullscale==1) ? (echoregs.acim|0x10) : echoregs.acim);
++
++ /* Set the digital echo canceller registers */
++ a24xx_spi_setreg(wc_dev, chan->chanpos - 1, 45, echoregs.coef1);
++ a24xx_spi_setreg(wc_dev, chan->chanpos - 1, 46, echoregs.coef2);
++ a24xx_spi_setreg(wc_dev, chan->chanpos - 1, 47, echoregs.coef3);
++ a24xx_spi_setreg(wc_dev, chan->chanpos - 1, 48, echoregs.coef4);
++ a24xx_spi_setreg(wc_dev, chan->chanpos - 1, 49, echoregs.coef5);
++ a24xx_spi_setreg(wc_dev, chan->chanpos - 1, 50, echoregs.coef6);
++ a24xx_spi_setreg(wc_dev, chan->chanpos - 1, 51, echoregs.coef7);
++ a24xx_spi_setreg(wc_dev, 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;
++ }
++
++ si3050_set_hwgain(wc_dev, 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 a24xx_open(struct dahdi_chan *chan)
++{
++ int channo;
++
++ struct a24xx *wc = chan->pvt;
++ struct a24xx_dev *wc_dev = &wc->dev;
++
++ channo = chan->chanpos - 1;
++
++ if (!(wc_dev->cardflag & (1 << (chan->chanpos - 1)))) {
++ return -ENODEV;
++ }
++ if (wc_dev->dead) {
++ return -ENODEV;
++ }
++ wc_dev->usecount++;
++
++ try_module_get(THIS_MODULE);
++
++ return 0;
++}
++
++static inline struct a24xx *a24xx_from_span(struct dahdi_span *span)
++{
++ return container_of(span, struct a24xx, span);
++}
++
++static int a24xx_watchdog(struct dahdi_span *span, int event)
++{
++ struct a24xx *wc = a24xx_from_span(span);
++ struct a24xx_dev *wc_dev = &wc->dev;
++
++ printk(KERN_INFO "opvxa24xx: Restarting DMA\n");
++ __opvx_a24xx_restart_dma(wc_dev->mem32);
++
++ return 0;
++}
++
++static int a24xx_close(struct dahdi_chan *chan)
++{
++ struct a24xx *wc = chan->pvt;
++ struct a24xx_dev *wc_dev = &wc->dev;
++
++ wc_dev->usecount--;
++
++ module_put(THIS_MODULE);
++
++ if (wc_dev->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) {
++ /*
++ if (reversepolarity) {
++ wc_dev->mod[chan->chanpos - 1].fxs.idletxhookstate = 5;
++ }
++ else {
++ wc_dev->mod[chan->chanpos - 1].fxs.idletxhookstate = 1;
++ }*/
++ int idlehookstate;
++ idlehookstate = POLARITY_XOR(chan->chanpos - 1)?
++ SLIC_LF_ACTIVE_REV :
++ SLIC_LF_ACTIVE_FWD;
++ wc_dev->mod[chan->chanpos - 1].fxs.idletxhookstate = idlehookstate;
++ }
++ /* If we're dead, release us now */
++ if (!wc_dev->usecount && wc_dev->dead) {
++ a24xx_release(wc);
++ }
++ return 0;
++}
++
++static int a24xx_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig)
++{
++ int reg=0;
++ struct a24xx *wc = chan->pvt;
++ struct a24xx_dev *wc_dev = &wc->dev;
++
++ if (wc_dev->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) {
++ /* XXX Enable hooksig for FXO XXX */
++ switch(txsig) {
++ case DAHDI_TXSIG_START:
++ case DAHDI_TXSIG_OFFHOOK:
++ wc_dev->mod[chan->chanpos - 1].fxo.offhook = 1;
++ a24xx_spi_setreg(wc_dev, chan->chanpos - 1, 5, 0x9);
++ if(cidsupport) {
++ wc_dev->cid_state[chan->chanpos - 1] = CID_STATE_IDLE;
++ wc_dev->cid_history_clone_cnt[chan->chanpos - 1] = 0;
++ wc_dev->cid_history_ptr[chan->chanpos - 1] = 0;
++ memset(wc_dev->cid_history_buf[chan->chanpos - 1], DAHDI_LIN2X(0, chan), cidbuflen * DAHDI_MAX_CHUNKSIZE);
++ }
++ break;
++ case DAHDI_TXSIG_ONHOOK:
++ wc_dev->mod[chan->chanpos - 1].fxo.offhook = 0;
++ a24xx_spi_setreg(wc_dev, 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_dev->mod[chan->chanpos-1].fxs.lasttxhook = wc_dev->mod[chan->chanpos-1].fxs.idletxhookstate;
++ /* Can't change Ring Generator during OHT */
++ if (!wc_dev->mod[chan->chanpos-1].fxs.ohttimer) {
++ si321x_set_ring_generator_mode(wc_dev,
++ chan->chanpos-1, wc_dev->mod[chan->chanpos-1].fxs.vmwi_hvac);
++ wc_dev->mod[chan->chanpos-1].fxs.lasttxhook = wc_dev->mod[chan->chanpos-1].fxs.vmwi_hvac ? SLIC_LF_RINGING : wc_dev->mod[chan->chanpos-1].fxs.idletxhookstate;
++ } else {
++ wc_dev->mod[chan->chanpos-1].fxs.lasttxhook = wc_dev->mod[chan->chanpos-1].fxs.idletxhookstate;
++ }
++ break;
++ case DAHDI_SIG_FXOGS:
++ wc_dev->mod[chan->chanpos-1].fxs.lasttxhook = 3;
++ break;
++ }
++ break;
++ case DAHDI_TXSIG_OFFHOOK:
++ switch(chan->sig) {
++ case DAHDI_SIG_EM:
++ wc_dev->mod[chan->chanpos-1].fxs.lasttxhook = 5;
++ break;
++ default:
++ wc_dev->mod[chan->chanpos-1].fxs.lasttxhook = wc_dev->mod[chan->chanpos-1].fxs.idletxhookstate;
++ break;
++ }
++ break;
++ case DAHDI_TXSIG_START:
++ //wc_dev->mod[chan->chanpos-1].fxs.lasttxhook = 4;
++ si321x_set_ring_generator_mode(wc_dev,
++ chan->chanpos-1, 0);
++ wc_dev->mod[chan->chanpos-1].fxs.lasttxhook = SLIC_LF_RINGING;
++ break;
++ case DAHDI_TXSIG_KEWL:
++ wc_dev->mod[chan->chanpos-1].fxs.lasttxhook = 0;
++ break;
++ default:
++ printk(KERN_NOTICE "opvxa24xx: 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
++ a24xx_spi_setreg(wc_dev, chan->chanpos - 1, 64, wc_dev->mod[chan->chanpos-1].fxs.lasttxhook);
++#endif
++ }
++ return 0;
++}
++
++#ifdef DAHDI_SPAN_OPS
++static const struct dahdi_span_ops a24xx_span_ops = {
++ .owner = THIS_MODULE,
++ .hooksig = a24xx_hooksig,
++ .open = a24xx_open,
++ .close = a24xx_close,
++ .ioctl = a24xx_ioctl,
++ .watchdog = a24xx_watchdog,
++#ifdef VPM_SUPPORT
++ .echocan_create = a24xx_echocan_create,
++#endif
++
++};
++#endif
++
++static int a24xx_init_spans(struct a24xx *wc)
++{
++ int x;
++ struct a24xx_dev *wc_dev = &(wc->dev);
++
++ /* Zapata stuff */
++ sprintf(wc->span.name, "OPVXA24XX/%d", wc_dev->pos);
++ snprintf(wc->span.desc, sizeof(wc->span.desc)-1, "%s Board %d", wc_dev->variety, wc_dev->pos + 1);
++// snprintf(wc->span.location, sizeof(wc->span.location) - 1,
++// "PCI Bus %02d Slot %02d", wc_dev->dev->bus->number, PCI_SLOT(wc_dev->dev->devfn) + 1);
++ wc_dev->ddev->location = kasprintf(GFP_KERNEL,
++ "PCI Bus %02d Slot %02d",
++ wc_dev->dev->bus->number,
++ PCI_SLOT(wc_dev->dev->devfn) + 1);
++
++ //printk("wc is 0x%x, wc_dev ix 0x%x, ddev is 0x%x\n", wc, &(wc->dev), wc_dev->ddev);
++ if (!wc_dev->ddev->location) {
++ dahdi_free_device(wc_dev->ddev);
++ wc_dev->ddev = NULL;
++ return -ENOMEM;
++ }
++
++ wc_dev->ddev->manufacturer = "OpenVox"; //Dennis
++ wc_dev->ddev->devicetype = wc_dev->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;
++ }
++
++ for (x = 0; x < wc_dev->max_cards/*MAX_NUM_CARDS*/; x++) {
++ sprintf(wc->chans[x]->name, "OPVXA24XX/%d/%d", wc_dev->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 = &a24xx_span_ops;
++#else
++ wc->span.hooksig = a24xx_hooksig;
++ wc->span.open = a24xx_open;
++ wc->span.close = a24xx_close;
++ wc->span.ioctl = a24xx_ioctl;
++ wc->span.watchdog = a24xx_watchdog;
++#ifdef VPM_SUPPORT
++ if (vpmsupport)
++ wc->span.echocan_create = a24xx_echocan_create;
++#endif
++ wc->span.pvt = wc;
++#endif
++ wc->span.chans = wc->chans;
++ wc->span.channels = wc_dev->max_cards; /*MAX_NUM_CARDS;*/
++ wc->span.flags = DAHDI_FLAG_RBS;
++// init_waitqueue_head(&wc->span.maintq);
++ list_add_tail(&wc->span.device_node, &wc->dev.ddev->spans);
++// init_waitqueue_head(&wc->regq)
++
++ if (dahdi_register_device(wc_dev->ddev, &wc->dev.dev->dev)) {
++ printk(KERN_NOTICE "Unable to register span with Dahdi\n");
++ kfree(wc_dev->ddev->location);
++ dahdi_free_device(wc_dev->ddev);
++ wc_dev->ddev = NULL;
++ printk("dahdi_register_device fail\n");
++ mdelay(3000);
++ return -1;
++ }
++ return 0;
++}
++
++static int __devinit a24xx_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
++{
++ int res;
++ int x;
++ int y;
++ struct a24xx *wc;
++ struct a24xx_desc *d = (struct a24xx_desc *)ent->driver_data;
++ struct a24xx_dev *wc_dev;
++ unsigned int fwbuild;
++ char span_flags[MAX_NUM_CARDS] = { 0, };
++ int z,index;
++ static int initd_ifaces=0;
++
++ if(!initd_ifaces){
++ memset((void *)ifaces,0,(sizeof(struct a24xx *))*WC_MAX_IFACES);
++ initd_ifaces=1;
++ }
++ for (x = 0; x < WC_MAX_IFACES; x++) {
++ if (!ifaces[x]) {
++ break;
++ }
++ }
++ if (x >= WC_MAX_IFACES) {
++ printk("Too many interfaces\n");
++ return -EIO;
++ }
++ index = x;
++
++ if (pci_enable_device(pdev)) {
++ res = -EIO;
++ } else {
++ int cardcount = 0;
++
++ wc = kmalloc(sizeof(struct a24xx), GFP_KERNEL);
++ if(!wc)
++ return -ENOMEM;
++
++ memset(wc, 0, sizeof(struct a24xx));
++ wc->dev.ddev = dahdi_create_device();
++#if 0
++ printk("wc is 0x%x, ddev is 0x%x\n", wc, wc->dev.ddev);
++#endif
++ if (!wc->dev.ddev) {
++ kfree(wc);
++ return -ENOMEM;
++ }
++ wc_dev = &wc->dev;
++ wc_dev->ledstate = 0;
++
++ for (x=0; x < sizeof(wc->chans)/sizeof(wc->chans[0]); ++x) {
++ wc->chans[x] = &wc->_chans[x];
++ }
++
++ spin_lock_init(&wc_dev->lock);
++ wc_dev->curcard = -1;
++
++ if(ent->device == 0x0810)
++ wc_dev->max_cards = 8;
++ else if(ent->device == 0x1610)
++ wc_dev->max_cards = 16;
++ else
++ wc_dev->max_cards = 24;
++
++ wc_dev->mem_region = pci_resource_start(pdev, 0);
++ wc_dev->mem_len = pci_resource_len(pdev, 0);
++ wc_dev->mem32 = (unsigned long)ioremap(wc_dev->mem_region, wc_dev->mem_len);
++ wc_dev->dev = pdev;
++ wc_dev->pos = x;
++ wc_dev->variety = d->name;
++ for (y = 0; y < wc_dev->max_cards; y++) {
++ wc_dev->flags[y] = d->flags;
++ }
++
++ /* Keep track of whether we need to free the region */
++ if (request_mem_region(wc_dev->mem_region, wc_dev->mem_len, "opvxa24xx")) {
++ wc_dev->freeregion = 1;
++ }
++
++ if(debug) {
++ printk("======= find a card @ mem32 0x%x, size %ud\n", (unsigned int)wc_dev->mem32, (unsigned int)wc_dev->mem_len);
++ }
++#if 0
++ printk("======= manual exit\n");
++ if(wc_dev->freeregion) {
++ release_mem_region(wc_dev->mem_region, wc_dev->mem_len);
++ iounmap((void *)wc_dev->mem32);
++ return -ENOMEM;
++ }
++#endif
++
++ wc_dev->fwversion = __opvx_a24xx_get_version(wc_dev->mem32);
++ if(wc_dev->max_cards == 24)
++ wc_dev->card_name = A2410P_Name;
++ else if(wc_dev->max_cards == 16)
++ wc_dev->card_name = A1610P_Name;
++ else if(wc_dev->max_cards == 8)
++ wc_dev->card_name = A810P_Name;
++
++ if(wc_dev->fwversion < ((1 << 16)|1) )
++ ms_per_irq = 1;
++
++ if((ms_per_irq != 1)&&
++ (ms_per_irq != 2)&&
++ (ms_per_irq != 4)&&
++ (ms_per_irq != 8)&&
++ (ms_per_irq != 16))
++ ms_per_irq = 1;
++
++ if(wc_dev->fwversion > ((1 << 16)|2)){
++ if(!index)
++ wc_dev->master = 1; //master card
++ else
++ wc_dev->master = 0;
++ if(!irq_stub)
++ wc_dev->master = 1;
++ }else
++ wc_dev->master = 0;
++
++ wc->index = index;
++
++ /* Allocate enough memory for two dahdi chunks, receive and transmit. Each sample uses
++ 8 bits. */
++ wc_dev->writechunk = pci_alloc_consistent(pdev, ms_per_irq * DAHDI_MAX_CHUNKSIZE * MAX_NUM_CARDS * 2 * 2, &wc_dev->writedma);
++ if (!wc_dev->writechunk) {
++ printk("opvxa24xx: Unable to allocate DMA-able memory\n");
++ if (wc_dev->freeregion) {
++ release_mem_region(wc_dev->mem_region, wc_dev->mem_len);
++ iounmap((void *)wc_dev->mem32);
++ }
++ return -ENOMEM;
++ }
++
++ if(debug) {
++ printk("opvxa24xx: dma buffer allocated at 0x%x, pci(0x%x)\n", (unsigned int)wc_dev->writechunk, (unsigned int)wc_dev->writedma);
++ }
++
++ __a24xx_malloc_chunk(wc_dev,ms_per_irq);
++
++ if (a24xx_init_spans(wc)) {
++ printk(KERN_NOTICE "opvxa24xx: Unable to intialize hardware\n");
++ /* Free Resources */
++ if (wc_dev->freeregion) {
++ release_mem_region(wc_dev->mem_region, wc_dev->mem_len);
++ iounmap((void *)wc_dev->mem32);
++ }
++ pci_free_consistent(pdev, ms_per_irq * DAHDI_MAX_CHUNKSIZE * MAX_NUM_CARDS * 2 * 2, (void *)wc_dev->writechunk, wc_dev->writedma);
++ return -ENOMEM;
++ }
++
++ /* Enable bus mastering */
++ pci_set_master(pdev);
++
++ /* Keep track of which device we are */
++ pci_set_drvdata(pdev, wc);
++
++ /* kmalloc ec */
++ for (x = 0; x < wc_dev->max_cards; x++) {
++ if (!(wc->ec[x] = kmalloc(sizeof(*wc->ec[x]), GFP_KERNEL))) {
++ free_wc(wc);
++ return -ENOMEM;
++ }
++ }
++
++ /* init hardware */
++#if 0
++ a24xx_hardware_init(wc_dev, span_flags);
++
++ __opvx_a24xx_setcreg(wc_dev->mem32, 0x4a0, 7, spi_cmd );
++ int spi_cmd_from_fpga = __opvx_a24xx_getcreg(wc_dev->mem32, 0x4a0, 7);
++ printk("%s:current spi_cmd is 0x%x\n", __FUNCTION__ , spi_cmd_from_fpga );
++#endif
++
++ res = a24xx_hardware_init_all(wc_dev, span_flags);
++ if (res < 0) {
++ /* Free Resources */
++ if (wc_dev->freeregion) {
++ release_mem_region(wc_dev->mem_region, wc_dev->mem_len);
++ iounmap((void *)wc_dev->mem32);
++ }
++ pci_free_consistent(pdev, ms_per_irq * DAHDI_MAX_CHUNKSIZE * MAX_NUM_CARDS * 2 * 2, (void *)wc_dev->writechunk, wc_dev->writedma);
++ pci_set_drvdata(pdev, NULL);
++ dahdi_unregister_device(wc->dev.ddev); //Dennis
++ kfree(wc->dev.ddev->location);
++ dahdi_free_device(wc->dev.ddev);
++ free_wc(wc);
++ return -EIO;
++ }
++ for (z = 0; z < wc_dev->max_cards/*MAX_NUM_CARDS*/; z++) {
++ if (span_flags[z]) {
++ wc->chans[z]->sigcap = __DAHDI_SIG_FXO | DAHDI_SIG_BROKEN;
++ }
++ }
++
++ /* init ec module */
++ if (!wc_dev->vpm) {
++ a24xx_vpm_init(wc);
++ }
++
++ if(wc_dev->master || ( wc_dev->fwversion < ((1 << 16)|2) ) ){
++ if (request_irq(pdev->irq, a24xx_interrupt, DAHDI_IRQ_SHARED, "opvxa24xx"/*wc_dev->card_name*/, wc)) {
++ printk(KERN_NOTICE "Unable to request IRQ %d\n", pdev->irq);
++ if (wc_dev->freeregion) {
++ release_mem_region(wc_dev->mem_region, wc_dev->mem_len);
++ iounmap((void *)wc_dev->mem32);
++ }
++ pci_free_consistent(pdev, ms_per_irq * DAHDI_MAX_CHUNKSIZE * MAX_NUM_CARDS * 2 * 2, (void *)wc_dev->writechunk, wc_dev->writedma);
++ pci_set_drvdata(pdev, NULL);
++ free_wc(wc);
++ return -EIO;
++ }
++ }
++
++ if(0) { // for debug;
++
++ printk("exit after alloc irq\n");
++ /* Free Resources */
++ if(wc_dev->master || ( wc_dev->fwversion < ((1 << 16)|2) ))
++ free_irq(pdev->irq, wc);
++ if (wc_dev->freeregion) {
++ release_mem_region(wc_dev->mem_region, wc_dev->mem_len);
++ iounmap((void *)wc_dev->mem32);
++ }
++ pci_free_consistent(pdev, ms_per_irq * DAHDI_MAX_CHUNKSIZE * MAX_NUM_CARDS * 2 * 2, (void *)wc_dev->writechunk, wc_dev->writedma);
++ pci_set_drvdata(pdev, NULL);
++ dahdi_unregister_device(wc->dev.ddev); //Dennis
++ kfree(wc->dev.ddev->location);
++ dahdi_free_device(wc->dev.ddev);
++ free_wc(wc);
++ return -EIO;
++ }
++
++#ifdef TEST_LOG_INCOME_VOICE
++ for(x=0; x<wc_dev->max_cards; x++) {
++ wc_dev->voc_buf[x] = kmalloc(voc_buffer_size, GFP_KERNEL);
++ wc_dev->voc_ptr[x] = 0;
++ }
++#endif
++ if(cidsupport) {
++ int len = cidbuflen * DAHDI_MAX_CHUNKSIZE;
++ if(debug) {
++ printk("cid support enabled, length is %d msec\n", cidbuflen);
++ }
++ for (x = 0; x < wc_dev->max_cards/*MAX_NUM_CARDS*/; x++) {
++ wc_dev->cid_history_buf[x] = kmalloc(len, GFP_KERNEL);
++ wc_dev->cid_history_ptr[x] = 0;
++ wc_dev->cid_history_clone_cnt[x] = 0;
++ wc_dev->cid_state[x] = CID_STATE_IDLE;
++ }
++ }
++
++ /* set channel sigcap */
++ a24xx_post_initialize(wc);
++
++ init_busydetect(wc, opermode);
++ init_callerid(wc);
++
++ /* Enable interrupts */
++ __opvx_a24xx_enable_interrupts(wc_dev->mem32);
++
++ /* Initialize Write/Buffers to all blank data */
++ memset((void *)wc_dev->writechunk,0, ms_per_irq * DAHDI_MAX_CHUNKSIZE * MAX_NUM_CARDS * 2 * 2);
++
++ /*set irq frequence*/
++ if(wc_dev->fwversion >= ((1 << 16)|1) )
++ __opvx_a24xx_set_irq_frq(wc_dev->mem32,ms_per_irq);
++
++ if(wc_dev->fwversion > ((1 << 16)|2)){
++ __opvx_a24xx_set_master(wc_dev->mem32,wc_dev->master^0x01);
++ }
++ /* Start DMA */
++ __opvx_a24xx_start_dma(wc_dev->mem32, wc_dev->writedma);
++
++ /* module count */
++ for (x = 0; x < wc_dev->max_cards/*MAX_NUM_CARDS*/; x++) {
++ if (wc_dev->cardflag & (1 << x)) {
++ cardcount++;
++ }
++ }
++ fwbuild = __opvx_a24xx_getcreg(wc_dev->mem32, 0x0, 0xe);
++ printk(KERN_NOTICE "Found an OpenVox %s: Version %x.%x (%d modules),Build 0x%08x\n", wc_dev->card_name, wc_dev->fwversion>>16, wc_dev->fwversion&0xffff, cardcount,fwbuild );
++ if(debug) {
++ printk(KERN_DEBUG "OpenVox %s debug On\n", wc_dev->card_name);
++ }
++
++ ifaces[index] = wc;
++ max_iface_index++;
++
++ res = 0;
++ }
++
++ return res;
++}
++
++
++static void a24xx_release(struct a24xx *wc)
++{
++#ifdef TEST_LOG_INCOME_VOICE
++ struct file * f = NULL;
++ mm_segment_t orig_fs;
++ int i;
++ char fname[20];
++#endif
++ int y;
++ char *s;
++ struct a24xx_dev *wc_dev = &wc->dev;
++
++ /*Unregister dahdi_span*/
++ dahdi_unregister_device(wc_dev->ddev); //Dennis
++ /* Release mem region and iounmap mem */
++ if (wc_dev->freeregion) {
++ release_mem_region(wc_dev->mem_region, wc_dev->mem_len);
++ iounmap((void *)wc_dev->mem32);
++ }
++
++#ifdef TEST_LOG_INCOME_VOICE
++ for(i=0; i<wc_dev->max_cards; 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_dev->voc_buf[i]) {
++ f->f_op->write(f, wc_dev->voc_buf[i], voc_buffer_size, &f->f_pos);
++ kfree(wc_dev->voc_buf[i]);
++ }
++
++ set_fs(orig_fs);
++ fput(f);
++ }
++#endif
++
++ /* Release cid history buffer */
++ if(cidsupport) {
++ int x;
++ for (x = 0; x < wc_dev->max_cards/*MAX_NUM_CARDS*/; x++) {
++ kfree(wc_dev->cid_history_buf[x]);
++ }
++ }
++
++ /* Release a24xx */
++ s = wc_dev->card_name;
++ for(y = 0; y < max_iface_index; y++)
++ if(ifaces[y] == wc)
++ break;
++
++
++ kfree(wc_dev->ddev->location); //Dennis
++ dahdi_free_device(wc_dev->ddev);
++ free_wc(wc);
++ ifaces[y]=NULL;
++
++ printk(KERN_INFO "Free an OpenVox %s card\n", s);
++}
++
++static void __devexit a24xx_remove_one(struct pci_dev *pdev)
++{
++ struct a24xx *wc = pci_get_drvdata(pdev);
++ struct a24xx_dev *wc_dev = &wc->dev;
++
++ if (wc) {
++ /* In case hardware is still there */
++ __opvx_a24xx_disable_interrupts(wc_dev->mem32);
++
++ /* Wait some time to handle the last irq */
++ __a24xx_wait_just_a_bit(HZ/10); /* delay 1/10 sec */
++
++ /* Stop any DMA */
++ __opvx_a24xx_stop_dma(wc_dev->mem32);
++
++ /* Reset tdm controller */
++ __opvx_a24xx_reset_tdm(wc_dev->mem32);
++
++ /* Release echo canceller */
++ if (wc_dev->vpm_ec) {
++ opvx_vpm_release(wc_dev->vpm_ec);
++ }
++ wc_dev->vpm_ec = NULL;
++
++ if(wc_dev->master || ( wc_dev->fwversion < ((1 << 16)|2) )){
++ free_irq(pdev->irq, wc);
++ }
++ /* Immediately free resources */
++ pci_free_consistent(pdev, ms_per_irq * DAHDI_MAX_CHUNKSIZE * MAX_NUM_CARDS * 2 * 2, (void *)wc_dev->writechunk, wc_dev->writedma);
++
++ destroy_callerid(wc);
++ destroy_busydetect(wc);
++
++ /* Release span, possibly delayed */
++ if (!wc_dev->usecount) {
++ a24xx_release(wc);
++ } else {
++ wc_dev->dead = 1;
++ }
++ }
++}
++
++static struct pci_device_id a24xx_pci_tbl[] = {
++ { 0x1B74, 0x2410, 0x1B74, 0x0001, 0, 0, (unsigned long) &a2410a }, // Information for A2410 revsion A.
++ { 0x1B74, 0x1610, 0x1B74, 0x0001, 0, 0, (unsigned long) &a1610a }, // Information for A1610 revsion A.
++ { 0x1B74, 0x0810, 0x1B74, 0x0001, 0, 0, (unsigned long) &a810a }, // Information for A810 revsion A.
++ { 0 }
++};
++
++MODULE_DEVICE_TABLE(pci, a24xx_pci_tbl);
++
++static struct pci_driver a24xx_driver = {
++ .name = "opvxa24xx",
++ .probe = a24xx_init_one,
++ .remove = __devexit_p(a24xx_remove_one),
++ .suspend = NULL,
++ .resume = NULL,
++ .id_table = a24xx_pci_tbl,
++};
++
++static int __init a24xx_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;
++ }
++ /* for the voicedaa_check_hook defaults, if the user has not overridden
++ them by specifying them as module parameters, then get the values
++ from the selected operating mode
++ */
++ 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(&a24xx_driver);
++ if (res) {
++ return -ENODEV;
++ }
++ return 0;
++}
++
++static void __exit a24xx_cleanup(void)
++{
++ pci_unregister_driver(&a24xx_driver);
++}
++
++module_param(spi_cmd, int, 0600);
++module_param(debug, int, 0600);
++module_param(ec_debug, int, 0600);
++module_param(vpmsupport, int, 0600);
++module_param(loopcurrent, int, 0600);
++module_param(reversepolarity, int, 0600);
++module_param(robust, int, 0600);
++module_param(_opermode, 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(battalarm, uint, 0600);
++module_param(battthresh, uint, 0600);
++module_param(ringdebounce, 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(cidsupport, int, 0600);
++module_param(cidbuflen, int, 0600);
++module_param(cidtimeout, int, 0600);
++module_param(fxofullscale, int, 0600);
++module_param(fixedtimepolarity, int, 0600);
++module_param(ms_per_irq, int, 0600);
++module_param(irq_stub, int, 0600);
++
++MODULE_DESCRIPTION("OpenVox A2410 Dahdi Driver");
++MODULE_AUTHOR("MiaoLin <lin.miao@openvox.cn>");
++MODULE_LICENSE("GPL v2");
++
++module_init(a24xx_init);
++module_exit(a24xx_cleanup);
++
diff --git a/dahdi-linux-2.10.1-openvox-2.patch b/dahdi-linux-2.10.1-openvox-2.patch
new file mode 100644
index 000000000000..3be86e27c9a4
--- /dev/null
+++ b/dahdi-linux-2.10.1-openvox-2.patch
@@ -0,0 +1,5851 @@
+--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa24xx/base.h 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa24xx/base.h 2015-02-10 14:19:03.000000000 +0100
+@@ -0,0 +1,321 @@
++/*
++ * OpenVox A24xx FXS/FXO Interface Driver for Zapata Telephony interface
++ *
++ * Written by MiaoLin<miaolin@openvox.cn>
++ * $Id: base.h 360 2011-04-06 06:11:45Z yangshugang $
++
++ * 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.
++ *
++ */
++
++
++#ifndef _OPVXA2410P_H
++#define _OPVXA2410P_H
++
++#include <dahdi/kernel.h>
++#include <linux/firmware.h>
++
++#ifndef SLIC_LF_OPEN
++/* Proslic Linefeed options for register 64 - Linefeed Control */
++#define SLIC_LF_OPEN 0x0
++#define SLIC_LF_ACTIVE_FWD 0x1
++#define SLIC_LF_OHTRAN_FWD 0x2 /* Forward On Hook Transfer */
++#define SLIC_LF_TIP_OPEN 0x3
++#define SLIC_LF_RINGING 0x4
++#define SLIC_LF_ACTIVE_REV 0x5
++#define SLIC_LF_OHTRAN_REV 0x6 /* Reverse On Hook Transfer */
++#define SLIC_LF_RING_OPEN 0x7
++
++#define SLIC_LF_SETMASK 0x7
++#define SLIC_LF_OPPENDING 0x10
++
++/* Mask used to reverse the linefeed mode between forward and
++ * reverse polarity. */
++#define SLIC_LF_REVMASK 0x4
++#endif
++
++struct ec;
++
++#define VPM_SUPPORT
++
++#define CARDS_PER_MODULE 4
++#define MOD_TYPE_FXS 0
++#define MOD_TYPE_FXO 1
++
++#define NUM_FXO_REGS 60
++#define WC_MAX_IFACES 128
++#define DEFAULT_RING_DEBOUNCE 64 /* Ringer Debounce (64 ms) */
++
++/* the constants below control the 'debounce' periods enforced by the
++ check_hook routines; these routines are called once every 4 interrupts
++ (the interrupt cycles around the four modules), so the periods are
++ specified in _4 millisecond_ increments
++*/
++#define DEFAULT_BATT_DEBOUNCE 4 /* Battery debounce (64 ms) */
++#define POLARITY_DEBOUNCE 64 /* Polarity debounce (64 ms) */
++#define DEFAULT_BATT_THRESH 3 /* Anything under this is "no battery" */
++
++#define OHT_TIMER 6000 /* How long after RING to retain OHT */
++
++#define NEON_MWI_RNGY_PULSEWIDTH 0x3e8 /*=> period of 250 mS */
++
++#define FLAG_3215 (1 << 0)
++
++#define MAX_NUM_CARDS 24
++
++enum cid_hook_state {
++ CID_STATE_IDLE = 0,
++ CID_STATE_RING_DELAY,
++ 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 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
++
++
++#define __CMD_RD (1 << 20) /* Read Operation */
++#define __CMD_WR (1 << 21) /* Write Operation */
++#define __CMD_FIN (1 << 22) /* Has finished receive */
++#define __CMD_TX (1 << 23) /* Has been transmitted */
++
++#define CMD_WR(a,b) (((a) << 8) | (b) | __CMD_WR)
++#define CMD_RD(a) (((a) << 8) | __CMD_RD)
++
++
++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 a24xx_dev {
++ struct pci_dev *dev;
++ char *variety;
++ 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;
++ struct dahdi_vmwi_info vmwisetting;
++ int vmwi_active_messages;
++ u32 vmwi_lrev:1; /*MWI Line Reversal*/
++ u32 vmwi_hvdc:1; /*MWI High Voltage DC Idle line*/
++ u32 vmwi_hvac:1; /*MWI Neon High Voltage AC Idle line*/
++ u32 neonringing:1;/*Ring Generator is set for NEON*/
++ int reversepolarity; /* polarity reversal */
++ spinlock_t lasttxhooklock;
++ } 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 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 */
++
++#ifdef TEST_LOG_INCOME_VOICE
++ char * voc_buf[MAX_NUM_CARDS];
++ int voc_ptr[MAX_NUM_CARDS];
++#endif
++ unsigned short ledstate;
++ unsigned int fwversion;
++ int max_cards;
++ char *card_name;
++ unsigned int master;
++
++ 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];
++
++#ifdef VPM_SUPPORT
++ struct ec *vpm_ec;
++ int vpm;
++
++ unsigned long dtmfactive;
++ unsigned long dtmfmask;
++ unsigned long dtmfmutemask;
++
++#endif
++
++};
++
++struct a24xx_desc {
++ char *name;
++ int flags;
++};
++
++struct a24xx {
++ struct a24xx_dev dev;
++ struct dahdi_span span;
++ struct dahdi_chan _chans[MAX_NUM_CARDS];
++ struct dahdi_chan *chans[MAX_NUM_CARDS];
++ struct dahdi_echocan_state *ec[32]; /* Echcan state for each channel */
++ unsigned int index;
++};
++
++/*
++ * from bin
++ */
++extern void __opvx_a24xx_setcreg(unsigned long mem32, unsigned int offset, unsigned int reg, unsigned int val);
++extern unsigned int __opvx_a24xx_getcreg(unsigned long mem32, unsigned int offset, unsigned char reg);
++extern unsigned char __opvx_a24xx_read_8bits(unsigned long mem32);
++
++extern void __opvx_a24xx_reset_modules(unsigned long mem32, void (*func)(int), int data);
++extern void __opvx_a24xx_reset_modules_v2(unsigned long mem32, void (*func)(int), int data);
++extern void __opvx_a24xx_setcard(unsigned long mem32, int card);
++extern void __opvx_a24xx_reset_spi(void *wc_dev, int card, void (*func)(void*, int));
++extern void __opvx_a24xx_write_8bits(unsigned long mem32, unsigned char bits);
++
++extern void __opvx_a24xx_set_master(unsigned long mem32,unsigned int master);
++extern unsigned int __opvx_a24xx_get_master(unsigned long mem32);
++extern void __opvx_a24xx_set_irq_frq(unsigned long mem32,unsigned int frq);
++extern unsigned int __opvx_a24xx_get_version(unsigned long mem32);
++extern unsigned int __opvx_a24xx_get_irqstatus(unsigned long mem32);
++extern void __opvx_a24xx_set_irqstatus(unsigned long mem32, unsigned int value);
++extern void __opvx_a24xx_clear_irqs(unsigned long mem32);
++extern void __opvx_a24xx_enable_interrupts(unsigned long mem32);
++extern void __opvx_a24xx_disable_interrupts(unsigned long mem32);
++extern unsigned int __opvx_a24xx_get_irqcnt_lo(unsigned long mem32);
++
++extern void __opvx_a24xx_restart_dma(unsigned long mem32);
++extern void __opvx_a24xx_start_dma(unsigned long mem32, unsigned int data);
++extern void __opvx_a24xx_stop_dma(unsigned long mem32);
++extern void __opvx_a24xx_reset_tdm(unsigned long mem32);
++
++extern void __opvx_a24xx_spi_setreg(void *wc_dev, unsigned long mem32, int card, int modtype, unsigned char reg, unsigned char value, void (*func)(void*, int));
++extern unsigned char __opvx_a24xx_spi_getreg(void *wc_dev, unsigned long mem32, int card, int modtype, unsigned char reg, void (*func)(void*, int));
++
++extern unsigned int __opvx_a24xx_oct_in(unsigned long mem32, unsigned int addr);
++extern unsigned int __opvx_a24xx_oct_in_v2(unsigned long mem32, unsigned int addr);
++extern void __opvx_a24xx_oct_out(unsigned long mem32, unsigned int addr, unsigned int value);
++extern void __opvx_a24xx_oct_out_v2(unsigned long mem32, unsigned int addr, unsigned int value);
++extern int __opvx_a24xx_check_vpm(unsigned long mem32);
++extern int __opvx_a24xx_check_vpm_v2(unsigned long mem32);
++extern void __opvx_a24xx_vpm_setpresent(unsigned long mem32);
++extern void __opvx_a24xx_vpm_setpresent_v2(unsigned long mem32);
++
++extern void __opvx_a24xx_transmit(unsigned long mem32, volatile unsigned char *writechunk, volatile unsigned char **txbuf,unsigned int irq_frq , unsigned int order);
++extern void __opvx_a24xx_receive(unsigned long mem32, volatile unsigned char *readchunk, volatile unsigned char **rxbuf,unsigned int irq_frq , unsigned int order);
++
++extern void __opvx_a24xx_set_chunk(void *readchunk, void *writechunk,unsigned int frq);
++
++/*
++ * from a24xx.c
++ */
++extern void __a24xx_wait_just_a_bit(int foo);
++extern void __a24xx_spi_setreg(struct a24xx_dev *wc_dev, int card, unsigned char reg, unsigned char value);
++extern unsigned char __a24xx_spi_getreg(struct a24xx_dev *wc_dev, int card, unsigned char reg);
++extern void a24xx_spi_setreg(struct a24xx_dev *wc_dev, int card, unsigned char reg, unsigned char value);
++extern unsigned char a24xx_spi_getreg(struct a24xx_dev *wc_dev, int card, unsigned char reg);
++extern void a24xx_reset_spi(struct a24xx_dev *wc_dev, int card);
++extern void __a24xx_setcard(void *wc_dev, int card);
++extern void oct_set_reg(void *data, unsigned int reg, unsigned int val);
++extern unsigned int oct_get_reg(void *data, unsigned int reg);
++extern void __a24xx_vpm_setpresent(struct a24xx_dev *wc_dev);
++extern int __a24xx_proslic_setreg_indirect(struct a24xx_dev *wc_dev, int card, unsigned char address, unsigned short data);
++extern int __a24xx_proslic_getreg_indirect(struct a24xx_dev *wc_dev, int card, unsigned char address);
++extern int __a24xx_malloc_chunk(struct a24xx_dev *wc_dev,unsigned int frq);
++
++/*
++ * from si321x.c
++ */
++extern int si321x_init_ring_generator_mode(struct a24xx_dev *wc_dev, int card);
++extern int si321x_set_ring_generator_mode(struct a24xx_dev *wc_dev, int card, int mode);
++extern int si321x_init_proslic(struct a24xx_dev *wc_dev, int card, int fast, int manual, int sane);
++extern int si321x_init_proslic_all(struct a24xx_dev *wc_dev, int fxs_flag,int fast, int manual, int sane,int *blk_flag);
++extern int si321x_proslic_setreg_indirect(struct a24xx_dev *wc_dev, int card, unsigned char address, unsigned short data);
++extern int si321x_proslic_getreg_indirect(struct a24xx_dev *wc_dev, int card, unsigned char address);
++extern void si321x_proslic_recheck_sanity(struct a24xx_dev *wc_dev, int card);
++
++/*
++ * from si3050.c
++ */
++int si3050_set_hwgain(struct a24xx_dev *wc_dev, int card, __s32 gain, __u32 tx);
++int si3050_init_voicedaa(struct a24xx_dev *wc_dev, int card, int fast, int manual, int sane);
++
++#endif
++
+--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa24xx/busydetect.c 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa24xx/busydetect.c 2015-02-10 14:19:03.000000000 +0100
+@@ -0,0 +1,876 @@
++/*
++ * OpenVox FXO Detect Busy Voice Driver for DAHDI Telephony interface
++ *
++ * Written by kevin.chen
++
++ * Copyright (C) 2012-2013 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 history
++ *
++ * Rev 0.10 remove structure country_busytone
++ * add GEN_BUSYTONE_ONTIME and GEN_BUSYTONE_OFFTIME
++ * Rev 0.20 fix bug
++ * after frequency is set to a valid value, cannot reset the zero value
++ * Rev 0.30 support new kernel version 3.10.0
++ *
++ */
++
++#include <linux/proc_fs.h>
++#include "busydetect.h"
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
++#include <linux/seq_file.h>
++#endif
++
++static const char *module_name = "opvxdsp";
++static int ref_count = 0;
++
++#define MAX_TONE_NUM 3
++#define TONE_FREQ_NUM 2
++
++#define GEN_BUSYTONE_ONTIME 500
++#define GEN_BUSYTONE_OFFTIME 500
++
++#define TONE_THRESHOLD 20548
++#define SILENT_THRESHOLD 2560
++#define ENERGY_SCALE (FRAME_SHORT_SIZE >> 1)
++
++struct freq_state {
++ int freq;
++ /* Calculate the sampling data*/
++ short prev2;
++ short prev;
++ short fac;
++};
++
++struct tone {
++ int busycount;
++ int threshold;
++ int ontime;
++ int offtime;
++ struct freq_state fs[TONE_FREQ_NUM];
++ struct proc_dir_entry *subentry;
++};
++
++struct silent_detect {
++ int detect_tx;
++ int detect_rx;
++ /* Mute timeout */
++ int length;
++ int threshold;
++ struct proc_dir_entry *subentry;
++
++ /* Statistics mute sample */
++ u32 tx_samples;
++ u32 rx_samples;
++};
++
++struct param {
++ struct tone tone[MAX_TONE_NUM];
++ struct silent_detect sd;
++};
++
++struct detect_state {
++ int index;
++ u32 length;
++};
++
++struct dsp_info {
++ /* The read data cache */
++ short rdata[FRAME_SHORT_SIZE];
++ int rlen;
++ /* The write data cache */
++ short wdata[FRAME_SHORT_SIZE];
++ int wlen;
++
++ struct param param;
++ struct proc_dir_entry *entry;
++
++ u32 energy;
++ int count;
++ int detected_tone;
++ struct detect_state state[3];
++
++ /* The busy tone appear */
++ int appear_busy;
++
++ /* Calculate parameters for generate waveform */
++ /* Read and write two direction */
++ int phase[2][TONE_FREQ_NUM];
++ int is_offtime[2];
++ int offset[2];
++};
++
++struct detect_info {
++ struct dsp_info dsp[TOTAL_CARDS];
++
++ /* Variable for generater waveform */
++ u32 phase_rate[TONE_FREQ_NUM];
++ short gain[TONE_FREQ_NUM];
++ int ontime;
++ int offtime;
++};
++
++static struct detect_info *di = NULL;
++
++static DECLARE_BITMAP(fxo_cardflag, TOTAL_CARDS);
++static DECLARE_BITMAP(fxs_cardflag, TOTAL_CARDS);
++
++static struct proc_dir_entry *opvxdsp_entry;
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Kevin.chen");
++MODULE_DESCRIPTION("DAHDI detect busy voice");
++
++static int detect_is_close(int channo)
++{
++ int i;
++ struct param *param = &di->dsp[channo - 1].param;
++
++ for (i = 0; i < MAX_TONE_NUM; i++) {
++ if (param->tone[i].busycount > 0) {
++ return 0;
++ }
++ }
++
++ if (param->sd.length > 0 && (param->sd.detect_tx || param->sd.detect_rx)) {
++ return 0;
++ }
++
++ return 1;
++}
++
++static void reset_dsp_detect_param(struct dsp_info *dsp)
++{
++ int i;
++
++ dsp->param.sd.tx_samples = 0;
++ dsp->param.sd.rx_samples = 0;
++
++ dsp->detected_tone = -1;
++ dsp->count = 0;
++ for (i = 0; i < 3; i++) {
++ dsp->state[i].index = -1;
++ dsp->state[i].length = 0;
++ }
++}
++
++static void reset_dsp_generate_param(struct dsp_info *dsp)
++{
++ int i, j;
++
++ if (dsp->appear_busy) {
++ dsp->appear_busy = 0;
++
++ for (i = 0; i < 2; i++) {
++ dsp->is_offtime[i] = 0;
++ dsp->offset[i] = 0;
++ for (j = 0; j < TONE_FREQ_NUM; j++) {
++ dsp->phase[i][j] = 0;
++ }
++ }
++ }
++}
++
++static void update_detect(struct dsp_info *dsp, short s)
++{
++ int i, j;
++ short tmp;
++ struct freq_state *fs;
++
++ for (i = 0; i < MAX_TONE_NUM; i++) {
++ if (dsp->param.tone[i].busycount <= 0) {
++ continue;
++ }
++
++ for (j = 0; j < TONE_FREQ_NUM; j++) {
++ fs = &dsp->param.tone[i].fs[j];
++ tmp = fs->prev2;
++ fs->prev2 = fs->prev;
++ fs->prev = (((int)fs->fac * fs->prev2) >> 14) - tmp + (s >> 7);
++ }
++ }
++}
++
++static u32 detect_result(struct freq_state *fs)
++{
++ u32 val;
++
++ val = fs->prev * fs->prev + fs->prev2 * fs->prev2 - ((fs->fac * fs->prev) >> 14) * fs->prev2;
++ /* Reset */
++ fs->prev = fs->prev2 = 0;
++
++ return val;
++}
++
++static int busy_detect(struct dsp_info *dsp, int len)
++{
++ int i, j;
++ u32 power, max_power = 0;
++ int index = -1;
++
++ for (i = 0; i < MAX_TONE_NUM; i++) {
++ power = 0;
++ for (j = 0; j < TONE_FREQ_NUM; j++) {
++ power += detect_result(&dsp->param.tone[i].fs[j]);
++ }
++
++ if (dsp->param.tone[i].busycount > 0 &&
++ dsp->energy > dsp->param.tone[i].threshold &&
++ power > ENERGY_SCALE * dsp->energy) {
++ if (power > max_power) {
++ max_power = power;
++ index = i;
++ }
++ }
++ }
++
++ if (index != -1 && dsp->detected_tone != index) {
++ dsp->detected_tone = index;
++ dsp->count = 0;
++ for (i = 0; i < 3; i++) {
++ dsp->state[i].index = -1;
++ dsp->state[i].length = 0;
++ }
++ }
++
++ if (dsp->state[2].index != index) {
++ dsp->state[2].index = index;
++ dsp->state[1].length += len;
++ } else {
++ if (dsp->state[1].index != index) {
++ if (dsp->detected_tone >= 0) {
++ if (dsp->state[0].index == dsp->detected_tone && dsp->state[1].index == -1) {
++ if ((dsp->state[0].length >= dsp->param.tone[dsp->detected_tone].ontime * 8 - FRAME_SHORT_SIZE * 2) && \
++ (dsp->state[0].length <= dsp->param.tone[dsp->detected_tone].ontime * 8 + FRAME_SHORT_SIZE * 2) && \
++ (dsp->state[1].length >= dsp->param.tone[dsp->detected_tone].offtime * 8 - FRAME_SHORT_SIZE * 2)) {
++ dsp->count++;
++ if (dsp->count >= dsp->param.tone[dsp->detected_tone].busycount) {
++ return 1;
++ }
++ }
++ }
++ }
++ memmove(&dsp->state[0], &dsp->state[1], 2 * sizeof(dsp->state[0]));
++ dsp->state[1].index = index;
++ dsp->state[1].length = len;
++ } else {
++ dsp->state[1].length += len;
++ }
++ }
++
++ return 0;
++}
++
++static int silent_detect(struct dsp_info *dsp, int len, int is_write)
++{
++ int res = 0;
++
++ if (dsp->param.sd.length <= 0) {
++ return res;
++ }
++
++ if (dsp->energy < dsp->param.sd.threshold) {
++ if (is_write) {
++ dsp->param.sd.tx_samples += len;
++ if (dsp->param.sd.tx_samples >= dsp->param.sd.length * SAMPLE_PER_SEC) {
++ res = 1;
++ }
++ } else {
++ dsp->param.sd.rx_samples += len;
++ if (dsp->param.sd.rx_samples >= dsp->param.sd.length * SAMPLE_PER_SEC) {
++ res = 1;
++ }
++ }
++ } else {
++ dsp->param.sd.tx_samples = 0;
++ dsp->param.sd.rx_samples = 0;
++ }
++
++ return res;
++}
++
++static short calc_amp(u32 *acc, u32 rate, short scale)
++{
++ u32 phase, step;
++ short amp;
++
++ phase = *acc;
++ phase >>= 23;
++ step = phase & (SIN_DIVISION - 1);
++ if ((phase & SIN_DIVISION)) {
++ step = SIN_DIVISION - step;
++ }
++
++ amp = sin_table[step];
++ if ((phase & (2 * SIN_DIVISION))) {
++ amp = -amp;
++ }
++
++ *acc += rate;
++ return (short)(((u32)amp * scale) >> 15);
++}
++
++static short generater_amp(struct dsp_info *dsp, int is_write)
++{
++ int i;
++ short amp = 0;
++ int index = (is_write == 0) ? 0 : 1;
++
++ dsp->offset[index]++;
++ if (!dsp->is_offtime[index]) {
++ for (i = 0; i < TONE_FREQ_NUM; i++) {
++ if (di->phase_rate[i] > 0) {
++ amp += calc_amp(&dsp->phase[index][i], di->phase_rate[i], di->gain[i]);
++ }
++ }
++ if (dsp->offset[index] >= di->ontime * 8) {
++ dsp->offset[index] = 0;
++ dsp->is_offtime[index] = 1;
++ }
++ } else {
++ if (dsp->offset[index] >= di->offtime * 8) {
++ dsp->offset[index] = 0;
++ dsp->is_offtime[index] = 0;
++ }
++ }
++
++ return amp;
++}
++
++static void analysis_dsp(struct dsp_info *dsp, short s[], int len, int is_write)
++{
++ int i, res = 0;
++ u16 temp;
++
++ for (i = 0; i < len; i++) {
++ temp = (s[i] < 0 ? -s[i] : s[i]) >> 7;
++ dsp->energy += temp * temp;
++ if (!is_write) {
++ update_detect(dsp, s[i]);
++ }
++ }
++
++ if (is_write) {
++
++ if (dsp->param.sd.detect_tx) {
++ res = silent_detect(dsp, len, 1);
++ }
++ } else {
++ res = busy_detect(dsp, len);
++
++ /* Read only support the silent detect */
++ if (!res && dsp->param.sd.detect_rx) {
++ res = silent_detect(dsp, len, 0);
++ }
++ }
++
++ dsp->energy = 0;
++
++ if (res) {
++ dsp->appear_busy = 1;
++ reset_dsp_detect_param(dsp);
++ }
++}
++
++void parser_busy_silent_process(struct a24xx *wc, int is_write)
++{
++ int i, j;
++ struct a24xx_dev *wc_dev = &wc->dev;
++ struct dahdi_chan *chan;
++ struct dsp_info *dsp;
++
++ if (!di) {
++ /* Initialize this module failed */
++ return;
++ }
++
++ for (i = 0; i < wc_dev->max_cards; i++) {
++ chan = wc->chans[i];
++ dsp = &di->dsp[chan->channo - 1];
++ if (chan->channo > TOTAL_CARDS) {
++ continue;
++ }
++ if ((wc_dev->modtype[i] != MOD_TYPE_FXO) ||
++ (wc_dev->modtype[i] == MOD_TYPE_FXO && !wc_dev->mod[i].fxo.offhook)) {
++ reset_dsp_generate_param(dsp);
++ continue;
++ }
++
++ if (detect_is_close(chan->channo)) {
++ /* The busy tone and silence detection has been closed */
++ continue;
++ }
++
++ if (is_write) {
++ if (dsp->appear_busy) {
++ for (j = 0; j < DAHDI_CHUNKSIZE; j++) {
++ chan->writechunk[j] = DAHDI_LIN2X(generater_amp(dsp, 1), chan);
++ }
++ } else {
++ for (j = 0; j < DAHDI_CHUNKSIZE; j++) {
++ dsp->wdata[dsp->wlen++] = DAHDI_XLAW(chan->writechunk[j], chan);
++ }
++ if (dsp->wlen == FRAME_SHORT_SIZE) {
++ dsp->wlen = 0;
++ analysis_dsp(dsp, dsp->wdata, FRAME_SHORT_SIZE, 1);
++ }
++ }
++ } else {
++ if (dsp->appear_busy) {
++ for (j = 0; j < DAHDI_CHUNKSIZE; j++) {
++ chan->readchunk[j] = DAHDI_LIN2X(generater_amp(dsp, 0), chan);
++ }
++ } else {
++ for (j = 0; j < DAHDI_CHUNKSIZE; j++) {
++ dsp->rdata[dsp->rlen++] = DAHDI_XLAW(chan->readchunk[j], chan);
++ }
++ if (dsp->rlen == FRAME_SHORT_SIZE) {
++ dsp->rlen = 0;
++ analysis_dsp(dsp, dsp->rdata, FRAME_SHORT_SIZE, 0);
++ }
++ }
++ }
++ }
++}
++
++static short get_fac(int freq)
++{
++ if (freq < MIN_INDEX_FREQ) {
++ freq = MIN_INDEX_FREQ;
++ } else if (freq > MAX_INDEX_FREQ) {
++ freq = MAX_INDEX_FREQ;
++ }
++
++ return fac_table[(freq - MIN_INDEX_FREQ) / STEP_FREQ];
++}
++
++static u32 get_phase_rate(int freq)
++{
++ return (freq * 65536 / SAMPLE_PER_SEC) * 65536;
++}
++
++static short get_gain(int level)
++{
++ if (level < MIN_INDEX_LEVEL) {
++ level = MIN_INDEX_LEVEL;
++ } else if (level > MAX_INDEX_LEVEL) {
++ level = MAX_INDEX_LEVEL;
++ }
++
++ return gain_table[(level - MIN_INDEX_LEVEL) / STEP_LEVEL];
++}
++
++static void config_proc_param(struct param *param)
++{
++ int i, j;
++
++ for (i = 0; i < MAX_TONE_NUM; i++) {
++ param->tone[i].busycount = 0;
++ param->tone[i].threshold = TONE_THRESHOLD;
++ param->tone[i].ontime = 0;
++ param->tone[i].offtime = 0;
++ for (j = 0; j < TONE_FREQ_NUM; j++) {
++ param->tone[i].fs[j].freq = 0;
++ param->tone[i].fs[j].fac = 0;
++ }
++ }
++ param->sd.detect_tx = 0;
++ param->sd.detect_rx = 0;
++ param->sd.length = 30;
++ param->sd.threshold = SILENT_THRESHOLD;
++}
++
++static void init_detect_info(struct detect_info *di, const char *opermode)
++{
++ int i, j;
++
++ di->ontime = GEN_BUSYTONE_ONTIME;
++ di->offtime = GEN_BUSYTONE_OFFTIME;
++ di->phase_rate[0] = get_phase_rate(450);
++ di->gain[0] = get_gain(-12);
++
++ /* Set default parameters */
++ for (i = 0; i < TOTAL_CARDS; i++) {
++ config_proc_param(&di->dsp[i].param);
++
++ di->dsp[i].detected_tone = -1;
++ for (j = 0; j < 3; j++) {
++ di->dsp[i].state[j].index = -1;
++ }
++ }
++}
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
++static int write_param_proc(struct file *filp, const char __user *buf,
++ unsigned long count, void *data)
++#else
++static ssize_t write_param_proc(struct file *file, const char __user *buf,
++ size_t count, loff_t *pos)
++#endif
++{
++ char temp[24];
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
++ int *param = (int *)data;
++#else
++ int *param = PDE_DATA(file_inode(file));
++#endif
++ int value;
++ int len;
++
++ len = count > (sizeof(temp) - 1) ? (sizeof(temp) - 1) : count;
++
++ if (copy_from_user(temp, buf, len)) {
++ return -EFAULT;
++ }
++
++ temp[len] = '\0';
++
++ value = simple_strtoul(temp, NULL, 10);
++ if (value >= 0) {
++ *param = value;
++ }
++
++ return count;
++}
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
++static int read_param_proc(char *buf, char **start, off_t off, int count,
++ int *eof, void *data)
++{
++ int res;
++ int *param = (int *)data;
++
++ if (off > 0) {
++ /* We have finished to read, return 0 */
++ res = 0;
++ } else {
++ res = sprintf(buf, "%d", *param);
++ }
++
++ return res;
++}
++
++static void create_param_proc(const char *name, struct proc_dir_entry *base, void *data)
++{
++ struct proc_dir_entry *entry;
++
++ entry = create_proc_entry(name, 0644, base);
++ if (entry) {
++ entry->data = data;
++ entry->read_proc = read_param_proc;
++ entry->write_proc = write_param_proc;
++ }
++}
++#else
++static int param_proc_show(struct seq_file *m, void *v)
++{
++ int *param = (int *)m->private;
++
++ seq_printf(m, "%d", *param);
++ return 0;
++}
++
++static int open_param_proc(struct inode *inode, struct file *file)
++{
++ return single_open(file, param_proc_show, PDE_DATA(inode));
++}
++
++static struct file_operations proc_param_fops = {
++ .open = open_param_proc,
++ .read = seq_read,
++ .write = write_param_proc,
++ .llseek = seq_lseek,
++ .release = single_release,
++};
++
++static void create_param_proc(const char *name, struct proc_dir_entry *base, void *data)
++{
++ proc_create_data(name, 0644, base, &proc_param_fops, data);
++}
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
++static int write_param_freq_proc(struct file *filp, const char __user *buf,
++ unsigned long count, void *data)
++#else
++static ssize_t write_param_freq_proc(struct file *file, const char __user *buf,
++ size_t count, loff_t *pos)
++#endif
++{
++ char temp[24];
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
++ struct freq_state *fs = (struct freq_state *)data;
++#else
++ struct freq_state *fs = PDE_DATA(file_inode(file));
++#endif
++ int value;
++ int len;
++
++ len = count > (sizeof(temp) - 1) ? (sizeof(temp) - 1) : count;
++
++ if (copy_from_user(temp, buf, len)) {
++ return -EFAULT;
++ }
++
++ temp[len] = '\0';
++
++ value = simple_strtoul(temp, NULL, 10);
++ if (!value || (value >= MIN_INDEX_FREQ && value <= MAX_INDEX_FREQ)) {
++ fs->freq = value;
++ if (fs->freq) {
++ fs->fac = get_fac(fs->freq);
++ } else {
++ fs->fac = 0;
++ }
++ }
++
++ return count;
++}
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
++static int read_param_freq_proc(char *buf, char **start, off_t off, int count,
++ int *eof, void *data)
++{
++ int res;
++ struct freq_state *fs = (struct freq_state *)data;
++
++ if (off > 0) {
++ /* We have finished to read, return 0 */
++ res = 0;
++ } else {
++ res = sprintf(buf, "%d", fs->freq);
++ }
++
++ return res;
++}
++
++static void create_param_freq_proc(const char *name, struct proc_dir_entry *base, void *data)
++{
++ struct proc_dir_entry *entry;
++
++ entry = create_proc_entry(name, 0644, base);
++ if (entry) {
++ entry->data = data;
++ entry->read_proc = read_param_freq_proc;
++ entry->write_proc = write_param_freq_proc;
++ }
++}
++#else
++static int param_freq_proc_show(struct seq_file *m, void *v)
++{
++ struct freq_state *fs = (struct freq_state *)m->private;
++
++ seq_printf(m, "%d", fs->freq);
++ return 0;
++}
++
++static int open_param_freq_proc(struct inode *inode, struct file *file)
++{
++ return single_open(file, param_freq_proc_show, PDE_DATA(inode));
++}
++
++static struct file_operations proc_param_freq_fops = {
++ .open = open_param_freq_proc,
++ .read = seq_read,
++ .write = write_param_freq_proc,
++ .llseek = seq_lseek,
++ .release = single_release,
++};
++
++static void create_param_freq_proc(const char *name, struct proc_dir_entry *base, void *data)
++{
++ proc_create_data(name, 0644, base, &proc_param_freq_fops, data);
++}
++#endif
++
++/*
++ * \brief parameter
++ * flag: DECLARE_BITMAP structure definition, TOTAL_CARDS length
++ * is_clean: 0 is to create, 1 is to remove
++ */
++static void rebuild_recur_proc(unsigned long *flag, int is_clean)
++{
++ int i, j, k;
++ char temp[24];
++ struct param *param;
++ struct proc_dir_entry *entry, *subentry;
++
++ if (!opvxdsp_entry) {
++ return;
++ }
++
++ if (is_clean) {
++ for (i = 0; i < TOTAL_CARDS; i++) {
++ if (test_bit(i, flag)) {
++ entry = di->dsp[i].entry;
++ if (entry) {
++ param = &di->dsp[i].param;
++ for (j = 0; j < MAX_TONE_NUM; j++) {
++ subentry = param->tone[j].subentry;
++ if (subentry) {
++ remove_proc_entry("busycount", subentry);
++ remove_proc_entry("threshold", subentry);
++ remove_proc_entry("ontime", subentry);
++ remove_proc_entry("offtime", subentry);
++ for (k = 0; k < TONE_FREQ_NUM; k++) {
++ sprintf(temp, "frequency%d", k + 1);
++ remove_proc_entry(temp, subentry);
++ }
++
++ sprintf(temp, "tone%d", j + 1);
++ remove_proc_entry(temp, entry);
++ }
++ }
++ subentry = param->sd.subentry;
++ if (subentry) {
++ remove_proc_entry("detect_tx", subentry);
++ remove_proc_entry("detect_rx", subentry);
++ remove_proc_entry("length", subentry);
++ remove_proc_entry("threshold", subentry);
++ remove_proc_entry("silent_detect", entry);
++ }
++
++ sprintf(temp, "%d", i + 1);
++ remove_proc_entry(temp, opvxdsp_entry);
++ }
++ }
++ }
++ } else {
++ for (i = 0; i < TOTAL_CARDS; i++) {
++ if (test_bit(i, flag)) {
++ sprintf(temp, "%d", i + 1);
++ entry = proc_mkdir(temp, opvxdsp_entry);
++ di->dsp[i].entry = entry;
++ if (entry) {
++ param = &di->dsp[i].param;
++ for (j = 0; j < MAX_TONE_NUM; j++) {
++ sprintf(temp, "tone%d", j + 1);
++ subentry = proc_mkdir(temp, entry);
++ param->tone[j].subentry = subentry;
++ if (subentry) {
++ create_param_proc("busycount", subentry, &param->tone[j].busycount);
++ create_param_proc("threshold", subentry, &param->tone[j].threshold);
++ create_param_proc("ontime", subentry, &param->tone[j].ontime);
++ create_param_proc("offtime", subentry, &param->tone[j].offtime);
++ for (k = 0; k < TONE_FREQ_NUM; k++) {
++ sprintf(temp, "frequency%d", k + 1);
++ create_param_freq_proc(temp, subentry, &param->tone[j].fs[k]);
++ }
++ }
++ }
++ subentry = proc_mkdir("silent_detect", entry);
++ param->sd.subentry = subentry;
++ if (subentry) {
++ create_param_proc("detect_tx", subentry, &param->sd.detect_tx);
++ create_param_proc("detect_rx", subentry, &param->sd.detect_rx);
++ create_param_proc("length", subentry, &param->sd.length);
++ create_param_proc("threshold", subentry, &param->sd.threshold);
++ }
++ }
++ }
++ }
++ }
++}
++
++static void set_chan_cards_bit(struct a24xx *wc)
++{
++ int i, channo;
++ DECLARE_BITMAP(tempflag, TOTAL_CARDS);
++ struct a24xx_dev *wc_dev = &wc->dev;
++
++ bitmap_zero(tempflag, TOTAL_CARDS);
++
++ for (i = 0; i < wc_dev->max_cards; i++) {
++ channo = wc->chans[i]->channo;
++ if (channo <= TOTAL_CARDS) {
++ if (wc_dev->modtype[i] == MOD_TYPE_FXO) {
++ set_bit(channo - 1, fxo_cardflag);
++ set_bit(channo - 1, tempflag);
++ }
++ }
++ }
++
++ rebuild_recur_proc(tempflag, 0);
++}
++
++static void clear_chan_cards_bit(struct a24xx *wc)
++{
++ int i, channo;
++ DECLARE_BITMAP(tempflag, TOTAL_CARDS);
++ struct a24xx_dev *wc_dev = &wc->dev;
++
++ bitmap_zero(tempflag, TOTAL_CARDS);
++
++ for (i = 0; i < wc_dev->max_cards; i++) {
++ channo = wc->chans[i]->channo;
++ if (channo <= TOTAL_CARDS) {
++ if (wc_dev->modtype[i] == MOD_TYPE_FXO) {
++ clear_bit(channo - 1, fxo_cardflag);
++ set_bit(channo - 1, tempflag);
++ }
++ }
++ }
++
++ rebuild_recur_proc(tempflag, 1);
++}
++
++int init_busydetect(struct a24xx *wc, const char *opermode)
++{
++ int res = 0;
++
++ if (!ref_count++) {
++ bitmap_zero(fxo_cardflag, TOTAL_CARDS);
++ bitmap_zero(fxs_cardflag, TOTAL_CARDS);
++
++ di = kzalloc(sizeof(*di), GFP_KERNEL);
++ if (!di) {
++ printk(KERN_ERR "Not enough memory, A2410P not support the busy tone and silence detection\n");
++ res = -ENOMEM;
++ goto out;
++ }
++
++ init_detect_info(di, opermode);
++
++ opvxdsp_entry = proc_mkdir(module_name, NULL);
++
++ printk(KERN_INFO "A2410P start the busy tone and silence detection\n");
++ }
++
++ set_chan_cards_bit(wc);
++out:
++ return res;
++}
++
++void destroy_busydetect(struct a24xx *wc)
++{
++ if (ref_count) {
++ clear_chan_cards_bit(wc);
++
++ if (!--ref_count) {
++ if (di) {
++ remove_proc_entry(module_name, NULL);
++ kfree(di);
++ printk(KERN_INFO "A2410P stop the busy tone and silence detection\n");
++ }
++ }
++ }
++}
+--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa24xx/busydetect.h 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa24xx/busydetect.h 2015-02-10 14:19:03.000000000 +0100
+@@ -0,0 +1,313 @@
++/*
++ * OpenVox FXO Detect Busy Voice Driver for DAHDI Telephony interface
++ *
++ * Written by kevin.chen
++
++ * Copyright (C) 2012-2013 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.
++ *
++ */
++
++#ifndef _BUSYDETECT_H
++#define _BUSYDETECT_H
++
++#include "base.h"
++
++#define FRAME_SHORT_SIZE (DAHDI_CHUNKSIZE * 20)
++#define SAMPLE_PER_SEC 8000
++
++#define TOTAL_CARDS 120
++
++#define SIN_DIVISION 128
++static const short sin_table[] =
++{
++ 201,
++ 603,
++ 1005,
++ 1407,
++ 1809,
++ 2210,
++ 2611,
++ 3012,
++ 3412,
++ 3812,
++ 4211,
++ 4609,
++ 5007,
++ 5404,
++ 5800,
++ 6195,
++ 6590,
++ 6983,
++ 7376,
++ 7767,
++ 8157,
++ 8546,
++ 8933,
++ 9319,
++ 9704,
++ 10088,
++ 10469,
++ 10850,
++ 11228,
++ 11605,
++ 11980,
++ 12354,
++ 12725,
++ 13095,
++ 13463,
++ 13828,
++ 14192,
++ 14553,
++ 14912,
++ 15269,
++ 15624,
++ 15976,
++ 16326,
++ 16673,
++ 17018,
++ 17361,
++ 17700,
++ 18037,
++ 18372,
++ 18703,
++ 19032,
++ 19358,
++ 19681,
++ 20001,
++ 20318,
++ 20632,
++ 20943,
++ 21251,
++ 21555,
++ 21856,
++ 22154,
++ 22449,
++ 22740,
++ 23028,
++ 23312,
++ 23593,
++ 23870,
++ 24144,
++ 24414,
++ 24680,
++ 24943,
++ 25202,
++ 25457,
++ 25708,
++ 25956,
++ 26199,
++ 26439,
++ 26674,
++ 26906,
++ 27133,
++ 27357,
++ 27576,
++ 27791,
++ 28002,
++ 28209,
++ 28411,
++ 28610,
++ 28803,
++ 28993,
++ 29178,
++ 29359,
++ 29535,
++ 29707,
++ 29875,
++ 30038,
++ 30196,
++ 30350,
++ 30499,
++ 30644,
++ 30784,
++ 30920,
++ 31050,
++ 31177,
++ 31298,
++ 31415,
++ 31527,
++ 31634,
++ 31737,
++ 31834,
++ 31927,
++ 32015,
++ 32099,
++ 32177,
++ 32251,
++ 32319,
++ 32383,
++ 32442,
++ 32496,
++ 32546,
++ 32590,
++ 32629,
++ 32664,
++ 32693,
++ 32718,
++ 32738,
++ 32753,
++ 32762,
++ 32767,
++ 32767
++};
++
++/* Level index range -30 ~ 0, step 1 */
++#define MIN_INDEX_LEVEL -30
++#define MAX_INDEX_LEVEL 0
++#define STEP_LEVEL 1
++static const short gain_table[] = {
++ 722,
++ 810,
++ 909,
++ 1020,
++ 1144,
++ 1284,
++ 1440,
++ 1616,
++ 1813,
++ 2034,
++ 2283,
++ 2561,
++ 2874,
++ 3224,
++ 3618,
++ 4059,
++ 4554,
++ 5110,
++ 5734,
++ 6433,
++ 7218,
++ 8099,
++ 9087,
++ 10196,
++ 11440,
++ 12836,
++ 14402,
++ 16160,
++ 18132,
++ 20344,
++ 22826,
++};
++
++/* Frequency index range 300 ~ 700, step 5 */
++#define MIN_INDEX_FREQ 300
++#define MAX_INDEX_FREQ 700
++#define STEP_FREQ 5
++static const short fac_table[] = {
++ 31861,
++ 31830,
++ 31800,
++ 31768,
++ 31737,
++ 31704,
++ 31672,
++ 31638,
++ 31605,
++ 31570,
++ 31536,
++ 31501,
++ 31465,
++ 31429,
++ 31392,
++ 31355,
++ 31318,
++ 31279,
++ 31241,
++ 31202,
++ 31162,
++ 31122,
++ 31082,
++ 31041,
++ 30999,
++ 30958,
++ 30915,
++ 30872,
++ 30829,
++ 30785,
++ 30741,
++ 30696,
++ 30651,
++ 30605,
++ 30559,
++ 30512,
++ 30465,
++ 30417,
++ 30369,
++ 30321,
++ 30272,
++ 30222,
++ 30172,
++ 30122,
++ 30071,
++ 30020,
++ 29968,
++ 29916,
++ 29863,
++ 29810,
++ 29756,
++ 29702,
++ 29648,
++ 29593,
++ 29537,
++ 29481,
++ 29425,
++ 29368,
++ 29311,
++ 29253,
++ 29195,
++ 29136,
++ 29077,
++ 29017,
++ 28957,
++ 28897,
++ 28836,
++ 28775,
++ 28713,
++ 28651,
++ 28588,
++ 28525,
++ 28462,
++ 28398,
++ 28333,
++ 28268,
++ 28203,
++ 28137,
++ 28071,
++ 28005,
++ 27938,
++};
++
++/* busydetect.c */
++void parser_busy_silent_process(struct a24xx *wc, int is_write);
++
++int init_busydetect(struct a24xx *wc, const char *opermode);
++void destroy_busydetect(struct a24xx *wc);
++
++/* callerid.c */
++void parser_callerid_process(struct a24xx *wc, int cidbuflen, int cidtimeout);
++void set_cidstart_desc_from_chan_num(int spanno, int channo, int cid_state);
++void set_signal_unknown_from_chan_num(int spanno, int channo);
++int is_callerid_disable(int spanno, int channo);
++char is_ring_delay_operation(int spanno, int channo);
++void reset_parser_variable_from_chan_num(int spanno, int channo);
++
++int init_callerid(struct a24xx *wc);
++void destroy_callerid(struct a24xx *wc);
++
++#endif /* _BUSYDETECT_H */
+--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa24xx/callerid.c 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa24xx/callerid.c 2015-02-10 14:19:03.000000000 +0100
+@@ -0,0 +1,1429 @@
++/*
++ * OpenVox Calling Identity Delivery Analysis Driver for DAHDI Telephony interface
++ *
++ * Written by kevin.chen
++
++ * Copyright (C) 2012-2013 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 history
++ *
++ * Rev 0.10 add ringdly_flag variable, deal with special caller id case.
++ * Rev 0.30 support new kernel version 3.10.0
++ *
++ */
++
++#include <linux/proc_fs.h>
++#include <linux/ctype.h>
++#include <linux/kmod.h>
++#include "base.h"
++#include "busydetect.h" /* use sin_table[] */
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
++#include <linux/seq_file.h>
++#endif
++
++static int cid_debug = 0;
++module_param(cid_debug, int, 0600);
++
++/* Before using this directory, the directory must had been created */
++static const char *module_name = "opvxdsp";
++
++#define MAX_CID_LEN 32
++#define DTMF_BLOCK_SIZE 102
++#define MAX_DTMF_DIGITS 64
++
++#define DTMF_TIMEOUT_SAMPLES (300 * DAHDI_CHUNKSIZE) /* default 300 msec */
++
++/* Support signals for caller id */
++enum {
++ /* Signal type for caller id fsk */
++ CALLERID_BELL202_OR_V23,
++ CALLERID_V23JP,
++ MAX_FSK_NUM,
++ /* Dtmf signal */
++ CALLERID_DTMF,
++};
++
++/* type for caller id start */
++enum {
++ CIDSTART_RING = 1,
++ CIDSTART_POLARITY,
++ CIDSTART_POLARITY_IN,
++ CIDSTART_DTMF,
++ MAX_CIDSTART,
++};
++
++/* Unknown caller id signal */
++#define UNKNOWN_CID_SIGNAL -1
++
++static const char *signal_desc[] = {
++ [CALLERID_BELL202_OR_V23] = "bell or v23",
++ [CALLERID_V23JP] = "v23_jp",
++ [CALLERID_DTMF] = "dtmf",
++};
++static const char *cidstart_desc[] = {
++ [CIDSTART_RING] = "ring",
++ [CIDSTART_POLARITY] = "polarity",
++ [CIDSTART_POLARITY_IN] = "polarity_in",
++ [CIDSTART_DTMF] = "dtmf",
++};
++static const char *unknown_desc = "unknown";
++
++static const int dtmf_fac[] = {27978, 26955, 25700, 24217, 19072, 16324, 13084, 9314};
++static const char dtmf_positions[] = "123A" "456B" "789C" "*0#D";
++
++typedef struct freq_state {
++ short prev2;
++ short prev;
++ short fac;
++}freq_state_t;
++
++typedef struct callerid_dtmf {
++ freq_state_t row[4];
++ freq_state_t col[4];
++
++ int cur_sample;
++ u32 energy;
++ u8 digit;
++ u8 last_hit;
++
++ int timeout;
++
++ /* Analytical results */
++ char digits[MAX_DTMF_DIGITS + 1];
++ int num;
++}__attribute__((packed))callerid_dtmf_t;
++
++typedef struct complex {
++ int re;
++ int im;
++}complex_t;
++
++typedef struct callerid_fsk {
++ int baud_rate;
++ int baud_frac;
++ int sig_present;
++ int last_bit;
++
++ int phase_rate[2];
++ u32 phase_acc[2];
++
++ complex_t window[2][24];
++ complex_t dot[2];
++ int dot_pos;
++ int span;
++
++ short last_amp;
++ int power;
++
++ /* Analytical results */
++ u8 name[MAX_CID_LEN];
++ u8 number[MAX_CID_LEN];
++
++ int bit_pos;
++ u8 msg[256];
++ int msg_len;
++ int continuous_one;
++ int val;
++}__attribute__((packed))callerid_fsk_t;
++
++typedef struct gen_wave {
++ int bit_no;
++ int bit_pos;
++ int byte_no;
++
++ int occupied_len;
++ int premark_len;
++ int postmark_len;
++
++ int msg_len;
++ u8 msg[256];
++
++ int baud_frac;
++ int baud_rate;
++
++ int scaling;
++ int phase_rates[2];
++ u32 phase_acc;
++}__attribute__((packed))gen_wave_t;
++
++typedef struct callerid {
++ /* Initial current signal unknown */
++ int cur_sig;
++ /* Generate the signal */
++ int appear;
++
++ /* DTMF signal analysis */
++ callerid_dtmf_t dtmf;
++ /* Variety of fsk signal analysis */
++ callerid_fsk_t fsk[MAX_FSK_NUM];
++
++ /* Structuration for generator waveform */
++ gen_wave_t gw;
++}__attribute__((packed))callerid_t;
++
++struct param {
++ /* The pointer of signal description from the last detection */
++ const char *last_signal;
++ /* The pointer of signal description from the current detection */
++ const char *detect_signal;
++ /* Signal the start of caller id */
++ const char *cidstart;
++ /* Close the current channel caller id support */
++ u8 disable;
++};
++
++struct detect_info {
++ int channo;
++ callerid_t cid;
++
++ /* Initialized to zero do not find any cidstart signal */
++ char cidstart_type;
++
++ /*
++ * Sometimes time interval is shorter between signal of caller id end and the next
++ * bell before, so unable to provide a complete signal to asterisk
++ * Set flag to postpone ring tones appears
++ */
++ char ringdly_flag;
++
++ struct param param;
++ struct proc_dir_entry *entry;
++
++ struct list_head list;
++}__attribute__((packed));
++
++#define MAX_LIST_SPAN 20
++static struct list_head di_list[MAX_LIST_SPAN];
++
++static void reset_parser_variable_result(callerid_t *cid);
++
++static const u16 crc16_table[] = {
++ 0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
++ 0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
++ 0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E,
++ 0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
++ 0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD,
++ 0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
++ 0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C,
++ 0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
++ 0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB,
++ 0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
++ 0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A,
++ 0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
++ 0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9,
++ 0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
++ 0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738,
++ 0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
++ 0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7,
++ 0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
++ 0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036,
++ 0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
++ 0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5,
++ 0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
++ 0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134,
++ 0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
++ 0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3,
++ 0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB,
++ 0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,
++ 0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A,
++ 0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1,
++ 0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9,
++ 0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330,
++ 0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78
++};
++
++static u16 check_crc16(const u8 *buf, int len)
++{
++ int i;
++ u16 crc = 0;
++
++ for (i = 0; i < len; i++) {
++ crc = (crc >> 8) ^ crc16_table[(crc ^ buf[i]) & 0xff];
++ }
++ return crc;
++}
++
++static int get_phase_rate(int freq)
++{
++ return (freq * 65536 / SAMPLE_PER_SEC) * 65536;
++}
++
++static short calc_amp(u32 acc)
++{
++ u32 phase, step;
++ short amp;
++
++ phase = acc;
++ phase >>= 23;
++ step = phase & (SIN_DIVISION - 1);
++ if ((phase & SIN_DIVISION)) {
++ step = SIN_DIVISION - step;
++ }
++
++ amp = sin_table[step];
++ if ((phase & (2 * SIN_DIVISION))) {
++ amp = -amp;
++ }
++ return amp;
++}
++
++static void fsk_put_msg(callerid_fsk_t *fsk, const u8 msg[], int len, int sig_type)
++{
++ int i, pos;
++ int res;
++
++ memset(fsk->name, 0, sizeof(fsk->name));
++ memset(fsk->number, 0, sizeof(fsk->number));
++ if (sig_type == CALLERID_V23JP) {
++ pos = 7;
++ for (i = 0; i < msg[6]; i++) {
++ if (msg[i + pos] == 0x02) {
++ i++;
++ res = (msg[i + pos] <= MAX_CID_LEN) ? msg[i + pos] : MAX_CID_LEN;
++ memcpy(fsk->number, msg + i + pos + 1, res);
++ i += msg[i + pos] + 1;
++ } else {
++ i++;
++ i += msg[i + pos] + 1;
++ }
++ }
++ } else {
++ if (msg[0] == 0x80 || msg[0] == 0x82) {
++ /* MDMF */
++ pos = 2;
++ for (i = 0; i < msg[1];) {
++ switch (msg[i + pos]) {
++ case 2:
++ case 4:
++ i++;
++ res = (msg[i + pos] <= MAX_CID_LEN) ? msg[i + pos] : MAX_CID_LEN;
++ memcpy(fsk->number, msg + i + pos + 1, res);
++ i += msg[i + pos] + 1;
++ break;
++ case 7:
++ case 8:
++ i++;
++ res = (msg[i + pos] <= MAX_CID_LEN) ? msg[i + pos] : MAX_CID_LEN;
++ memcpy(fsk->name, msg + i + pos + 1, res);
++ i += msg[i + pos] + 1;
++ break;
++ default:
++ i++;
++ i += msg[i + pos] + 1;
++ break;
++ }
++ }
++ } else if (msg[0] == 0x04) {
++ /* SDMF */
++ if (msg[1] > 8) {
++ memcpy(fsk->number, msg + 10, (msg[1] - 8) <= MAX_CID_LEN ? (msg[1] - 8) : MAX_CID_LEN);
++ }
++ }
++ }
++}
++
++static void fsk_put_bit(callerid_fsk_t *fsk, int bit, int sig_type)
++{
++ int i, sum;
++
++ if (fsk->bit_pos == 0) {
++ if (!bit) {
++ /* Start bit */
++ fsk->bit_pos++;
++ if (fsk->continuous_one > 10) {
++ fsk->msg_len = 0;
++ }
++ fsk->continuous_one = 0;
++ } else {
++ fsk->continuous_one++;
++ }
++ } else if (fsk->bit_pos <= 8) {
++ fsk->val >>= 1;
++ if (bit) {
++ fsk->val |= 0x80;
++ }
++ fsk->bit_pos++;
++ } else {
++ /* Stop bit */
++ if (bit && fsk->msg_len < 256) {
++ if (sig_type == CALLERID_V23JP) {
++ if (fsk->msg_len == 0) {
++ if (fsk->val == 0x90) {
++ fsk->msg[fsk->msg_len++] = (u8)fsk->val;
++ }
++ } else {
++ fsk->msg[fsk->msg_len++] = (u8)fsk->val;
++ }
++ if (fsk->msg_len >= 11 && fsk->msg_len == ((fsk->msg[6] & 0x7f) + 11)) {
++ if (check_crc16(fsk->msg + 2, fsk->msg_len - 2) == 0) {
++ for (i = 0; i < fsk->msg_len - 2; i++) {
++ fsk->msg[i] &= 0x7f;
++ }
++ fsk_put_msg(fsk, fsk->msg, fsk->msg_len - 2, sig_type);
++ }
++ fsk->msg_len = 0;
++ }
++ } else {
++ fsk->msg[fsk->msg_len++] = (u8)fsk->val;
++ if (fsk->msg_len >= 3 && fsk->msg_len == (fsk->msg[1] + 3)) {
++ sum = 0;
++ for (i = 0; i < fsk->msg_len - 1; i++) {
++ sum += fsk->msg[i];
++ }
++ if (256 - (sum & 0xff) == fsk->msg[i]) {
++ fsk_put_msg(fsk, fsk->msg, fsk->msg_len - 1, sig_type);
++ }
++ fsk->msg_len = 0;
++ }
++ }
++ } /* if (bit && fsk->msg_len < 256) */
++
++ fsk->bit_pos = 0;
++ fsk->val = 0;
++ }
++}
++
++static void analysis_fsk(callerid_fsk_t *fsk, short amp[], int len, int sig_type)
++{
++ int i, j;
++ int dot_pos;
++ int hit_bit;
++ short x;
++ int dot;
++ int sum[2];
++ complex_t ph;
++
++ dot_pos = fsk->dot_pos;
++
++ for (i = 0; i < len; i++) {
++ for (j = 0; j < 2; j++) {
++ fsk->dot[j].re -= fsk->window[j][dot_pos].re;
++ fsk->dot[j].im -= fsk->window[j][dot_pos].im;
++
++ ph.re = calc_amp(fsk->phase_acc[j] + (1 << 30));
++ ph.im = calc_amp(fsk->phase_acc[j]);
++ fsk->phase_acc[j] += fsk->phase_rate[j];
++ fsk->window[j][dot_pos].re = (ph.re * amp[i]) >> 3;
++ fsk->window[j][dot_pos].im = (ph.im * amp[i]) >> 3;
++
++ fsk->dot[j].re += fsk->window[j][dot_pos].re;
++ fsk->dot[j].im += fsk->window[j][dot_pos].im;
++
++ dot = fsk->dot[j].re >> 15;
++ sum[j] = dot * dot;
++ dot = fsk->dot[j].im >> 15;
++ sum[j] += dot * dot;
++ }
++
++ x = (amp[i] >> 1) - fsk->last_amp;
++ fsk->power += ((x * x - fsk->power) >> 4);
++ fsk->last_amp = amp[i] >> 1;
++ if (fsk->sig_present) {
++ /* calc result 36380=pow(10.0,(level-14.7)/10.0)*32767.0*32767.0, level=-30 */
++ if (fsk->power < 36380) {
++ fsk->sig_present = 0;
++ fsk->baud_frac = 0;
++ continue;
++ }
++ } else {
++ /* calc result 115046=pow(10.0,(level-9.7)/10.0)*32767.0*32767.0, level=-30 */
++ if (fsk->power < 115046) {
++ fsk->baud_frac = 0;
++ continue;
++ }
++
++ fsk->sig_present = 1;
++ fsk->baud_frac = 0;
++ fsk->last_bit = 0;
++ }
++
++ hit_bit = (sum[0] < sum[1]);
++ if (fsk->last_bit != hit_bit) {
++ fsk->last_bit = hit_bit;
++ fsk->baud_frac = (SAMPLE_PER_SEC >> 1);
++ }
++
++ fsk->baud_frac += fsk->baud_rate;
++ if (fsk->baud_frac >= SAMPLE_PER_SEC) {
++ fsk->baud_frac -= SAMPLE_PER_SEC;
++ fsk_put_bit(fsk, hit_bit, sig_type);
++ }
++
++ if (++dot_pos >= fsk->span) {
++ dot_pos = 0;
++ }
++ }
++
++ fsk->dot_pos = dot_pos;
++}
++
++static inline u32 detect_result(freq_state_t *fs)
++{
++ u32 val;
++
++ val = fs->prev * fs->prev + fs->prev2 * fs->prev2 - ((fs->fac * fs->prev) >> 14) * fs->prev2;
++ fs->prev = fs->prev2 = 0;
++
++ return val;
++}
++
++static inline void update_detect(freq_state_t *fs, short s)
++{
++ short tmp;
++
++ tmp = fs->prev2;
++ fs->prev2 = fs->prev;
++ fs->prev = (((int)fs->fac * fs->prev2) >> 14) - tmp + (s >> 7);
++}
++
++static void get_dtmf_number(callerid_dtmf_t *dtmf, u8 number[])
++{
++ int i;
++ int code;
++ char *p = dtmf->digits;
++
++ if (dtmf->num < 2) {
++ return;
++ }
++
++ if (p[0] == 'B') {
++ code = simple_strtoul(p + 1, NULL, 10);
++ if (code == 0) {
++ /* Unknown caller id number */
++ number[0] = 'O';
++ } else if (code == 10) {
++ /* Private caller id number */
++ number[0] = 'P';
++ }
++ } else if (p[0] == 'D' && p[2] == '#') {
++ if (p[1] == '1') {
++ /* Private caller id number */
++ number[0] = 'P';
++ } else if (p[1] == '2' || p[2] == '3') {
++ /* Unknown caller id number */
++ number[0] = 'O';
++ }
++ } else if (p[0] == 'D' || p[0] == 'A') {
++ for (i = 1; i < dtmf->num; i++) {
++ if (p[i] == 'C' || p[i] == '#') {
++ break;
++ }
++ if (isdigit(p[i]) && i <= MAX_CID_LEN) {
++ number[i - 1] = p[i];
++ }
++ }
++ } else if (isdigit(p[0])) {
++ for (i = 0; i < dtmf->num; i++) {
++ if (isdigit(p[i]) && i < MAX_CID_LEN) {
++ number[i] = p[i];
++ } else {
++ break;
++ }
++ }
++ } else {
++ /* Unknown caller id number */
++ number[0] = 'O';
++ }
++}
++
++static void analysis_dtmf(callerid_dtmf_t *dtmf, short s[], int len)
++{
++ int i, j, k;
++ int limit;
++ short temp;
++ int row_energy[4];
++ int col_energy[4];
++ int best_row, best_col;
++ u8 hit;
++
++ for (i = 0; i < len; i = limit) {
++ if (len - i >= DTMF_BLOCK_SIZE - dtmf->cur_sample) {
++ limit = i + DTMF_BLOCK_SIZE - dtmf->cur_sample;
++ } else {
++ limit = len;
++ }
++
++ for (j = i; j < limit; j++) {
++ temp = (s[j] < 0 ? -s[j] : s[j]) >> 7;
++ dtmf->energy += temp * temp;
++ for (k = 0; k < 4; k++) {
++ update_detect(&dtmf->row[k], s[j]);
++ update_detect(&dtmf->col[k], s[j]);
++ }
++ }
++
++ dtmf->cur_sample += (limit - i);
++ if (dtmf->cur_sample < DTMF_BLOCK_SIZE) {
++ break;
++ }
++
++ row_energy[0] = detect_result(&dtmf->row[0]);
++ col_energy[0] = detect_result(&dtmf->col[0]);
++ best_row = 0;
++ best_col = 0;
++ for (j = 1; j < 4; j++) {
++ row_energy[j] = detect_result(&dtmf->row[j]);
++ if (row_energy[j] > row_energy[best_row]) {
++ best_row = j;
++ }
++ col_energy[j] = detect_result(&dtmf->col[j]);
++ if (col_energy[j] > col_energy[best_col]) {
++ best_col = j;
++ }
++ }
++
++ hit = 0;
++ if (row_energy[best_row] >= 10438 &&
++ col_energy[best_col] >= 10438 &&
++ col_energy[best_col] < row_energy[best_row] * 2 &&
++ col_energy[best_col] * 6 > row_energy[best_row]) {
++ for (j = 0; j < 4; j++) {
++ if ((j != best_row && row_energy[j] * 6 > row_energy[best_row]) ||
++ (j != best_col && col_energy[j] * 6 > col_energy[best_col])) {
++ break;
++ }
++ }
++ if (j >= 4 && ((row_energy[best_row] + col_energy[best_col]) > 42 * dtmf->energy)) {
++ hit = dtmf_positions[(best_row << 2) + best_col];
++ }
++ }
++
++ if (hit) {
++ dtmf->timeout = 0;
++ } else {
++ dtmf->timeout += DTMF_BLOCK_SIZE;
++ }
++
++ if (hit != dtmf->digit) {
++ if (dtmf->last_hit != dtmf->digit) {
++ hit = (hit && hit == dtmf->last_hit) ? hit : 0;
++ if (hit) {
++ if (dtmf->num < MAX_DTMF_DIGITS) {
++ dtmf->digits[dtmf->num++] = (char)hit;
++ dtmf->digits[dtmf->num] = '\0';
++ }
++ }
++ dtmf->digit = hit;
++ }
++ }
++ dtmf->last_hit = hit;
++ dtmf->energy = 0;
++ dtmf->cur_sample = 0;
++ }
++}
++
++static const u16 mon_yday[2][13] = {
++ /* Normal year */
++ {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
++ /* Leap year */
++ {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
++};
++
++static void get_systime_val(int *mon, int *mday, int *hour, int *min)
++{
++ struct timeval tv;
++ const u16 *ip;
++ int days, rem, y;
++ int yg;
++
++ do_gettimeofday(&tv);
++
++#define SECS_PER_HOUR (60 * 60)
++#define SECS_PER_DAY (SECS_PER_HOUR * 24)
++ days = tv.tv_sec / SECS_PER_DAY;
++ rem = tv.tv_sec % SECS_PER_DAY;
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
++ extern struct timezone sys_tz;
++#endif
++ rem += (-sys_tz.tz_minuteswest * 60);
++ while (rem < 0) {
++ rem += SECS_PER_DAY;
++ days--;
++ }
++ while (rem > SECS_PER_DAY) {
++ rem -= SECS_PER_DAY;
++ days++;
++ }
++
++ *hour = rem / SECS_PER_HOUR;
++ rem %= SECS_PER_HOUR;
++ *min = rem / 60;
++
++#define IS_LEAP(y) (((y) % 4 == 0) && ((y) % 100 != 0 || (y) % 400 == 0))
++#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
++#define LEAP_BETWEEN(y) (DIV(y, 4) - DIV(y, 100) + DIV(y, 400))
++ y = 1970;
++ while (days < 0 || days >= (IS_LEAP(y) ? 366 : 365)) {
++ yg = y + DIV(days, 365);
++ days -= ((yg - y) * 365 + LEAP_BETWEEN(yg - 1) - LEAP_BETWEEN(y - 1));
++ y = yg;
++ }
++
++ ip = mon_yday[IS_LEAP(y)];
++ for (y = 11; days < ip[y]; y--) {
++ continue;
++ }
++ days -= ip[y];
++
++ *mon = y + 1;
++ *mday = days + 1;
++}
++
++static int build_msg(u8 *msg, const char *number, const char *name)
++{
++ int i, res;
++ int len;
++ int mon, mday, hour, min;
++ int sum = 0;
++ u8 *ptr = msg + 2;
++
++ get_systime_val(&mon, &mday, &hour, &min);
++ res = sprintf(ptr, "\001\010%02d%02d%02d%02d", mon, mday, hour, min);
++ ptr += res;
++
++ if (strlen(number)) {
++ len = strlen(number);
++ res = sprintf(ptr, "\002%c", len);
++ ptr += res;
++ for (i = 0; i < len; i++) {
++ ptr[i] = number[i];
++ }
++ ptr[i] = '\0';
++ ptr += len;
++ } else {
++ res = sprintf(ptr, "\004\001O");
++ ptr += res;
++ }
++
++ if (strlen(name)) {
++ len = strlen(name);
++ res = sprintf(ptr, "\007%c", len);
++ ptr += res;
++ for (i = 0; i < len; i++) {
++ ptr[i] = name[i];
++ }
++ ptr[i] = '\0';
++ ptr += len;
++ } else {
++ res = sprintf(ptr, "\010\001O");
++ ptr += res;
++ }
++
++ msg[0] = 0x80;
++ msg[1] = ptr - msg - 2;
++
++ for (i = 0; i < ptr - msg; i++) {
++ sum += msg[i];
++ }
++ msg[ptr - msg] = 256 - (sum & 0xff);
++ ptr++;
++
++ return (ptr - msg);
++}
++
++static int fsk_get_bit(gen_wave_t *gw)
++{
++ int bit;
++
++ if (gw->bit_no < gw->occupied_len) {
++ bit = gw->bit_no & 1;
++ gw->bit_no++;
++ } else if (gw->bit_no < gw->occupied_len + gw->premark_len) {
++ bit = 1;
++ gw->bit_no++;
++ } else if (gw->bit_no == gw->occupied_len + gw->premark_len) {
++ if (gw->bit_pos == 0) {
++ /* Start bit */
++ bit = 0;
++ gw->bit_pos++;
++ } else if (gw->bit_pos <= 8) {
++ bit = (gw->msg[gw->byte_no] >> (gw->bit_pos - 1)) & 1;
++ gw->bit_pos++;
++ } else {
++ /* Stop bit */
++ bit = 1;
++ gw->bit_pos = 0;
++ if (++gw->byte_no >= gw->msg_len) {
++ gw->bit_no++;
++ }
++ }
++ } else if (gw->bit_no <= gw->occupied_len + gw->premark_len + gw->postmark_len) {
++ bit = 1;
++ gw->bit_no++;
++ } else {
++ /* Completion */
++ bit = -1;
++ }
++
++ return bit;
++}
++
++static short generater_amp(u32 *phase_acc, int phase_rate, int scale)
++{
++ short amp;
++
++ amp = (short)(((int)calc_amp(*phase_acc) * scale) >> 15);
++ *phase_acc += phase_rate;
++ return amp;
++}
++
++static int fsk_gen(gen_wave_t *gw, short amp[], int len)
++{
++ int i = 0;
++ int bit = 0;
++ static int cur_bit = 1;
++ int cur_phase_rate;
++
++ cur_phase_rate = gw->phase_rates[cur_bit];
++ while (i < len) {
++ if ((gw->baud_frac += gw->baud_rate) >= SAMPLE_PER_SEC) {
++ gw->baud_frac -= SAMPLE_PER_SEC;
++ bit = fsk_get_bit(gw);
++ if (bit == -1) {
++ /* Completion */
++ cur_bit = 1;
++ break;
++ }
++ cur_bit = bit & 1;
++ cur_phase_rate = gw->phase_rates[cur_bit];
++ }
++ amp[i++] = generater_amp(&gw->phase_acc, cur_phase_rate, gw->scaling);
++ }
++
++ return bit;
++}
++
++static void analysis_all_signal(callerid_t *cid, short s[], int len)
++{
++ int i;
++
++ analysis_dtmf(&cid->dtmf, s, len);
++ for (i = 0; i < MAX_FSK_NUM; i++) {
++ analysis_fsk(&cid->fsk[i], s, len, i);
++ }
++}
++
++static void analysis_current_signal(callerid_t *cid, short s[], int len)
++{
++ switch (cid->cur_sig) {
++ case CALLERID_BELL202_OR_V23:
++ case CALLERID_V23JP:
++ analysis_fsk(&cid->fsk[cid->cur_sig], s, len, cid->cur_sig);
++ break;
++ case CALLERID_DTMF:
++ analysis_dtmf(&cid->dtmf, s, len);
++ break;
++ default: /* UNKNOWN_CID_SIGNAL */
++ /* signal of caller id is unknown, re-check */
++ analysis_all_signal(cid, s, len);
++ break;
++ }
++}
++
++static struct detect_info *get_detect_info_from_chan_num(int spanno, int channo)
++{
++ struct detect_info *di;
++
++ if (spanno <= 0 || spanno > MAX_LIST_SPAN) {
++ return NULL;
++ }
++
++ list_for_each_entry(di, &di_list[spanno - 1], list) {
++ if (di->channo == channo) {
++ /* Find the corresponding matching */
++ return di;
++ }
++ }
++
++ return NULL;
++}
++
++static void set_signal_desc(struct detect_info *di)
++{
++ di->param.last_signal = di->param.detect_signal;
++
++ switch (di->cid.cur_sig) {
++ case CALLERID_BELL202_OR_V23:
++ case CALLERID_V23JP:
++ case CALLERID_DTMF:
++ di->param.detect_signal = signal_desc[di->cid.cur_sig];
++ break;
++ default: /* UNKNOWN_CID_SIGNAL */
++ di->param.detect_signal = unknown_desc;
++ break;
++ }
++}
++
++void set_signal_unknown_from_chan_num(int spanno, int channo)
++{
++ struct detect_info *di;
++
++ di = get_detect_info_from_chan_num(spanno, channo);
++ if (di) {
++ /* Default standard does not analyze the current signal */
++ di->cid.cur_sig = UNKNOWN_CID_SIGNAL;
++ set_signal_desc (di);
++ /* set the ring delay flag */
++ if (di->cid.dtmf.num > 1) {
++ di->ringdly_flag = 1;
++ } else {
++ di->ringdly_flag = di->cid.appear > 0 ? 1 : 0;
++ }
++ }
++}
++
++char is_ring_delay_operation(int spanno, int channo)
++{
++ struct detect_info *di;
++
++ di = get_detect_info_from_chan_num(spanno, channo);
++ if (di) {
++ return di->ringdly_flag;
++ } else {
++ return 0;
++ }
++}
++
++static void clear_cidstart_type(struct detect_info *di)
++{
++ di->cidstart_type = 0;
++}
++
++static void set_cidstart_desc_force(struct detect_info *di, int type)
++{
++ if (type >= CIDSTART_RING && type < MAX_CIDSTART) {
++ di->cidstart_type = type;
++ di->param.cidstart = cidstart_desc[type];
++ }
++}
++
++static void set_cidstart_desc(struct detect_info *di, int type)
++{
++ if (!di->cidstart_type) {
++ /* Cidstart signal has not yet appeared */
++ if (type >= CIDSTART_RING && type < MAX_CIDSTART) {
++ di->cidstart_type = type;
++ di->param.cidstart = cidstart_desc[type];
++ }
++ }
++}
++
++void set_cidstart_desc_from_chan_num(int spanno, int channo, int cid_state)
++{
++ int type;
++ struct detect_info *di;
++
++ if(cid_state == CID_STATE_IDLE) {
++ type = CIDSTART_POLARITY;
++ } else {
++ type = CIDSTART_POLARITY_IN;
++ }
++
++ di = get_detect_info_from_chan_num(spanno, channo);
++ if (di) {
++ set_cidstart_desc(di, type);
++ }
++}
++
++int is_callerid_disable(int spanno, int channo)
++{
++ int res = 0;
++ struct detect_info *di;
++
++ di = get_detect_info_from_chan_num(spanno, channo);
++ if (di) {
++ res = di->param.disable;
++ }
++
++ return res;
++}
++
++static void print_cidinfo(u8 *number, u8 *name)
++{
++ struct timeval tv;
++ int mon, mday, hour, min;
++
++ do_gettimeofday(&tv);
++ get_systime_val(&mon, &mday, &hour, &min);
++ printk(KERN_INFO "cid infor: %02d-%02d %02d:%02d number=%s, name=%s\n", mon, mday, hour, min, number, name);
++}
++
++static void check_callerid_signal(struct detect_info *di, int timeout)
++{
++ int i, res = 0;
++ callerid_fsk_t *fsk;
++ u8 name[MAX_CID_LEN + 1];
++ u8 number[MAX_CID_LEN + 1];
++ callerid_t *cid = &di->cid;
++
++ if (cid->appear) {
++ /* Last time we have found signal of caller id, directly to exit */
++ return;
++ }
++ memset(name, 0, sizeof(name));
++ memset(number, 0, sizeof(number));
++ if (cid->dtmf.num > 1) {
++ if (cid->dtmf.timeout >= timeout) {
++ get_dtmf_number(&cid->dtmf, number);
++ cid->cur_sig = CALLERID_DTMF;
++ set_cidstart_desc(di, CIDSTART_DTMF);
++ res = 1;
++ }
++ } else {
++ for (i = 0; i < MAX_FSK_NUM; i++) {
++ fsk = &cid->fsk[i];
++ if (strlen(fsk->number)) {
++ memcpy(number, fsk->number, MAX_CID_LEN);
++ memcpy(name, fsk->name, MAX_CID_LEN);
++ if (cid->dtmf.num == 1) {
++ set_cidstart_desc_force(di, CIDSTART_DTMF);
++ }
++ cid->cur_sig = i;
++ res = 1;
++ break;
++ }
++ }
++ }
++
++ if (res) {
++ if (strlen(number)) {
++ cid->gw.msg_len = build_msg(cid->gw.msg, number, name);
++ cid->appear = 1;
++ set_signal_desc(di);
++ if (cid_debug) {
++ print_cidinfo(number, name);
++ }
++ }
++ }
++}
++
++void reset_parser_variable_from_chan_num(int spanno, int channo)
++{
++ struct detect_info *di;
++
++ di = get_detect_info_from_chan_num(spanno, channo);
++ if (di) {
++ reset_parser_variable_result(&di->cid);
++ }
++}
++
++static int generate_callerid(callerid_t *cid, short s[], int len)
++{
++ if (fsk_gen(&cid->gw, s, len) == -1) {
++ /* Signal of caller id stop transmission */
++ reset_parser_variable_result(cid);
++ return 0;
++ } else {
++ return 1;
++ }
++}
++
++void parser_callerid_process(struct a24xx *wc, int cidbuflen, int cidtimeout)
++{
++ int i, j;
++ /* Analytical work to ensure that within the ring tones */
++#define LENGTH_PER_PTR (10 * DAHDI_CHUNKSIZE)
++ short data[LENGTH_PER_PTR];
++ struct a24xx_dev *wc_dev = &wc->dev;
++ struct dahdi_chan *chan;
++ struct detect_info *di;
++
++ for (i = 0; i < wc_dev->max_cards; i++) {
++ if (wc_dev->modtype[i] == MOD_TYPE_FXO && !wc_dev->mod[i].fxo.offhook) {
++ chan = wc->chans[i];
++ if (wc_dev->cid_state[i] == CID_STATE_IDLE ||
++ wc_dev->cid_state[i] == CID_STATE_RING_DELAY) {
++ /* We need copy data to the caller id voice buffer */
++ memcpy(wc_dev->cid_history_buf[i] + wc_dev->cid_history_ptr[i], chan->readchunk, DAHDI_CHUNKSIZE);
++ wc_dev->cid_history_ptr[i] = (wc_dev->cid_history_ptr[i] + DAHDI_CHUNKSIZE)%(cidbuflen * DAHDI_MAX_CHUNKSIZE);
++ di = get_detect_info_from_chan_num(wc->span.spanno, chan->channo);
++ if (di && !di->param.disable) {
++ /* Empty data to prevent interference */
++ memset(chan->readchunk, DAHDI_LIN2X(0, chan), DAHDI_CHUNKSIZE);
++ }
++ } else if (wc_dev->cid_state[i] == CID_STATE_RING_ON) {
++ di = get_detect_info_from_chan_num(wc->span.spanno, chan->channo);
++ if (di) {
++ if (wc_dev->cid_history_clone_cnt[i] > 0) {
++ for (j = 0; j < LENGTH_PER_PTR; j++) {
++ data[j] = DAHDI_XLAW(*(u8 *)(wc_dev->cid_history_buf[i] + wc_dev->cid_history_ptr[i] + j), chan);
++ }
++ analysis_current_signal(&di->cid, data, LENGTH_PER_PTR);
++ wc_dev->cid_history_clone_cnt[i] -= (LENGTH_PER_PTR / DAHDI_CHUNKSIZE);
++ if (wc_dev->cid_history_clone_cnt[i] < 0) {
++ wc_dev->cid_history_clone_cnt[i] = 0;
++ }
++ wc_dev->cid_history_ptr[i] = (wc_dev->cid_history_ptr[i] + LENGTH_PER_PTR)%(cidbuflen * DAHDI_MAX_CHUNKSIZE);
++ } else if (wc_dev->cid_history_clone_cnt[i] == 0) {
++ check_callerid_signal(di, 0);
++ wc_dev->cid_history_clone_cnt[i] = -1;
++ }
++ }
++ } else if (wc_dev->cid_state[i] == CID_STATE_RING_OFF) {
++ di = get_detect_info_from_chan_num(wc->span.spanno, chan->channo);
++ if (di) {
++ if (di->cid.appear) {
++ set_cidstart_desc(di, CIDSTART_RING);
++ if (generate_callerid(&di->cid, data, DAHDI_CHUNKSIZE)) {
++ if (!di->param.disable) {
++ /* Create effective caller id signal */
++ for (j = 0; j < DAHDI_CHUNKSIZE; j++) {
++ chan->readchunk[j] = DAHDI_LIN2X(data[j], chan);
++ }
++ }
++ } else {
++ clear_cidstart_type(di);
++ wc_dev->cid_state[i] = CID_STATE_WAIT_RING_FINISH;
++ wc_dev->cid_history_clone_cnt[i] = cidtimeout;
++ }
++ } else {
++ for (j = 0; j < DAHDI_CHUNKSIZE; j++) {
++ data[j] = DAHDI_XLAW(chan->readchunk[j], chan);
++ if (!di->param.disable) {
++ /* Empty data to prevent repeated parsing */
++ chan->readchunk[j] = DAHDI_LIN2X(0, chan);
++ }
++ }
++ analysis_current_signal(&di->cid, data, DAHDI_CHUNKSIZE);
++ check_callerid_signal(di, DTMF_TIMEOUT_SAMPLES);
++ } /* if (di->cid.appear) */
++ }
++ } else if (wc_dev->cid_state[i] == CID_STATE_WAIT_RING_FINISH) {
++ if (wc_dev->cid_history_clone_cnt[i] > 0) {
++ wc_dev->cid_history_clone_cnt[i]--;
++ } else {
++ wc_dev->cid_state[i] = CID_STATE_IDLE;
++ }
++ }
++ }
++ }
++}
++
++static void reset_parser_variable_result(callerid_t *cid)
++{
++ int i;
++ callerid_dtmf_t *dtmf;
++ callerid_fsk_t *fsk;
++ gen_wave_t *gw;
++
++ /* Generate the signal reset */
++ cid->appear = 0;
++
++ /* Reset the dtmf analytical variables and results */
++ dtmf = &cid->dtmf;
++ memset(dtmf, 0, sizeof(*dtmf));
++ for (i = 0; i < 4; i++) {
++ dtmf->row[i].fac = dtmf_fac[i];
++ dtmf->col[i].fac = dtmf_fac[4 + i];
++ }
++
++ /* Reset the variety of fsk analytical variables and results */
++ for (i = 0; i < MAX_FSK_NUM; i++) {
++ fsk = &cid->fsk[i];
++ memset(fsk, 0, sizeof(*fsk));
++ fsk->baud_rate = 1200;
++ fsk->span = SAMPLE_PER_SEC / fsk->baud_rate;
++ if (i == CALLERID_BELL202_OR_V23) {
++ fsk->phase_rate[0] = get_phase_rate(2200);
++ fsk->phase_rate[1] = get_phase_rate(1200);
++ } else {
++ fsk->phase_rate[0] = get_phase_rate(2100);
++ fsk->phase_rate[1] = get_phase_rate(1300);
++ }
++ }
++
++ /* Reset the waveform analytical variables and results */
++ fsk = &cid->fsk[CALLERID_BELL202_OR_V23];
++ gw = &cid->gw;
++ memset(gw, 0, sizeof(*gw));
++ gw->occupied_len = 300;
++ gw->premark_len = 180;
++ gw->postmark_len = 60;
++ gw->scaling = 2873; /* pow(10.0,(level-3.14)/20.0)*32767.0, level=-14 */
++ gw->baud_rate = fsk->baud_rate;
++ gw->phase_rates[0] = fsk->phase_rate[0];
++ gw->phase_rates[1] = fsk->phase_rate[1];
++}
++
++static void init_callerid_info(struct detect_info *di, int channo)
++{
++ /* Save channel number */
++ di->channo = channo;
++
++ INIT_LIST_HEAD(&di->list);
++
++ /* Initial description of the parameters are unknown */
++ di->param.detect_signal = unknown_desc;
++ di->param.last_signal = unknown_desc;
++ di->param.cidstart = unknown_desc;
++
++ /* Initial signal is unknown */
++ di->cid.cur_sig = UNKNOWN_CID_SIGNAL;
++
++ /* Initialize dtmf, variety of fsk, waveform parameters */
++ reset_parser_variable_result(&di->cid);
++}
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
++static int read_param_proc(char *buf, char **start, off_t off, int count,
++ int *eof, void *data)
++{
++ int res;
++ const char **p = (const char **)data;
++
++ if (off > 0) {
++ /* We have finished to read, return 0 */
++ res = 0;
++ } else {
++ res = sprintf(buf, "%s", *p);
++ }
++
++ return res;
++}
++
++static void create_param_proc(const char *name, struct proc_dir_entry *base, void *data)
++{
++ struct proc_dir_entry *entry;
++
++ entry = create_proc_entry(name, 0444, base);
++ if (entry) {
++ entry->data = (void *)data;
++ entry->read_proc = read_param_proc;
++ }
++}
++#else
++static int param_proc_show(struct seq_file *m, void *v)
++{
++ const char **p = (const char **)m->private;
++
++ seq_printf(m, "%s", *p);
++ return 0;
++}
++
++static int open_param_proc(struct inode *inode, struct file *file)
++{
++ return single_open(file, param_proc_show, PDE_DATA(inode));
++}
++
++static struct file_operations proc_param_fops = {
++ .open = open_param_proc,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = single_release,
++};
++
++static void create_param_proc(const char *name, struct proc_dir_entry *base, void *data)
++{
++ proc_create_data(name, 0444, base, &proc_param_fops, data);
++}
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
++static int write_param_off_proc(struct file *file, const char __user *buf,
++ unsigned long count, void *data)
++#else
++static ssize_t write_param_off_proc(struct file *file, const char __user *buf,
++ size_t count, loff_t *pos)
++#endif
++{
++ char temp[24];
++ int newval, len;
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
++ u8 *val = (u8 *)data;
++#else
++ u8 *val = PDE_DATA(file_inode(file));
++#endif
++
++ len = count > (sizeof(temp) - 1) ? (sizeof(temp) - 1) : count;
++
++ if (copy_from_user(temp, buf, len)) {
++ return -EFAULT;
++ }
++
++ temp[len] = '\0';
++
++ newval = simple_strtoul(temp, NULL, 10);
++ *val = newval > 0 ? 1 : 0;
++
++ return count;
++}
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
++static int read_param_off_proc(char *buf, char **start, off_t off, int count,
++ int *eof, void *data)
++{
++ int res;
++ u8 *val = (u8 *)data;
++
++ if (off > 0) {
++ /* We have finished to read, return 0 */
++ res = 0;
++ } else {
++ res = sprintf(buf, "%d", *val);
++ }
++
++ return res;
++}
++
++static void create_param_off_proc(const char *name, struct proc_dir_entry *base, void *data)
++{
++ struct proc_dir_entry *entry;
++
++ entry = create_proc_entry(name, 0644, base);
++ if (entry) {
++ entry->data = data;
++ entry->read_proc = read_param_off_proc;
++ entry->write_proc = write_param_off_proc;
++ }
++}
++#else
++static int param_off_proc_show(struct seq_file *m, void *v)
++{
++ u8 *val = (u8 *)m->private;
++
++ seq_printf(m, "%d", *val);
++ return 0;
++}
++
++static int open_param_off_proc(struct inode *inode, struct file *file)
++{
++ return single_open(file, param_off_proc_show, PDE_DATA(inode));
++}
++
++static struct file_operations proc_param_off_fops = {
++ .open = open_param_off_proc,
++ .read = seq_read,
++ .write = write_param_off_proc,
++ .llseek = seq_lseek,
++ .release = single_release,
++};
++
++static void create_param_off_proc(const char *name, struct proc_dir_entry *base, void *data)
++{
++ proc_create_data(name, 0644, base, &proc_param_off_fops, data);
++}
++#endif
++
++/*
++ * \brief parameter
++ * is_clean: 0 is to create, 1 is to remove
++ */
++static void rebuild_callerid_proc(struct detect_info *di, int is_clean)
++{
++ char temp[24];
++ struct proc_dir_entry *entry;
++
++ if (is_clean) {
++ entry = di->entry;
++ if (entry) {
++ remove_proc_entry("last_signal", entry);
++ remove_proc_entry("detect_signal", entry);
++ remove_proc_entry("cidstart", entry);
++ remove_proc_entry("disable", entry);
++
++ sprintf(temp, "%s/%d/opencid", module_name, di->channo);
++ remove_proc_entry(temp, NULL);
++ }
++ } else {
++ sprintf(temp, "%s/%d/opencid", module_name, di->channo);
++ entry = proc_mkdir(temp, NULL);
++ di->entry = entry;
++ if (entry) {
++ create_param_proc("last_signal", entry, &di->param.last_signal);
++ create_param_proc("detect_signal", entry, &di->param.detect_signal);
++ create_param_proc("cidstart", entry, &di->param.cidstart);
++ create_param_off_proc("disable", entry, &di->param.disable);
++ }
++ }
++}
++
++static void release_callerid_resource(struct a24xx *wc)
++{
++ int i;
++ struct detect_info *cur, *next;
++ struct a24xx_dev *wc_dev = &wc->dev;
++
++ if (wc->span.spanno <= 0 || wc->span.spanno > MAX_LIST_SPAN) {
++ return;
++ }
++
++ for (i = 0; i < wc_dev->max_cards; i++) {
++ if (wc_dev->modtype[i] == MOD_TYPE_FXO) {
++ list_for_each_entry_safe(cur, next, &di_list[wc->span.spanno - 1], list) {
++ if (cur->channo == wc->chans[i]->channo) {
++ /* Find the corresponding matching */
++ list_del(&cur->list);
++ rebuild_callerid_proc(cur, 1);
++ kfree(cur);
++ break;
++ }
++ }
++ }
++ }
++}
++
++/* Called after created the top-level parameters directory "module_name" */
++int init_callerid(struct a24xx *wc)
++{
++ int i, res = 0;
++ struct detect_info *di;
++ struct a24xx_dev *wc_dev = &wc->dev;
++ static int first_in = 1;
++
++ if (first_in) {
++ first_in = 0;
++ for (i = 0; i < MAX_LIST_SPAN; i++) {
++ INIT_LIST_HEAD(&di_list[i]);
++ }
++ }
++
++ if (wc->span.spanno <= 0 || wc->span.spanno > MAX_LIST_SPAN) {
++ return -ENXIO;
++ }
++
++ for (i = 0; i < wc_dev->max_cards; i++) {
++ if (wc_dev->modtype[i] == MOD_TYPE_FXO) {
++ di = kzalloc(sizeof(*di), GFP_KERNEL);
++ if (!di) {
++ printk(KERN_ERR "Not enough memory, A2410P not support the calling identity delivery analysis");
++ res = -ENOMEM;
++ goto out;
++ }
++
++ init_callerid_info(di, wc->chans[i]->channo);
++ rebuild_callerid_proc(di, 0);
++
++ list_add(&di->list, &di_list[wc->span.spanno - 1]);
++ }
++ }
++
++ return 0;
++out:
++ release_callerid_resource(wc);
++ return res;
++}
++
++/* Called before the release of the top-level parameters directory "module_name" */
++void destroy_callerid(struct a24xx *wc)
++{
++ release_callerid_resource(wc);
++}
+--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa24xx/ec3000.c 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa24xx/ec3000.c 2015-02-10 14:19:03.000000000 +0100
+@@ -0,0 +1,589 @@
++/*
++ * Copyright (C) 2005-2006 Digium, Inc.
++ *
++ * Mark Spencer <markster@digium.com>
++ * Mark liu <mark.liu@openvox.cn>
++ *
++ * $Id: ec3000.c 159 2010-12-08 03:27:04Z liuyuan $
++ * 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 "ec3000.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("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 ec {
++ 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 opvx_vpm_setecmode(struct ec *ec, int channel, int mode)
++{
++ tOCT6100_CHANNEL_MODIFY *modify;
++ UINT32 ulResult;
++
++ if (ec->ecmode[channel] == mode)
++ return;
++ modify = kmalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_ATOMIC);
++ if (!modify) {
++ printk("opvxa24xx: Unable to allocate memory for setec!\n");
++ return;
++ }
++ Oct6100ChannelModifyDef(modify);
++ modify->ulEchoOperationMode = mode;
++ modify->ulChannelHndl = ec->aulEchoChanHndl[channel];
++ ulResult = Oct6100ChannelModify(ec->pApiInstance, modify);
++ if (ulResult != GENERIC_OK) {
++ printk("Failed to apply echo can changes on channel %d, 0x%x!\n", channel, ulResult);
++ } else {
++#ifdef OCTASIC_DEBUG
++ printk("Echo can on channel %d set to %d\n", channel, mode);
++#endif
++ ec->ecmode[channel] = mode;
++ }
++ kfree(modify);
++}
++
++void opvx_vpm_setdtmf(struct ec *ec, int channel, int detect, int mute)
++{
++ tOCT6100_CHANNEL_MODIFY *modify;
++ UINT32 ulResult;
++
++ modify = kmalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_KERNEL);
++ if (!modify) {
++ printk("opvxa24xx: Unable to allocate memory for setdtmf!\n");
++ return;
++ }
++ Oct6100ChannelModifyDef(modify);
++ modify->ulChannelHndl = ec->aulEchoChanHndl[channel];
++ if (mute) {
++ ec->chanflags[channel] |= FLAG_MUTE;
++ modify->VqeConfig.fDtmfToneRemoval = TRUE;
++ } else {
++ ec->chanflags[channel] &= ~FLAG_MUTE;
++ modify->VqeConfig.fDtmfToneRemoval = FALSE;
++ }
++ if (detect)
++ ec->chanflags[channel] |= FLAG_DTMF;
++ else
++ ec->chanflags[channel] &= ~FLAG_DTMF;
++ if (ec->chanflags[channel] & (FLAG_DTMF|FLAG_MUTE)) {
++ if (!(ec->chanflags[channel] & FLAG_ECHO)) {
++ opvx_vpm_setecmode(ec, channel, cOCT6100_ECHO_OP_MODE_HT_RESET);
++ opvx_vpm_setecmode(ec, channel, cOCT6100_ECHO_OP_MODE_HT_FREEZE);
++ }
++ } else {
++ if (!(ec->chanflags[channel] & FLAG_ECHO))
++ opvx_vpm_setecmode(ec, channel, cOCT6100_ECHO_OP_MODE_DIGITAL);
++ }
++
++ ulResult = Oct6100ChannelModify(ec->pApiInstance, modify);
++ if (ulResult != GENERIC_OK) {
++ printk("Failed to apply dtmf mute changes on channel %d!\n", channel);
++ }
++/* printk("VPM450m: Setting DTMF on channel %d: %s / %s\n", channel, (detect ? "DETECT" : "NO DETECT"), (mute ? "MUTE" : "NO MUTE")); */
++ kfree(modify);
++}
++
++
++void opvx_vpm_setec(struct ec *ec, int channel, int eclen)
++{
++ if (eclen) {
++ ec->chanflags[channel] |= FLAG_ECHO;
++ opvx_vpm_setecmode(ec, channel, cOCT6100_ECHO_OP_MODE_HT_RESET);
++ opvx_vpm_setecmode(ec, channel, cOCT6100_ECHO_OP_MODE_NORMAL);
++ } else {
++ ec->chanflags[channel] &= ~FLAG_ECHO;
++ if (ec->chanflags[channel] & (FLAG_DTMF | FLAG_MUTE)) {
++ opvx_vpm_setecmode(ec, channel, cOCT6100_ECHO_OP_MODE_HT_RESET);
++ opvx_vpm_setecmode(ec, channel, cOCT6100_ECHO_OP_MODE_HT_FREEZE);
++ } else
++ opvx_vpm_setecmode(ec, channel, cOCT6100_ECHO_OP_MODE_DIGITAL);
++ }
++/* printk("VPM450m: Setting EC on channel %d to %d\n", channel, eclen); */
++}
++
++int opvx_vpm_checkirq(struct ec *ec)
++{
++ tOCT6100_INTERRUPT_FLAGS InterruptFlags;
++
++ Oct6100InterruptServiceRoutineDef(&InterruptFlags);
++ Oct6100InterruptServiceRoutine(ec->pApiInstance, &InterruptFlags);
++
++ return InterruptFlags.fToneEventsPending ? 1 : 0;
++}
++
++int opvx_vpm_getdtmf(struct ec *ec, 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(ec->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("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 opvx_vpm_getcapacity(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 ec *opvx_vpm_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 ec *ec;
++ int x,y,law;
++#ifdef CONFIG_4KSTACKS
++ unsigned long flags;
++#endif
++
++ if (!(ec = kmalloc(sizeof(struct ec), GFP_KERNEL)))
++ return NULL;
++
++ memset(ec, 0, sizeof(struct ec));
++
++ if (!(ChipOpen = kmalloc(sizeof(tOCT6100_CHIP_OPEN), GFP_KERNEL))) {
++ kfree(ec);
++ return NULL;
++ }
++
++ memset(ChipOpen, 0, sizeof(tOCT6100_CHIP_OPEN));
++
++ if (!(ChannelOpen = kmalloc(sizeof(tOCT6100_CHANNEL_OPEN), GFP_KERNEL))) {
++ kfree(ec);
++ kfree(ChipOpen);
++ return NULL;
++ }
++
++ memset(ChannelOpen, 0, sizeof(tOCT6100_CHANNEL_OPEN));
++
++ for (x=0;x<128;x++)
++ ec->ecmode[x] = -1;
++
++ ec->numchans = numspans * 32;
++ printk("OpenVox VPM: echo cancellation for %d channels\n", ec->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 = ec->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(ec);
++ kfree(ChipOpen);
++ kfree(ChannelOpen);
++ return NULL;
++ }
++
++
++ ec->pApiInstance = vmalloc(InstanceSize.ulApiInstanceSize);
++ if (!ec->pApiInstance) {
++ printk("Out of memory (can't allocate %d bytes)!\n", InstanceSize.ulApiInstanceSize);
++ kfree(ec);
++ 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(ec->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(ec);
++ 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 (( numspans > 2) || ((x & 0x03) <2)) {
++ //if ((x & 0x03) < numspans) {
++ if ( x < ec->numchans) {
++ if (isalaw[x / 24]) // each span have 24 channels, it is spec for the 24 channel card. miaolin
++ law = cOCT6100_PCM_A_LAW;
++ else
++ law = cOCT6100_PCM_U_LAW;
++ Oct6100ChannelOpenDef(ChannelOpen);
++ ChannelOpen->pulChannelHndl = &ec->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(ec->pApiInstance, ChannelOpen);
++ if (ulResult != GENERIC_OK) {
++ printk("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 = ec->aulEchoChanHndl[x];
++ enable.ulToneNumber = tones[y];
++ if (Oct6100ToneDetectionEnable(ec->pApiInstance, &enable) != GENERIC_OK)
++ printk("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 ec;
++}
++
++void opvx_vpm_release(struct ec *ec)
++{
++ UINT32 ulResult;
++ tOCT6100_CHIP_CLOSE ChipClose;
++
++ Oct6100ChipCloseDef(&ChipClose);
++ ulResult = Oct6100ChipClose(ec->pApiInstance, &ChipClose);
++ if (ulResult != cOCT6100_ERR_OK) {
++ printk("Failed to close chip, code %08x!\n", ulResult);
++ }
++ vfree(ec->pApiInstance);
++ kfree(ec);
++}
++
+--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa24xx/ec3000.h 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa24xx/ec3000.h 2015-02-10 14:19:03.000000000 +0100
+@@ -0,0 +1,50 @@
++/*
++ * OpenVox A24xx FXS/FXO Interface Driver for Zapata Telephony interface
++ *
++ * Written by MiaoLin<miaolin@openvox.cn>
++ * Written by mark.liu<mark.liu@openvox.cn>
++ * $Id: ec3000.h 165 2010-12-09 05:38:49Z liuyuan $
++ *
++ * 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.
++ *
++ */
++
++#ifndef _EC3000_H_
++#define _EC3000_H_
++
++#include <linux/firmware.h>
++
++struct ec;
++
++/* 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 */
++extern void opvx_vpm_setec(struct ec *ec, int channel, int eclen);
++extern void opvx_vpm_setdtmf(struct ec *ec, int channel, int detect, int mute);
++extern int opvx_vpm_getdtmf(struct ec *ec, int *channel, int *tone, int *start);
++extern int opvx_vpm_checkirq(struct ec *ec);
++extern unsigned int opvx_vpm_getcapacity(void *wc);
++
++extern struct ec *opvx_vpm_init(void *wc, int *isalaw, int numspans, const struct firmware *firmware);
++extern void opvx_vpm_release(struct ec *ec);
++
++#endif
++
+--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa24xx/private.c 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa24xx/private.c 2015-02-10 14:19:03.000000000 +0100
+@@ -0,0 +1,513 @@
++/*
++ * OpenVox A24xx FXS/FXO Interface Driver for Zapata Telephony interface
++ *
++ * Written by MiaoLin<miaolin@openvox.cn>
++ * Written by mark.liu<mark.liu@openvox.cn>
++ * $Id: private.c 446 2011-05-12 04:01:57Z liuyuan $
++ *
++ * 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.
++ *
++ */
++
++//#ifdef VPM_SUPPORT
++/* ec debug */
++extern int ec_debug;
++extern int vpmsupport;
++//#endif
++#define PEDANTIC_OCTASIC_CHECKING
++
++#define ZT_CHUNKSIZE 8
++#define ZT_MIN_CHUNKSIZE ZT_CHUNKSIZE
++#define ZT_DEFAULT_CHUNKSIZE ZT_CHUNKSIZE
++#define ZT_MAX_CHUNKSIZE ZT_CHUNKSIZE
++
++#define MAX_NUM_CARDS 24
++
++#define CARDS_PER_MODULE 4
++#define MOD_TYPE_FXS 0
++#define MOD_TYPE_FXO 1
++
++
++/* register base address */
++#define REG_BASE 0x00000000
++#define PIO_BASE 0x000004e0
++#define TDM0_BASE 0x00000400
++#define SPI_PCM_BASE 0x000004a0
++#define TDM_MEM_BASE 0x00001000
++#define PCI_BASE 0x00004000
++
++/* swap memory offset */
++#define OPVX_RUN 0x0
++#define OPVX_FWREADY 0x1
++#define OPVX_FWVERSION 0x2
++#define OPVX_DMA_REG 0x3
++#define OPVX_ERR_REG 0x4
++#define OPVX_IRQ_CNT_LO 0x5
++#define OPVX_IRQ_CNT_HI 0x6
++#define OPVX_BURST_SIZE 0x7
++#define OPVX_BURST_INTERVAL 0x8
++#define OPVX_PCI_IRQ_FRQ 0x9
++#define OPVX_CARD_MASTER 0xa
++#define OPVX_IRQ_COMMAND 0xb
++#define OPVX_VPM_PRESENT 0x12
++#define V2_OPVX_PIO_DATA 0x1c
++#define OPVX_TEST 0x1f
++#define V2_EC_BASE 0x00000100
++
++/* irq status register */
++#define OPVX_IRQ_STATUS (0x40>>2) /* irq status register */
++#define OPVX_IRQ_ENABLE (0x50>>2) /* irq status register */
++
++/* PIO register offset */
++#define OPVX_PIO_DATA 0
++#define OPVX_PIO_DIR 1
++#define OPVX_PIO_CNTL 2
++
++/* SPI register offset */
++#define OPVX_SPI_IN 0
++#define OPVX_SPI_OUT 1
++#define OPVX_SPI_STATUS 2
++#define OPVX_SPI_CNTL 3
++#define OPVX_SPI_CS 5
++/* ec controller base addr */
++#define EC_BASE 0x00000540
++
++#define OPVX_EC_CNTL 0
++#define OPVX_EC_DATA 1
++#define OPVX_EC_VPM 2
++
++/* echo canceller stuff */
++#define BIT_EC_ADDR_STAGE (1<<0)
++#define BIT_EC_CS (1<<1)
++#define BIT_EC_WR (1<<2)
++#define BIT_EC_RD (1<<3)
++#define BIT_EC_ALE (1<<4)
++#define BIT_EC_RDY (1<<5)
++#define BIT_EC_DAS (1<<6)
++#define BIT_EC_IRQ (1<<7)
++
++#define BIT_EC_PRESENT (1<<0)
++
++void __opvx_a24xx_setcreg(unsigned long mem32, unsigned int offset, unsigned int reg, unsigned int val)
++{
++ //printk("writing offset %d, reg %d at %d, value %d\n", offset, reg, offset + (reg<<2), val);
++ unsigned int *p = (unsigned int*)(mem32 + offset + (reg<<2));
++ *p = val;
++}
++
++unsigned int __opvx_a24xx_getcreg(unsigned long mem32, unsigned int offset, unsigned char reg)
++{
++ //printk("reding offset %d, reg %d at %d\n", offset, reg, offset + (reg<<2));
++ volatile unsigned int *p = (unsigned int*)(mem32 + offset + (reg<<2));
++ return (*p);
++}
++
++unsigned char __opvx_a24xx_read_8bits(unsigned long mem32)
++{
++ unsigned int res=0;
++
++ while((__opvx_a24xx_getcreg(mem32, SPI_PCM_BASE, OPVX_SPI_STATUS)&0x40)!=0x40);
++ __opvx_a24xx_setcreg(mem32, SPI_PCM_BASE, OPVX_SPI_OUT, 0); /* we have to write something so the spi can work */
++ while( (__opvx_a24xx_getcreg(mem32, SPI_PCM_BASE, OPVX_SPI_STATUS)&0x80) != 0x80); /* wait rx finish */
++ res = __opvx_a24xx_getcreg(mem32, SPI_PCM_BASE, OPVX_SPI_IN);
++
++ return res&0xff;
++}
++
++void __opvx_a24xx_start_dma(unsigned long mem32, unsigned int data)
++{
++ __opvx_a24xx_setcreg(mem32, REG_BASE, OPVX_DMA_REG, data);
++}
++
++void __opvx_a24xx_stop_dma(unsigned long mem32)
++{
++ __opvx_a24xx_setcreg(mem32, REG_BASE, OPVX_DMA_REG, 0xffffffff); // -1 means stop dma.
++}
++
++void __opvx_a24xx_restart_dma(unsigned long mem32)
++{
++ /* Reset Master and TDM */
++ // TODO: do our work here.
++}
++
++void __opvx_a24xx_reset_tdm(unsigned long mem32)
++{
++ /* Reset TDM */
++ //TODO: do our work here;
++}
++
++void __opvx_a24xx_set_irq_frq(unsigned long mem32,unsigned int frq)
++{
++ __opvx_a24xx_setcreg(mem32, REG_BASE, OPVX_PCI_IRQ_FRQ,frq);
++}
++void __opvx_a24xx_set_master(unsigned long mem32,unsigned int master)
++{
++ __opvx_a24xx_setcreg(mem32, REG_BASE, OPVX_CARD_MASTER,master);
++}
++unsigned int __opvx_a24xx_get_master(unsigned long mem32)
++{
++ return __opvx_a24xx_getcreg(mem32, REG_BASE, OPVX_CARD_MASTER);
++}
++
++unsigned int __opvx_a24xx_get_version(unsigned long mem32)
++{
++ return __opvx_a24xx_getcreg(mem32, REG_BASE, OPVX_FWVERSION);
++}
++
++unsigned int __opvx_a24xx_get_irqstatus(unsigned long mem32)
++{
++ return __opvx_a24xx_getcreg(mem32, PCI_BASE, OPVX_IRQ_STATUS);
++}
++
++void __opvx_a24xx_set_irqstatus(unsigned long mem32, unsigned int value)
++{
++ __opvx_a24xx_setcreg(mem32, PCI_BASE, OPVX_IRQ_STATUS, value); // clear interrupt register.
++}
++
++void __opvx_a24xx_clear_irqs(unsigned long mem32)
++{
++ __opvx_a24xx_setcreg(mem32, PCI_BASE, OPVX_IRQ_STATUS, 0xffffffff); /* clear all pending irqs */
++ __opvx_a24xx_setcreg(mem32, SPI_PCM_BASE, OPVX_SPI_CNTL, 0); /* init spi port */
++}
++
++void __opvx_a24xx_enable_interrupts(unsigned long mem32)
++{
++ __opvx_a24xx_setcreg(mem32, REG_BASE, OPVX_BURST_INTERVAL, 0);
++ __opvx_a24xx_setcreg(mem32, REG_BASE, OPVX_BURST_SIZE, 2);
++ __opvx_a24xx_setcreg(mem32, REG_BASE, OPVX_RUN, 1);
++}
++
++void __opvx_a24xx_disable_interrupts(unsigned long mem32)
++{
++ __opvx_a24xx_setcreg(mem32, REG_BASE, OPVX_RUN, 0);
++}
++
++unsigned int __opvx_a24xx_get_irqcnt_lo(unsigned long mem32)
++{
++ return __opvx_a24xx_getcreg(mem32, REG_BASE, OPVX_IRQ_CNT_LO);
++}
++
++void __opvx_a24xx_reset_modules(unsigned long mem32, void (*func)(int), int data)
++{
++ __opvx_a24xx_setcreg(mem32, PIO_BASE, OPVX_PIO_DIR, 0xffffffff); /* all io as output */
++ __opvx_a24xx_setcreg(mem32, PIO_BASE, OPVX_PIO_CNTL, 0); /* disable irq */
++
++// if(debug) {
++// printk("opvxa24xx: raise reset\n");
++// }
++
++ __opvx_a24xx_setcreg(mem32, PIO_BASE, OPVX_PIO_DATA, 0x1); /* GPIO0 As reset*/
++ /* Wait for 1 second */
++
++ (*func)(data/2); /* delay 1/2 sec */
++
++ __opvx_a24xx_setcreg(mem32, PIO_BASE, OPVX_PIO_DATA, 0x0); /* GPIO0 As reset*/
++// if(debug) {
++// printk("opvxa24xx: pull down reset\n");
++// }
++
++ (*func)(data/2); /* delay 1/2 sec */
++
++ __opvx_a24xx_setcreg(mem32, PIO_BASE, OPVX_PIO_DATA, 0x1); /* GPIO0 As reset*/
++// if(debug) {
++// printk("opvxa24xx: raise reset finally\n");
++// }
++
++ (*func)(data/2); /* delay 1/2 sec */
++}
++
++
++void __opvx_a24xx_write_8bits(unsigned long mem32, unsigned char bits)
++{
++ volatile unsigned int t;
++
++ while((__opvx_a24xx_getcreg(mem32, SPI_PCM_BASE, OPVX_SPI_STATUS)&0x40)!=0x40);
++ __opvx_a24xx_setcreg(mem32, SPI_PCM_BASE, OPVX_SPI_OUT, bits); /* we have to write something so the spi can work */
++ while( (__opvx_a24xx_getcreg(mem32, SPI_PCM_BASE, OPVX_SPI_STATUS)&0x40) != 0x40); /* wait tx finish */
++ t = __opvx_a24xx_getcreg(mem32, SPI_PCM_BASE, OPVX_SPI_IN);
++}
++
++static inline void __reset_spi(void *wc_dev)
++{
++ return; /* we do nothing here */
++}
++
++
++void __opvx_a24xx_setcard(unsigned long mem32, int card)
++{
++ __opvx_a24xx_setcreg(mem32, SPI_PCM_BASE, OPVX_SPI_CS, 1<<(card/CARDS_PER_MODULE));
++}
++
++void __opvx_a24xx_reset_spi(void *wc_dev, int card, void (*func)(void*, int))
++{
++ (*func)(wc_dev, card);
++ __reset_spi(wc_dev);
++ __reset_spi(wc_dev);
++}
++
++static inline int __opvx_a24xx_hit_fxo_daisy(int number_daisy)
++{
++ int cid;
++
++ if (number_daisy==0) {
++ cid=0;
++ } else if (number_daisy==1) {
++ cid=0x8;
++ } else if (number_daisy==2) {
++ cid=0x4;
++ } else if (number_daisy==3) {
++ cid=0xc;
++ } else {
++ cid= -1;
++ }
++
++ return cid;
++}
++
++void __opvx_a24xx_spi_setreg(void *wc_dev, unsigned long mem32, int card, int modtype, unsigned char reg, unsigned char value, void (*func)(void*, int))
++{
++ (*func)(wc_dev, card);
++ if (modtype == MOD_TYPE_FXO) {
++ __opvx_a24xx_write_8bits(mem32, 0x20 | __opvx_a24xx_hit_fxo_daisy(card%CARDS_PER_MODULE)); // fxo daisy operate.
++ __opvx_a24xx_write_8bits(mem32, reg & 0x7f);
++ } else {
++ __opvx_a24xx_write_8bits(mem32, 1<<(card%CARDS_PER_MODULE)); // fxs daisy operate.
++ __opvx_a24xx_write_8bits(mem32, reg & 0x7f);
++ }
++ __opvx_a24xx_write_8bits(mem32, value);
++}
++
++unsigned char __opvx_a24xx_spi_getreg(void *wc_dev, unsigned long mem32, int card, int modtype, unsigned char reg, void (*func)(void*, int))
++{
++ (*func)(wc_dev, card);
++ if (modtype == MOD_TYPE_FXO) {
++ __opvx_a24xx_write_8bits(mem32, 0x60 | __opvx_a24xx_hit_fxo_daisy(card%CARDS_PER_MODULE)); // fxo daisy operate.
++ __opvx_a24xx_write_8bits(mem32, reg & 0x7f);
++ } else {
++ __opvx_a24xx_write_8bits(mem32, 1<<(card%CARDS_PER_MODULE)); // fxs daisy operate.
++ __opvx_a24xx_write_8bits(mem32, reg | 0x80);
++ }
++ return __opvx_a24xx_read_8bits(mem32);
++}
++
++
++static inline void __a24xx_raw_oct_out(unsigned long mem32, const unsigned int addr, const unsigned int value)
++{
++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_CNTL, (addr<<16) | BIT_EC_ADDR_STAGE);
++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_CNTL, (addr<<16) | BIT_EC_ADDR_STAGE | BIT_EC_WR);
++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_CNTL, (addr<<16) | BIT_EC_ADDR_STAGE | BIT_EC_WR | BIT_EC_ALE);
++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_DATA, value);
++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_CNTL, BIT_EC_WR | BIT_EC_ALE | BIT_EC_CS);
++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_CNTL, 0);
++}
++
++static inline unsigned int __a24xx_raw_oct_in(unsigned long mem32, const unsigned int addr)
++{
++ unsigned int ret;
++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_CNTL, (addr<<16) | BIT_EC_ADDR_STAGE);
++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_CNTL, (addr<<16) | BIT_EC_ADDR_STAGE | BIT_EC_WR);
++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_CNTL, (addr<<16) | BIT_EC_ADDR_STAGE | BIT_EC_WR | BIT_EC_ALE);
++#ifdef PEDANTIC_OCTASIC_CHECKING
++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_CNTL, (addr<<16) | BIT_EC_ADDR_STAGE | BIT_EC_ALE);
++#endif
++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_CNTL, BIT_EC_RD | BIT_EC_ALE | BIT_EC_CS);
++ ret = __opvx_a24xx_getcreg(mem32, EC_BASE, OPVX_EC_DATA);
++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_CNTL, 0);
++
++ return ret&0xffff;
++}
++
++unsigned int __opvx_a24xx_oct_in(unsigned long mem32, unsigned int addr)
++{
++#ifdef PEDANTIC_OCTASIC_CHECKING
++ int count = 1000;
++#endif
++ __a24xx_raw_oct_out(mem32, 0x0008, (addr >> 20));
++ __a24xx_raw_oct_out(mem32, 0x000a, (addr >> 4) & ((1 << 16) - 1));
++ __a24xx_raw_oct_out(mem32, 0x0000, (((addr >> 1) & 0x7) << 9) | (1 << 8) | (1));
++#ifdef PEDANTIC_OCTASIC_CHECKING
++ while((__a24xx_raw_oct_in(mem32, 0x0000) & (1 << 8)) && --count);
++ if (count != 1000) {
++// printk("Yah, read can be slow...\n");
++ }
++ if (!count) {
++// printk("Read timed out!\n");
++ }
++#endif
++ return __a24xx_raw_oct_in(mem32, 0x0004);
++}
++
++void __opvx_a24xx_oct_out(unsigned long mem32, unsigned int addr, unsigned int value)
++{
++#ifdef PEDANTIC_OCTASIC_CHECKING
++ int count = 1000;
++#endif
++ __a24xx_raw_oct_out(mem32, 0x0008, (addr >> 20));
++ __a24xx_raw_oct_out(mem32, 0x000a, (addr >> 4) & ((1 << 16) - 1));
++ __a24xx_raw_oct_out(mem32, 0x0004, value);
++ __a24xx_raw_oct_out(mem32, 0x0000, (((addr >> 1) & 0x7) << 9) | (1 << 8) | (3 << 12) | 1);
++#ifdef PEDANTIC_OCTASIC_CHECKING
++ while((__a24xx_raw_oct_in(mem32, 0x0000) & (1 << 8)) && --count);
++ if (count != 1000) {
++// printk("Yah, write can be slow\n");
++ }
++ if (!count) {
++// printk("Write timed out!\n");
++ }
++#endif
++}
++
++int __opvx_a24xx_check_vpm(unsigned long mem32)
++{
++ unsigned int check1, check2;
++
++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_VPM, 0); //disable vpm support at first.
++
++ if (!vpmsupport) {
++// printk("OpenVox VPM: Support Disabled\n");
++ return -1;
++ }
++
++ __a24xx_raw_oct_out(mem32, 0x000a, 0x5678);
++ __a24xx_raw_oct_out(mem32, 0x0004, 0x1234);
++ check1 = __a24xx_raw_oct_in(mem32, 0x0004);
++ check2 = __a24xx_raw_oct_in(mem32, 0x000a);
++
++// if (ec_debug) {
++// printk("OCT Result: %04x/%04x\n", __a24xx_raw_oct_in(mem32, 0x0004), __a24xx_raw_oct_in(mem32, 0x000a));
++// }
++
++ if (__a24xx_raw_oct_in(mem32, 0x0004) != 0x1234) {
++// printk("OpenVox VPM: Not Present\n");
++ return -2;
++ }
++
++ return 0;
++}
++
++void __opvx_a24xx_vpm_setpresent(unsigned long mem32)
++{
++ __opvx_a24xx_setcreg(mem32, EC_BASE, OPVX_EC_VPM, BIT_EC_PRESENT);
++}
++
++void __opvx_a24xx_set_chunk(void *readchunk, void *writechunk,unsigned int frq,int buf_mult)
++{
++ unsigned char *tmp;
++ tmp = *((unsigned char **)(writechunk)) + frq * ZT_MAX_CHUNKSIZE * (MAX_NUM_CARDS) * 2; /* in bytes */
++ *(char **)readchunk = tmp;
++}
++
++void __opvx_a24xx_transmit(unsigned long mem32, volatile unsigned char *writechunk, volatile unsigned char **txbuf,unsigned int irq_frq , unsigned int order)
++{
++ unsigned int int_cnt_lo = __opvx_a24xx_get_irqcnt_lo(mem32);
++
++ if (int_cnt_lo & 0x01) {
++ /* Write is at interrupt address. Start writing from normal offset */
++ *txbuf = writechunk + ZT_CHUNKSIZE * MAX_NUM_CARDS * order;
++ } else {
++ *txbuf = writechunk + ZT_CHUNKSIZE * MAX_NUM_CARDS * irq_frq + ZT_CHUNKSIZE * MAX_NUM_CARDS * order;
++ }
++}
++
++void __opvx_a24xx_receive(unsigned long mem32, volatile unsigned char *readchunk, volatile unsigned char **rxbuf,unsigned int irq_frq , unsigned int order)
++{
++ unsigned int int_cnt_lo = __opvx_a24xx_get_irqcnt_lo(mem32);
++
++ if (int_cnt_lo & 0x01) {
++ /* Read is at interrupt address. Valid data is available at normal offset */
++ *rxbuf = readchunk + ZT_CHUNKSIZE * MAX_NUM_CARDS * order;
++ } else {
++ *rxbuf = readchunk + ZT_CHUNKSIZE * MAX_NUM_CARDS * irq_frq + ZT_CHUNKSIZE * MAX_NUM_CARDS * order;
++ }
++}
++
++void __opvx_a24xx_reset_modules_v2(unsigned long mem32, void (*func)(int), int data)
++{
++ __opvx_a24xx_setcreg(mem32, REG_BASE, V2_OPVX_PIO_DATA, 0x02020200); /* gpio bit[1] set to 1*/
++ (*func)(data/2); /* delay 1/2 sec */
++ __opvx_a24xx_setcreg(mem32, REG_BASE, V2_OPVX_PIO_DATA, 0x02020000); /* gpio bit[1] set to 0*/
++ (*func)(data/2); /* delay 1/2 sec */
++ __opvx_a24xx_setcreg(mem32, REG_BASE, V2_OPVX_PIO_DATA, 0x02020200); /* gpio bit[1] set to 1*/
++ (*func)(data/2); /* delay 1/2 sec */
++}
++
++unsigned int __opvx_a24xx_oct_in_v2(unsigned long mem32, unsigned int addr)
++{
++ int count = 1000;
++ __opvx_a24xx_setcreg(mem32, V2_EC_BASE, 0x0008, (addr >> 20));
++ __opvx_a24xx_setcreg(mem32, V2_EC_BASE, 0x000a, (addr >> 4) & ((1 << 16) - 1));
++ __opvx_a24xx_setcreg(mem32, V2_EC_BASE, 0x0000, (((addr >> 1) & 0x7) << 9) | (1 << 8) | (1));
++ while((__opvx_a24xx_getcreg(mem32, V2_EC_BASE, 0x0000) & (1 << 8)) && --count);
++ if (count != 1000) {
++// printk("Yah, read can be slow...\n");
++ }
++ if (!count) {
++// printk("Read timed out!\n");
++ }
++ return __opvx_a24xx_getcreg(mem32,V2_EC_BASE, 0x0004);
++}
++
++
++void __opvx_a24xx_oct_out_v2(unsigned long mem32, unsigned int addr, unsigned int value)
++{
++ int count = 1000;
++ __opvx_a24xx_setcreg(mem32, V2_EC_BASE, 0x0008, (addr >> 20));
++ __opvx_a24xx_setcreg(mem32, V2_EC_BASE, 0x000a, (addr >> 4) & ((1 << 16) - 1));
++ __opvx_a24xx_setcreg(mem32, V2_EC_BASE, 0x0004, value);
++ __opvx_a24xx_setcreg(mem32, V2_EC_BASE, 0x0000, (((addr >> 1) & 0x7) << 9) | (1 << 8) | (3 << 12) | 1);
++ while((__opvx_a24xx_getcreg(mem32,V2_EC_BASE, 0x0000) & (1 << 8)) && --count);
++ if (count != 1000) {
++// printk("Yah, write can be slow\n");
++ }
++ if (!count) {
++// printk("Write timed out!\n");
++ }
++}
++
++int __opvx_a24xx_check_vpm_v2(unsigned long mem32)
++{
++ unsigned int check1, check2;
++ __opvx_a24xx_setcreg(mem32, REG_BASE, OPVX_VPM_PRESENT, 0); //disable vpm support at first.
++
++ if (!vpmsupport) {
++// printk("OpenVox VPM: Support Disabled\n");
++ return -1;
++ }
++
++ __opvx_a24xx_setcreg(mem32, V2_EC_BASE, 0x000a, 0x5678);
++ __opvx_a24xx_setcreg(mem32, V2_EC_BASE, 0x0004, 0x1234);
++ check1 = __opvx_a24xx_getcreg(mem32, V2_EC_BASE, 0x0004);
++ check2 = __opvx_a24xx_getcreg(mem32, V2_EC_BASE, 0x000a);
++
++// if (ec_debug) {
++// printk("OCT Result: %04x/%04x\n", __opvx_a24xx_getcreg(mem32, V2_EC_BASE, 0x0004), __opvx_a24xx_getcreg(mem32, V2_EC_BASE, 0x000a));
++// }
++
++ if (__opvx_a24xx_getcreg(mem32, V2_EC_BASE, 0x0004) != 0x1234) {
++// printk("OpenVox VPM: Not Present\n");
++ return -2;
++ }
++ return 0;
++}
++
++void __opvx_a24xx_vpm_setpresent_v2(unsigned long mem32)
++{
++ __opvx_a24xx_setcreg(mem32, REG_BASE, OPVX_VPM_PRESENT, BIT_EC_PRESENT);
++
++}
+--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa24xx/si3050.c 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa24xx/si3050.c 2015-02-10 14:19:03.000000000 +0100
+@@ -0,0 +1,228 @@
++/*
++ * OpenVox A24xx FXS/FXO Interface Driver for Zapata Telephony interface
++ *
++ * Written by MiaoLin<miaolin@openvox.cn>
++ * Written by mark.liu<mark.liu@openvox.cn>
++ * $Id: si3050.c 301 2011-01-19 05:20:32Z yangshugang $
++ *
++ * 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.
++ *
++ */
++
++#include <linux/string.h>
++#include <linux/param.h>
++#include <linux/jiffies.h>
++
++#include "fxo_modes.h"
++#include "base.h"
++
++extern int debug;
++extern int alawoverride;
++extern int fxofullscale; /* fxo full scale tx/rx, register 30, acim */
++extern int fwringdetect;
++extern int _opermode;
++extern int fxotxgain;
++extern int fxorxgain;
++extern int fastpickup;
++
++static int si3050_voicedaa_insane(struct a24xx_dev *wc_dev, int card)
++{
++ int blah;
++ blah = a24xx_spi_getreg(wc_dev, card, 2);
++ if (blah != 0x3) {
++ return -2;
++ }
++ blah = a24xx_spi_getreg(wc_dev, card, 11);
++ if (debug) {
++ printk("VoiceDAA System: %02x\n", blah & 0xf);
++ }
++ return 0;
++}
++
++/*********************************************************************
++ * 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)
++ *
++ *******************************************************************/
++int si3050_set_hwgain(struct a24xx_dev *wc_dev, int card, __s32 gain, __u32 tx)
++{
++ if (!(wc_dev->modtype[card] == MOD_TYPE_FXO)) {
++ printk("Cannot adjust gain. Unsupported module type!\n");
++ return -1;
++ }
++ if (tx) {
++ if (debug) {
++ printk("setting FXO tx gain for card=%d to %d\n", card, gain);
++ }
++ if (gain >= -150 && gain <= 0) {
++ a24xx_spi_setreg(wc_dev, card, 38, 16 + (gain/-10));
++ a24xx_spi_setreg(wc_dev, card, 40, 16 + (-gain%10));
++ } else if (gain <= 120 && gain > 0) {
++ a24xx_spi_setreg(wc_dev, card, 38, gain/10);
++ a24xx_spi_setreg(wc_dev, card, 40, (gain%10));
++ } else {
++ printk("FXO tx gain is out of range (%d)\n", gain);
++ return -1;
++ }
++ } else { /* rx */
++ if (debug) {
++ printk("setting FXO rx gain for card=%d to %d\n", card, gain);
++ }
++ if (gain >= -150 && gain <= 0) {
++ a24xx_spi_setreg(wc_dev, card, 39, 16+ (gain/-10));
++ a24xx_spi_setreg(wc_dev, card, 41, 16 + (-gain%10));
++ } else if (gain <= 120 && gain > 0) {
++ a24xx_spi_setreg(wc_dev, card, 39, gain/10);
++ a24xx_spi_setreg(wc_dev, card, 41, (gain%10));
++ } else {
++ printk("FXO rx gain is out of range (%d)\n", gain);
++ return -1;
++ }
++ }
++
++ return 0;
++}
++
++int si3050_init_voicedaa(struct a24xx_dev *wc_dev, int card, int fast, int manual, int sane)
++{
++ unsigned char reg16=0, reg26=0, reg30=0, reg31=0;
++ unsigned char ch;
++ long newjiffies;
++
++ wc_dev->modtype[card] = MOD_TYPE_FXO;
++ /* Sanity check the ProSLIC */
++ a24xx_reset_spi(wc_dev, card);
++ if (!sane && si3050_voicedaa_insane(wc_dev, card)) {
++ return -2;
++ }
++
++ /* Software reset */
++ a24xx_spi_setreg(wc_dev, card, 1, 0x80);
++
++ /* Wait just a bit */
++ __a24xx_wait_just_a_bit(HZ/10);
++
++ /* Enable PCM, ulaw */
++ if (alawoverride) {
++ a24xx_spi_setreg(wc_dev, card, 33, 0x20);
++ } else {
++ a24xx_spi_setreg(wc_dev, 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);
++ a24xx_spi_setreg(wc_dev, card, 16, reg16);
++
++ if(fwringdetect) {
++ /* Enable ring detector full-wave rectifier mode */
++ a24xx_spi_setreg(wc_dev, card, 18, 2);
++ a24xx_spi_setreg(wc_dev, card, 24, 0);
++ } else {
++ /* Set to the device defaults */
++ a24xx_spi_setreg(wc_dev, card, 18, 0);
++ a24xx_spi_setreg(wc_dev, 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);
++ a24xx_spi_setreg(wc_dev, card, 26, reg26);
++
++ /* Set AC Impedence */
++ reg30 = (fxofullscale==1) ? (fxo_modes[_opermode].acim|0x10) : (fxo_modes[_opermode].acim);
++ a24xx_spi_setreg(wc_dev, card, 30, reg30);
++
++ /* Misc. DAA parameters */
++ if (fastpickup) {
++ reg31 = 0xb3;
++ } else {
++ reg31 = 0xa3;
++ }
++
++ reg31 |= (fxo_modes[_opermode].ohs2 << 3);
++ a24xx_spi_setreg(wc_dev, card, 31, reg31);
++
++ /* Set Transmit/Receive timeslot */
++ //printk("set card %d to %d\n", card, (3-(card%4)) * 8 + (card/4) * 64);
++ a24xx_spi_setreg(wc_dev, card, 34, (card%4) * 8 + (card/4) * 32);
++ a24xx_spi_setreg(wc_dev, card, 35, 0x00);
++ a24xx_spi_setreg(wc_dev, card, 36, (card%4) * 8 + (card/4) * 32);
++ a24xx_spi_setreg(wc_dev, card, 37, 0x00);
++
++ /* Enable ISO-Cap */
++ a24xx_spi_setreg(wc_dev, card, 6, 0x00);
++
++ if (fastpickup) {
++ a24xx_spi_setreg(wc_dev, card, 17, a24xx_spi_getreg(wc_dev, card, 17) | 0x20);
++ }
++
++ /* Wait 1000ms for ISO-cap to come up */
++ newjiffies = jiffies;
++ newjiffies += 2 * HZ;
++ while((jiffies < newjiffies) && !(a24xx_spi_getreg(wc_dev, card, 11) & 0xf0)) {
++ __a24xx_wait_just_a_bit(HZ/10);
++ }
++
++ /*if (!(a24xx_spi_getreg(wc_dev, card, 11) & 0xf0)) {*/
++ ch = a24xx_spi_getreg(wc_dev, card, 11);
++ if( ch == 0xff ) {
++ printk("VoiceDAA not installed at card %d\n", card);
++ return -1;
++ }
++ if (!(ch & 0xf0)) {
++ printk("VoiceDAA did not bring up ISO link properly!\n");
++ return -1;
++ }
++ if (debug) {
++ printk("ISO-Cap is now up, line side: %02x rev %02x\n",
++ a24xx_spi_getreg(wc_dev, card, 11) >> 4,
++ (a24xx_spi_getreg(wc_dev, card, 13) >> 2) & 0xf);
++ }
++ /* Enable on-hook line monitor */
++ a24xx_spi_setreg(wc_dev, card, 5, 0x08);
++
++ /* Take values for fxotxgain and fxorxgain and apply them to module */
++ si3050_set_hwgain(wc_dev, card, fxotxgain, 1);
++ si3050_set_hwgain(wc_dev, card, fxorxgain, 0);
++
++ /* NZ -- crank the tx gain up by 7 dB */
++ if (!strcmp(fxo_modes[_opermode].name, "NEWZEALAND")) {
++ printk("Adjusting gain\n");
++ si3050_set_hwgain(wc_dev, card, 7, 1);
++ }
++
++ if(debug) {
++ printk("DEBUG fxotxgain:%i.%i fxorxgain:%i.%i\n",
++ (a24xx_spi_getreg(wc_dev, card, 38)/16) ? -(a24xx_spi_getreg(wc_dev, card, 38) - 16) : a24xx_spi_getreg(wc_dev, card, 38),
++ (a24xx_spi_getreg(wc_dev, card, 40)/16) ? -(a24xx_spi_getreg(wc_dev, card, 40) - 16) : a24xx_spi_getreg(wc_dev, card, 40),
++ (a24xx_spi_getreg(wc_dev, card, 39)/16) ? -(a24xx_spi_getreg(wc_dev, card, 39) - 16) : a24xx_spi_getreg(wc_dev, card, 39),
++ (a24xx_spi_getreg(wc_dev, card, 41)/16) ? -(a24xx_spi_getreg(wc_dev, card, 41) - 16) : a24xx_spi_getreg(wc_dev, card, 41));
++ }
++
++ /* battery state still unknown */
++ return 0;
++
++}
+--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxa24xx/si321x.c 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxa24xx/si321x.c 2015-02-10 14:19:03.000000000 +0100
+@@ -0,0 +1,1469 @@
++/*
++ * OpenVox A24xx FXS/FXO Interface Driver for Zapata Telephony interface
++ *
++ * Written by MiaoLin<miaolin@openvox.cn>
++ * Written by mark.liu<mark.liu@openvox.cn>
++ * $Id: si321x.c 482 2011-06-02 08:58:56Z liuyuan $
++ *
++ * 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.
++ *
++ */
++
++#include <linux/param.h>
++#include <linux/jiffies.h>
++#include <linux/sched.h>
++
++#include "proslic.h"
++#include "fxo_modes.h"
++#include "base.h"
++
++/* module param */
++extern int debug;
++extern int loopcurrent;
++extern int reversepolarity;
++extern int fxstxgain;
++extern int fxsrxgain;
++extern int boostringer;
++extern int fastringer;
++extern int lowpower;
++extern int _opermode;
++extern int alawoverride;
++extern int fxshonormode;
++
++static int acim2tiss[16] = { 0x0, 0x1, 0x4, 0x5, 0x7, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x2, 0x0, 0x3 };
++
++/* indirect_resg */
++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",0x0ff4},
++{33,20,"PWR_ALARM_Q3Q4",0x6e7e},
++{34,21,"PWR_ALARM_Q5Q6",0x0ff4},
++{35,22,"LOOP_CLOSURE_FILTER",0x8000},
++{36,23,"RING_TRIP_FILTER",0x0320},
++{37,24,"TERM_LP_POLE_Q1Q2",0x0012},
++{38,25,"TERM_LP_POLE_Q3Q4",0x0012},
++{39,26,"TERM_LP_POLE_Q5Q6",0x0012},
++{40,27,"CM_BIAS_RINGING",0x0C00},
++{41,64,"DCDC_MIN_V",0x0C00},
++{42,255,"DCDC_XTRA",0x1000},
++{43,66,"LOOP_CLOSE_TRES_LOW",0x1000},
++};
++
++static int si321x_powerup_proslic(struct a24xx_dev *wc_dev, int card, int fast)
++{
++ unsigned char vbat;
++ unsigned long origjiffies;
++ int lim;
++
++ /* Set period of DC-DC converter to 1/64 khz */
++ a24xx_spi_setreg(wc_dev, card, 92, 0xff /* was 0xff */);
++
++ /* Wait for VBat to powerup */
++ origjiffies = jiffies;
++
++ /* Disable powerdown */
++ a24xx_spi_setreg(wc_dev, card, 14, 0);
++
++ /* If fast, don't bother checking anymore */
++ if (fast)
++ return 0;
++
++ while((vbat = a24xx_spi_getreg(wc_dev, card, 82)) < 0xc0) {
++ /* Wait no more than 500ms */
++ if ((jiffies - origjiffies) > HZ/2) {
++ break;
++ }
++ }
++
++ if (vbat < 0xc0) {
++ if (wc_dev->proslic_power == PROSLIC_POWER_UNKNOWN) {
++ printk("ProSLIC on module %d failed to powerup within %d ms (%d mV only)\n\n -- DID YOU REMEMBER TO PLUG IN THE HD POWER CABLE TO THE A24xxP??\n",
++ card, (int)(((jiffies - origjiffies) * 1000 / HZ)),
++ vbat * 375);
++ }
++ wc_dev->proslic_power = PROSLIC_POWER_WARNED;
++ return -1;
++ } else if (debug) {
++ printk("ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n",
++ card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ)));
++ }
++ wc_dev->proslic_power = PROSLIC_POWER_ON;
++
++ /* Proslic max allowed loop current, reg 71 LOOP_I_LIMIT */
++ /* If out of range, just set it to the default value */
++ lim = (loopcurrent - 20) / 3;
++ if ( loopcurrent > 41 ) {
++ lim = 0;
++ if (debug) {
++ printk("Loop current out of range! Setting to default 20mA!\n");
++ }
++ }
++ else if (debug) {
++ printk("Loop current set to %dmA!\n",(lim*3)+20);
++ }
++ a24xx_spi_setreg(wc_dev,card,LOOP_I_LIMIT,lim);
++
++ /* Engage DC-DC converter */
++ a24xx_spi_setreg(wc_dev, card, 93, 0x19 /* was 0x19 */);
++#if 0
++ origjiffies = jiffies;
++ while(0x80 & opvx_a24xx_spi_getreg(wc_dev, card, 93)) {
++ if ((jiffies - origjiffies) > 2 * HZ) {
++ printk("Timeout waiting for DC-DC calibration on module %d\n", card);
++ return -1;
++ }
++ }
++
++#if 0
++ /* Wait a full two seconds */
++ while((jiffies - origjiffies) < 2 * HZ);
++
++ /* Just check to be sure */
++ vbat = opvx_a24xx_spi_getreg(wc_dev, card, 82);
++ printk("ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n",
++ card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ)));
++#endif
++#endif
++ return 0;
++
++}
++
++/*return not OK modules flag*/
++static int si321x_powerup_proslic_all(struct a24xx_dev *wc_dev, int flag, int fast)
++{
++ unsigned long origjiffies;
++ struct stat{
++ unsigned char vbat;
++ unsigned long jifs;
++ };
++
++ struct stat stats[24];
++ int lim;
++ int tmp_flag,x;
++
++
++ for(x=0; x < wc_dev->max_cards; x++){
++ if(flag & (1 << x)){
++ /* Set period of DC-DC converter to 1/64 khz */
++ a24xx_spi_setreg(wc_dev, x, 92, 0xff /* was 0xff */);
++
++ /* Disable powerdown */
++ a24xx_spi_setreg(wc_dev, x, 14, 0);
++
++ stats[x].vbat = 0;
++ stats[x].jifs = 0;
++ }
++ }
++
++ /* Wait for VBat to powerup */
++ origjiffies = jiffies;
++
++ /* If fast, don't bother checking anymore */
++ if (fast)
++ return 0;
++
++ tmp_flag = flag;
++ while( ((jiffies - origjiffies) <= HZ/2) && tmp_flag){ /* Wait no more than 500ms */
++ for(x=0;x<wc_dev->max_cards;x++){
++ if(tmp_flag & (1 << x)){
++ //stats[x].vbat = a24xx_spi_getreg(wc_dev, x, 82);
++ stats[x].vbat = a24xx_spi_getreg(wc_dev, x, 82);
++ if(stats[x].vbat >= 0xc0){
++ stats[x].jifs = jiffies - origjiffies;
++ tmp_flag &=~(1 << x);
++ }
++ }
++ }
++ }
++
++ for(x=0; x < wc_dev->max_cards; x++){
++ if(flag & (1 << x)){
++ if(stats[x].vbat < 0xc0){
++ if (wc_dev->proslic_power == PROSLIC_POWER_UNKNOWN){
++ printk("ProSLIC on module %d failed to powerup within %d ms (%d mV only)\n\n -- DID YOU REMEMBER TO PLUG IN THE HD POWER CABLE TO THE A24xxP??\n",
++ x, (int)(((jiffies - origjiffies) * 1000 / HZ)),
++ stats[x].vbat * 375);
++ }
++ //wc_dev->proslic_power = PROSLIC_POWER_WARNED;
++ }
++ else if(debug){
++ printk("ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n",
++ x, stats[x].vbat * 376 / 1000, stats[x].vbat, (int)((stats[x].jifs * 1000 / HZ)));
++ }
++ }
++ }
++ if(tmp_flag == flag){
++ wc_dev->proslic_power = PROSLIC_POWER_WARNED;
++ return tmp_flag;
++ }
++
++ wc_dev->proslic_power = PROSLIC_POWER_ON;
++
++ /* Proslic max allowed loop current, reg 71 LOOP_I_LIMIT */
++ /* If out of range, just set it to the default value */
++ lim = (loopcurrent - 20) / 3;
++ if ( loopcurrent > 41 ) {
++ lim = 0;
++ if (debug) {
++ printk("Loop current out of range! Setting to default 20mA!\n");
++ }
++ }
++ else if (debug) {
++ printk("Loop current set to %dmA!\n",(lim*3)+20);
++ }
++ flag &= ~tmp_flag;
++
++ for(x=0;x < wc_dev->max_cards;x++){
++ if(flag & (1 << x)){
++ a24xx_spi_setreg(wc_dev,x,LOOP_I_LIMIT,lim);
++
++ /* Engage DC-DC converter */
++ a24xx_spi_setreg(wc_dev, x, 93, 0x19 /* was 0x19 */);
++ }
++ }
++
++ return tmp_flag;
++}
++
++static int si321x_proslic_insane(struct a24xx_dev *wc_dev, int card)
++{
++ int blah,insane_report;
++ insane_report=0;
++
++ blah = a24xx_spi_getreg(wc_dev, card, 0);
++ if (debug) {
++ printk("ProSLIC on module %d, product %d, version %d (0x%x)\n", card, (blah & 0x30) >> 4, (blah & 0xf), blah&0xff);
++ }
++
++#if 0
++ if ((blah & 0x30) >> 4) {
++ printk("ProSLIC on module %d is not a 3210.\n", card);
++ return -1;
++ }
++#endif
++ if (((blah & 0xf) == 0) || ((blah & 0xf) == 0xf)) {
++ /* SLIC not loaded */
++ return -1;
++ }
++ if ((blah & 0xf) < 2) {
++ printk("ProSLIC 3210 version %d is too old\n", blah & 0xf);
++ return -1;
++ }
++ if (a24xx_spi_getreg(wc_dev, card, 1) & 0x80) {
++ /* ProSLIC 3215, not a 3210 */
++ wc_dev->flags[card] |= FLAG_3215;
++ }
++
++ blah = a24xx_spi_getreg(wc_dev, card, 8);
++ if (blah != 0x2) {
++ printk("ProSLIC on module %d insane (1) %d should be 2\n", card, blah);
++ return -1;
++ } else if ( insane_report) {
++ printk("ProSLIC on module %d Reg 8 Reads %d Expected is 0x2\n",card,blah);
++ }
++
++ blah = a24xx_spi_getreg(wc_dev, card, 64);
++ if (blah != 0x0) {
++ printk("ProSLIC on module %d insane (2)\n", card);
++ return -1;
++ } else if ( insane_report) {
++ printk("ProSLIC on module %d Reg 64 Reads %d Expected is 0x0\n",card,blah);
++ }
++
++ blah = a24xx_spi_getreg(wc_dev, card, 11);
++ if (blah != 0x33) {
++ printk("ProSLIC on module %d insane (3)\n", card);
++ return -1;
++ } else if ( insane_report) {
++ printk("ProSLIC on module %d Reg 11 Reads %d Expected is 0x33\n",card,blah);
++ }
++
++ /* Just be sure it's setup right. */
++ a24xx_spi_setreg(wc_dev, card, 30, 0);
++
++ if (debug) {
++ printk("ProSLIC on module %d seems sane.\n", card);
++ }
++
++ return 0;
++}
++
++
++#if 1
++static int si321x_proslic_calibrate(struct a24xx_dev *wc_dev, int card)
++{
++ unsigned long origjiffies;
++ int x;
++ /* Perform all calibrations */
++ a24xx_spi_setreg(wc_dev, card, 97, 0x1f);
++
++ /* Begin, no speedup */
++ a24xx_spi_setreg(wc_dev, card, 96, 0x5f);
++
++ /* Wait for it to finish */
++ origjiffies = jiffies;
++ while(a24xx_spi_getreg(wc_dev, card, 96)) {
++ if ((jiffies - origjiffies) > 2 * HZ) {
++ printk("Timeout waiting for calibration of module %d\n", card);
++ return -1;
++ }
++ }
++
++ if (debug) {
++ /* Print calibration parameters */
++ printk("Calibration Vector Regs 98 - 107: \n");
++ for (x=98;x<108;x++) {
++ printk("%d: %02x\n", x, a24xx_spi_getreg(wc_dev, card, x));
++ }
++ }
++ return 0;
++}
++#endif
++
++/*return not OK cards flag*/
++static int si321x_proslic_calibrate_all(struct a24xx_dev *wc_dev, int flag)
++{
++ unsigned long origjiffies;
++ int i,x,tmp_flag;
++
++ for(x=0;x<wc_dev->max_cards;x++){
++ if(flag & (1 << x)){
++ /* Perform all calibrations */
++ a24xx_spi_setreg(wc_dev, x, 97, 0x1f);
++
++ /* Begin, no speedup */
++ a24xx_spi_setreg(wc_dev, x, 96, 0x5f);
++ }
++ }
++
++ /* Wait for it to finish */
++ origjiffies = jiffies;
++ tmp_flag = flag;
++
++ while(((jiffies - origjiffies) < 2 * HZ) && tmp_flag){
++ for(x=0;x<wc_dev->max_cards;x++){
++ if(tmp_flag & (1 << x)){
++ if(!a24xx_spi_getreg(wc_dev, x, 96)){
++ tmp_flag &= ~(1 << x);
++ }
++ }
++ }
++ }
++
++ if (debug) {
++ /* Print calibration parameters */
++ for(x=0;x<wc_dev->max_cards;x++){
++ if(flag & (1 << x)){
++ printk("Calibration Vector Regs 98 - 107: \n");
++ for (i=98;i<108;i++) {
++ printk("Module %d %d: %02x\n", x,i, a24xx_spi_getreg(wc_dev, x, i));
++ }
++ }
++ }
++ }
++ return tmp_flag;
++}
++
++static int si321x_proslic_powerleak_test(struct a24xx_dev *wc_dev, int card)
++{
++ unsigned long origjiffies;
++ unsigned char vbat;
++
++ /* Turn off linefeed */
++ a24xx_spi_setreg(wc_dev, card, 64, 0);
++
++ /* Power down */
++ a24xx_spi_setreg(wc_dev, card, 14, 0x10);
++
++ /* Wait for one second */
++ origjiffies = jiffies;
++
++ while((vbat = a24xx_spi_getreg(wc_dev, card, 82)) > 0x6) {
++ if ((jiffies - origjiffies) >= (HZ/2)) {
++ break;
++ }
++ }
++
++ if (vbat < 0x06) {
++ printk("Excessive leakage detected on module %d: %d volts (%02x) after %d ms\n", card,
++ 376 * vbat / 1000, vbat, (int)((jiffies - origjiffies) * 1000 / HZ));
++ return -1;
++ } else if (debug) {
++ printk("Post-leakage voltage: %d volts\n", 376 * vbat / 1000);
++ }
++ return 0;
++}
++
++/* return OK cards flag*/
++static int si321x_proslic_powerleak_test_all(struct a24xx_dev *wc_dev, int flag)
++{
++ unsigned long origjiffies;
++ struct stat{
++ unsigned char vbat;
++ unsigned long jifs;
++ };
++
++ struct stat stats[24];
++ int x,tmp_flag=0;
++
++ for(x=0;x<wc_dev->max_cards;x++){
++ if(flag & (1 << x)){
++ /* Turn off linefeed */
++ a24xx_spi_setreg(wc_dev, x, 64, 0);
++
++ /* Power down */
++ a24xx_spi_setreg(wc_dev, x, 14, 0x10);
++ }
++ stats[x].vbat=0;
++ stats[x].jifs=0;
++ }
++
++ /* Wait for one second */
++ origjiffies = jiffies;
++
++ tmp_flag = flag;
++ while(((jiffies - origjiffies) < (HZ/2)) && tmp_flag){
++ for(x=0;x<wc_dev->max_cards;x++){
++ if(tmp_flag & (1 << x)){
++ if((stats[x].vbat = a24xx_spi_getreg(wc_dev, x, 82)) < 0x6){
++ tmp_flag &= ~(1 << x);
++ stats[x].jifs = jiffies - origjiffies;
++ }
++ }
++ }
++ }
++
++ for(x=0;x<wc_dev->max_cards;x++){
++ if(flag & (1 << x)){
++ if (stats[x].vbat < 0x06) {
++ printk("Excessive leakage detected on module %d: %d volts (%02x) after %d ms\n", x,
++ 376 * stats[x].vbat / 1000, stats[x].vbat, (int)(stats[x].jifs * 1000 / HZ));
++ } else if (debug) {
++ printk("Post-leakage voltage: %d volts\n", 376 * stats[x].vbat / 1000);
++ }
++ }
++ }
++
++ return tmp_flag;
++}
++
++static int si321x_proslic_manual_calibrate(struct a24xx_dev *wc_dev, int card)
++{
++ unsigned long origjiffies;
++ unsigned char i;
++
++ a24xx_spi_setreg(wc_dev, card, 21, 0);//(0) Disable all interupts in DR21
++ a24xx_spi_setreg(wc_dev, card, 22, 0);//(0)Disable all interupts in DR21
++ a24xx_spi_setreg(wc_dev, card, 23, 0);//(0)Disable all interupts in DR21
++ a24xx_spi_setreg(wc_dev, card, 64, 0);//(0)
++
++ a24xx_spi_setreg(wc_dev, card, 97, 0x18); //(0x18)Calibrations without the ADC and DAC offset and without common mode calibration.
++ a24xx_spi_setreg(wc_dev, card, 96, 0x47); //(0x47) Calibrate common mode and differential DAC mode DAC + ILIM
++
++ origjiffies=jiffies;
++ while( a24xx_spi_getreg(wc_dev,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);
++ si321x_proslic_setreg_indirect(wc_dev, card, 88,0);
++ si321x_proslic_setreg_indirect(wc_dev,card,89,0);
++ si321x_proslic_setreg_indirect(wc_dev,card,90,0);
++ si321x_proslic_setreg_indirect(wc_dev,card,91,0);
++ si321x_proslic_setreg_indirect(wc_dev,card,92,0);
++ si321x_proslic_setreg_indirect(wc_dev,card,93,0);
++
++ a24xx_spi_setreg(wc_dev, card, 98,0x10); // This is necessary if the calibration occurs other than at reset time
++ a24xx_spi_setreg(wc_dev, card, 99,0x10);
++
++ for ( i=0x1f; i>0; i--) {
++ a24xx_spi_setreg(wc_dev, card, 98,i);
++ origjiffies=jiffies;
++ while((jiffies-origjiffies)<4);
++ if((a24xx_spi_getreg(wc_dev,card,88)) == 0)
++ break;
++ } // for
++
++ for ( i=0x1f; i>0; i--) {
++ a24xx_spi_setreg(wc_dev, card, 99,i);
++ origjiffies=jiffies;
++ while((jiffies-origjiffies)<4);
++ if((a24xx_spi_getreg(wc_dev,card,89)) == 0)
++ break;
++ }//for
++
++ /*******************************The preceding is the manual gain mismatch calibration****************************/
++ /**********************************The following is the longitudinal Balance Cal***********************************/
++ a24xx_spi_setreg(wc_dev,card,64,1);
++ while((jiffies-origjiffies)<10); // Sleep 100?
++
++ a24xx_spi_setreg(wc_dev, card, 64, 0);
++ a24xx_spi_setreg(wc_dev, card, 23, 0x4); // enable interrupt for the balance Cal
++ a24xx_spi_setreg(wc_dev, card, 97, 0x1); // this is a singular calibration bit for longitudinal calibration
++ a24xx_spi_setreg(wc_dev, card, 96,0x40);
++
++ a24xx_spi_getreg(wc_dev,card,96); /* Read Reg 96 just cause */
++
++ a24xx_spi_setreg(wc_dev, card, 21, 0xFF);
++ a24xx_spi_setreg(wc_dev, card, 22, 0xFF);
++ a24xx_spi_setreg(wc_dev, card, 23, 0xFF);
++
++ /**The preceding is the longitudinal Balance Cal***/
++ return(0);
++
++}
++
++/*return not OK cards flag*/
++static int si321x_proslic_manual_calibrate_all(struct a24xx_dev *wc_dev, int flag)
++{
++ unsigned long origjiffies;
++ unsigned char i;
++ int x,tmp_flag;
++
++ for(x=0;x<wc_dev->max_cards;x++){
++ if(flag & (1 << x)){
++ a24xx_spi_setreg(wc_dev, x, 21, 0);//(0) Disable all interupts in DR21
++ a24xx_spi_setreg(wc_dev, x, 22, 0);//(0)Disable all interupts in DR21
++ a24xx_spi_setreg(wc_dev, x, 23, 0);//(0)Disable all interupts in DR21
++ a24xx_spi_setreg(wc_dev, x, 64, 0);//(0)
++
++ a24xx_spi_setreg(wc_dev, x, 97, 0x18); //(0x18)Calibrations without the ADC and DAC offset and without common mode calibration.
++ a24xx_spi_setreg(wc_dev, x, 96, 0x47); //(0x47) Calibrate common mode and differential DAC mode DAC + ILIM
++ }
++ }
++
++ origjiffies=jiffies;
++
++ /*remove not OK cards flag*/
++ tmp_flag = flag;
++ while(((jiffies-origjiffies)< 80) && tmp_flag){
++ for(x=0;x<wc_dev->max_cards;x++){
++ if(tmp_flag & (1 << x)){
++ if(a24xx_spi_getreg(wc_dev,x,96) == 0){
++ tmp_flag &= ~(1 << x);
++ }
++ }
++ }
++ }
++
++ flag &= ~tmp_flag;
++
++ //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);
++
++ for(x=0;x<wc_dev->max_cards;x++){
++ if(flag & (1 << x)){
++ si321x_proslic_setreg_indirect(wc_dev,x,88,0);
++ si321x_proslic_setreg_indirect(wc_dev,x,89,0);
++ si321x_proslic_setreg_indirect(wc_dev,x,90,0);
++ si321x_proslic_setreg_indirect(wc_dev,x,91,0);
++ si321x_proslic_setreg_indirect(wc_dev,x,92,0);
++ si321x_proslic_setreg_indirect(wc_dev,x,93,0);
++
++ a24xx_spi_setreg(wc_dev, x, 98,0x10); // This is necessary if the calibration occurs other than at reset time
++ a24xx_spi_setreg(wc_dev, x, 99,0x10);
++
++ for ( i=0x1f; i>0; i--) {
++ a24xx_spi_setreg(wc_dev, x, 98,i);
++ origjiffies=jiffies;
++ while((jiffies-origjiffies)<4);
++ if((a24xx_spi_getreg(wc_dev,x,88)) == 0)
++ break;
++ } // for
++
++ for ( i=0x1f; i>0; i--) {
++ a24xx_spi_setreg(wc_dev, x, 99,i);
++ origjiffies=jiffies;
++ while((jiffies-origjiffies)<4);
++ if((a24xx_spi_getreg(wc_dev,x,89)) == 0)
++ break;
++ }//for
++
++ /*******************************The preceding is the manual gain mismatch calibration****************************/
++ /**********************************The following is the longitudinal Balance Cal***********************************/
++ a24xx_spi_setreg(wc_dev,x,64,1);
++ }
++ }
++
++ while((jiffies-origjiffies)<10); // Sleep 100?
++
++ for(x=0;x<wc_dev->max_cards;x++){
++ if(flag & (1 << x)){
++ a24xx_spi_setreg(wc_dev, x, 64, 0);
++ a24xx_spi_setreg(wc_dev, x, 23, 0x4); // enable interrupt for the balance Cal
++ a24xx_spi_setreg(wc_dev, x, 97, 0x1); // this is a singular calibration bit for longitudinal calibration
++ a24xx_spi_setreg(wc_dev, x, 96,0x40);
++
++ a24xx_spi_getreg(wc_dev,x,96); /* Read Reg 96 just cause */
++
++ a24xx_spi_setreg(wc_dev, x, 21, 0xFF);
++ a24xx_spi_setreg(wc_dev, x, 22, 0xFF);
++ a24xx_spi_setreg(wc_dev, x, 23, 0xFF);
++ }
++ }
++
++ /**The preceding is the longitudinal Balance Cal***/
++ return tmp_flag;
++}
++
++static int si321x_proslic_verify_indirect_regs(struct a24xx_dev *wc_dev, 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 = si321x_proslic_getreg_indirect(wc_dev, card, (unsigned char) indirect_regs[i].address)) < 0) {
++ printk("Failed to read indirect register %d\n", i);
++ return -1;
++ }
++ initial= indirect_regs[i].initial;
++
++ if ( j != initial && (!(wc_dev->flags[card] & FLAG_3215) || (indirect_regs[i].altaddr != 255))) {
++ printk("!!!!!!! %s iREG %X = %X should be %X\n",
++ indirect_regs[i].name,indirect_regs[i].address,j,initial );
++ passed = 0;
++ }
++ }
++
++ if (passed) {
++ if (debug) {
++ printk("Init Indirect Registers completed successfully.\n");
++ }
++ } else {
++ printk(" !!!!! Init Indirect Registers UNSUCCESSFULLY.\n");
++ return -1;
++ }
++ return 0;
++}
++
++static int si321x_proslic_init_indirect_regs(struct a24xx_dev *wc_dev, int card)
++{
++ unsigned char i;
++
++ for (i=0; i<sizeof(indirect_regs) / sizeof(indirect_regs[0]); i++) {
++ if(si321x_proslic_setreg_indirect(wc_dev, card, indirect_regs[i].address,indirect_regs[i].initial)) {
++ return -1;
++ }
++ }
++
++ return 0;
++}
++
++int si321x_set_ring_generator_mode(struct a24xx_dev *wc_dev, int card, int mode)
++{
++ int reg20, reg21, reg74; /* RCO, RNGX, VBATH */
++ struct fxs *const fxs = &wc_dev->mod[card].fxs;
++
++ fxs->neonringing = mode; /* track ring generator mode */
++
++ if (mode) { /* Neon */
++ if (debug)
++ printk(KERN_DEBUG "NEON ring on chan %d, "
++ "lasttxhook was 0x%x\n", card, fxs->lasttxhook);
++ /* Must be in FORWARD ACTIVE before setting ringer */
++ fxs->lasttxhook = SLIC_LF_ACTIVE_FWD;
++ a24xx_spi_setreg(wc_dev, card, LINE_STATE, fxs->lasttxhook);
++
++ si321x_proslic_setreg_indirect(wc_dev, card, 22,
++ NEON_MWI_RNGY_PULSEWIDTH);
++ si321x_proslic_setreg_indirect(wc_dev, card, 21,
++ 0x7bef); /* RNGX (91.5Vpk) */
++ si321x_proslic_setreg_indirect(wc_dev, card, 20,
++ 0x009f); /* RCO (RNGX, t rise)*/
++
++ a24xx_spi_setreg(wc_dev, card, 34, 0x19); /* Ringing Osc. Control */
++ a24xx_spi_setreg(wc_dev, card, 74, 0x3f); /* VBATH 94.5V */
++ si321x_proslic_setreg_indirect(wc_dev, card, 29, 0x4600); /* RPTP */
++ /* A write of 0x04 to register 64 will turn on the VM led */
++ } else {
++ a24xx_spi_setreg(wc_dev, card, 34, 0x00); /* Ringing Osc. Control */
++ /* RNGY Initial Phase */
++ si321x_proslic_setreg_indirect(wc_dev, card, 22, 0x0000);
++ si321x_proslic_setreg_indirect(wc_dev, card, 29, 0x3600); /* RPTP */
++ /* A write of 0x04 to register 64 will turn on the ringer */
++
++ if (fastringer) {
++ /* Speed up Ringer */
++ reg20 = 0x7e6d;
++ reg74 = 0x32; /* Default */
++ /* Beef up Ringing voltage to 89V */
++ if (boostringer) {
++ reg74 = 0x3f;
++ reg21 = 0x0247; /* RNGX */
++ if (debug)
++ printk(KERN_DEBUG "Boosting fast ringer"
++ " on chan %d (89V peak)\n",
++ card);
++ } else if (lowpower) {
++ reg21 = 0x014b; /* RNGX */
++ if (debug)
++ printk(KERN_DEBUG "Reducing fast ring "
++ "power on chan %d (50V peak)\n",
++ card);
++ } else if (fxshonormode &&
++ fxo_modes[_opermode].ring_x) {
++ reg21 = fxo_modes[_opermode].ring_x;
++ if (debug)
++ printk(KERN_DEBUG "fxshonormode: fast "
++ "ring_x power on chan %d\n",
++ card);
++ } else {
++ reg21 = 0x01b9;
++ if (debug)
++ printk(KERN_DEBUG "Speeding up ringer "
++ "on chan %d (25Hz)\n",
++ card);
++ }
++ /* VBATH */
++ a24xx_spi_setreg(wc_dev, card, 74, reg74);
++ /*RCO*/
++ si321x_proslic_setreg_indirect(wc_dev, card, 20, reg20);
++ /*RNGX*/
++ si321x_proslic_setreg_indirect(wc_dev, card, 21, reg21);
++
++ } else {
++ /* Ringer Speed */
++ if (fxshonormode && fxo_modes[_opermode].ring_osc) {
++ reg20 = fxo_modes[_opermode].ring_osc;
++ if (debug)
++ printk(KERN_DEBUG "fxshonormode: "
++ "ring_osc speed on chan %d\n",
++ card);
++ } else {
++ reg20 = 0x7ef0; /* Default */
++ }
++
++ reg74 = 0x32; /* Default */
++ /* Beef up Ringing voltage to 89V */
++ if (boostringer) {
++ reg74 = 0x3f;
++ reg21 = 0x1d1;
++ if (debug)
++ printk(KERN_DEBUG "Boosting ringer on "
++ "chan %d (89V peak)\n",
++ card);
++ } else if (lowpower) {
++ reg21 = 0x108;
++ if (debug)
++ printk(KERN_DEBUG "Reducing ring power "
++ "on chan %d (50V peak)\n",
++ card);
++ } else if (fxshonormode &&
++ fxo_modes[_opermode].ring_x) {
++ reg21 = fxo_modes[_opermode].ring_x;
++ if (debug)
++ printk(KERN_DEBUG "fxshonormode: ring_x"
++ " power on chan %d\n",
++ card);
++ } else {
++ reg21 = 0x160;
++ if (debug)
++ printk(KERN_DEBUG "Normal ring power on"
++ " chan %d\n",
++ card);
++ }
++ /* VBATH */
++ a24xx_spi_setreg(wc_dev, card, 74, reg74);
++ /* RCO */
++ si321x_proslic_setreg_indirect(wc_dev, card, 20, reg20);
++ /* RNGX */
++ si321x_proslic_setreg_indirect(wc_dev, card, 21, reg21);
++ }
++ }
++ return 0;
++}
++
++int si321x_init_ring_generator_mode(struct a24xx_dev *wc_dev, int card){
++ a24xx_spi_setreg(wc_dev, card, 34, 0x00); /* Ringing Osc. Control */
++ /* neon trapezoid timers */
++ a24xx_spi_setreg(wc_dev, card, 48, 0xe0); /* Active Timer low byte */
++ a24xx_spi_setreg(wc_dev, card, 49, 0x01); /* Active Timer high byte */
++ a24xx_spi_setreg(wc_dev, card, 50, 0xF0); /* Inactive Timer low byte */
++ a24xx_spi_setreg(wc_dev, card, 51, 0x05); /* Inactive Timer high byte */
++
++ si321x_set_ring_generator_mode(wc_dev, card, 0);
++
++ return 0;
++}
++
++int si321x_init_proslic(struct a24xx_dev *wc_dev, 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 && si321x_proslic_insane(wc_dev, card)) {
++ return -2;
++ }
++
++ /* default messages to none and method to FSK */
++ memset(&wc_dev->mod[card].fxs.vmwisetting, 0, sizeof(wc_dev->mod[card].fxs.vmwisetting));
++ wc_dev->mod[card].fxs.vmwi_lrev = 0;
++ wc_dev->mod[card].fxs.vmwi_hvdc = 0;
++ wc_dev->mod[card].fxs.vmwi_hvac = 0;
++
++ /* By default, don't send on hook */
++ if (reversepolarity) {
++ wc_dev->mod[card].fxs.idletxhookstate = 5;
++ } else {
++ wc_dev->mod[card].fxs.idletxhookstate = 1;
++ }
++
++ if (sane) {
++ /* Make sure we turn off the DC->DC converter to prevent anything from blowing up */
++ a24xx_spi_setreg(wc_dev, card, 14, 0x10);
++ }
++
++ if (si321x_proslic_init_indirect_regs(wc_dev, card)) {
++ printk(KERN_INFO "Indirect Registers failed to initialize on module %d.\n", card);
++ return -1;
++ }
++
++ /* Clear scratch pad area */
++ si321x_proslic_setreg_indirect(wc_dev, card, 97,0);
++
++ /* Clear digital loopback */
++ a24xx_spi_setreg(wc_dev, card, 8, 0);
++
++ /* Revision C optimization */
++ a24xx_spi_setreg(wc_dev, card, 108, 0xeb);
++
++ /* Disable automatic VBat switching for safety to prevent
++ Q7 from accidently turning on and burning out. */
++ a24xx_spi_setreg(wc_dev, card, 67, 0x07); /* Note, if pulse dialing has problems at high REN loads
++ change this to 0x17 */
++
++ /* Turn off Q7 */
++ a24xx_spi_setreg(wc_dev, card, 66, 1);
++
++ /* Flush ProSLIC digital filters by setting to clear, while
++ saving old values */
++ for (x=0;x<5;x++) {
++ tmp[x] = si321x_proslic_getreg_indirect(wc_dev, card, x + 35);
++ si321x_proslic_setreg_indirect(wc_dev, card, x + 35, 0x8000);
++ }
++
++ /* Power up the DC-DC converter */
++ if (si321x_powerup_proslic(wc_dev, card, fast)) { ////////////****************
++ printk("Unable to do INITIAL ProSLIC powerup on module %d\n", card);
++ return -1;
++ }
++
++ if (!fast) {
++ /* Check for power leaks */
++ if (si321x_proslic_powerleak_test(wc_dev, card)) {///////****************
++ printk("ProSLIC module %d failed leakage test. Check for short circuit\n", card);
++ }
++ /* Power up again */
++ if (si321x_powerup_proslic(wc_dev, card, fast)) { ////////////****************
++ printk("Unable to do FINAL ProSLIC powerup on module %d\n", card);
++ return -1;
++ }
++#ifndef NO_CALIBRATION
++ /* Perform calibration */
++ if(manual) {
++ if (si321x_proslic_manual_calibrate(wc_dev, card)) { //////////****************
++ //printk("Proslic failed on Manual Calibration\n");
++ if (si321x_proslic_manual_calibrate(wc_dev, card)) { ////////////****************
++ printk("Proslic Failed on Second Attempt to Calibrate Manually. (Try -DNO_CALIBRATION in Makefile)\n");
++ return -1;
++ }
++ printk("Proslic Passed Manual Calibration on Second Attempt\n");
++ }
++ }
++ else {
++ if(si321x_proslic_calibrate(wc_dev, card)) { ///////****************
++ //printk("ProSlic died on Auto Calibration.\n");
++ if (si321x_proslic_calibrate(wc_dev, card)) { ////////////****************
++ printk("Proslic Failed on Second Attempt to Auto Calibrate\n");
++ return -1;
++ }
++ printk("Proslic Passed Auto Calibration on Second Attempt\n");
++ }
++ }
++ /* Perform DC-DC calibration */
++ a24xx_spi_setreg(wc_dev, card, 93, 0x99);
++ r19 = a24xx_spi_getreg(wc_dev, card, 107);
++ if ((r19 < 0x2) || (r19 > 0xd)) {
++ printk("DC-DC cal has a surprising direct 107 of 0x%02x!\n", r19);
++ a24xx_spi_setreg(wc_dev, card, 107, 0x8);
++ }
++
++ /* Save calibration vectors */
++ for (x=0;x<NUM_CAL_REGS;x++) {
++ wc_dev->mod[card].fxs.calregs.vals[x] = a24xx_spi_getreg(wc_dev, card, 96 + x);
++ }
++#endif
++
++ } else {
++ /* Restore calibration registers */
++ for (x=0;x<NUM_CAL_REGS;x++) {
++ a24xx_spi_setreg(wc_dev, card, 96 + x, wc_dev->mod[card].fxs.calregs.vals[x]);
++ }
++ }
++ /* Calibration complete, restore original values */
++ for (x=0;x<5;x++) {
++ si321x_proslic_setreg_indirect(wc_dev, card, x + 35, tmp[x]);
++ }
++
++ if (si321x_proslic_verify_indirect_regs(wc_dev, card)) {
++ printk(KERN_INFO "Indirect Registers failed verification.\n");
++ return -1;
++ }
++
++
++#if 0
++ /* Disable Auto Power Alarm Detect and other "features" */
++ a24xx_spi_setreg(wc_dev, card, 67, 0x0e);
++ blah = opvx_a24xx_spi_getreg(wc_dev, card, 67);
++#endif
++
++#if 0
++ if (si321x_proslic_setreg_indirect(wc_dev, card, 97, 0x0)) { // Stanley: for the bad recording fix
++ printk(KERN_INFO "ProSlic IndirectReg Died.\n");
++ return -1;
++ }
++#endif
++
++ if (alawoverride) {
++ a24xx_spi_setreg(wc_dev, card, 1, 0x20);
++ } else {
++ a24xx_spi_setreg(wc_dev, card, 1, 0x28);
++ }
++ // U-Law 8-bit interface
++ a24xx_spi_setreg(wc_dev, card, 2, (card%4) * 8 + (card/4) * 32); // Tx Start count low byte 0
++ a24xx_spi_setreg(wc_dev, card, 3, 0); // Tx Start count high byte 0
++ a24xx_spi_setreg(wc_dev, card, 4, (card%4) * 8 + (card/4) * 32); // Rx Start count low byte 0
++ a24xx_spi_setreg(wc_dev, card, 5, 0); // Rx Start count high byte 0
++ a24xx_spi_setreg(wc_dev, card, 18, 0xff); // clear all interrupt
++ a24xx_spi_setreg(wc_dev, card, 19, 0xff);
++ a24xx_spi_setreg(wc_dev, card, 20, 0xff);
++ a24xx_spi_setreg(wc_dev, card, 73, 0x04);
++ if (fxshonormode) {
++ fxsmode = acim2tiss[fxo_modes[_opermode].acim];
++ a24xx_spi_setreg(wc_dev, card, 10, 0x08 | fxsmode);
++ if (fxo_modes[_opermode].ring_osc) {
++ si321x_proslic_setreg_indirect(wc_dev, card, 20, fxo_modes[_opermode].ring_osc);
++ }
++ if (fxo_modes[_opermode].ring_x) {
++ si321x_proslic_setreg_indirect(wc_dev, card, 21, fxo_modes[_opermode].ring_x);
++ }
++ }
++ if (lowpower) {
++ a24xx_spi_setreg(wc_dev, card, 72, 0x10);
++ }
++
++#if 0
++ a24xx_spi_setreg(wc_dev, card, 21, 0x00); // enable interrupt
++ a24xx_spi_setreg(wc_dev, card, 22, 0x02); // Loop detection interrupt
++ a24xx_spi_setreg(wc_dev, card, 23, 0x01); // DTMF detection interrupt
++#endif
++
++#if 0
++ /* Enable loopback */
++ a24xx_spi_setreg(wc_dev, card, 8, 0x2);
++ a24xx_spi_setreg(wc_dev, card, 14, 0x0);
++ a24xx_spi_setreg(wc_dev, card, 64, 0x0);
++ a24xx_spi_setreg(wc_dev, card, 1, 0x08);
++#endif
++
++ if (fastringer) {
++ /* Speed up Ringer */
++ si321x_proslic_setreg_indirect(wc_dev, card, 20, 0x7e6d);
++ si321x_proslic_setreg_indirect(wc_dev, card, 21, 0x01b9);
++ /* Beef up Ringing voltage to 89V */
++ if (boostringer) {
++ a24xx_spi_setreg(wc_dev, card, 74, 0x3f);
++ if (si321x_proslic_setreg_indirect(wc_dev, card, 21, 0x247)) {
++ return -1;
++ }
++ printk("Boosting fast ringer on slot %d (89V peak)\n", card + 1);
++ } else if (lowpower) {
++ if (si321x_proslic_setreg_indirect(wc_dev, card, 21, 0x14b)) {
++ return -1;
++ }
++ printk("Reducing fast ring power on slot %d (50V peak)\n", card + 1);
++ } else {
++ printk("Speeding up ringer on slot %d (25Hz)\n", card + 1);
++ }
++ } else {
++ /* Beef up Ringing voltage to 89V */
++ if (boostringer) {
++ a24xx_spi_setreg(wc_dev, card, 74, 0x3f);
++ if (si321x_proslic_setreg_indirect(wc_dev, card, 21, 0x1d1)) {
++ return -1;
++ }
++ printk("Boosting ringer on slot %d (89V peak)\n", card + 1);
++ } else if (lowpower) {
++ if (si321x_proslic_setreg_indirect(wc_dev, card, 21, 0x108)) {
++ return -1;
++ }
++ printk("Reducing ring power on slot %d (50V peak)\n", card + 1);
++ }
++ }
++ if (si321x_init_ring_generator_mode(wc_dev, card)) {
++ return -1;
++ }
++ if(fxstxgain || fxsrxgain) {
++ r9 = a24xx_spi_getreg(wc_dev, 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;
++ }
++ a24xx_spi_setreg(wc_dev,card,9,r9);
++ }
++
++ if(debug) {
++ printk("DEBUG: fxstxgain:%s fxsrxgain:%s\n",
++ ((a24xx_spi_getreg(wc_dev, card, 9)/8) == 1) ? "3.5":
++ (((a24xx_spi_getreg(wc_dev, card, 9)/4) == 1) ? "-3.5":"0.0"),
++ ((a24xx_spi_getreg(wc_dev, card, 9)/2) == 1) ?"3.5":((a24xx_spi_getreg(wc_dev,card,9)%2) ? "-3.5":"0.0")
++ );
++ }
++
++ a24xx_spi_setreg(wc_dev, card, 64, 0x01);
++
++ return 0;
++}
++
++/***
++*return
++ ret_flag : not OK cards flag
++ blk_flag : not installed cards flag
++***/
++int si321x_init_proslic_all(struct a24xx_dev *wc_dev, int fxs_flag,int fast, int manual, int sane,int *blk_flag)
++{
++ int flag=fxs_flag,tmp_flag=0,ret_flag=0;
++ unsigned short tmp[24][5];
++ unsigned char r19, r9;
++ int x,i;
++ int fxsmode=0;
++
++
++ for(i=0;i<wc_dev->max_cards;i++)
++ {
++ if(flag & (1 << i)){
++ if((i%4) == 0){
++ __a24xx_setcard(wc_dev, i);
++ __opvx_a24xx_write_8bits(wc_dev->mem32, 0x00);
++ __opvx_a24xx_write_8bits(wc_dev->mem32, 0x80);
++ }
++
++ /* Sanity check the ProSLIC */
++ if (!sane && si321x_proslic_insane(wc_dev, i)) {
++ tmp_flag |= 1<<i;
++ continue;
++ }
++
++ /* default messages to none and method to FSK */
++ memset(&wc_dev->mod[i].fxs.vmwisetting, 0, sizeof(wc_dev->mod[i].fxs.vmwisetting));
++ wc_dev->mod[i].fxs.vmwi_lrev = 0;
++ wc_dev->mod[i].fxs.vmwi_hvdc = 0;
++ wc_dev->mod[i].fxs.vmwi_hvac = 0;
++
++ /* By default, don't send on hook */
++ if (reversepolarity) {
++ wc_dev->mod[i].fxs.idletxhookstate = 5;
++ } else {
++ wc_dev->mod[i].fxs.idletxhookstate = 1;
++ }
++
++ if (sane) {
++ /* Make sure we turn off the DC->DC converter to prevent anything from blowing up */
++ a24xx_spi_setreg(wc_dev, i, 14, 0x10);
++ }
++
++ if (si321x_proslic_init_indirect_regs(wc_dev, i)) {
++ printk(KERN_INFO "Indirect Registers failed to initialize on module %d.\n", i);
++ tmp_flag |= 1<<i;
++ continue;
++ }
++
++ /* Clear scratch pad area */
++ si321x_proslic_setreg_indirect(wc_dev, i, 97,0);
++
++ /* Clear digital loopback */
++ a24xx_spi_setreg(wc_dev, i, 8, 0);
++
++ /* Revision C optimization */
++ a24xx_spi_setreg(wc_dev, i, 108, 0xeb);
++
++ /* Disable automatic VBat switching for safety to prevent
++ Q7 from accidently turning on and burning out. */
++ a24xx_spi_setreg(wc_dev, i, 67, 0x07); /* Note, if pulse dialing has problems at high REN loads
++ change this to 0x17 */
++
++ /* Turn off Q7 */
++ a24xx_spi_setreg(wc_dev, i, 66, 1);
++
++ /* Flush ProSLIC digital filters by setting to clear, while
++ saving old values */
++ for (x=0;x<5;x++) {
++ tmp[i][x] = si321x_proslic_getreg_indirect(wc_dev, i, x + 35);
++ si321x_proslic_setreg_indirect(wc_dev, i, x + 35, 0x8000);
++ }
++ }
++ }
++
++ /*remove not installed cards*/
++ flag &= ~(tmp_flag);
++ if(blk_flag)
++ *blk_flag = tmp_flag;
++
++
++ touch_softlockup_watchdog();
++
++ /* Power up the DC-DC converter */
++ tmp_flag = si321x_powerup_proslic_all(wc_dev, flag, fast);
++ for(i=0;i<wc_dev->max_cards;i++){
++ if(tmp_flag & (1 << i))
++ printk("Unable to do INITIAL ProSLIC powerup on module %d\n",i);
++ }
++
++ /*remove not OK cards flag*/
++ flag &= ~(tmp_flag);
++ ret_flag |= tmp_flag;
++
++
++ if (!fast){
++ /* Check for power leaks */
++ tmp_flag=si321x_proslic_powerleak_test_all(wc_dev,flag);
++ for(i=0;i<wc_dev->max_cards;i++){
++ if( (flag & (1 << i)) && !(tmp_flag & (1 << i)) ){
++ printk("ProSLIC module %d failed leakage test. Check for short circuit\n", i);
++ }
++ }
++
++ /* Power up again */
++ tmp_flag = si321x_powerup_proslic_all(wc_dev, flag, fast);
++ for(i=0;i<wc_dev->max_cards;i++){
++ if(tmp_flag & (1 << i))
++ printk("Unable to do FINAL ProSLIC powerup on module %d\n",i);
++ }
++
++ /*remove not OK cards flag*/
++ flag &= ~(tmp_flag);
++ ret_flag |= tmp_flag;
++
++#ifndef NO_CALIBRATION
++ if(manual) {
++ tmp_flag = si321x_proslic_manual_calibrate_all(wc_dev,flag);
++ if(tmp_flag){
++ tmp_flag = si321x_proslic_manual_calibrate_all(wc_dev,tmp_flag);
++ for(i=0;i<wc_dev->max_cards;i++){
++ if(flag & (1 << i)){
++ if(tmp_flag & (1 << i))
++ printk("Proslic Failed on Second Attempt to Calibrate Manually module %d . (Try -DNO_CALIBRATION in Makefile)\n",i);
++ else
++ printk("Proslic Passed Manual Calibration on Second Attempt on module %d \n",i);
++ }
++ }
++ }
++ }else{
++ tmp_flag = si321x_proslic_calibrate_all(wc_dev,flag);
++ if(tmp_flag){
++ tmp_flag = si321x_proslic_calibrate_all(wc_dev,tmp_flag);
++ for(i=0;i<wc_dev->max_cards;i++){
++ if(flag & (1 << i)){
++ if(tmp_flag & (1 << i))
++ printk("Proslic Failed on Second Attempt to Auto Calibrate module %d . (Try -DNO_CALIBRATION in Makefile)\n",i);
++ else
++ printk("Proslic Passed Auto Calibration on Second Attempt on module %d \n",i);
++ }
++ }
++ }
++ }
++
++ /*remove not OK cards flag*/
++ flag &= ~(tmp_flag);
++ ret_flag |= tmp_flag;
++
++ for(i=0;i<wc_dev->max_cards;i++){
++ if(flag & (1 << i)){
++ /* Perform DC-DC calibration */
++ a24xx_spi_setreg(wc_dev, i, 93, 0x99);
++ r19 = a24xx_spi_getreg(wc_dev, i, 107);
++ if ((r19 < 0x2) || (r19 > 0xd)) {
++ printk("DC-DC cal has a surprising direct 107 of 0x%02x!\n", r19);
++ a24xx_spi_setreg(wc_dev, i, 107, 0x8);
++ }
++ /* Save calibration vectors */
++ for (x=0;x<NUM_CAL_REGS;x++) {
++ wc_dev->mod[i].fxs.calregs.vals[x] = a24xx_spi_getreg(wc_dev, i, 96 + x);
++ }
++ }
++ }
++#endif
++ }else{
++ for(i=0;i<wc_dev->max_cards;i++){
++ if(flag & (1 << i)){
++ /* Restore calibration registers */
++ for (x=0;x<NUM_CAL_REGS;x++) {
++ a24xx_spi_setreg(wc_dev, i, 96 + x, wc_dev->mod[i].fxs.calregs.vals[x]);
++ }
++ }
++ }
++ }
++
++ tmp_flag = 0;
++ for(i=0;i<wc_dev->max_cards;i++){
++ if(flag & (1 << i)){
++ /* Calibration complete, restore original values */
++ for (x=0;x<5;x++) {
++ si321x_proslic_setreg_indirect(wc_dev, i, x + 35, tmp[i][x]);
++ }
++
++ if (si321x_proslic_verify_indirect_regs(wc_dev, i)) {
++ printk(KERN_INFO "Indirect Registers failed verification on module %d.\n",i);
++ tmp_flag |=(1<<i);
++ continue;
++ }
++
++
++ if (alawoverride) {
++ a24xx_spi_setreg(wc_dev, i, 1, 0x20);
++ } else {
++ a24xx_spi_setreg(wc_dev, i, 1, 0x28);
++ }
++ // U-Law 8-bit interface
++ a24xx_spi_setreg(wc_dev, i, 2, (i%4) * 8 + (i/4) * 32); // Tx Start count low byte 0
++ a24xx_spi_setreg(wc_dev, i, 3, 0); // Tx Start count high byte 0
++ a24xx_spi_setreg(wc_dev, i, 4, (i%4) * 8 + (i/4) * 32); // Rx Start count low byte 0
++ a24xx_spi_setreg(wc_dev, i, 5, 0); // Rx Start count high byte 0
++ a24xx_spi_setreg(wc_dev, i, 18, 0xff); // clear all interrupt
++ a24xx_spi_setreg(wc_dev, i, 19, 0xff);
++ a24xx_spi_setreg(wc_dev, i, 20, 0xff);
++ a24xx_spi_setreg(wc_dev, i, 73, 0x04);
++ if (fxshonormode) {
++ fxsmode = acim2tiss[fxo_modes[_opermode].acim];
++ a24xx_spi_setreg(wc_dev, i, 10, 0x08 | fxsmode);
++ if (fxo_modes[_opermode].ring_osc) {
++ si321x_proslic_setreg_indirect(wc_dev, i, 20, fxo_modes[_opermode].ring_osc);
++ }
++ if (fxo_modes[_opermode].ring_x) {
++ si321x_proslic_setreg_indirect(wc_dev, i, 21, fxo_modes[_opermode].ring_x);
++ }
++ }
++ if (lowpower) {
++ a24xx_spi_setreg(wc_dev, i, 72, 0x10);
++ }
++
++ if (fastringer) {
++ /* Speed up Ringer */
++ si321x_proslic_setreg_indirect(wc_dev, i, 20, 0x7e6d);
++ si321x_proslic_setreg_indirect(wc_dev, i, 21, 0x01b9);
++ /* Beef up Ringing voltage to 89V */
++ if (boostringer) {
++ a24xx_spi_setreg(wc_dev, i, 74, 0x3f);
++ if (si321x_proslic_setreg_indirect(wc_dev, i, 21, 0x247)) {
++ tmp_flag |=(1<<i);
++ continue;
++ }
++ printk("Boosting fast ringer on slot %d (89V peak)\n", i + 1);
++ } else if (lowpower) {
++ if (si321x_proslic_setreg_indirect(wc_dev, i, 21, 0x14b)) {
++ tmp_flag |=(1<<i);
++ continue;
++ }
++ printk("Reducing fast ring power on slot %d (50V peak)\n", i + 1);
++ } else {
++ printk("Speeding up ringer on slot %d (25Hz)\n", i + 1);
++ }
++ } else {
++ /* Beef up Ringing voltage to 89V */
++ if (boostringer) {
++ a24xx_spi_setreg(wc_dev, i, 74, 0x3f);
++ if (si321x_proslic_setreg_indirect(wc_dev, i, 21, 0x1d1)) {
++ tmp_flag |=(1<<i);
++ continue;
++ }
++ printk("Boosting ringer on slot %d (89V peak)\n", i + 1);
++ } else if (lowpower) {
++ if (si321x_proslic_setreg_indirect(wc_dev, i, 21, 0x108)) {
++ tmp_flag |=(1<<i);
++ continue;
++ }
++ printk("Reducing ring power on slot %d (50V peak)\n", i + 1);
++ }
++ }
++
++ if (si321x_init_ring_generator_mode(wc_dev, i)) {
++ tmp_flag |=(1<<i);
++ continue;
++ }
++
++ if(fxstxgain || fxsrxgain) {
++ r9 = a24xx_spi_getreg(wc_dev, i, 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;
++ }
++ a24xx_spi_setreg(wc_dev,i,9,r9);
++ }
++
++ if(debug) {
++ printk("DEBUG: fxstxgain:%s fxsrxgain:%s\n",
++ ((a24xx_spi_getreg(wc_dev, i, 9)/8) == 1) ? "3.5":
++ (((a24xx_spi_getreg(wc_dev, i, 9)/4) == 1) ? "-3.5":"0.0"),
++ ((a24xx_spi_getreg(wc_dev, i, 9)/2) == 1) ?"3.5":((a24xx_spi_getreg(wc_dev,i,9)%2) ? "-3.5":"0.0")
++ );
++ }
++
++ a24xx_spi_setreg(wc_dev, i, 64, 0x01);
++ }
++ }
++ ret_flag |= tmp_flag;
++
++ return ret_flag;
++}
++
++int si321x_proslic_setreg_indirect(struct a24xx_dev *wc_dev, int card, unsigned char address, unsigned short data)
++{
++ unsigned long flags;
++ int ret;
++
++ spin_lock_irqsave(&wc_dev->lock, flags);
++ ret = __a24xx_proslic_setreg_indirect(wc_dev, card, address, data);
++ spin_unlock_irqrestore(&wc_dev->lock, flags);
++
++ return ret;
++}
++
++int si321x_proslic_getreg_indirect(struct a24xx_dev *wc_dev, int card, unsigned char address)
++{
++ unsigned long flags;
++ int ret;
++
++ spin_lock_irqsave(&wc_dev->lock, flags);
++ ret = __a24xx_proslic_getreg_indirect(wc_dev, card, address);
++ spin_unlock_irqrestore(&wc_dev->lock, flags);
++
++ return ret;
++}
++
++void si321x_proslic_recheck_sanity(struct a24xx_dev *wc_dev, int card)
++{
++ int res;
++
++ /* Check loopback */
++ res = wc_dev->reg1shadow[card];
++ if (!res && (res != wc_dev->mod[card].fxs.lasttxhook)) // read real state from register By wx
++ res=a24xx_spi_getreg(wc_dev, card, 64);
++ if (!res && (res != wc_dev->mod[card].fxs.lasttxhook)) {
++ res = a24xx_spi_getreg(wc_dev, card, 8);
++ if (res) {
++ printk("Ouch, part reset, quickly restoring reality (%d)\n", card);
++ si321x_init_proslic(wc_dev, card, 1, 0, 1);
++ } else {
++ if (wc_dev->mod[card].fxs.palarms++ < MAX_ALARMS) {
++ printk("Power alarm on module %d, resetting!\n", card + 1);
++ if (wc_dev->mod[card].fxs.lasttxhook == 4)
++ wc_dev->mod[card].fxs.lasttxhook = 1;
++ a24xx_spi_setreg(wc_dev, card, 64, wc_dev->mod[card].fxs.lasttxhook);
++ } else {
++ if (wc_dev->mod[card].fxs.palarms == MAX_ALARMS)
++ printk("Too many power alarms on card %d, NOT resetting!\n", card + 1);
++ }
++ }
++ }
++}
+--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxd115/Kbuild 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxd115/Kbuild 2015-02-10 14:19:03.000000000 +0100
+@@ -0,0 +1,33 @@
++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OPVXD115) += opvxd115.o
++
++FIRM_DIR := ../firmware
++
++EXTRA_CFLAGS += -I$(src)/.. -I$(src)/../oct612x/ $(shell $(src)/../oct612x/octasic-helper cflags $(src)/../oct612x) -Wno-undef
++
++# The OCT612X source files are from a vendor drop and we do not want to edit
++# them to make this warning go away. Therefore, turn off the
++# unused-but-set-variable warning for this driver.
++
++EXTRA_CFLAGS += $(call cc-option, -Wno-unused-but-set-variable)
++
++ifeq ($(HOTPLUG_FIRMWARE),yes)
++ EXTRA_CFLAGS+=-DHOTPLUG_FIRMWARE
++endif
++
++opvxd115-objs := base.o vpm450m.o
++
++ifneq ($(HOTPLUG_FIRMWARE),yes)
++opvxd115-objs += $(FIRM_DIR)/dahdi-fw-oct6114-032.o $(FIRM_DIR)/dahdi-fw-oct6114-064.o $(FIRM_DIR)/dahdi-fw-oct6114-128.o $(FIRM_DIR)/dahdi-fw-oct6114-256.o
++$(warning WARNING: You are compiling firmware into wct4xxp.ko which is not available under the terms of the GPL. It may be a violation of the GPL to distribute the resulting image since it combines both GPL and non-GPL work. You should consult a lawyer of your own before distributing such an image.)
++endif
++$(obj)/$(FIRM_DIR)/dahdi-fw-oct6114-032.o: $(obj)/base.o
++ $(MAKE) -C $(obj)/$(FIRM_DIR) dahdi-fw-oct6114-032.o
++
++$(obj)/$(FIRM_DIR)/dahdi-fw-oct6114-064.o: $(obj)/base.o
++ $(MAKE) -C $(obj)/$(FIRM_DIR) dahdi-fw-oct6114-064.o
++
++$(obj)/$(FIRM_DIR)/dahdi-fw-oct6114-128.o: $(obj)/base.o
++ $(MAKE) -C $(obj)/$(FIRM_DIR) dahdi-fw-oct6114-128.o
++
++$(obj)/$(FIRM_DIR)/dahdi-fw-oct6114-256.o: $(obj)/base.o
++ $(MAKE) -C $(obj)/$(FIRM_DIR) dahdi-fw-oct6114-256.o
diff --git a/dahdi-linux-2.10.1-openvox-3.patch b/dahdi-linux-2.10.1-openvox-3.patch
new file mode 100644
index 000000000000..e2598b5e8bef
--- /dev/null
+++ b/dahdi-linux-2.10.1-openvox-3.patch
@@ -0,0 +1,7272 @@
+--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxd115/base.c 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxd115/base.c 2015-02-10 14:19:03.000000000 +0100
+@@ -0,0 +1,5624 @@
++/*
++ * TE410P Quad-T1/E1 PCI Driver version 0.1, 12/16/02
++ *
++ * Written by Mark Spencer <markster@digium.com>
++ * Based on previous works, designs, and archetectures conceived and
++ * written by Jim Dixon <jim@lambdatel.com>.
++ * Further modified, optimized, and maintained by
++ * Matthew Fredrickson <creslin@digium.com> and
++ * Russ Meyerriecks <rmeyerriecks@digium.com>
++ *
++ * Copyright (C) 2001 Jim Dixon / Zapata Telephony.
++ * Copyright (C) 2001-2012, 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 <linux/crc32.h>
++#include <linux/slab.h>
++
++#include <stdbool.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
++
++/* Support first generation cards? */
++#define SUPPORT_GEN1
++
++/* Define to get more attention-grabbing but slightly more I/O using
++ alarm status */
++#undef 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
++
++#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 */
++ struct cpu_workqueue_struct *cpu_wq;
++ 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_FORCE_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
++ */
++/* #define CONFIG_FORCE_EXTENDED_RESET */
++/* #define CONFIG_NOEXTENDED_RESET */
++
++/*
++ * Uncomment the following definition in order to disable Active-State Power
++ * Management on the PCIe bridge for PCIe cards. This has been known to work
++ * around issues where the BIOS enables it on the cards even though the
++ * platform does not support it.
++ *
++ */
++/* #define CONFIG_WCT4XXP_DISABLE_ASPM */
++
++#if defined(CONFIG_FORCE_EXTENDED_RESET) && defined(CONFIG_NOEXTENDED_RESET)
++#error "You cannot define both CONFIG_FORCE_EXTENDED_RESET and " \
++ "CONFIG_NOEXTENDED_RESET."
++#endif
++
++int debug = 0;
++static int timingcable = 0;
++static int t1e1override = -1; /* deprecated */
++static char *default_linemode = "auto";
++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*/
++#endif /* VPM_SUPPORT */
++
++/* 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;
++static int ignore_rotary;
++
++#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 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_EXPRESS (1 << 9)
++#define FLAG_5THGEN (1 << 10)
++#define FLAG_8PORT (1 << 11)
++
++#define FLAG_1PORT (1 << 15)
++#define CANARY 0xc0de
++
++/* names of available HWEC modules */
++#ifdef VPM_SUPPORT
++#define T4_VPM_PRESENT (1 << 28)
++static const char *vpmoct032_name = "VPMOCT032";
++static const char *vpmoct064_name = "VPMOCT064";
++static const char *vpmoct128_name = "VPMOCT128";
++static const char *vpmoct256_name = "VPMOCT256";
++#endif
++
++struct devtype {
++ char *desc;
++ unsigned int flags;
++};
++
++static struct devtype opvxd115p2 = { "OpenVox D115P/D115E Single-port E1/T1 card (2nd GEN))", FLAG_2NDGEN | FLAG_1PORT};
++static struct devtype opvxd130p5 = { "OpenVox D130P/D130E Single-port E1/T1 card (3rd GEN)", FLAG_5THGEN | FLAG_BURST | FLAG_2NDGEN | FLAG_3RDGEN | FLAG_1PORT};
++
++
++struct t4;
++
++enum linemode {T1, E1, J1};
++
++struct spi_state {
++ int wrreg;
++ int rdreg;
++};
++
++struct t4_span {
++ struct t4 *owner;
++ u32 *writechunk; /* Double-word aligned write memory */
++ u32 *readchunk; /* Double-word aligned read memory */
++ enum linemode linemode;
++ int sync;
++ int alarmtimer;
++ int notclear;
++ unsigned long alarm_time;
++ unsigned long losalarm_time;
++ unsigned long aisalarm_time;
++ unsigned long yelalarm_time;
++ unsigned long alarmcheck_time;
++ 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
++ /* 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;
++#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 */
++ unsigned int intcount;
++ int num; /* Which card we are */
++ int syncsrc; /* active sync source */
++ struct dahdi_device *ddev;
++ struct t4_span *tspans[8]; /* 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 */
++ const struct devtype *devtype;
++ unsigned int reset_required:1; /* If reset needed in serial_setup */
++ unsigned int falc31:1; /* are we falc v3.1 (atomic not necessary) */
++ unsigned int t1e1:8; /* T1 / E1 select pins */
++ int ledreg; /* LED Register */
++ int ledreg2; /* LED Register2 */
++ unsigned int gpio;
++ unsigned int gpioctl;
++ int e1recover; /* E1 recovery timer */
++ spinlock_t reglock; /* lock register access */
++ int spansstarted; /* number of spans started */
++ u32 *writechunk; /* Double-word aligned write memory */
++ u32 *readchunk; /* Double-word aligned read memory */
++#ifdef ENABLE_WORKQUEUES
++ atomic_t worklist;
++ struct workqueue_struct *workq;
++#endif
++ int last0; /* for detecting double-missed IRQ */
++
++ /* DMA related fields */
++ unsigned int dmactrl;
++ dma_addr_t readdma;
++ dma_addr_t writedma;
++ void __iomem *membase; /* Base address of card */
++
++#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
++ unsigned long checkflag;
++ struct work_struct bh_work;
++ /* Latency related additions */
++ unsigned char rxident;
++ unsigned char lastindex;
++ int numbufs;
++ int needed_latency;
++
++#ifdef VPM_SUPPORT
++ struct vpm450m *vpm;
++#endif
++ struct spi_state st;
++};
++
++static inline bool is_pcie(const struct t4 *wc)
++{
++ return (wc->devtype->flags & FLAG_EXPRESS) > 0;
++}
++
++static inline bool has_e1_span(const struct t4 *wc)
++{
++ return (wc->t1e1 > 0);
++}
++
++static inline bool is_octal(const struct t4 *wc)
++{
++ return (wc->devtype->flags & FLAG_8PORT) > 0;
++}
++
++static inline int T4_BASE_SIZE(struct t4 *wc)
++{
++ if (is_octal(wc))
++ return DAHDI_MAX_CHUNKSIZE * 32 * 8;
++ else
++ return DAHDI_MAX_CHUNKSIZE * 32 * 4;
++}
++
++/**
++ * ports_on_framer - The number of ports on the framers.
++ * @wc: Board to check.
++ *
++ * The framer ports could be different the the number of ports on the card
++ * since the dual spans have four ports internally but two ports extenally.
++ *
++ */
++static inline unsigned int ports_on_framer(const struct t4 *wc)
++{
++ return (is_octal(wc)) ? 8 : 4;
++}
++
++#ifdef VPM_SUPPORT
++static void t4_vpm_init(struct t4 *wc);
++
++static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec);
++
++static const struct dahdi_echocan_features vpm_ec_features = {
++ .NLP_automatic = 1,
++ .CED_tx_detect = 1,
++ .CED_rx_detect = 1,
++};
++
++static const struct dahdi_echocan_ops vpm_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_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);
++static int t4_reset_counters(struct dahdi_span *span);
++#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_LEDS2 12
++
++#define WC_SET_AUTH (1 << 20)
++#define WC_GET_AUTH (1 << 12)
++
++#define WC_LFRMR_CS (1 << 10) /* Framer's ChipSelect signal */
++#define WC_LCS (1 << 11)
++#define WC_LCS2 (1 << 12)
++#define WC_LALE (1 << 13)
++#define WC_LFRMR_CS2 (1 << 14) /* Framer's ChipSelect signal 2 */
++#define WC_LREAD (1 << 15)
++#define WC_LWRITE (1 << 16)
++
++#define WC_ACTIVATE (1 << 12)
++
++#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 FMR5 0x21 /* Framer Mode Register 5 */
++#define FMR5_XLU (1 << 4) /* Transmit loopup code */
++#define FMR5_XLD (1 << 5) /* Transmit loopdown code */
++#define FMR5_EIBR (1 << 6) /* Internal Bit Robbing Access */
++#define DEC_T 0x60 /* Diable Error Counter */
++#define IERR_T 0x1B /* Single Bit Defect Insertion Register */
++#define IBV (1 << 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 struct t4 *cards[MAX_T4_CARDS];
++
++struct t8_firm_header {
++ u8 header[6];
++ __le32 chksum;
++ u8 pad[18];
++ __le32 version;
++} __packed;
++
++#define MAX_TDM_CHAN 32
++#define MAX_DTMF_DET 16
++
++#define HDLC_IMR0_MASK (FRMR_IMR0_RME | FRMR_IMR0_RPF)
++#define HDLC_IMR1_MASK (FRMR_IMR1_XDU | FRMR_IMR1_XPR)
++
++static inline unsigned int __t4_pci_in(struct t4 *wc, const unsigned int addr)
++{
++ unsigned int res = readl(wc->membase + (addr * sizeof(u32)));
++ return res;
++}
++
++static inline void __t4_pci_out(struct t4 *wc, const unsigned int addr, const unsigned int value)
++{
++#ifdef DEBUG
++ unsigned int tmp;
++#endif
++ writel(value, wc->membase + (addr * sizeof(u32)));
++#ifdef DEBUG
++ tmp = __t4_pci_in(wc, WC_VERSION);
++ if ((tmp & 0xffff0000) != 0xc01a0000)
++ dev_notice(&wc->dev->dev,
++ "Version Synchronization Error!\n");
++#else
++ __t4_pci_in(wc, WC_VERSION);
++#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)
++{
++ if (span <= 3) {
++ 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);
++ } else {
++ int oldreg = wc->ledreg2;
++
++ span &= 3;
++ wc->ledreg2 &= ~(0x3 << (span << 1));
++ wc->ledreg2 |= (color << (span << 1));
++ if (oldreg != wc->ledreg2)
++ __t4_pci_out(wc, WC_LEDS2, wc->ledreg2);
++ }
++}
++
++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 unsigned int __t4_framer_in(const struct t4 *wc, int unit,
++ const unsigned int addr)
++{
++ unsigned int ret;
++ register u32 val;
++ void __iomem *const wc_laddr = wc->membase + (WC_LADDR*sizeof(u32));
++ void __iomem *const wc_version = wc->membase + (WC_VERSION*sizeof(u32));
++ void __iomem *const wc_ldata = wc->membase + (WC_LDATA*sizeof(u32));
++ int haddr = (((unit & 4) ? 0 : WC_LFRMR_CS2));
++ unit &= 0x3;
++
++ val = ((unit & 0x3) << 8) | (addr & 0xff) | haddr;
++ writel(val, wc_laddr);
++ readl(wc_version);
++ writel(val | WC_LFRMR_CS | WC_LREAD, wc_laddr);
++ readl(wc_version);
++ ret = readb(wc_ldata);
++ writel(val, wc_laddr);
++ readl(wc_version);
++ return ret;
++}
++
++static 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 void __t4_framer_out(const struct t4 *wc, int unit, const u8 addr,
++ const unsigned int value)
++{
++ register u32 val;
++ void __iomem *const wc_laddr = wc->membase + (WC_LADDR*sizeof(u32));
++ void __iomem *const wc_version = wc->membase + (WC_VERSION*sizeof(u32));
++ void __iomem *const wc_ldata = wc->membase + (WC_LDATA*sizeof(u32));
++ int haddr = (((unit & 4) ? 0 : WC_LFRMR_CS2));
++
++ val = ((unit & 0x3) << 8) | (addr & 0xff) | haddr;
++ writel(val, wc_laddr);
++ readl(wc_version);
++ writel(value, wc_ldata);
++ readl(wc_version);
++ writel(val | WC_LFRMR_CS | WC_LWRITE, wc_laddr);
++ readl(wc_version);
++ writel(val, wc_laddr);
++ readl(wc_version);
++}
++
++static 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 __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 (!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));
++ __t4_pci_out(wc, WC_LADDR, (0));
++}
++
++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));
++ __t4_pci_out(wc, WC_LADDR, (WC_LWRITE | WC_LALE));
++ __t4_pci_out(wc, WC_LADDR, (WC_LALE));
++ 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 (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 (!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 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 void t4_check_vpm(struct t4 *wc)
++{
++ int channel, tone, start, span;
++
++ if (vpm450m_checkirq(wc->vpm)) {
++ while(vpm450m_getdtmf(wc->vpm, &channel, &tone, &start)) {
++ span = channel & 0x3;
++ channel >>= 2;
++ if (!has_e1_span(wc))
++ 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));
++ }
++ }
++ }
++ }
++}
++
++#endif /* VPM_SUPPORT */
++
++static void hdlc_stop(struct t4 *wc, unsigned int span)
++{
++ struct t4_span *t = wc->tspans[span];
++ unsigned char imr0, imr1, mode;
++ unsigned long flags;
++ 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);
++ }
++
++ spin_lock_irqsave(&wc->reglock, flags);
++ 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);
++ spin_unlock_irqrestore(&wc->reglock, flags);
++
++ 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;
++
++ spin_lock_irqsave(&wc->reglock, flags);
++
++ /* 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);
++
++ spin_unlock_irqrestore(&wc->reglock, flags);
++
++ /* 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 (E1 != ts->linemode) {
++ 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);
++ }
++}
++
++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 const char *__t4_echocan_name(struct t4 *wc)
++{
++ if (wc->vpm) {
++ if (wc->numspans == 1)
++ return vpmoct032_name;
++ if (wc->numspans == 2)
++ return vpmoct064_name;
++ else if (wc->numspans == 4)
++ return vpmoct128_name;
++ else if (wc->numspans == 8)
++ return vpmoct256_name;
++ }
++ return NULL;
++}
++
++static const char *t4_echocan_name(const struct dahdi_chan *chan)
++{
++ struct t4 *wc = chan->pvt;
++ return __t4_echocan_name(wc);
++}
++
++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 bool alaw = (chan->span->deflaw == 2);
++
++ if (!vpmsupport || !wc->vpm)
++ return -ENODEV;
++
++ if (ecp->param_count > 0) {
++ dev_warn(&wc->dev->dev, "%s echo canceller does not support "
++ "parameters; failing request\n",
++ chan->ec_factory->get_name(chan));
++ return -EINVAL;
++ }
++
++ *ec = tspan->ec[chan->chanpos - 1];
++ (*ec)->ops = &vpm_ec_ops;
++ (*ec)->features = vpm_ec_features;
++
++ channel = has_e1_span(wc) ? chan->chanpos : chan->chanpos + 4;
++
++ if (is_octal(wc))
++ channel = channel << 3;
++ else
++ 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_set_alaw_companding(wc->vpm, channel, alaw);
++ vpm450m_setec(wc->vpm, channel, ecp->tap_length);
++ return 0;
++}
++
++static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec)
++{
++ struct t4 *wc = chan->pvt;
++ int channel;
++
++ if (!wc->vpm)
++ return;
++
++ memset(ec, 0, sizeof(*ec));
++ channel = has_e1_span(wc) ? chan->chanpos : chan->chanpos + 4;
++
++ if (is_octal(wc))
++ channel = channel << 3;
++ else
++ 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->vpm, channel, 0);
++}
++#endif
++
++static int t4_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data)
++{
++ struct t4_regs regs;
++ struct t4_reg reg;
++ int x;
++ struct t4 *wc = chan->pvt;
++#ifdef VPM_SUPPORT
++ int j;
++ int channel;
++ struct t4_span *ts = wc->tspans[chan->span->offset];
++#endif
++
++ switch(cmd) {
++ case WCT4_SET_REG:
++ if (copy_from_user(&reg, (struct t4_reg __user *)data,
++ sizeof(reg)))
++ return -EFAULT;
++ t4_pci_out(wc, reg.reg, reg.val);
++ break;
++ case WCT4_GET_REG:
++ if (copy_from_user(&reg, (struct t4_reg __user *)data,
++ sizeof(reg)))
++ return -EFAULT;
++ reg.val = t4_pci_in(wc, reg.reg);
++ if (copy_to_user((struct t4_reg __user *)data,
++ &reg, sizeof(reg)))
++ return -EFAULT;
++ break;
++ 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((void __user *) 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);
++
++ channel = has_e1_span(wc) ? chan->chanpos : chan->chanpos + 4;
++ if (is_octal(wc))
++ channel = channel << 3;
++ else
++ channel = channel << 2;
++ channel |= chan->span->offset;
++ vpm450m_setdtmf(wc->vpm, 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);
++ ++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);
++}
++
++/**
++ * t4_set_framer_bits - Atomically set bits in a framer register.
++ */
++static void t4_set_framer_bits(struct t4 *wc, unsigned int spanno,
++ unsigned int const addr, u16 bits)
++{
++ unsigned long flags;
++ unsigned int reg;
++
++ spin_lock_irqsave(&wc->reglock, flags);
++ reg = __t4_framer_in(wc, spanno, addr);
++ __t4_framer_out(wc, spanno, addr, (reg | bits));
++ spin_unlock_irqrestore(&wc->reglock, flags);
++}
++
++static int t4_maint(struct dahdi_span *span, int cmd)
++{
++ struct t4_span *ts = container_of(span, struct t4_span, span);
++ struct t4 *wc = ts->owner;
++ unsigned int reg;
++ unsigned long flags;
++
++ if (E1 == ts->linemode) {
++ 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);
++ t4_set_framer_bits(wc, span->offset, LIM0_T, LIM0_LL);
++ break;
++ case DAHDI_MAINT_NETWORKLINELOOP:
++ dev_info(&wc->dev->dev,
++ "Turning on network line loopback\n");
++ t4_clear_maint(span);
++ t4_set_framer_bits(wc, span->offset, LIM1_T, LIM1_RL);
++ break;
++ case DAHDI_MAINT_NETWORKPAYLOADLOOP:
++ dev_info(&wc->dev->dev,
++ "Turning on network payload loopback\n");
++ t4_clear_maint(span);
++ t4_set_framer_bits(wc, span->offset, FMR2_T, FMR2_PLB);
++ break;
++ case DAHDI_MAINT_LOOPUP:
++ case DAHDI_MAINT_LOOPDOWN:
++ dev_info(&wc->dev->dev,
++ "Loopup & loopdown not supported in E1 mode\n");
++ return -ENOSYS;
++ 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");
++ t4_set_framer_bits(wc, span->offset, FMR0, FMR0_SIM);
++ break;
++ 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);
++ t4_set_framer_bits(wc, span->offset, LIM0_T, LIM0_LL);
++ break;
++ case DAHDI_MAINT_NETWORKLINELOOP:
++ dev_info(&wc->dev->dev,
++ "Turning on network line loopback\n");
++ t4_clear_maint(span);
++ t4_set_framer_bits(wc, span->offset, LIM1_T, LIM1_RL);
++ break;
++ case DAHDI_MAINT_NETWORKPAYLOADLOOP:
++ dev_info(&wc->dev->dev,
++ "Turning on network payload loopback\n");
++ t4_clear_maint(span);
++ t4_set_framer_bits(wc, span->offset, FMR2_T, FMR2_PLB);
++ break;
++ case DAHDI_MAINT_LOOPUP:
++ dev_info(&wc->dev->dev, "Transmitting loopup code\n");
++ t4_clear_maint(span);
++ t4_set_framer_bits(wc, span->offset, FMR5, FMR5_XLU);
++ ts->span.maintstat = DAHDI_MAINT_REMOTELOOP;
++ break;
++ case DAHDI_MAINT_LOOPDOWN:
++ dev_info(&wc->dev->dev, "Transmitting loopdown code\n");
++ t4_clear_maint(span);
++ t4_set_framer_bits(wc, span->offset, FMR5, FMR5_XLD);
++ break;
++ 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");
++ return -ENOSYS;
++ case DAHDI_RESET_COUNTERS:
++ t4_reset_counters(span);
++ break;
++ case DAHDI_MAINT_ALARM_SIM:
++ spin_lock_irqsave(&wc->reglock, flags);
++ 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
++ */
++ __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;
++ 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 = container_of(span, struct t4_span, span);
++ struct t4 *wc = ts->owner;
++ unsigned int reg;
++ unsigned long flags;
++
++ spin_lock_irqsave(&wc->reglock, flags);
++
++ /* 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)));
++
++ /* Clear loopup/loopdown signals on the line */
++ reg = __t4_framer_in(wc, span->offset, FMR5);
++ __t4_framer_out(wc, span->offset, FMR5, (reg & ~(FMR5_XLU | FMR5_XLD)));
++
++ spin_unlock_irqrestore(&wc->reglock, flags);
++ span->mainttimer = 0;
++
++ return 0;
++}
++
++static int t4_reset_counters(struct dahdi_span *span)
++{
++ struct t4_span *ts = container_of(span, struct t4_span, span);
++ memset(&ts->span.count, 0, sizeof(ts->span.count));
++ return 0;
++}
++
++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 (E1 == ts->linemode) {
++ 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 = container_of(span, struct t4_span, span);
++ struct t4 *wc = ts->owner;
++
++ tspan = span->offset + 1;
++ if (tspan < 0) {
++ dev_notice(&wc->dev->dev, "D115: 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 == 8) &&
++ (!(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->tspans[4]->span.flags & DAHDI_FLAG_RUNNING)) &&
++ (!(wc->tspans[5]->span.flags & DAHDI_FLAG_RUNNING)) &&
++ (!(wc->tspans[6]->span.flags & DAHDI_FLAG_RUNNING)) &&
++ (!(wc->tspans[7]->span.flags & DAHDI_FLAG_RUNNING)))
++ ||
++ ((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 */
++ 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 (E1 == wc->linemode) {
++ /* 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 = container_of(span, struct t4_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, "D115: Configuring span %d\n",
++ span->spanno);
++
++ if (lc->sync < 0)
++ lc->sync = 0;
++ if (lc->sync > wc->numspans) {
++ dev_warn(&wc->dev->dev, "WARNING: Cannot set priority on span %d to %d. Please set to a number between 1 and %d\n",
++ span->spanno, 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[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;
++
++ 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_spanconfig(struct file *file, struct dahdi_span *span,
++ struct dahdi_lineconfig *lc)
++{
++ int ret;
++ struct dahdi_device *const ddev = span->parent;
++ struct dahdi_span *s;
++
++ ret = _t4_spanconfig(file, span, lc);
++
++ /* Make sure all the spans have a basic configuration in case they are
++ * not all specified in the configuration files. */
++ lc->sync = 0;
++ list_for_each_entry(s, &ddev->spans, device_node) {
++ WARN_ON(!s->channels);
++ if (!s->channels)
++ continue;
++ if (!s->chans[0]->sigcap)
++ _t4_spanconfig(file, s, lc);
++ }
++ return ret;
++}
++
++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, "D115: Reconfigured "
++ "channel %d (%s) sigtype %d\n",
++ chan->channo, chan->name, sigtype);
++ else
++ dev_notice(&wc->dev->dev, "D115: 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 set_span_devicetype(struct t4 *wc)
++{
++#ifdef VPM_SUPPORT
++ const char *vpmstring = __t4_echocan_name(wc);
++
++ if (vpmstring) {
++ wc->ddev->devicetype = kasprintf(GFP_KERNEL, "%s (%s)",
++ wc->devtype->desc, vpmstring);
++ } else {
++ wc->ddev->devicetype = kasprintf(GFP_KERNEL, wc->devtype->desc);
++ }
++#else
++ wc->ddev->devicetype = kasprintf(GFP_KERNEL, wc->devtype->desc);
++#endif
++
++ if (!wc->ddev->devicetype)
++ return -ENOMEM;
++ return 0;
++}
++
++/* 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;
++ int basesize = T4_BASE_SIZE(wc) >> 2;
++
++ if (!has_e1_span(wc))
++ 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 * (basesize)));
++ ts->readchunk = (void *)(wc->readchunk + (x * 32 * 2) + (which * (basesize)));
++ 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 * (basesize)));
++ mychans->readchunk = (void *)(wc->readchunk + ((x * 32 + y + offset) * 2) + (which * (basesize)));
++ }
++ }
++ }
++}
++
++static int __t4_hardware_init_1(struct t4 *wc, unsigned int cardflags,
++ bool first_time);
++static int __t4_hardware_init_2(struct t4 *wc, bool first_time);
++
++static int t4_hardware_stop(struct t4 *wc);
++
++static void t4_framer_reset(struct t4 *wc)
++{
++ const bool first_time = false;
++ bool have_vpm = wc->vpm != NULL;
++ if (have_vpm) {
++ release_vpm450m(wc->vpm);
++ wc->vpm = NULL;
++ }
++ t4_hardware_stop(wc);
++ __t4_set_sclk_src(wc, WC_SELF, 0, 0);
++ __t4_hardware_init_1(wc, wc->devtype->flags, first_time);
++ __t4_hardware_init_2(wc, first_time);
++ if (have_vpm) {
++ t4_vpm_init(wc);
++ wc->dmactrl |= (wc->vpm) ? T4_VPM_PRESENT : 0;
++ t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++ }
++ setup_chunks(wc, 0);
++ wc->lastindex = 0;
++}
++
++/**
++ * t4_serial_setup - Setup serial parameters and system interface.
++ * @wc: The card to configure.
++ *
++ */
++static void t4_serial_setup(struct t4 *wc)
++{
++ unsigned long flags;
++ unsigned int unit;
++ bool reset_required = false;
++
++ if (debug) {
++ dev_info(&wc->dev->dev,
++ "D115: Setting up global serial parameters\n");
++ }
++
++ spin_lock_irqsave(&wc->reglock, flags);
++ reset_required = wc->reset_required > 0;
++ wc->reset_required = 0;
++ spin_unlock_irqrestore(&wc->reglock, flags);
++
++ if (reset_required)
++ t4_framer_reset(wc);
++
++ spin_lock_irqsave(&wc->reglock, flags);
++ /* GPC1: Multiplex mode enabled, FSC is output, active low, RCLK from
++ * channel 0 */
++ __t4_framer_out(wc, 0, 0x85, 0xe0);
++ if (is_octal(wc))
++ __t4_framer_out(wc, 0, FRMR_GPC2, 0x00);
++
++ /* IPC: Interrupt push/pull active low */
++ __t4_framer_out(wc, 0, 0x08, 0x01);
++
++ if (is_octal(wc)) {
++ /* Global clocks (16.384 Mhz CLK) */
++ __t4_framer_out(wc, 0, 0x92, 0x00); /* GCM1 */
++ __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, 0x01);
++ __t4_framer_out(wc, 0, 0x97, 0x0b);
++ __t4_framer_out(wc, 0, 0x98, 0xdb);
++ __t4_framer_out(wc, 0, 0x99, 0xdf);
++ } else {
++ /* 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);
++ }
++ spin_unlock_irqrestore(&wc->reglock, flags);
++
++ for (unit = 0; unit < ports_on_framer(wc); ++unit) {
++ spin_lock_irqsave(&wc->reglock, flags);
++
++ /* Configure interrupts */
++ /* GCR: Interrupt on Activation/Deactivation of each */
++ __t4_framer_out(wc, unit, FRMR_GCR, 0x00);
++
++ /* Configure system interface */
++ if (is_octal(wc)) {
++ /* SIC1: 16.384 Mhz clock/bus, double buffer receive /
++ * transmit, byte interleaved */
++ __t4_framer_out(wc, unit, FRMR_SIC1, 0xc2 | 0x08);
++ } else {
++ /* SIC1: 8.192 Mhz clock/bus, double buffer receive /
++ * transmit, byte interleaved */
++ __t4_framer_out(wc, unit, FRMR_SIC1, 0xc2);
++ }
++ /* SIC2: No FFS, no center receive eliastic buffer, phase */
++ __t4_framer_out(wc, unit, FRMR_SIC2, 0x20 | (unit << 1));
++ /* SIC3: Edges for capture */
++ if (is_octal(wc)) {
++ __t4_framer_out(wc, unit, FRMR_SIC3, 0x04 | (1 << 4));
++ } else {
++ __t4_framer_out(wc, unit, FRMR_SIC3, 0x04);
++ }
++ /* CMR2: We provide sync and clock for tx and rx. */
++ __t4_framer_out(wc, unit, FRMR_CMR2, 0x00);
++
++ if (is_octal(wc)) {
++ /* Set RCLK to 16 MHz */
++ __t4_framer_out(wc, unit, FRMR_CMR4, 0x5);
++
++ if (!has_e1_span(wc)) { /* T1/J1 mode */
++ __t4_framer_out(wc, unit, FRMR_XC0, 0x07);
++ __t4_framer_out(wc, unit, FRMR_XC1, 0x04);
++ if (wc->tspans[unit]->linemode == J1)
++ __t4_framer_out(wc, unit, FRMR_RC0, 0x87);
++ else
++ __t4_framer_out(wc, unit, FRMR_RC0, 0x07);
++ __t4_framer_out(wc, unit, FRMR_RC1, 0x04);
++ } else { /* E1 mode */
++ __t4_framer_out(wc, unit, FRMR_XC0, 0x00);
++ __t4_framer_out(wc, unit, FRMR_XC1, 0x04);
++ __t4_framer_out(wc, unit, FRMR_RC0, 0x00);
++ __t4_framer_out(wc, unit, FRMR_RC1, 0x04);
++ }
++
++ } else {
++ if (!has_e1_span(wc)) { /* T1/J1 mode */
++ __t4_framer_out(wc, unit, FRMR_XC0, 0x03);
++ __t4_framer_out(wc, unit, FRMR_XC1, 0x84);
++ if (J1 == wc->tspans[unit]->linemode)
++ __t4_framer_out(wc, unit, FRMR_RC0, 0x83);
++ else
++ __t4_framer_out(wc, unit, FRMR_RC0, 0x03);
++ __t4_framer_out(wc, unit, FRMR_RC1, 0x84);
++ } else { /* E1 mode */
++ __t4_framer_out(wc, unit, FRMR_XC0, 0x00);
++ __t4_framer_out(wc, unit, FRMR_XC1, 0x04);
++ __t4_framer_out(wc, unit, FRMR_RC0, 0x04);
++ __t4_framer_out(wc, unit, FRMR_RC1, 0x04);
++ }
++ }
++
++ /* Configure ports */
++
++ /* PC1: SPYR/SPYX input on RPA/XPA */
++ __t4_framer_out(wc, unit, 0x80, 0x00);
++
++ /* PC2: RMFB/XSIG output/input on RPB/XPB */
++ /* PC3: Some unused stuff */
++ /* PC4: Some more unused stuff */
++ if (is_octal(wc)) {
++ __t4_framer_out(wc, unit, 0x81, 0xBB);
++ __t4_framer_out(wc, unit, 0x82, 0xf5);
++ __t4_framer_out(wc, unit, 0x83, 0x35);
++ } else if (wc->falc31) {
++ __t4_framer_out(wc, unit, 0x81, 0xBB);
++ __t4_framer_out(wc, unit, 0x82, 0xBB);
++ __t4_framer_out(wc, unit, 0x83, 0xBB);
++ } else {
++ __t4_framer_out(wc, unit, 0x81, 0x22);
++ __t4_framer_out(wc, unit, 0x82, 0x65);
++ __t4_framer_out(wc, unit, 0x83, 0x35);
++ }
++
++ /* PC5: XMFS active low, SCLKR is input, RCLK is output */
++ __t4_framer_out(wc, unit, 0x84, 0x01);
++
++ if (debug & DEBUG_MAIN) {
++ dev_notice(&wc->dev->dev,
++ "Successfully initialized serial bus "
++ "for unit %d\n", unit);
++ }
++
++ spin_unlock_irqrestore(&wc->reglock, flags);
++ }
++}
++
++/**
++ * t4_span_assigned - Called when the span is assigned by DAHDI.
++ * @span: Span that has been assigned.
++ *
++ * When this function is called, the span has a valid spanno and all the
++ * channels on the span have valid channel numbers assigned.
++ *
++ * This function is necessary because a device may be registered, and
++ * then user space may then later decide to assign span numbers and the
++ * channel numbers.
++ *
++ */
++static void t4_span_assigned(struct dahdi_span *span)
++{
++ struct t4_span *tspan = container_of(span, struct t4_span, span);
++ struct t4 *wc = tspan->owner;
++ struct dahdi_span *pos;
++ unsigned int unassigned_spans = 0;
++ unsigned long flags;
++
++ /* We use this to make sure all the spans are assigned before
++ * running the serial setup. */
++ list_for_each_entry(pos, &wc->ddev->spans, device_node) {
++ if (!test_bit(DAHDI_FLAGBIT_REGISTERED, &pos->flags))
++ ++unassigned_spans;
++ }
++
++ if (0 == unassigned_spans) {
++ t4_serial_setup(wc);
++
++ 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);
++ }
++}
++
++static void free_wc(struct t4 *wc)
++{
++ unsigned int x, y;
++
++ flush_scheduled_work();
++
++ for (x = 0; x < ARRAY_SIZE(wc->tspans); x++) {
++ if (!wc->tspans[x])
++ continue;
++ for (y = 0; y < ARRAY_SIZE(wc->tspans[x]->chans); y++) {
++ kfree(wc->tspans[x]->chans[y]);
++ kfree(wc->tspans[x]->ec[y]);
++ }
++ kfree(wc->tspans[x]);
++ }
++
++ kfree(wc->ddev->devicetype);
++ kfree(wc->ddev->location);
++ kfree(wc->ddev->hardware_id);
++ dahdi_free_device(wc->ddev);
++ kfree(wc);
++}
++
++/**
++ * t4_alloc_channels - Allocate the channels on a span.
++ * @wc: The board we're allocating for.
++ * @ts: The span we're allocating for.
++ * @linemode: Which mode (T1/E1/J1) to use for this span.
++ *
++ * This function must only be called before the span is assigned it's
++ * possible for user processes to have an open reference to the
++ * channels.
++ *
++ */
++static int t4_alloc_channels(struct t4 *wc, struct t4_span *ts,
++ enum linemode linemode)
++{
++ int i;
++
++ if (test_bit(DAHDI_FLAGBIT_REGISTERED, &ts->span.flags)) {
++ dev_dbg(&wc->dev->dev,
++ "Cannot allocate channels on a span that is already "
++ "assigned.\n");
++ return -EINVAL;
++ }
++
++ /* Cleanup any previously allocated channels. */
++ for (i = 0; i < ARRAY_SIZE(ts->chans); ++i) {
++ kfree(ts->chans[i]);
++ kfree(ts->ec[i]);
++ ts->chans[i] = NULL;
++ ts->ec[i] = NULL;
++ }
++
++ ts->linemode = linemode;
++ for (i = 0; i < ((E1 == ts->linemode) ? 31 : 24); i++) {
++ struct dahdi_chan *chan;
++ struct dahdi_echocan_state *ec;
++
++ chan = kzalloc(sizeof(*chan), GFP_KERNEL);
++ if (!chan) {
++ free_wc(wc);
++ return -ENOMEM;
++ }
++ ts->chans[i] = chan;
++
++ ec = kzalloc(sizeof(*ec), GFP_KERNEL);
++ if (!ec) {
++ free_wc(wc);
++ return -ENOMEM;
++ }
++ ts->ec[i] = ec;
++ }
++
++ return 0;
++}
++
++static void t4_init_one_span(struct t4 *wc, struct t4_span *ts)
++{
++ unsigned long flags;
++ unsigned int reg;
++ int i;
++
++ snprintf(ts->span.name, sizeof(ts->span.name) - 1,
++ "D115/%d/%d", wc->num, ts->span.offset + 1);
++ snprintf(ts->span.desc, sizeof(ts->span.desc) - 1,
++ "D115 (E1/T1) Card %d Span %d", wc->num,
++ ts->span.offset + 1);
++
++ switch (ts->linemode) {
++ case T1:
++ ts->span.spantype = SPANTYPE_DIGITAL_T1;
++ break;
++ case E1:
++ ts->span.spantype = SPANTYPE_DIGITAL_E1;
++ break;
++ case J1:
++ ts->span.spantype = SPANTYPE_DIGITAL_J1;
++ break;
++ }
++
++ /* HDLC Specific init */
++ ts->sigchan = NULL;
++ ts->sigmode = sigmode;
++ ts->sigactive = 0;
++
++ if (E1 != ts->linemode) {
++ 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;
++
++ for (i = 0; i < ts->span.channels; i++) {
++ struct dahdi_chan *const chan = ts->chans[i];
++ chan->pvt = wc;
++ snprintf(chan->name, sizeof(chan->name) - 1,
++ "%s/%d", ts->span.name, i + 1);
++ t4_chan_set_sigcap(&ts->span, i);
++ chan->chanpos = i + 1;
++ }
++
++ /* Enable 1sec timer interrupt */
++ spin_lock_irqsave(&wc->reglock, flags);
++ reg = __t4_framer_in(wc, ts->span.offset, FMR1_T);
++ __t4_framer_out(wc, ts->span.offset, FMR1_T, (reg | FMR1_ECM));
++
++ /* Enable Errored Second interrupt */
++ __t4_framer_out(wc, ts->span.offset, ESM, 0);
++ spin_unlock_irqrestore(&wc->reglock, flags);
++
++ t4_reset_counters(&ts->span);
++}
++
++/**
++ * t4_set_linemode - Allows user space to change the linemode before spans are assigned.
++ * @span: span on which to change the linemode.
++ * @linemode: A value from enumerated spantypes
++ *
++ * This callback is used to override the E1/T1 mode jumper settings and set
++ * the linemode on for each span. Called when the "spantype" attribute
++ * is written in sysfs under the dahdi_device.
++ *
++ */
++static int t4_set_linemode(struct dahdi_span *span, enum spantypes linemode)
++{
++ struct t4_span *ts = container_of(span, struct t4_span, span);
++ struct t4 *wc = ts->owner;
++ int res = 0;
++ enum linemode mode;
++ const char *old_name;
++ static DEFINE_MUTEX(linemode_lock);
++ unsigned long flags;
++
++ dev_dbg(&wc->dev->dev, "Setting '%s' to '%s'\n", span->name,
++ dahdi_spantype2str(linemode));
++
++ if (span->spantype == linemode)
++ return 0;
++
++ spin_lock_irqsave(&wc->reglock, flags);
++ wc->reset_required = 1;
++ spin_unlock_irqrestore(&wc->reglock, flags);
++
++ /* Do not allow the t1e1 member to be changed by multiple threads. */
++ mutex_lock(&linemode_lock);
++ old_name = dahdi_spantype2str(span->spantype);
++ switch (linemode) {
++ case SPANTYPE_DIGITAL_T1:
++ dev_info(&wc->dev->dev,
++ "Changing from %s to T1 line mode.\n", old_name);
++ mode = T1;
++ wc->t1e1 &= ~(1 << span->offset);
++ break;
++ case SPANTYPE_DIGITAL_E1:
++ dev_info(&wc->dev->dev,
++ "Changing from %s to E1 line mode.\n", old_name);
++ mode = E1;
++ wc->t1e1 |= (1 << span->offset);
++ break;
++ case SPANTYPE_DIGITAL_J1:
++ dev_info(&wc->dev->dev,
++ "Changing from %s to J1 line mode.\n", old_name);
++ mode = J1;
++ wc->t1e1 &= ~(1 << span->offset);
++ break;
++ default:
++ dev_err(&wc->dev->dev,
++ "Got invalid linemode %d from dahdi\n", linemode);
++ res = -EINVAL;
++ }
++
++ if (!res) {
++ t4_alloc_channels(wc, ts, mode);
++ t4_init_one_span(wc, ts);
++ dahdi_init_span(span);
++ }
++
++ mutex_unlock(&linemode_lock);
++ return res;
++}
++
++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,
++ .ioctl = t4_ioctl,
++ .hdlc_hard_xmit = t4_hdlc_hard_xmit,
++ .assigned = t4_span_assigned,
++ .set_spantype = t4_set_linemode,
++};
++
++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,
++ .ioctl = t4_ioctl,
++ .hdlc_hard_xmit = t4_hdlc_hard_xmit,
++ .dacs = t4_dacs,
++ .assigned = t4_span_assigned,
++ .set_spantype = t4_set_linemode,
++#ifdef VPM_SUPPORT
++ .echocan_create = t4_echocan_create,
++ .echocan_name = t4_echocan_name,
++#endif
++};
++
++/**
++ * init_spans - Do first initialization on all the spans
++ * @wc: Card to initialize the spans on.
++ *
++ * This function is called *before* the dahdi_device is first registered
++ * with the system. What happens in t4_init_one_span can happen between
++ * when the device is registered and when the spans are assigned via
++ * sysfs (or automatically).
++ *
++ */
++static void init_spans(struct t4 *wc)
++{
++ int x, y;
++ int gen2;
++ struct t4_span *ts;
++ unsigned int reg;
++ unsigned long flags;
++
++ gen2 = (wc->tspans[0]->spanflags & FLAG_2NDGEN);
++ for (x = 0; x < wc->numspans; x++) {
++ ts = wc->tspans[x];
++
++ sprintf(ts->span.name, "D115/%d/%d", wc->num, x + 1);
++ snprintf(ts->span.desc, sizeof(ts->span.desc) - 1,
++ "D115 (E1/T1) Card %d Span %d", wc->num, x+1);
++ switch (ts->linemode) {
++ case T1:
++ ts->span.spantype = SPANTYPE_DIGITAL_T1;
++ break;
++ case E1:
++ ts->span.spantype = SPANTYPE_DIGITAL_E1;
++ break;
++ case J1:
++ ts->span.spantype = SPANTYPE_DIGITAL_J1;
++ break;
++ }
++
++ /* HDLC Specific init */
++ ts->sigchan = NULL;
++ ts->sigmode = sigmode;
++ ts->sigactive = 0;
++
++ if (E1 != ts->linemode) {
++ 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);
++
++ if (gen2) {
++ ts->span.ops = &t4_gen2_span_ops;
++ } else {
++ ts->span.ops = &t4_gen1_span_ops;
++ }
++
++ for (y=0;y<wc->tspans[x]->span.channels;y++) {
++ struct dahdi_chan *mychans = ts->chans[y];
++ sprintf(mychans->name, "D115/%d/%d/%d", wc->num, x + 1, y + 1);
++ t4_chan_set_sigcap(&ts->span, x);
++ mychans->pvt = wc;
++ mychans->chanpos = y + 1;
++ }
++
++ /* Start checking for alarms in 250 ms */
++ ts->alarmcheck_time = jiffies + msecs_to_jiffies(250);
++
++ /* Enable 1sec timer interrupt */
++ spin_lock_irqsave(&wc->reglock, flags);
++ 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);
++ spin_unlock_irqrestore(&wc->reglock, flags);
++
++ t4_reset_counters(&ts->span);
++
++ }
++
++ set_span_devicetype(wc);
++ setup_chunks(wc, 0);
++ wc->lastindex = 0;
++}
++
++static int syncsrc = 0;
++static int syncnum = 0 /* -1 */;
++static int syncspan = 0;
++static DEFINE_SPINLOCK(synclock);
++
++static void __t4_set_rclk_src(struct t4 *wc, int span)
++{
++ if (is_octal(wc)) {
++ int cmr5 = 0x00 | (span << 5);
++ int cmr1 = 0x38; /* Clock Mode: RCLK sourced by DCO-R1
++ by default, Disable Clock-Switching */
++
++ __t4_framer_out(wc, 0, 0x44, cmr1);
++ __t4_framer_out(wc, 0, FRMR_CMR5, cmr5);
++ } else {
++ 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);
++}
++
++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);
++}
++
++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; /* wct4xxp card number */
++ int newsyncspan = 0; /* span on given wct4xxp card */
++ spin_lock_irqsave(&synclock, flags);
++ 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 < cards[x]->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);
++ spin_unlock_irqrestore(&synclock, flags);
++ return 0;
++}
++
++static void __t4_set_timing_source_auto(struct t4 *wc)
++{
++ int x, i;
++ 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);
++ }
++
++ /* Propagate sync selection to dahdi_span struct
++ * this is read by dahdi_tool to display the span's
++ * master/slave sync information */
++ for (i = 0; i < wc->numspans; i++) {
++ wc->tspans[i]->span.syncsrc = wc->syncsrc + 1;
++ }
++ }
++}
++
++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;
++
++ if (is_octal(wc))
++ fmr1 = 0x9c | 0x02; /* FMR1: Mode 1, T1 mode, CRC on for ESF, 8.192 Mhz system data rate, no XAIS */
++ else
++ 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 = (is_octal(wc) || 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, FMR5, FMR5_EIBR); /* 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;
++ if (is_octal(wc)) {
++ /* 16 MHz */
++ fmr1 = 0x44 | 0x02; /* FMR1: E1 mode, Automatic force resync, PCM30 mode, 8.192 Mhz backplane, no XAIS */
++ } else {
++ /* 8 MHz */
++ 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 = (is_octal(wc) || 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 */
++
++ __t4_framer_out(wc, unit, 0x2f, 0x00);
++ __t4_framer_out(wc, unit, 0x30, 0x00);
++ __t4_framer_out(wc, unit, 0x31, 0x00);
++
++ dev_info(&wc->dev->dev, "opvxd115: Span %d configured for %s/%s%s\n",
++ unit + 1, framing, line, crc4);
++}
++
++/**
++ * t4_check_for_interrupts - Return 0 if the card is generating interrupts.
++ * @wc: The card to check.
++ *
++ * If the card is not generating interrupts, this function will also place all
++ * the spans on the card into red alarm.
++ *
++ */
++static int t4_check_for_interrupts(struct t4 *wc)
++{
++ unsigned int starting_intcount = wc->intcount;
++ unsigned long stop_time = jiffies + HZ*2;
++ unsigned long flags;
++ int x;
++
++ msleep(20);
++ spin_lock_irqsave(&wc->reglock, flags);
++ while (starting_intcount == wc->intcount) {
++ spin_unlock_irqrestore(&wc->reglock, flags);
++ if (time_after(jiffies, stop_time)) {
++ for (x = 0; x < wc->numspans; x++)
++ wc->tspans[x]->span.alarms = DAHDI_ALARM_RED;
++ dev_err(&wc->dev->dev, "Interrupts not detected.\n");
++ return -EIO;
++ }
++ msleep(100);
++ spin_lock_irqsave(&wc->reglock, flags);
++ }
++ spin_unlock_irqrestore(&wc->reglock, flags);
++
++ return 0;
++}
++
++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 = container_of(span, struct t4_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 (E1 == ts->linemode)
++ __t4_configure_e1(wc, span->offset, span->lineconfig);
++ else
++ __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->devtype->flags & FLAG_5THGEN)
++ __t4_pci_out(wc, 5, (ms_per_irq << 16) | wc->numbufs);
++ else
++ __t4_pci_out(wc, 5, (1 << 16) | 1);
++ /* enable interrupts */
++ /* Start DMA, enabling DMA interrupts on read only */
++ wc->dmactrl |= (ts->spanflags & FLAG_2NDGEN) ? 0xc0000000 : 0xc0000003;
++#ifdef VPM_SUPPORT
++ wc->dmactrl |= (wc->vpm) ? T4_VPM_PRESENT : 0;
++#endif
++ /* Seed interrupt register */
++ __t4_pci_out(wc, WC_INTR, 0x0c);
++ if (noburst || !(ts->spanflags & FLAG_BURST))
++ wc->dmactrl |= (1 << 26);
++ else
++ 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);
++
++ local_irq_save(flags);
++ t4_check_alarms(wc, span->offset);
++ t4_check_sigbits(wc, span->offset);
++ local_irq_restore(flags);
++
++ if (wc->tspans[0]->sync == span->spanno)
++ dev_info(&wc->dev->dev, "SPAN %d: Primary Sync Source\n",
++ span->spanno);
++ if (wc->numspans >= 2) {
++ if (wc->tspans[1]->sync == span->spanno)
++ dev_info(&wc->dev->dev, "SPAN %d: Secondary Sync Source\n",
++ span->spanno);
++ }
++ if (wc->numspans >= 4) {
++ if (wc->tspans[2]->sync == span->spanno)
++ dev_info(&wc->dev->dev, "SPAN %d: Tertiary Sync Source"
++ "\n", span->spanno);
++ if (wc->tspans[3]->sync == span->spanno)
++ dev_info(&wc->dev->dev, "SPAN %d: Quaternary Sync "
++ "Source\n", span->spanno);
++ }
++ if (wc->numspans == 8) {
++ if (wc->tspans[4]->sync == span->spanno)
++ dev_info(&wc->dev->dev, "SPAN %d: Quinary Sync "
++ "Source\n", span->spanno);
++ if (wc->tspans[5]->sync == span->spanno)
++ dev_info(&wc->dev->dev, "SPAN %d: Senary Sync "
++ "Source\n", span->spanno);
++ if (wc->tspans[6]->sync == span->spanno)
++ dev_info(&wc->dev->dev, "SPAN %d: Septenary Sync "
++ "Source\n", span->spanno);
++ if (wc->tspans[7]->sync == span->spanno)
++ dev_info(&wc->dev->dev, "SPAN %d: Octonary Sync "
++ "Source\n", span->spanno);
++ }
++
++ if (!alreadyrunning) {
++ if (t4_check_for_interrupts(wc))
++ return -EIO;
++ }
++
++ if (debug)
++ dev_info(&wc->dev->dev, "Completed startup!\n");
++ clear_bit(T4_IGNORE_LATENCY, &wc->checkflag);
++ return 0;
++}
++
++static int t4_startup(struct file *file, struct dahdi_span *span)
++{
++ int ret;
++ struct dahdi_device *const ddev = span->parent;
++ struct dahdi_span *s;
++
++ ret = _t4_startup(file, span);
++ list_for_each_entry(s, &ddev->spans, device_node) {
++ if (!test_bit(DAHDI_FLAGBIT_RUNNING, &s->flags)) {
++ _t4_startup(file, s);
++ }
++ }
++ return ret;
++}
++
++#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;
++ if (wc->numspans == 1)
++ wc->tspans[0]->e1check = 0;
++ if (wc->numspans >= 2)
++ wc->tspans[0]->e1check = wc->tspans[1]->e1check = 0;
++ if (wc->numspans == 4)
++ wc->tspans[2]->e1check = wc->tspans[3]->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)
++{
++ unsigned int *readchunk;
++ int dbl = 0;
++ int x,y,z;
++ unsigned int tmp;
++ int offset=0;
++ if (!has_e1_span(wc))
++ 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 (unlikely(dbl && (debug & DEBUG_MAIN)))
++ dev_notice(&wc->dev->dev, "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];
++ if (wc->numspans == 4) {
++ wc->tspans[3]->span.chans[z]->readchunk[x] = tmp & 0xff;
++ wc->tspans[2]->span.chans[z]->readchunk[x] = (tmp & 0xff00) >> 8;
++ }
++ wc->tspans[1]->span.chans[z]->readchunk[x] = (tmp & 0xff0000) >> 16;
++ wc->tspans[0]->span.chans[z]->readchunk[x] = tmp >> 24;
++ }
++ if (has_e1_span(wc)) {
++ if (wc->e1recover > 0)
++ wc->e1recover--;
++ tmp = readchunk[0];
++ if (wc->numspans == 4) {
++ e1_check(wc, 3, (tmp & 0x7f));
++ e1_check(wc, 2, (tmp & 0x7f00) >> 8);
++ }
++ e1_check(wc, 1, (tmp & 0x7f0000) >> 16);
++ e1_check(wc, 0, (tmp & 0x7f000000) >> 24);
++ for (z=24;z<31;z++) {
++ /* Only E1 channels now */
++ tmp = readchunk[z+1];
++ if (wc->numspans == 4) {
++ if (wc->tspans[3]->span.channels > 24)
++ wc->tspans[3]->span.chans[z]->readchunk[x] = tmp & 0xff;
++ if (wc->tspans[2]->span.channels > 24)
++ wc->tspans[2]->span.chans[z]->readchunk[x] = (tmp & 0xff00) >> 8;
++ }
++ if (wc->tspans[1]->span.channels > 24)
++ wc->tspans[1]->span.chans[z]->readchunk[x] = (tmp & 0xff0000) >> 16;
++ 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 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
++ _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)
++{
++ u32 *writechunk;
++ int x, y, z;
++ unsigned int tmp;
++ int offset = 0;
++ if (!has_e1_span(wc))
++ 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[3]->span.chans[z]->writechunk[x]) |
++ (wc->tspans[2]->span.chans[z]->writechunk[x] << 8) |
++ (wc->tspans[1]->span.chans[z]->writechunk[x] << 16) |
++ (wc->tspans[0]->span.chans[z]->writechunk[x] << 24);
++ writechunk[z+offset] = tmp;
++ }
++ if (has_e1_span(wc)) {
++ for (z=24;z<31;z++) {
++ /* Only E1 channels now */
++ tmp = 0;
++ if (wc->numspans == 4) {
++ if (wc->tspans[3]->span.channels > 24)
++ tmp |= wc->tspans[3]->span.chans[z]->writechunk[x];
++ if (wc->tspans[2]->span.channels > 24)
++ tmp |= (wc->tspans[2]->span.chans[z]->writechunk[x] << 8);
++ }
++ if (wc->tspans[1]->span.channels > 24)
++ tmp |= (wc->tspans[1]->span.chans[z]->writechunk[x] << 16);
++ 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_dahdi_rbsbits(struct dahdi_chan *const chan, int rxs)
++{
++ if ((debug & DEBUG_RBS) && printk_ratelimit()) {
++ const struct t4_span *tspan = container_of(chan->span,
++ struct t4_span,
++ span);
++ const struct t4 *const wc = tspan->owner;
++ dev_notice(&wc->dev->dev, "Detected sigbits change on " \
++ "channel %s to %04x\n", chan->name, rxs);
++ }
++ dahdi_rbsbits(chan, rxs);
++}
++
++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 (E1 == ts->linemode) {
++ 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)
++ t4_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)
++ t4_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)
++ t4_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)
++ t4_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)
++ t4_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)
++ t4_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) {
++ t4_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) {
++ t4_dahdi_rbsbits(ts->span.chans[i], rxs);
++ }
++ }
++ }
++ }
++}
++
++/* Must be called from within hardirq context. */
++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];
++
++ if (time_before(jiffies, ts->alarmcheck_time))
++ return;
++
++ if (!(ts->span.flags & DAHDI_FLAG_RUNNING))
++ return;
++
++ spin_lock(&wc->reglock);
++
++ 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 (E1 == ts->linemode) {
++ 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,
++ "Lost crc4-multiframe alignment\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,
++ "Obtained crc4-multiframe alignment\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)) {
++ dev_notice(&wc->dev->dev,
++ "span %d: Loopup detected,"\
++ " enabling remote loop\n",
++ span+1);
++ __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)) {
++ dev_notice(&wc->dev->dev,
++ "span %d: Loopdown detected,"\
++ " disabling remote loop\n",
++ span+1);
++ __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) ||
++ dahdi_have_netdev(ts->span.chans[x]))
++ j++;
++ if (!j)
++ alarms |= DAHDI_ALARM_NOTOPEN;
++ }
++
++ /* Loss of Frame Alignment */
++ if (c & 0x20) {
++ if (!ts->alarm_time) {
++ if (unlikely(debug)) {
++ /* starting to debounce LOF/LFA */
++ dev_info(&wc->dev->dev, "D115: LOF/LFA "
++ "detected on span %d but debouncing "
++ "for %d ms\n", span + 1,
++ alarmdebounce);
++ }
++ ts->alarm_time = jiffies +
++ msecs_to_jiffies(alarmdebounce);
++ } else if (time_after(jiffies, ts->alarm_time)) {
++ /* Disable Slip Interrupts */
++ e = __t4_framer_in(wc, span, 0x17);
++ __t4_framer_out(wc, span, 0x17, (e|0x03));
++
++ alarms |= DAHDI_ALARM_RED;
++ }
++ } else {
++ ts->alarm_time = 0;
++ }
++
++ /* Loss of Signal */
++ if (c & 0x80) {
++ if (!ts->losalarm_time) {
++ if (unlikely(debug)) {
++ /* starting to debounce LOS */
++ dev_info(&wc->dev->dev, "D115: LOS "
++ "detected on span %d but debouncing "
++ "for %d ms\n",
++ span + 1, losalarmdebounce);
++ }
++ ts->losalarm_time = jiffies +
++ msecs_to_jiffies(losalarmdebounce);
++ } else if (time_after(jiffies, ts->losalarm_time)) {
++ /* Disable Slip Interrupts */
++ e = __t4_framer_in(wc, span, 0x17);
++ __t4_framer_out(wc, span, 0x17, (e|0x03));
++
++ alarms |= DAHDI_ALARM_RED;
++ }
++ } else {
++ ts->losalarm_time = 0;
++ }
++
++ /* Alarm Indication Signal */
++ if (c & 0x40) {
++ if (!ts->aisalarm_time) {
++ if (unlikely(debug)) {
++ /* starting to debounce AIS */
++ dev_info(&wc->dev->dev, "D115: AIS "
++ "detected on span %d but debouncing "
++ "for %d ms\n",
++ span + 1, aisalarmdebounce);
++ }
++ ts->aisalarm_time = jiffies +
++ msecs_to_jiffies(aisalarmdebounce);
++ } else if (time_after(jiffies, ts->aisalarm_time)) {
++ alarms |= DAHDI_ALARM_BLUE;
++ }
++ } else {
++ ts->aisalarm_time = 0;
++ }
++
++ /* 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");
++ }
++ }
++
++ 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->yelalarm_time) {
++ if (unlikely(debug)) {
++ /* starting to debounce AIS */
++ dev_info(&wc->dev->dev, "D115: yellow "
++ "(RAI) detected on span %d but "
++ "debouncing for %d ms\n",
++ span + 1,
++ yelalarmdebounce);
++ }
++ ts->yelalarm_time = jiffies +
++ msecs_to_jiffies(yelalarmdebounce);
++ } else if (time_after(jiffies, ts->yelalarm_time)) {
++ alarms |= DAHDI_ALARM_YELLOW;
++ }
++ } else {
++ ts->yelalarm_time = 0;
++ }
++
++ if (alarms)
++ ts->alarmcheck_time = jiffies + msecs_to_jiffies(100);
++ else
++ ts->alarmcheck_time = jiffies + msecs_to_jiffies(50);
++
++ if (ts->span.mainttimer || ts->span.maintstat)
++ alarms |= DAHDI_ALARM_LOOPBACK;
++ ts->span.alarms = alarms;
++
++ spin_unlock(&wc->reglock);
++ 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];
++
++ spin_lock(&wc->reglock);
++ if (ts->alarmtimer && (0 == (--ts->alarmtimer)))
++ ts->span.alarms &= ~(DAHDI_ALARM_RECOVER);
++ spin_unlock(&wc->reglock);
++
++ t4_check_alarms(wc, 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->losalarm_time) {
++#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 >= ARRAY_SIZE(altab))
++ wc->alarmpos = 0;
++ }
++#else
++ if (wc->blinktimer == 480)
++ wc->blinktimer = 0;
++#endif
++}
++
++static inline void t4_framer_interrupt(struct t4 *wc, int span)
++{
++ /* Check interrupts for a given span */
++ unsigned char gis, isr0, isr1, isr2, isr3, isr4;
++ int readsize = -1;
++ struct t4_span *ts = wc->tspans[span];
++ struct dahdi_chan *sigchan;
++ unsigned long flags;
++ bool recheck_sigbits = false;
++
++
++ /* 1st gen cards isn't used interrupts */
++ spin_lock_irqsave(&wc->reglock, flags);
++ 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);
++ }
++
++ /* 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);
++ if (DAHDI_RXSIG_INITIAL == ts->span.chans[0]->rxhooksig)
++ recheck_sigbits = true;
++ }
++ spin_unlock_irqrestore(&wc->reglock, flags);
++
++ /* Collect errored second counter once per second */
++ if (isr3 & ISR3_ES) {
++ ts->span.count.errsec += 1;
++ }
++
++ if (isr0 & 0x08 || recheck_sigbits)
++ t4_check_sigbits(wc, span);
++
++ if (E1 == ts->linemode) {
++ /* 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];
++ unsigned char rsis_reg = t4_framer_in(wc, span, FRMR_RSIS);
++
++ ++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);
++ }
++ } 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
++static irqreturn_t _t4_interrupt(int irq, void *dev_id)
++{
++ struct t4 *wc = dev_id;
++ unsigned long flags;
++ int x;
++
++ unsigned int status;
++ unsigned int status2;
++
++ /* 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 (status & 0x3) {
++ t4_receiveprep(wc, status);
++ t4_transmitprep(wc, status);
++ }
++
++ 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);
++}
++
++DAHDI_IRQ_HANDLER(t4_interrupt)
++{
++ irqreturn_t ret;
++ unsigned long flags;
++ local_irq_save(flags);
++ ret = _t4_interrupt(irq, dev_id);
++ local_irq_restore(flags);
++ return ret;
++}
++#endif
++
++static int t4_allocate_buffers(struct t4 *wc, int numbufs,
++ void **oldalloc, dma_addr_t *oldwritedma)
++{
++ void *alloc;
++ dma_addr_t writedma;
++
++ /* 32 channels, Double-buffer, Read/Write, 4 spans */
++ alloc = pci_alloc_consistent(wc->dev, numbufs * T4_BASE_SIZE(wc) * 2,
++ &writedma);
++
++ if (!alloc) {
++ dev_notice(&wc->dev->dev, "D115: Unable to allocate "
++ "DMA-able memory\n");
++ 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(wc) * numbufs) / 4;
++
++ /* Same thing but in bytes... */
++ wc->readdma = wc->writedma + (T4_BASE_SIZE(wc) * numbufs);
++
++ wc->numbufs = numbufs;
++
++ /* Initialize Write/Buffers to all blank data */
++ memset(wc->writechunk, 0x00, T4_BASE_SIZE(wc) * numbufs);
++ memset(wc->readchunk, 0xff, T4_BASE_SIZE(wc) * numbufs);
++
++ if (debug) {
++ dev_notice(&wc->dev->dev, "DMA memory base of size %d at " \
++ "%p. Read: %p and Write %p\n",
++ numbufs * T4_BASE_SIZE(wc) * 2, wc->writechunk,
++ wc->readchunk, wc->writechunk);
++ }
++
++ return 0;
++}
++
++static void t4_increase_latency(struct t4 *wc, int newlatency)
++{
++ unsigned long flags;
++ void *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(wc) * oldbufs * 2,
++ oldalloc, oldaddr);
++
++ dev_info(&wc->dev->dev, "Increased latency to %d\n", newlatency);
++
++}
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
++static void t4_work_func(void *data)
++{
++ struct t4 *wc = data;
++#else
++static void t4_work_func(struct work_struct *work)
++{
++ struct t4 *wc = container_of(work, struct t4, bh_work);
++#endif
++
++ 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)) {
++ /* 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_vpm(wc);
++ }
++ }
++#endif
++}
++
++static irqreturn_t _t4_interrupt_gen2(int irq, void *dev_id)
++{
++ 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->devtype->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) {
++ dev_info(&wc->dev->dev, "Need to increase "
++ "latency. Estimated latency should "
++ "be %d\n", needed_latency);
++ 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;
++ }
++
++#ifdef DEBUG
++ if (unlikely((wc->intcount < 20)))
++ dev_dbg(&wc->dev->dev, "2G: Got interrupt, status = %08x, "
++ "CIS = %04x\n", status, t4_framer_in(wc, 0, FRMR_CIS));
++#endif
++
++ 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);
++ if (wc->tspans[1]->span.flags & DAHDI_FLAG_RUNNING)
++ t4_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)
++ t4_queue_work(wc->workq, &wc->tspans[2]->swork, 2 % cpus);
++ else
++ atomic_dec(&wc->worklist);
++ if (wc->tspans[3]->span.flags & DAHDI_FLAG_RUNNING)
++ t4_queue_work(wc->workq, &wc->tspans[3]->swork, 3 % cpus);
++ else
++ atomic_dec(&wc->worklist);
++ }
++#else
++ unsigned int reg5 = __t4_pci_in(wc, 5);
++
++#ifdef DEBUG
++ if (wc->intcount < 20)
++ dev_info(&wc->dev->dev, "Reg 5 is %08x\n", reg5);
++#endif
++
++ if (wc->devtype->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 (is_octal(wc)) {
++ if (cis & FRMR_CIS_GIS5)
++ t4_framer_interrupt(wc, 4);
++ if (cis & FRMR_CIS_GIS6)
++ t4_framer_interrupt(wc, 5);
++ if (cis & FRMR_CIS_GIS7)
++ t4_framer_interrupt(wc, 6);
++ if (cis & FRMR_CIS_GIS8)
++ t4_framer_interrupt(wc, 7);
++ }
++ }
++
++#ifdef VPM_SUPPORT
++ if (wc->vpm && vpmdtmfsupport) {
++ /* 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);
++ }
++#endif
++
++ 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)))
++ schedule_work(&wc->bh_work);
++
++#ifndef ENABLE_WORKQUEUES
++ __t4_pci_out(wc, WC_INTR, 0);
++#endif
++
++ return IRQ_RETVAL(1);
++}
++
++DAHDI_IRQ_HANDLER(t4_interrupt_gen2)
++{
++ irqreturn_t ret;
++ unsigned long flags;
++ local_irq_save(flags);
++ ret = _t4_interrupt_gen2(irq, dev_id);
++ local_irq_restore(flags);
++ return ret;
++}
++
++#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) ? T4_VPM_PRESENT : 0);
++#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_init(struct t4 *wc)
++{
++ int laws[8] = { 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 void _binary_dahdi_fw_oct6114_256_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[];
++ extern u8 _binary_dahdi_fw_oct6114_256_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";
++ static const char oct256_firmware[] = "dahdi-fw-oct6114-256.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);
++ __t4_raw_oct_in(wc, 0x0004);
++ __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;
++ }
++
++ vpm_capacity = get_vpm450m_capacity(&wc->dev->dev);
++ if (vpm_capacity != wc->numspans * 32) {
++ dev_info(&wc->dev->dev, "Disabling VPMOCT%03d. D115"\
++ " requires a VPMOCT%03d", vpm_capacity,
++ wc->numspans*32);
++ return;
++ }
++
++ switch (vpm_capacity) {
++ 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;
++ case 256:
++#if defined(HOTPLUG_FIRMWARE)
++ if ((request_firmware(&firmware, oct256_firmware, &wc->dev->dev) != 0) ||
++ !firmware) {
++ dev_notice(&wc->dev->dev, "VPM450: firmware %s not "
++ "available from userspace\n", oct256_firmware);
++ return;
++ }
++#else
++ embedded_firmware.data = _binary_dahdi_fw_oct6114_256_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_256_bin_size;
++#endif
++ break;
++ default:
++ dev_notice(&wc->dev->dev, "Unsupported channel capacity found "
++ "on VPM module (%d).\n", vpm_capacity);
++ return;
++ }
++
++ wc->vpm = init_vpm450m(&wc->dev->dev, laws, wc->numspans, firmware);
++ if (!wc->vpm) {
++ 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_info(&wc->dev->dev, "VPM450: hardware DTMF disabled.\n");
++ vpmdtmfsupport = 0;
++ }
++
++ dev_info(&wc->dev->dev, "VPM450: Present and operational servicing %d "
++ "span(s)\n", wc->numspans);
++
++}
++#endif /* VPM_SUPPORT */
++
++static void t4_tsi_reset(struct t4 *wc)
++{
++ int x;
++ if (is_octal(wc)) {
++ for (x = 0; x < 256; x++) {
++ wc->dmactrl &= ~0x0001ffff;
++ wc->dmactrl |= (0x00004000 | ((x & 0x7f) << 7) | ((x >> 7) << 15));
++ t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++ }
++ wc->dmactrl &= ~0x0001ffff;
++ } else {
++ 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 (!has_e1_span(wc)) {
++ fromts += 4;
++ tots += 4;
++ }
++ spin_lock_irqsave(&wc->reglock, flags);
++ if (is_octal(wc)) {
++ int fromts_b = fromts & 0x7f;
++ int fromts_t = fromts >> 7;
++ int tots_b = tots & 0x7f;
++ int tots_t = tots >> 7;
++
++ wc->dmactrl &= ~0x0001ffff;
++ wc->dmactrl |= ((fromts_t << 16) | (tots_t << 15) | 0x00004000 | (tots_b << 7) | (fromts_b));
++ __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++ wc->dmactrl &= ~0x0001ffff;
++ __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++ } else {
++ 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 (!has_e1_span(wc))
++ tots += 4;
++ spin_lock_irqsave(&wc->reglock, flags);
++ if (is_octal(wc)) {
++ int tots_b = tots & 0x7f;
++ int tots_t = tots >> 7;
++
++ wc->dmactrl &= ~0x0001ffff;
++ wc->dmactrl |= ((tots_t << 15) | 0x00004000 | (tots_b << 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 &= ~0x0001ffff;
++ __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++ } else {
++ 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);
++}
++
++#ifndef CONFIG_NOEXTENDED_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
++
++#define SPI_CS (0)
++#define SPI_CLK (1)
++#define SPI_IO0 (2)
++#define SPI_IO1 (3)
++#define SPI_IO3 (4)
++#define SPI_IO2 (5)
++#define SPI_IN (0)
++#define SPI_OUT (1)
++#define ESPI_REG 13
++
++static void t8_clear_bit(struct t4 *wc, int whichb)
++{
++ wc->st.wrreg &= ~(1 << whichb);
++}
++
++static void t8_set_bit(struct t4 *wc, int whichb, int val)
++{
++ t8_clear_bit(wc, whichb);
++ wc->st.wrreg |= (val << whichb);
++}
++
++static int t8_get_bit(struct t4 *wc, int whichb)
++{
++ return (wc->st.rdreg >> whichb) & 1;
++}
++
++static void set_iodir(struct t4 *wc, int whichb, int dir)
++{
++ whichb += 16;
++ t8_clear_bit(wc, whichb);
++ t8_set_bit(wc, whichb, dir);
++}
++
++static void write_hwreg(struct t4 *wc)
++{
++ t4_pci_out(wc, ESPI_REG, wc->st.wrreg);
++}
++
++static void read_hwreg(struct t4 *wc)
++{
++ wc->st.rdreg = t4_pci_in(wc, ESPI_REG);
++}
++
++static void set_cs(struct t4 *wc, int state)
++{
++ t8_set_bit(wc, SPI_CS, state);
++ write_hwreg(wc);
++}
++
++static void set_clk(struct t4 *wc, int clk)
++{
++ t8_set_bit(wc, SPI_CLK, clk);
++ write_hwreg(wc);
++}
++
++static void clk_bit_out(struct t4 *wc, int val)
++{
++ t8_set_bit(wc, SPI_IO0, val & 1);
++ set_clk(wc, 0);
++ set_clk(wc, 1);
++}
++
++static void shift_out(struct t4 *wc, int val)
++{
++ int i;
++ for (i = 7; i >= 0; i--)
++ clk_bit_out(wc, (val >> i) & 1);
++}
++
++static int clk_bit_in(struct t4 *wc)
++{
++ int ret;
++ set_clk(wc, 0);
++ read_hwreg(wc);
++ ret = t8_get_bit(wc, SPI_IO1);
++ set_clk(wc, 1);
++ return ret;
++}
++
++static int shift_in(struct t4 *wc)
++{
++ int ret = 0;
++ int i;
++ int bit;
++
++ for (i = 7; i >= 0; i--) {
++ bit = clk_bit_in(wc);
++ ret |= ((bit & 1) << i);
++ }
++ return ret;
++}
++
++static void write_enable(struct t4 *wc)
++{
++ int cmd = 0x06;
++ set_cs(wc, 0);
++ shift_out(wc, cmd);
++ set_cs(wc, 1);
++}
++
++static int read_sr1(struct t4 *wc)
++{
++ int cmd = 0x05;
++ int ret;
++ set_cs(wc, 0);
++ shift_out(wc, cmd);
++ ret = shift_in(wc);
++ set_cs(wc, 1);
++ return ret;
++}
++
++static void clear_busy(struct t4 *wc)
++{
++ static const int SR1_BUSY = (1 << 0);
++ unsigned long stop;
++
++ stop = jiffies + 2*HZ;
++ while (read_sr1(wc) & SR1_BUSY) {
++ if (time_after(jiffies, stop)) {
++ if (printk_ratelimit()) {
++ dev_err(&wc->dev->dev,
++ "Lockup in %s\n", __func__);
++ }
++ break;
++ }
++ cond_resched();
++ }
++}
++
++static void sector_erase(struct t4 *wc, uint32_t addr)
++{
++ int cmd = 0x20;
++ write_enable(wc);
++ set_cs(wc, 0);
++ shift_out(wc, cmd);
++ shift_out(wc, (addr >> 16) & 0xff);
++ shift_out(wc, (addr >> 8) & 0xff);
++ shift_out(wc, (addr >> 0) & 0xff);
++ set_cs(wc, 1);
++ clear_busy(wc);
++}
++
++static void erase_half(struct t4 *wc)
++{
++ uint32_t addr = 0x00080000;
++ uint32_t i;
++
++ dev_info(&wc->dev->dev, "Erasing octal firmware\n");
++
++ for (i = addr; i < (addr + 0x80000); i += 4096)
++ sector_erase(wc, i);
++}
++
++
++#define T8_FLASH_PAGE_SIZE 256UL
++
++static void t8_update_firmware_page(struct t4 *wc, u32 address,
++ const u8 *page_data, size_t size)
++{
++ int i;
++
++ write_enable(wc);
++ set_cs(wc, 0);
++ shift_out(wc, 0x02);
++ shift_out(wc, (address >> 16) & 0xff);
++ shift_out(wc, (address >> 8) & 0xff);
++ shift_out(wc, (address >> 0) & 0xff);
++
++ for (i = 0; i < size; ++i)
++ shift_out(wc, page_data[i]);
++
++ set_cs(wc, 1);
++ clear_busy(wc);
++}
++
++static int t8_update_firmware(struct t4 *wc, const struct firmware *fw,
++ const char *t8_firmware)
++{
++ int res;
++ size_t offset = 0;
++ const u32 BASE_ADDRESS = 0x00080000;
++ const u8 *data, *end;
++ size_t size = 0;
++
++ /* Erase flash */
++ erase_half(wc);
++
++ dev_info(&wc->dev->dev,
++ "Uploading %s. This can take up to 30 seconds.\n", t8_firmware);
++
++ data = &fw->data[sizeof(struct t8_firm_header)];
++ end = &fw->data[fw->size];
++
++ while (data < end) {
++ /* Calculate the tail end of data that's shorter than a page */
++ size = min(T8_FLASH_PAGE_SIZE, (unsigned long)(end - data));
++
++ t8_update_firmware_page(wc, BASE_ADDRESS + offset,
++ data, size);
++ data += T8_FLASH_PAGE_SIZE;
++ offset += T8_FLASH_PAGE_SIZE;
++
++ cond_resched();
++ }
++
++ /* Reset te820 fpga after loading firmware */
++ dev_info(&wc->dev->dev, "Firmware load complete. Reseting device.\n");
++ res = pci_save_state(wc->dev);
++ if (res)
++ goto error_exit;
++ /* Set the fpga reset bits and clobber the remainder of the
++ * register, device will be reset anyway */
++ t4_pci_out(wc, WC_LEDS, 0xe0000000);
++ msleep(1000);
++
++ pci_restore_state(wc->dev);
++
++ /* Signal the driver to restart initialization.
++ * This will back out all initialization so far and
++ * restart the driver load process */
++ return -EAGAIN;
++
++error_exit:
++ return res;
++}
++
++static char read_flash_byte(struct t4 *wc, unsigned int addr)
++{
++ int cmd = 0x03;
++ char data;
++
++ set_cs(wc, 0);
++
++ shift_out(wc, cmd);
++
++ shift_out(wc, (addr >> 16) & 0xff);
++ shift_out(wc, (addr >> 8) & 0xff);
++ shift_out(wc, (addr >> 0) & 0xff);
++
++ data = shift_in(wc) & 0xff;
++
++ set_cs(wc, 1);
++
++ return data;
++}
++
++/**
++ * t8_read_serial - Returns the serial number of the board.
++ * @wc: The board whos serial number we are reading.
++ *
++ * The buffer returned is dynamically allocated and must be kfree'd by the
++ * caller. If memory could not be allocated, NULL is returned.
++ *
++ * Must be called in process context.
++ *
++ */
++static char *t8_read_serial(struct t4 *wc)
++{
++ int i;
++ static const int MAX_SERIAL = 14;
++ static const u32 base_addr = 0x00080000-256;
++ unsigned char c;
++ unsigned char *serial = kzalloc(MAX_SERIAL + 1, GFP_KERNEL);
++
++ if (!serial)
++ return NULL;
++
++ for (i = 0; i < MAX_SERIAL; ++i) {
++ c = read_flash_byte(wc, base_addr+i);
++ if ((c >= 'a' && c <= 'z') ||
++ (c >= 'A' && c <= 'Z') ||
++ (c >= '0' && c <= '9'))
++ serial[i] = c;
++ else
++ break;
++
++ }
++
++ if (!i) {
++ kfree(serial);
++ serial = NULL;
++ }
++
++ return serial;
++}
++
++static void setup_spi(struct t4 *wc)
++{
++ wc->st.rdreg = wc->st.wrreg = 0;
++
++ set_iodir(wc, SPI_IO0, SPI_OUT);
++ set_iodir(wc, SPI_IO1, SPI_IN);
++ set_iodir(wc, SPI_CS, SPI_OUT);
++ set_iodir(wc, SPI_CLK, SPI_OUT);
++
++ t8_set_bit(wc, SPI_CS, 1);
++ t8_set_bit(wc, SPI_CLK, 1);
++
++ write_hwreg(wc);
++}
++
++static int t8_check_firmware(struct t4 *wc, unsigned int version)
++{
++ const struct firmware *fw;
++ static const char t8_firmware[] = "dahdi-fw-te820.bin";
++ const struct t8_firm_header *header;
++ int res = 0;
++ u32 crc;
++
++ res = request_firmware(&fw, t8_firmware, &wc->dev->dev);
++ if (res) {
++ dev_info(&wc->dev->dev, "firmware %s not "
++ "available from userspace\n", t8_firmware);
++ goto cleanup;
++ }
++
++ header = (const struct t8_firm_header *)fw->data;
++
++ /* Check the crc before anything else */
++ crc = crc32(~0, &fw->data[10], fw->size - 10) ^ ~0;
++ if (memcmp("DIGIUM", header->header, sizeof(header->header)) ||
++ (le32_to_cpu(header->chksum) != crc)) {
++ dev_info(&wc->dev->dev,
++ "%s is invalid. Please reinstall.\n", t8_firmware);
++ goto cleanup;
++ }
++
++ /* Spi struct must be setup before any access to flash memory */
++ setup_spi(wc);
++
++ /* Check the two firmware versions */
++ if (le32_to_cpu(header->version) == version)
++ goto cleanup;
++
++ dev_info(&wc->dev->dev, "%s Version: %08x available for flash\n",
++ t8_firmware, header->version);
++
++ res = t8_update_firmware(wc, fw, t8_firmware);
++ if (res && res != -EAGAIN) {
++ dev_info(&wc->dev->dev, "Failed to load firmware %s\n",
++ t8_firmware);
++ }
++
++cleanup:
++ release_firmware(fw);
++ return res;
++}
++
++static int
++__t4_hardware_init_1(struct t4 *wc, unsigned int cardflags, bool first_time)
++{
++ unsigned int version;
++ int res;
++
++ version = t4_pci_in(wc, WC_VERSION);
++ if (is_octal(wc) && first_time) {
++ dev_info(&wc->dev->dev, "Firmware Version: %01x.%02x\n",
++ (version & 0xf00) >> 8,
++ version & 0xff);
++ } else if (first_time) {
++ dev_info(&wc->dev->dev, "Firmware Version: %08x\n", version);
++ }
++ if (debug) {
++ 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
++ }
++
++ /* Check the field updatable firmware for the wcte820 */
++ if (is_octal(wc)) {
++ res = t8_check_firmware(wc, version);
++ if (res)
++ return res;
++
++ wc->ddev->hardware_id = t8_read_serial(wc);
++ }
++
++#if defined(CONFIG_FORCE_EXTENDED_RESET)
++ t4_extended_reset(wc);
++#elif !defined(CONFIG_NOEXTENDED_RESET)
++ if (wc->devtype->flags & FLAG_EXPRESS)
++ 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 (first_time) {
++ if (!strcasecmp("auto", default_linemode)) {
++ if (-1 == t1e1override) {
++ wc->t1e1 = (((t4_pci_in(wc, WC_LEDS)) &
++ 0x0f00) >> 8);
++ wc->t1e1 &= 0xf;
++ if (is_octal(wc)) {
++ wc->t1e1 |= ((t4_pci_in(wc, WC_LEDS2)) &
++ 0x0f00) >> 4;
++ }
++ } else {
++ dev_warn(&wc->dev->dev,
++ "'t1e1override' is deprecated. Please use 'default_linemode'.\n");
++ wc->t1e1 = t1e1override & 0xf;
++ }
++ } else if (!strcasecmp("t1", default_linemode)) {
++ wc->t1e1 = 0;
++ } else if (!strcasecmp("e1", default_linemode)) {
++ wc->t1e1 = 0xff;
++ } else if (!strcasecmp("j1", default_linemode)) {
++ wc->t1e1 = 0;
++ j1mode = 1;
++ } else {
++ dev_err(&wc->dev->dev, "'%s' is an unknown linemode.\n",
++ default_linemode);
++ wc->t1e1 = 0;
++ }
++ }
++
++ wc->order = ((t4_pci_in(wc, WC_LEDS)) & 0xf0000000) >> 28;
++ order_index[wc->order]++;
++
++ /* TE820 Auth Check */
++ if (is_octal(wc)) {
++ unsigned long stop = jiffies + HZ;
++ uint32_t donebit;
++
++ t4_pci_out(wc, WC_LEDS2, WC_SET_AUTH);
++ donebit = t4_pci_in(wc, WC_LEDS2);
++ while (!(donebit & WC_GET_AUTH)) {
++ if (time_after(jiffies, stop)) {
++ /* Encryption check failed, stop operation */
++ dev_info(&wc->dev->dev,
++ "Failed encryption check. "
++ "Unloading driver.\n");
++ return -EIO;
++ }
++ msleep(20);
++ donebit = t4_pci_in(wc, WC_LEDS2);
++ }
++ }
++
++ return 0;
++}
++
++static int t4_hardware_init_1(struct t4 *wc, unsigned int cardflags)
++{
++ return __t4_hardware_init_1(wc, cardflags, true);
++}
++
++static int __t4_hardware_init_2(struct t4 *wc, bool first_time)
++{
++ int x;
++ unsigned int regval;
++ unsigned long flags;
++
++ if (t4_pci_in(wc, WC_VERSION) >= 0xc01a0165) {
++ wc->tspans[0]->spanflags |= FLAG_OCTOPT;
++ }
++ /* Setup LEDS, take out of reset */
++ t4_pci_out(wc, WC_LEDS, 0x000000ff);
++ udelay(100);
++ t4_activate(wc);
++ udelay(100);
++
++ /* In order to find out the QFALC framer version, we have to
++ * temporarily turn off compat mode and take a peak at VSTR. We turn
++ * compat back on when we are done.
++ *
++ */
++ spin_lock_irqsave(&wc->reglock, flags);
++ regval = __t4_framer_in(wc, 0, 0xd6);
++ if (is_octal(wc))
++ regval |= (1 << 4); /* SSI16 - For 16 MHz multiplex mode with comp = 1 */
++ else
++ regval |= (1 << 5); /* set COMP_DIS*/
++
++ __t4_framer_out(wc, 0, 0xd6, regval);
++ spin_unlock_irqrestore(&wc->reglock, flags);
++
++ if (!is_octal(wc)) {
++ regval = t4_framer_in(wc, 0, 0x4a);
++ if (first_time && regval == 0x05) {
++ dev_info(&wc->dev->dev, "FALC Framer Version: 2.1 or "
++ "earlier\n");
++ } else if (regval == 0x20) {
++ if (first_time)
++ dev_info(&wc->dev->dev, "FALC Framer Version: 3.1\n");
++ wc->falc31 = 1;
++ } else if (first_time) {
++ dev_info(&wc->dev->dev, "FALC Framer Version: Unknown "
++ "(VSTR = 0x%02x)\n", regval);
++ }
++ }
++
++ spin_lock_irqsave(&wc->reglock, flags);
++ 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);
++ spin_unlock_irqrestore(&wc->reglock, flags);
++
++ if (debug) {
++ 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));
++ }
++ }
++
++ 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));
++
++ return 0;
++}
++
++static int t4_hardware_init_2(struct t4 *wc)
++{
++ return __t4_hardware_init_2(wc, true);
++}
++
++static int __devinit t4_launch(struct t4 *wc)
++{
++ int x;
++ int res;
++
++ if (test_bit(DAHDI_FLAGBIT_REGISTERED, &wc->tspans[0]->span.flags))
++ return 0;
++
++ if (debug) {
++ dev_info(&wc->dev->dev,
++ "opvxd115: Launching card: %d\n",
++ wc->order);
++ }
++
++ wc->ddev->manufacturer = "OpenVox";
++ if (!ignore_rotary && (1 == order_index[wc->order])) {
++ wc->ddev->location = kasprintf(GFP_KERNEL,
++ "Board ID Switch %d", wc->order);
++ } else {
++ bool express = ((wc->tspans[0]->spanflags & FLAG_EXPRESS) > 0);
++ wc->ddev->location = kasprintf(GFP_KERNEL,
++ "PCI%s Bus %02d Slot %02d",
++ (express) ? " Express" : "",
++ wc->dev->bus->number,
++ PCI_SLOT(wc->dev->devfn) + 1);
++ }
++
++ if (!wc->ddev->location)
++ return -ENOMEM;
++
++ for (x = 0; x < wc->numspans; ++x) {
++ list_add_tail(&wc->tspans[x]->span.device_node,
++ &wc->ddev->spans);
++ }
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
++ INIT_WORK(&wc->bh_work, t4_work_func, wc);
++#else
++ INIT_WORK(&wc->bh_work, t4_work_func);
++#endif
++
++ res = dahdi_register_device(wc->ddev, &wc->dev->dev);
++ if (res) {
++ dev_err(&wc->dev->dev, "Failed to register with DAHDI.\n");
++ return res;
++ }
++
++ return 0;
++}
++
++/**
++ * wct4xxp_sort_cards - Sort the cards in card array by rotary switch settings.
++ *
++ */
++static void wct4xxp_sort_cards(void)
++{
++ int x;
++
++ /* 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;
++ }
++ }
++}
++
++static int __devinit
++t4_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
++{
++ int res;
++ struct t4 *wc;
++ unsigned int x;
++ int init_latency;
++
++ if (pci_enable_device(pdev)) {
++ return -EIO;
++ }
++
++ wc = kzalloc(sizeof(*wc), GFP_KERNEL);
++ if (!wc)
++ return -ENOMEM;
++
++ wc->ddev = dahdi_create_device();
++ if (!wc->ddev) {
++ kfree(wc);
++ return -ENOMEM;
++ }
++
++ spin_lock_init(&wc->reglock);
++ wc->devtype = (const struct devtype *)(ent->driver_data);
++
++#ifdef CONFIG_WCT4XXP_DISABLE_ASPM
++ if (is_pcie(wc)) {
++ pci_disable_link_state(pdev->bus->self, PCIE_LINK_STATE_L0S |
++ PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM);
++ };
++#endif
++
++ if (is_octal(wc))
++ wc->numspans = 8;
++ else if (wc->devtype->flags & FLAG_1PORT)
++ wc->numspans = 1;
++ else if (wc->devtype->flags & FLAG_2PORT)
++ wc->numspans = 2;
++ else
++ wc->numspans = 4;
++
++ wc->membase = pci_iomap(pdev, 0, 0);
++ /* This rids of the Double missed interrupt message after loading */
++ wc->last0 = 1;
++ if (pci_request_regions(pdev, wc->devtype->desc))
++ dev_info(&pdev->dev, "D115: Unable to request regions\n");
++
++ if (debug)
++ dev_info(&pdev->dev, "Found OPVXD115\n");
++
++ wc->dev = pdev;
++
++ /* Enable bus mastering */
++ pci_set_master(pdev);
++
++ /* Keep track of which device we are */
++ pci_set_drvdata(pdev, wc);
++
++ if (wc->devtype->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->devtype->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 */
++ res = t4_hardware_init_1(wc, wc->devtype->flags);
++ if (res) {
++ /* If this function returns -EAGAIN, we expect
++ * to attempt another driver load. Clean everything
++ * up first */
++ pci_iounmap(wc->dev, wc->membase);
++ pci_release_regions(wc->dev);
++ pci_free_consistent(wc->dev, T4_BASE_SIZE(wc) * wc->numbufs * 2,
++ wc->writechunk, wc->writedma);
++ pci_set_drvdata(wc->dev, NULL);
++ free_wc(wc);
++ return res;
++ }
++
++ 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->devtype->flags & FLAG_2NDGEN) {
++ char tmp[20];
++
++ sprintf(tmp, "te%dxxp[%d]", wc->numspans, wc->num);
++ wc->workq = create_workqueue(tmp);
++ }
++#endif
++
++ /* Allocate pieces we need here */
++ for (x = 0; x < ports_on_framer(wc); x++) {
++ struct t4_span *ts;
++ enum linemode linemode;
++
++ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
++ if (!ts) {
++ free_wc(wc);
++ return -ENOMEM;
++ }
++ wc->tspans[x] = ts;
++
++#ifdef ENABLE_WORKQUEUES
++ INIT_WORK(&ts->swork, workq_handlespan, ts);
++#endif
++ ts->spanflags |= wc->devtype->flags;
++ linemode = (wc->t1e1 & (1 << x)) ? E1 : ((j1mode) ? J1 : T1);
++ t4_alloc_channels(wc, wc->tspans[x], linemode);
++ }
++
++ /* Continue hardware intiialization */
++ t4_hardware_init_2(wc);
++
++#ifdef SUPPORT_GEN1
++ if (request_irq(pdev->irq, (wc->devtype->flags & FLAG_2NDGEN) ?
++ t4_interrupt_gen2 : t4_interrupt,
++ DAHDI_IRQ_SHARED,
++ (wc->numspans == 8) ? "wct8xxp" :
++ (wc->numspans == 1) ? "opvxd115" :
++ "wct4xxp",
++ 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, "t4xxp", wc)) {
++#endif
++ dev_notice(&wc->dev->dev, "Unable to request IRQ %d\n",
++ pdev->irq);
++ free_wc(wc);
++ return -EIO;
++ }
++
++ init_spans(wc);
++
++ if (!ignore_rotary)
++ wct4xxp_sort_cards();
++
++ if (wc->ddev->hardware_id) {
++ dev_info(&wc->dev->dev,
++ "Found a Wildcard: %s (SN: %s)\n", wc->devtype->desc,
++ wc->ddev->hardware_id);
++ } else {
++ dev_info(&wc->dev->dev,
++ "Found a Wildcard: %s\n", wc->devtype->desc);
++ }
++
++#ifdef VPM_SUPPORT
++ if (!wc->vpm) {
++ t4_vpm_init(wc);
++ wc->dmactrl |= (wc->vpm) ? T4_VPM_PRESENT : 0;
++ t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
++ if (wc->vpm)
++ set_span_devicetype(wc);
++ }
++#endif
++
++ create_sysfs_files(wc);
++
++ res = 0;
++ if (ignore_rotary)
++ res = t4_launch(wc);
++
++ return res;
++}
++
++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);
++
++ if (debug) {
++ dev_notice(&wc->dev->dev, "Stopped D115, Turned off DMA\n");
++ }
++ return 0;
++}
++
++static int __devinit
++t4_init_one_retry(struct pci_dev *pdev, const struct pci_device_id *ent)
++{
++ int res;
++ res = t4_init_one(pdev, ent);
++
++ /* If the driver was reset by a firmware load,
++ * try to load once again */
++ if (-EAGAIN == res) {
++ res = t4_init_one(pdev, ent);
++ if (-EAGAIN == res) {
++ dev_err(&pdev->dev, "Failed to update firmware.\n");
++ res = -EIO;
++ }
++ }
++
++ return res;
++}
++
++static void _t4_remove_one(struct t4 *wc)
++{
++ int basesize;
++
++ if (!wc)
++ return;
++
++ dahdi_unregister_device(wc->ddev);
++
++ remove_sysfs_files(wc);
++
++ /* Stop hardware */
++ t4_hardware_stop(wc);
++
++#ifdef VPM_SUPPORT
++ /* Release vpm */
++ if (wc->vpm)
++ release_vpm450m(wc->vpm);
++ wc->vpm = NULL;
++#endif
++ /* Unregister spans */
++
++ basesize = DAHDI_MAX_CHUNKSIZE * 32 * 4;
++ if (!(wc->tspans[0]->spanflags & FLAG_2NDGEN))
++ basesize = basesize * 2;
++
++#ifdef ENABLE_WORKQUEUES
++ if (wc->workq) {
++ flush_workqueue(wc->workq);
++ destroy_workqueue(wc->workq);
++ }
++#endif
++
++ free_irq(wc->dev->irq, wc);
++
++ if (wc->membase)
++ pci_iounmap(wc->dev, wc->membase);
++
++ pci_release_regions(wc->dev);
++
++ /* Immediately free resources */
++ pci_free_consistent(wc->dev, T4_BASE_SIZE(wc) * wc->numbufs * 2,
++ wc->writechunk, wc->writedma);
++
++ order_index[wc->order]--;
++
++ cards[wc->num] = NULL;
++ pci_set_drvdata(wc->dev, NULL);
++ free_wc(wc);
++}
++
++static void __devexit t4_remove_one(struct pci_dev *pdev)
++{
++ struct t4 *wc = pci_get_drvdata(pdev);
++ if (!wc)
++ return;
++
++ _t4_remove_one(wc);
++}
++
++
++static DEFINE_PCI_DEVICE_TABLE(t4_pci_tbl) =
++{
++ { 0x1b74, 0x0115, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&opvxd115p2 }, /* OpenVox D115P/D115E */
++ { 0x1b74, 0xd130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&opvxd130p5 }, /* OpenVox D130P/D130E */
++ { 0, }
++};
++
++static void _t4_shutdown(struct pci_dev *pdev)
++{
++ struct t4 *wc = pci_get_drvdata(pdev);
++ t4_hardware_stop(wc);
++}
++
++static int t4_suspend(struct pci_dev *pdev, pm_message_t state)
++{
++ return -ENOSYS;
++}
++
++static struct pci_driver t4_driver = {
++ .name = "opvxd115",
++ .probe = t4_init_one_retry,
++ .remove = __devexit_p(t4_remove_one),
++ .shutdown = _t4_shutdown,
++ .suspend = t4_suspend,
++ .id_table = t4_pci_tbl,
++};
++
++static int __init t4_init(void)
++{
++ int i;
++ int res;
++
++ if (-1 != t1e1override) {
++ pr_info("'t1e1override' module parameter is deprecated. "
++ "Please use 'default_linemode' instead.\n");
++ }
++
++ res = dahdi_pci_module(&t4_driver);
++ if (res)
++ return -ENODEV;
++
++ /* If we're ignoring the rotary switch settings, then we've already
++ * registered in the context of .probe */
++ if (!ignore_rotary) {
++
++ /* 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 (i = 0; cards[i]; i++) {
++ /* warn the user of duplicate ident values it is
++ * probably unintended */
++ if (debug && res < 15 && cards[i+1] &&
++ cards[res]->order == cards[i+1]->order) {
++ printk(KERN_NOTICE "opvxd115: Duplicate ident "
++ "value found (%d)\n", cards[i]->order);
++ }
++ res = t4_launch(cards[i]);
++ if (res) {
++ int j;
++ for (j = 0; j < i; ++j)
++ _t4_remove_one(cards[j]);
++ break;
++ }
++ }
++ }
++ return res;
++}
++
++static void __exit t4_cleanup(void)
++{
++ pci_unregister_driver(&t4_driver);
++}
++
++MODULE_AUTHOR("OpenVox Incorporated <support@openvox.cn>");
++MODULE_DESCRIPTION("OpenVox Single port Digital Card Driver");
++MODULE_ALIAS("opvxd115");
++MODULE_LICENSE("GPL v2");
++
++module_param(debug, int, 0600);
++module_param(noburst, int, 0600);
++module_param(timingcable, int, 0600);
++module_param(t1e1override, int, 0400);
++module_param(default_linemode, charp, S_IRUGO);
++MODULE_PARM_DESC(default_linemode, "\"auto\"(default), \"e1\", \"t1\", "
++ "or \"j1\". \"auto\" will use the value from any hardware "
++ "jumpers.");
++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);
++module_param(ignore_rotary, int, 0400);
++MODULE_PARM_DESC(ignore_rotary, "Set to > 0 to ignore the rotary switch when " \
++ "registering with DAHDI.");
++
++#ifdef VPM_SUPPORT
++module_param(vpmsupport, int, 0600);
++module_param(vpmdtmfsupport, int, 0600);
++#endif
++
++MODULE_DEVICE_TABLE(pci, t4_pci_tbl);
++
++module_init(t4_init);
++module_exit(t4_cleanup);
+--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxd115/opvxd115-diag.c 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxd115/opvxd115-diag.c 2015-02-10 14:19:03.000000000 +0100
+@@ -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 "wct4xxp.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: wct4xxp-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);
++}
+--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxd115/opvxd115.h 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxd115/opvxd115.h 2015-02-10 14:19:03.000000000 +0100
+@@ -0,0 +1,144 @@
++/*
++ * Wildcard T400P FXS Interface Driver for DAHDI Telephony interface
++ *
++ * Written by Mark Spencer <markster@linux-support.net>
++ *
++ * 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/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
++/* OctalFALC Only */
++#define FRMR_CMR4 0x41
++#define FRMR_CMR5 0x42
++#define FRMR_CMR6 0x43
++#define FRMR_GPC2 0x8a
++/* End Octal */
++#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
++
++/* CIS - Octal falc bits */
++#define FRMR_CIS_GIS5 0x10
++#define FRMR_CIS_GIS6 0x20
++#define FRMR_CIS_GIS7 0x40
++#define FRMR_CIS_GIS8 0x80
++
++#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];
++};
++
++struct t4_reg {
++ unsigned int reg;
++ unsigned int val;
++};
++
++#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)
++#define WCT4_GET_REG _IOW(DAHDI_CODE, 61, struct t4_reg)
++#define WCT4_SET_REG _IOW(DAHDI_CODE, 62, struct t4_reg)
++
+--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxd115/vpm450m.c 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxd115/vpm450m.c 2015-02-10 14:19:03.000000000 +0100
+@@ -0,0 +1,623 @@
++/*
++ * Copyright (C) 2005-2012 Digium, Inc.
++ *
++ * Mark Spencer <markster@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.
++ */
++
++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
++
++#include <linux/slab.h>
++#include <linux/vmalloc.h>
++#include <linux/string.h>
++#include <linux/time.h>
++#include <linux/version.h>
++
++#include <dahdi/kernel.h>
++#include <stdbool.h>
++
++#include "vpm450m.h"
++#include <oct612x.h>
++
++static int wct4xxp_oct612x_write(struct oct612x_context *context,
++ u32 address, u16 value)
++{
++ struct t4 *wc = dev_get_drvdata(context->dev);
++ oct_set_reg(wc, address, value);
++ return 0;
++}
++
++static int wct4xxp_oct612x_read(struct oct612x_context *context, u32 address,
++ u16 *value)
++{
++ struct t4 *wc = dev_get_drvdata(context->dev);
++ *value = (u16)oct_get_reg(wc, address);
++ return 0;
++}
++
++static int wct4xxp_oct612x_write_smear(struct oct612x_context *context,
++ u32 address, u16 value, size_t count)
++{
++ struct t4 *wc = dev_get_drvdata(context->dev);
++ int i;
++ for (i = 0; i < count; ++i)
++ oct_set_reg(wc, address + (i << 1), value);
++ return 0;
++}
++
++static int wct4xxp_oct612x_write_burst(struct oct612x_context *context,
++ u32 address, const u16 *buffer,
++ size_t count)
++{
++ struct t4 *wc = dev_get_drvdata(context->dev);
++ int i;
++ for (i = 0; i < count; ++i)
++ oct_set_reg(wc, address + (i << 1), buffer[i]);
++ return 0;
++}
++
++static int wct4xxp_oct612x_read_burst(struct oct612x_context *context,
++ u32 address, u16 *buffer, size_t count)
++{
++ struct t4 *wc = dev_get_drvdata(context->dev);
++ int i;
++ for (i = 0; i < count; ++i)
++ buffer[i] = oct_get_reg(wc, address + (i << 1));
++ return 0;
++}
++
++static const struct oct612x_ops wct4xxp_oct612x_ops = {
++ .write = wct4xxp_oct612x_write,
++ .read = wct4xxp_oct612x_read,
++ .write_smear = wct4xxp_oct612x_write_smear,
++ .write_burst = wct4xxp_oct612x_write_burst,
++ .read_burst = wct4xxp_oct612x_read_burst,
++};
++
++#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;
++ struct oct612x_context context;
++ UINT32 aulEchoChanHndl[256];
++ int chanflags[256];
++ int ecmode[256];
++ int numchans;
++};
++
++#define FLAG_DTMF (1 << 0)
++#define FLAG_MUTE (1 << 1)
++#define FLAG_ECHO (1 << 2)
++#define FLAG_ALAW (1 << 3)
++
++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,
++};
++
++void vpm450m_set_alaw_companding(struct vpm450m *vpm450m, int channel,
++ bool alaw)
++{
++ tOCT6100_CHANNEL_MODIFY *modify;
++ UINT32 ulResult;
++ UINT32 law_to_use = (alaw) ? cOCT6100_PCM_A_LAW :
++ cOCT6100_PCM_U_LAW;
++
++ if (channel >= ARRAY_SIZE(vpm450m->chanflags)) {
++ pr_err("Channel out of bounds in %s\n", __func__);
++ return;
++ }
++ /* If we're already in this companding mode, no need to do anything. */
++ if (alaw == ((vpm450m->chanflags[channel] & FLAG_ALAW) > 0))
++ return;
++
++ modify = kzalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_ATOMIC);
++ if (!modify) {
++ pr_notice("Unable to allocate memory for setec!\n");
++ return;
++ }
++
++ Oct6100ChannelModifyDef(modify);
++ modify->ulChannelHndl = vpm450m->aulEchoChanHndl[channel];
++ modify->fTdmConfigModified = TRUE;
++ modify->TdmConfig.ulSinPcmLaw = law_to_use;
++ modify->TdmConfig.ulRinPcmLaw = law_to_use;
++ modify->TdmConfig.ulSoutPcmLaw = law_to_use;
++ modify->TdmConfig.ulRoutPcmLaw = law_to_use;
++ ulResult = Oct6100ChannelModify(vpm450m->pApiInstance, modify);
++ if (ulResult != GENERIC_OK) {
++ pr_notice("Failed to apply echo can changes on channel %d %d %08x!\n",
++ vpm450m->aulEchoChanHndl[channel], channel, ulResult);
++ } else {
++ if (debug) {
++ pr_info("Changed companding on channel %d to %s.\n",
++ channel, (alaw) ? "alaw" : "ulaw");
++ }
++ if (alaw)
++ vpm450m->chanflags[channel] |= FLAG_ALAW;
++ else
++ vpm450m->chanflags[channel] &= ~(FLAG_ALAW);
++ }
++ kfree(modify);
++}
++
++static void vpm450m_setecmode(struct vpm450m *vpm450m, int channel, int mode)
++{
++ tOCT6100_CHANNEL_MODIFY *modify;
++ UINT32 ulResult;
++
++ if (vpm450m->ecmode[channel] == mode)
++ return;
++ modify = kzalloc(sizeof(*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 %08x!\n", channel, ulResult);
++ } 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;
++
++ if (channel >= ARRAY_SIZE(vpm450m->chanflags)) {
++ pr_err("Channel out of bounds in %s\n", __func__);
++ return;
++ }
++
++ modify = kzalloc(sizeof(*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 (channel >= ARRAY_SIZE(vpm450m->chanflags)) {
++ pr_err("Channel out of bounds in %s\n", __func__);
++ return;
++ }
++
++ 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;
++
++ Oct6100EventGetToneDef(&tonesearch);
++ tonesearch.pToneEvent = &tonefound;
++ tonesearch.ulMaxToneEvent = 1;
++ 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(struct device *device)
++{
++ struct oct612x_context context;
++ UINT32 ulResult;
++
++ tOCT6100_API_GET_CAPACITY_PINS CapacityPins;
++
++ context.dev = device;
++ context.ops = &wct4xxp_oct612x_ops;
++
++ Oct6100ApiGetCapacityPinsDef(&CapacityPins);
++ CapacityPins.pProcessContext = &context;
++ 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(struct device *device, int *isalaw,
++ int numspans, const struct firmware *firmware)
++{
++ tOCT6100_CHIP_OPEN *ChipOpen;
++ tOCT6100_GET_INSTANCE_SIZE InstanceSize;
++ tOCT6100_CHANNEL_OPEN *ChannelOpen;
++ UINT32 ulResult;
++ const unsigned int mask = (8 == numspans) ? 0x7 : 0x3;
++ unsigned int sout_stream, rout_stream;
++ struct vpm450m *vpm450m;
++ int x,y,law;
++
++ vpm450m = kzalloc(sizeof(*vpm450m), GFP_KERNEL);
++ if (!vpm450m)
++ return NULL;
++
++ vpm450m->context.dev = device;
++ vpm450m->context.ops = &wct4xxp_oct612x_ops;
++
++ ChipOpen = kzalloc(sizeof(*ChipOpen), GFP_KERNEL);
++ if (!ChipOpen) {
++ kfree(vpm450m);
++ kfree(vpm450m);
++ return NULL;
++ }
++
++ ChannelOpen = kzalloc(sizeof(*ChannelOpen), GFP_KERNEL);
++ if (!ChannelOpen) {
++ kfree(vpm450m);
++ kfree(ChipOpen);
++ return NULL;
++ }
++
++ for (x = 0; x < ARRAY_SIZE(vpm450m->ecmode); 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 = &vpm450m->context;
++
++ 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->aulTdmStreamFreqs[0] = cOCT6100_TDM_STREAM_FREQ_8MHZ;
++ ChipOpen->ulMaxFlexibleConfParticipants = 0;
++ ChipOpen->ulMaxConfBridges = 0;
++ ChipOpen->ulMaxRemoteDebugSessions = 0;
++ ChipOpen->fEnableChannelRecording = FALSE;
++ ChipOpen->ulSoftToneEventsBufSize = 64;
++
++ if (vpm450m->numchans <= 128) {
++ ChipOpen->ulMaxTdmStreams = 4;
++ ChipOpen->ulTdmSampling = cOCT6100_TDM_SAMPLE_AT_FALLING_EDGE;
++ } else {
++ ChipOpen->ulMaxTdmStreams = 32;
++ ChipOpen->fEnableFastH100Mode = TRUE;
++ ChipOpen->ulTdmSampling = cOCT6100_TDM_SAMPLE_AT_RISING_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;
++ }
++
++ ulResult = Oct6100ChipOpen(vpm450m->pApiInstance, ChipOpen);
++ if (ulResult != cOCT6100_ERR_OK) {
++ printk(KERN_NOTICE "Failed to open chip, code %08x!\n", ulResult);
++ vfree(vpm450m->pApiInstance);
++ kfree(vpm450m);
++ kfree(ChipOpen);
++ kfree(ChannelOpen);
++ return NULL;
++ }
++
++ sout_stream = (8 == numspans) ? 29 : 2;
++ rout_stream = (8 == numspans) ? 24 : 3;
++
++ for (x = 0; x < ((8 == numspans) ? 256 : 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 & 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;
++ vpm450m->chanflags[x] |= FLAG_ALAW;
++ } else {
++ law = cOCT6100_PCM_U_LAW;
++ vpm450m->chanflags[x] &= ~(FLAG_ALAW);
++ }
++ 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 = sout_stream;
++ ChannelOpen->TdmConfig.ulSoutTimeslot = x;
++#if 1
++ ChannelOpen->TdmConfig.ulRoutPcmLaw = law;
++ ChannelOpen->TdmConfig.ulRoutStream = rout_stream;
++ ChannelOpen->TdmConfig.ulRoutTimeslot = x;
++#endif
++ 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 %x!\n", x, ulResult);
++ continue;
++ }
++ for (y = 0; y < ARRAY_SIZE(tones); 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);
++ }
++ }
++ }
++
++ 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);
++}
+--- dahdi-linux-2.10.0.1/drivers/dahdi/opvxd115/vpm450m.h 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/opvxd115/vpm450m.h 2015-02-10 14:19:03.000000000 +0100
+@@ -0,0 +1,49 @@
++/*
++ * Copyright (C) 2005-2006 Digium, Inc.
++ *
++ * Mark Spencer <markster@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 t4;
++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(struct device *device, int *isalaw,
++ int numspans, const struct firmware *firmware);
++unsigned int get_vpm450m_capacity(struct device *device);
++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);
++void vpm450m_set_alaw_companding(struct vpm450m *vpm450m,
++ int channel, bool alaw);
++
++extern int debug;
++
++#endif
+--- dahdi-linux-2.10.0.1/drivers/dahdi/wct4xxp/base.c 2014-09-22 20:40:19.000000000 +0200
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/wct4xxp/base.c 2015-02-10 14:19:03.000000000 +0100
+@@ -40,7 +40,6 @@
+ #include <linux/delay.h>
+ #include <linux/moduleparam.h>
+ #include <linux/crc32.h>
+-#include <linux/slab.h>
+
+ #include <stdbool.h>
+ #include <dahdi/kernel.h>
+@@ -213,6 +212,7 @@
+
+ static int ms_per_irq = 1;
+ static int ignore_rotary;
++static int compatible = 0;
+
+ #ifdef FANCY_ALARM
+ static int altab[] = {
+@@ -371,14 +371,9 @@
+ dma_addr_t writedma;
+ void __iomem *membase; /* Base address of card */
+
+-#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
++ /* Flags for our bottom half */
+ unsigned long checkflag;
+- struct work_struct bh_work;
++ struct tasklet_struct t4_tlet;
+ /* Latency related additions */
+ unsigned char rxident;
+ unsigned char lastindex;
+@@ -554,6 +549,8 @@
+
+ #define MAX_T4_CARDS 64
+
++static void t4_isr_bh(unsigned long data);
++
+ static struct t4 *cards[MAX_T4_CARDS];
+
+ struct t8_firm_header {
+@@ -687,6 +684,8 @@
+
+ val = ((unit & 0x3) << 8) | (addr & 0xff) | haddr;
+ writel(val, wc_laddr);
++ if(!compatible)
++ {
+ readl(wc_version);
+ writel(val | WC_LFRMR_CS | WC_LREAD, wc_laddr);
+ readl(wc_version);
+@@ -694,6 +693,15 @@
+ writel(val, wc_laddr);
+ readl(wc_version);
+ return ret;
++ }
++ else
++ {
++ writel(val | WC_LFRMR_CS | WC_LREAD, wc_laddr);
++ writel(0,wc_version);
++ ret = readb(wc_ldata);
++ writel(val, wc_laddr);
++ return ret;
++ }
+ }
+
+ static unsigned int
+@@ -718,6 +726,8 @@
+
+ val = ((unit & 0x3) << 8) | (addr & 0xff) | haddr;
+ writel(val, wc_laddr);
++ if(!compatible)
++ {
+ readl(wc_version);
+ writel(value, wc_ldata);
+ readl(wc_version);
+@@ -725,6 +735,14 @@
+ readl(wc_version);
+ writel(val, wc_laddr);
+ readl(wc_version);
++ }
++ else
++ {
++ writel(value, wc_ldata);
++ writel(val | WC_LFRMR_CS | WC_LWRITE, wc_laddr);
++ writel(0,wc_version);
++ writel(val, wc_laddr);
++ }
+ }
+
+ static void t4_framer_out(struct t4 *wc, int unit,
+@@ -1125,11 +1143,16 @@
+ 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;
+ const bool alaw = (chan->span->deflaw == 2);
+
+ if (!vpmsupport || !wc->vpm)
+ return -ENODEV;
+
++ ops = &vpm_ec_ops;
++ features = &vpm_ec_features;
++
+ if (ecp->param_count > 0) {
+ dev_warn(&wc->dev->dev, "%s echo canceller does not support "
+ "parameters; failing request\n",
+@@ -1138,8 +1161,8 @@
+ }
+
+ *ec = tspan->ec[chan->chanpos - 1];
+- (*ec)->ops = &vpm_ec_ops;
+- (*ec)->features = vpm_ec_features;
++ (*ec)->ops = ops;
++ (*ec)->features = *features;
+
+ channel = has_e1_span(wc) ? chan->chanpos : chan->chanpos + 4;
+
+@@ -1832,6 +1855,16 @@
+ return 0;
+ }
+
++static int t4_open(struct dahdi_chan *chan)
++{
++ return 0;
++}
++
++static int t4_close(struct dahdi_chan *chan)
++{
++ return 0;
++}
++
+ static int set_span_devicetype(struct t4 *wc)
+ {
+ #ifdef VPM_SUPPORT
+@@ -2109,8 +2142,6 @@
+ {
+ unsigned int x, y;
+
+- flush_scheduled_work();
+-
+ for (x = 0; x < ARRAY_SIZE(wc->tspans); x++) {
+ if (!wc->tspans[x])
+ continue;
+@@ -2324,6 +2355,8 @@
+ .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,
+ .assigned = t4_span_assigned,
+@@ -2338,6 +2371,8 @@
+ .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,
+@@ -2710,7 +2745,7 @@
+ if (lineconfig & DAHDI_CONFIG_AMI) {
+ line = "AMI";
+ /* workaround for errata #2 in ES v3 09-10-16 */
+- fmr0 = (is_octal(wc) || wc->falc31) ? 0xb0 : 0xa0;
++ fmr0 = (wc->falc31) ? 0xb0 : 0xa0;
+ } else {
+ line = "B8ZS";
+ fmr0 = 0xf0;
+@@ -2809,7 +2844,7 @@
+ if (lineconfig & DAHDI_CONFIG_AMI) {
+ line = "AMI";
+ /* workaround for errata #2 in ES v3 09-10-16 */
+- fmr0 = (is_octal(wc) || wc->falc31) ? 0xb0 : 0xa0;
++ fmr0 = (wc->falc31) ? 0xb0 : 0xa0;
+ } else {
+ line = "HDB3";
+ fmr0 = 0xf0;
+@@ -3296,6 +3331,8 @@
+ dev_notice(&wc->dev->dev, "Checking sigbits on span %d\n",
+ span + 1);
+
++ if (!(ts->span.flags & DAHDI_FLAG_RUNNING))
++ return;
+ if (E1 == ts->linemode) {
+ for (i = 0; i < 15; i++) {
+ a = t4_framer_in(wc, span, 0x71 + i);
+@@ -3685,7 +3722,6 @@
+ struct t4_span *ts = wc->tspans[span];
+ struct dahdi_chan *sigchan;
+ unsigned long flags;
+- bool recheck_sigbits = false;
+
+
+ /* 1st gen cards isn't used interrupts */
+@@ -3711,8 +3747,6 @@
+ 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);
+- if (DAHDI_RXSIG_INITIAL == ts->span.chans[0]->rxhooksig)
+- recheck_sigbits = true;
+ }
+ spin_unlock_irqrestore(&wc->reglock, flags);
+
+@@ -3721,7 +3755,7 @@
+ ts->span.count.errsec += 1;
+ }
+
+- if (isr0 & 0x08 || recheck_sigbits)
++ if (isr0)
+ t4_check_sigbits(wc, span);
+
+ if (E1 == ts->linemode) {
+@@ -4048,15 +4082,9 @@
+
+ }
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+-static void t4_work_func(void *data)
++static void t4_isr_bh(unsigned long data)
+ {
+- struct t4 *wc = data;
+-#else
+-static void t4_work_func(struct work_struct *work)
+-{
+- struct t4 *wc = container_of(work, struct t4, bh_work);
+-#endif
++ struct t4 *wc = (struct t4 *)data;
+
+ if (test_bit(T4_CHANGE_LATENCY, &wc->checkflag)) {
+ if (wc->needed_latency != wc->numbufs) {
+@@ -4263,7 +4291,7 @@
+
+ out:
+ if (unlikely(test_bit(T4_CHANGE_LATENCY, &wc->checkflag) || test_bit(T4_CHECK_VPM, &wc->checkflag)))
+- schedule_work(&wc->bh_work);
++ tasklet_schedule(&wc->t4_tlet);
+
+ #ifndef ENABLE_WORKQUEUES
+ __t4_pci_out(wc, WC_INTR, 0);
+@@ -5038,7 +5066,7 @@
+
+ static int __t4_hardware_init_2(struct t4 *wc, bool first_time)
+ {
+- int x;
++ int x, temp_var;
+ unsigned int regval;
+ unsigned long flags;
+
+@@ -5067,18 +5095,30 @@
+ spin_unlock_irqrestore(&wc->reglock, flags);
+
+ if (!is_octal(wc)) {
++ for(temp_var = 0; temp_var < 5; temp_var++)
++ {
+ regval = t4_framer_in(wc, 0, 0x4a);
+ if (first_time && regval == 0x05) {
+ dev_info(&wc->dev->dev, "FALC Framer Version: 2.1 or "
+ "earlier\n");
++ break;
+ } else if (regval == 0x20) {
+ if (first_time)
+ dev_info(&wc->dev->dev, "FALC Framer Version: 3.1\n");
+ wc->falc31 = 1;
++ break;
+ } else if (first_time) {
++ if (temp_var == 4){
+ dev_info(&wc->dev->dev, "FALC Framer Version: Unknown "
+ "(VSTR = 0x%02x)\n", regval);
++ break;
++ } else if (temp_var == 3){
++ compatible = 0;
++ } else {
++ compatible = 1;
++ }
+ }
++ }
+ }
+
+ spin_lock_irqsave(&wc->reglock, flags);
+@@ -5144,11 +5184,7 @@
+ &wc->ddev->spans);
+ }
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+- INIT_WORK(&wc->bh_work, t4_work_func, wc);
+-#else
+- INIT_WORK(&wc->bh_work, t4_work_func);
+-#endif
++ tasklet_init(&wc->t4_tlet, t4_isr_bh, (unsigned long)wc);
+
+ res = dahdi_register_device(wc->ddev, &wc->dev->dev);
+ if (res) {
+@@ -5499,6 +5535,11 @@
+
+ static DEFINE_PCI_DEVICE_TABLE(t4_pci_tbl) =
+ {
++ { 0x1b74, 0xd210, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&wct210p5 }, /* OpenVox D210E/D115E */
++ { 0x1b74, 0x1420, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&wct4xxp }, /* OpenVox D420x */
++ { 0x1b74, 0xd230, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&wct210p5 }, /* OpenVox D230P/D230E */
++ { 0x1b74, 0xd410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&wct4xxp }, /* OpenVox D410x */
++ { 0x1b74, 0xd430, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&wct4xxp }, /* OpenVox D430x */
+ { 0x10ee, 0x0314, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&wct4xxp },
+
+ { 0xd161, 0x1820, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&wct820p5 },
+@@ -5622,6 +5663,7 @@
+ module_param(ignore_rotary, int, 0400);
+ MODULE_PARM_DESC(ignore_rotary, "Set to > 0 to ignore the rotary switch when " \
+ "registering with DAHDI.");
++module_param(compatible, int, 0600);
+
+ #ifdef VPM_SUPPORT
+ module_param(vpmsupport, int, 0600);
+--- dahdi-linux-2.10.0.1/drivers/dahdi/wct4xxp/vpm450m.c 2014-09-22 20:40:19.000000000 +0200
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/wct4xxp/vpm450m.c 2015-02-10 14:19:03.000000000 +0100
+@@ -239,7 +239,7 @@
+
+ if (vpm450m->ecmode[channel] == mode)
+ return;
+- modify = kzalloc(sizeof(*modify), GFP_ATOMIC);
++ modify = kmalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_ATOMIC);
+ if (!modify) {
+ printk(KERN_NOTICE "wct4xxp: Unable to allocate memory for setec!\n");
+ return;
+@@ -269,7 +269,7 @@
+ return;
+ }
+
+- modify = kzalloc(sizeof(*modify), GFP_KERNEL);
++ modify = kmalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_KERNEL);
+ if (!modify) {
+ printk(KERN_NOTICE "wct4xxp: Unable to allocate memory for setdtmf!\n");
+ return;
+@@ -454,27 +454,28 @@
+ struct vpm450m *vpm450m;
+ int x,y,law;
+
+- vpm450m = kzalloc(sizeof(*vpm450m), GFP_KERNEL);
+- if (!vpm450m)
++ if (!(vpm450m = kmalloc(sizeof(struct vpm450m), GFP_KERNEL)))
+ return NULL;
+
++ memset(vpm450m, 0, sizeof(struct vpm450m));
+ vpm450m->context.dev = device;
+ vpm450m->context.ops = &wct4xxp_oct612x_ops;
+
+- ChipOpen = kzalloc(sizeof(*ChipOpen), GFP_KERNEL);
+- if (!ChipOpen) {
+- kfree(vpm450m);
++ if (!(ChipOpen = kmalloc(sizeof(tOCT6100_CHIP_OPEN), GFP_KERNEL))) {
+ kfree(vpm450m);
+ return NULL;
+ }
+
+- ChannelOpen = kzalloc(sizeof(*ChannelOpen), GFP_KERNEL);
+- if (!ChannelOpen) {
++ 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 < ARRAY_SIZE(vpm450m->ecmode); x++)
+ vpm450m->ecmode[x] = -1;
+
+--- dahdi-linux-2.10.0.1/drivers/dahdi/wct4xxp/wct4xxp.h 2014-09-22 20:40:19.000000000 +0200
++++ dahdi-linux-2.10.0.1-openvox/drivers/dahdi/wct4xxp/wct4xxp.h 2015-02-10 14:19:03.000000000 +0100
+@@ -131,6 +131,13 @@
+ unsigned int val;
+ };
+
++#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)
+ #define WCT4_GET_REG _IOW(DAHDI_CODE, 61, struct t4_reg)
+ #define WCT4_SET_REG _IOW(DAHDI_CODE, 62, struct t4_reg)
diff --git a/dahdi-linux-2.10.1-yeastar.patch b/dahdi-linux-2.10.1-yeastar.patch
new file mode 100644
index 000000000000..207b2bceda07
--- /dev/null
+++ b/dahdi-linux-2.10.1-yeastar.patch
@@ -0,0 +1,6269 @@
+diff -Nur dahdi-linux-2.10.0.1/drivers/dahdi/Kbuild dahdi-linux-2.10.0.1-yeastar/drivers/dahdi/Kbuild
+--- dahdi-linux-2.10.0.1/drivers/dahdi/Kbuild 2014-09-22 20:40:19.000000000 +0200
++++ dahdi-linux-2.10.0.1-yeastar/drivers/dahdi/Kbuild 2015-02-10 15:33:19.353714552 +0100
+@@ -14,6 +14,9 @@
+ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTE12XP) += wcte12xp/
+ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTE13XP) += wcte13xp.o
+
++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_YSTDM8XX) += ystdm8xx.o
++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_YSTDM16XX) += ystdm16xx.o
++
+ wcte13xp-objs := wcte13xp-base.o wcxb_spi.o wcxb.o wcxb_flash.o
+ CFLAGS_wcte13xp-base.o += -I$(src)/oct612x -I$(src)/oct612x/include -I$(src)/oct612x/octdeviceapi -I$(src)/oct612x/octdeviceapi/oct6100api
+ ifeq ($(HOTPLUG_FIRMWARE),yes)
+diff -Nur dahdi-linux-2.10.0.1/drivers/dahdi/Kconfig dahdi-linux-2.10.0.1-yeastar/drivers/dahdi/Kconfig
+--- dahdi-linux-2.10.0.1/drivers/dahdi/Kconfig 2014-09-22 20:40:19.000000000 +0200
++++ dahdi-linux-2.10.0.1-yeastar/drivers/dahdi/Kconfig 2015-02-10 15:33:19.353714552 +0100
+@@ -291,4 +291,28 @@
+
+ If unsure, say Y.
+
++config DAHDI_YSTDM8XX
++
++ tristate "Yeastar YSTDM8xx Support"
++ depends on DAHDI && PCI
++ default DAHDI
++ ---help---
++ This driver provides support for the Yeastar YSTDM8xx.
++ To compile this driver as a module, choose M here: the
++ module will be called ystdm8xx.
++
++ If unsure, say Y.
++
++config DAHDI_YSTDM16XX
++
++ tristate "Yeastar YSTDM16xx Support"
++ depends on DAHDI && PCI
++ default DAHDI
++ ---help---
++ This driver provides support for the Yeastar YSTDM16xx.
++ To compile this driver as a module, choose M here: the
++ module will be called ystdm16xx.
++
++ If unsure, say Y.
++
+ source "drivers/dahdi/xpp/Kconfig"
+diff -Nur dahdi-linux-2.10.0.1/drivers/dahdi/ystdm16xx.c dahdi-linux-2.10.0.1-yeastar/drivers/dahdi/ystdm16xx.c
+--- dahdi-linux-2.10.0.1/drivers/dahdi/ystdm16xx.c 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-yeastar/drivers/dahdi/ystdm16xx.c 2015-02-10 15:33:19.357047652 +0100
+@@ -0,0 +1,3151 @@
++/*
++ * Yeastar YSTDM16xx TDM FXS/FXO Interface Driver for Zapata Telephony interface
++ *
++ * Derived from wctdm.c written by Mark Spencer <markster@linux-support.net>
++ * Matthew Fredrickson <creslin@linux-support.net>
++ *
++ * Copyright (C) 2006, Yeastar Technology Co.,Ltd. <support@yeastar.com>
++ * Copyright (C) 2001, Linux Support Services, Inc.
++ *
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * 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/init.h>
++#include <linux/pci.h>
++#include <linux/interrupt.h>
++#include <linux/moduleparam.h>
++#include <linux/sched.h>
++#include <linux/ioctl.h>
++#include <asm/io.h>
++#include "proslic.h"
++/*
++ * 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;
++#define POLARITY_XOR (\
++ (reversepolarity != 0) ^ (fxs->reversepolarity != 0) ^\
++ (fxs->vmwi_lrev != 0) ^\
++ ((fxs->vmwisetting.vmwi_type & DAHDI_VMWI_HVAC) != 0))
++
++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",0x4000}, // playback volume set lower
++{27,14,"XMIT_DIGITAL_GAIN",0x3000},
++//{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 "fxo_modes.h"
++
++
++#define NUM_FXO_REGS 60
++
++#define WC_MAX_IFACES 128
++
++#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_SYNC 0x0
++#define WC_TEST 0x1
++#define WC_CS 0x2
++#define WC_CS1 0x6
++#define WC_VER 0x3
++#define YS_SLC 0x4
++#define YS_DCH 0x7
++#define YS_E0H 0x8
++
++#define BIT_SYNC (1 << 0)
++#define BIT_CS (1 << 2)
++#define BIT_SCLK (1 << 3)
++#define BIT_SDI (1 << 4)
++#define BIT_SDO (1 << 5)
++
++#define FLAG_EMPTY 0
++#define FLAG_WRITE 1
++#define FLAG_READ 2
++
++/* the constants below control the 'debounce' periods enforced by the
++ check_hook routines; these routines are called once every 4 interrupts
++ (the interrupt cycles around the four modules), so the periods are
++ specified in _4 millisecond_ increments
++*/
++#define DEFAULT_RING_DEBOUNCE 32 /* Ringer Debounce (32 ms) */
++
++#define POLARITY_DEBOUNCE 32 /* Polarity debounce (32 ms) */
++
++#define OHT_TIMER 6000 /* How long after RING to retain OHT */
++
++/* NEON MWI pulse width - Make larger for longer period time
++ * For more information on NEON MWI generation using the proslic
++ * refer to Silicon Labs App Note "AN33-SI321X NEON FLASHING"
++ * RNGY = RNGY 1/2 * Period * 8000
++ */
++#define NEON_MWI_RNGY_PULSEWIDTH 0x3e8 /*=> period of 250 mS */
++
++#define FLAG_3215 (1 << 0)
++
++#define NUM_CARDS 16
++
++#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,
++};
++
++#define NUM_REGS 109
++#define NUM_INDIRECT_REGS 105
++
++struct ystdm_stats {
++ int tipvolt; /* TIP voltage (mV) */
++ int ringvolt; /* RING voltage (mV) */
++ int batvolt; /* VBAT voltage (mV) */
++};
++
++struct ystdm_regs {
++ unsigned char direct[NUM_REGS];
++ unsigned short indirect[NUM_INDIRECT_REGS];
++};
++
++struct ystdm_regop {
++ int indirect;
++ unsigned char reg;
++ unsigned short val;
++};
++
++struct ystdm_echo_coefs {
++ unsigned char acim;
++ unsigned char coef1;
++ unsigned char coef2;
++ unsigned char coef3;
++ unsigned char coef4;
++ unsigned char coef5;
++ unsigned char coef6;
++ unsigned char coef7;
++ unsigned char coef8;
++};
++
++#define WCTDM_GET_STATS _IOR (DAHDI_CODE, 60, struct ystdm_stats)
++#define WCTDM_GET_REGS _IOR (DAHDI_CODE, 61, struct ystdm_regs)
++#define WCTDM_SET_REG _IOW (DAHDI_CODE, 62, struct ystdm_regop)
++#define WCTDM_SET_ECHOTUNE _IOW (DAHDI_CODE, 63, struct ystdm_echo_coefs)
++
++struct ystdm {
++ 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[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;
++ int readcid;
++ unsigned int cidtimer;
++ } fxo;
++ struct fxs {
++ int oldrxhook;
++ int debouncehook;
++ int lastrxhook;
++ int debounce;
++ int ohttimer;
++ int idletxhookstate; /* IDLE changing hook state */
++ int lasttxhook;
++ int palarms;
++ int reversepolarity; /* Reverse Line */
++ int mwisendtype;
++ struct dahdi_vmwi_info vmwisetting;
++ int vmwi_active_messages;
++ u32 vmwi_lrev:1; /* MWI Line Reversal*/
++ u32 vmwi_hvdc:1; /* MWI High Voltage DC Idle line */
++ u32 vmwi_hvac:1; /* MWI Neon High Voltage AC Idle line */
++ u32 neonringing:1; /* Ring Generator is set for NEON */
++ struct calregs calregs;
++ } fxs;
++ } mod[NUM_CARDS];
++
++ /* Receive hook state and debouncing */
++ int modtype[NUM_CARDS];
++ unsigned char reg0shadow[NUM_CARDS];
++ unsigned char reg1shadow[NUM_CARDS];
++
++ unsigned long ioaddr;
++ 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[NUM_CARDS];
++ struct dahdi_chan *chans[NUM_CARDS];
++};
++
++
++struct ystdm_desc {
++ char *name;
++ int flags;
++};
++
++static struct ystdm_desc ystdme = { "YSTDM16xx REV E", 0 };
++static int acim2tiss[16] = { 0x0, 0x1, 0x4, 0x5, 0x7, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x2, 0x0, 0x3 };
++
++static struct ystdm *ifaces[WC_MAX_IFACES];
++
++static void ystdm_release(struct ystdm *wc);
++
++static unsigned int fxovoltage;
++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 dtmf = 0;
++static int fastpickup = 0;
++static int fxotxgain = 0;
++static int fxorxgain = 0;
++static int fxstxgain = 0;
++static int fxsrxgain = 0;
++
++static int ystdm_init_proslic(struct ystdm *wc, int card, int fast , int manual, int sane);
++static int ystdm_init_ring_generator_mode(struct ystdm *wc, int card);
++static int ystdm_set_ring_generator_mode(struct ystdm *wc, int card, int mode);
++
++static inline void ystdm_transmitprep(struct ystdm *wc, unsigned char ints)
++{
++ volatile unsigned int *writechunk;
++ int x;
++ if (ints & 0x01)
++ /* Write is at interrupt address. Start writing from normal offset */
++ writechunk = wc->writechunk;
++ else
++ writechunk = wc->writechunk + DAHDI_CHUNKSIZE * (NUM_CARDS / 4);
++ /* Calculate Transmission */
++ dahdi_transmit(&wc->span);
++
++ for (x=0;x<DAHDI_CHUNKSIZE;x++) {
++ /* Send a sample, as a 32-bit word */
++ writechunk[4 * x] = 0;
++ writechunk[4 * x + 1] = 0;
++ writechunk[4 * x + 2] = 0;
++ writechunk[4 * x + 3] = 0;
++#ifdef __BIG_ENDIAN
++ if (wc->cardflag & (1 << 15))
++ writechunk[4 * x + 3] |= (wc->chans[15]->writechunk[x]);
++ if (wc->cardflag & (1 << 14))
++ writechunk[4 * x + 3] |= (wc->chans[14]->writechunk[x] << 8);
++ if (wc->cardflag & (1 << 13))
++ writechunk[4 * x + 3] |= (wc->chans[13]->writechunk[x] << 16);
++ if (wc->cardflag & (1 << 12))
++ writechunk[4 * x + 3] |= (wc->chans[12]->writechunk[x] << 24);
++
++ if (wc->cardflag & (1 << 11))
++ writechunk[4 * x + 2] |= (wc->chans[11]->writechunk[x]);
++ if (wc->cardflag & (1 << 10))
++ writechunk[4 * x + 2] |= (wc->chans[10]->writechunk[x] << 8);
++ if (wc->cardflag & (1 << 9))
++ writechunk[4 * x + 2] |= (wc->chans[9]->writechunk[x] << 16);
++ if (wc->cardflag & (1 << 8))
++ writechunk[4 * x + 2] |= (wc->chans[8]->writechunk[x] << 24);
++
++ if (wc->cardflag & (1 << 7))
++ writechunk[4 * x + 1] |= (wc->chans[7]->writechunk[x]);
++ if (wc->cardflag & (1 << 6))
++ writechunk[4 * x + 1] |= (wc->chans[6]->writechunk[x] << 8);
++ if (wc->cardflag & (1 << 5))
++ writechunk[4 * x + 1] |= (wc->chans[5]->writechunk[x] << 16);
++ if (wc->cardflag & (1 << 4))
++ writechunk[4 * x + 1] |= (wc->chans[4]->writechunk[x] << 24);
++
++ if (wc->cardflag & (1 << 3))
++ writechunk[4 * x + 0] |= (wc->chans[3]->writechunk[x]);
++ if (wc->cardflag & (1 << 2))
++ writechunk[4 * x + 0] |= (wc->chans[2]->writechunk[x] << 8);
++ if (wc->cardflag & (1 << 1))
++ writechunk[4 * x + 0] |= (wc->chans[1]->writechunk[x] << 16);
++ if (wc->cardflag & (1 << 0))
++ writechunk[4 * x + 0] |= (wc->chans[0]->writechunk[x] << 24);
++#else
++ if (wc->cardflag & (1 << 15))
++ writechunk[4 * x + 3] |= (wc->chans[15]->writechunk[x] << 24);
++ if (wc->cardflag & (1 << 14))
++ writechunk[4 * x + 3] |= (wc->chans[14]->writechunk[x] << 16);
++ if (wc->cardflag & (1 << 13))
++ writechunk[4 * x + 3] |= (wc->chans[13]->writechunk[x] << 8);
++ if (wc->cardflag & (1 << 12))
++ writechunk[4 * x + 3] |= (wc->chans[12]->writechunk[x]);
++
++ if (wc->cardflag & (1 << 11))
++ writechunk[4 * x + 2] |= (wc->chans[11]->writechunk[x] << 24);
++ if (wc->cardflag & (1 << 10))
++ writechunk[4 * x + 2] |= (wc->chans[10]->writechunk[x] << 16);
++ if (wc->cardflag & (1 << 9))
++ writechunk[4 * x + 2] |= (wc->chans[9]->writechunk[x] << 8);
++ if (wc->cardflag & (1 << 8))
++ writechunk[4 * x + 2] |= (wc->chans[8]->writechunk[x]);
++
++ if (wc->cardflag & (1 << 7))
++ writechunk[4 * x + 1] |= (wc->chans[7]->writechunk[x] << 24);
++ if (wc->cardflag & (1 << 6))
++ writechunk[4 * x + 1] |= (wc->chans[6]->writechunk[x] << 16);
++ if (wc->cardflag & (1 << 5))
++ writechunk[4 * x + 1] |= (wc->chans[5]->writechunk[x] << 8);
++ if (wc->cardflag & (1 << 4))
++ writechunk[4 * x + 1] |= (wc->chans[4]->writechunk[x]);
++
++ if (wc->cardflag & (1 << 3))
++ writechunk[4 * x + 0] |= (wc->chans[3]->writechunk[x] << 24);
++ if (wc->cardflag & (1 << 2))
++ writechunk[4 * x + 0] |= (wc->chans[2]->writechunk[x] << 16);
++ if (wc->cardflag & (1 << 1))
++ writechunk[4 * x + 0] |= (wc->chans[1]->writechunk[x] << 8);
++ if (wc->cardflag & (1 << 0))
++ writechunk[4 * x + 0] |= (wc->chans[0]->writechunk[x]);
++#endif
++ }
++
++}
++
++#ifdef AUDIO_RINGCHECK
++static inline void ring_check(struct ystdm *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("High peg!\n");
++ if ((wc->mod[card].fxo.pegtimer < PEGTIME) && (wc->mod[card].fxo.pegtimer > MINPEGTIME))
++ wc->mod[card].fxo.pegcount++;
++ wc->mod[card].fxo.pegtimer = 0;
++ wc->mod[card].fxo.peg = 1;
++ } else if ((sample < -10000) && (wc->mod[card].fxo.peg != -1)) {
++ if (debug > 1) printk("Low peg!\n");
++ if ((wc->mod[card].fxo.pegtimer < (PEGTIME >> 2)) && (wc->mod[card].fxo.pegtimer > (MINPEGTIME >> 2)))
++ wc->mod[card].fxo.pegcount++;
++ wc->mod[card].fxo.pegtimer = 0;
++ wc->mod[card].fxo.peg = -1;
++ }
++ }
++ if (wc->mod[card].fxo.pegtimer > PEGTIME) {
++ /* Reset pegcount if our timer expires */
++ wc->mod[card].fxo.pegcount = 0;
++ }
++ /* Decrement debouncer if appropriate */
++ if (wc->mod[card].fxo.ringdebounce)
++ wc->mod[card].fxo.ringdebounce--;
++ if (!wc->mod[card].fxo.offhook && !wc->mod[card].fxo.ringdebounce) {
++ if (!wc->mod[card].fxo.ring && (wc->mod[card].fxo.pegcount > PEGCOUNT)) {
++ /* It's ringing */
++ if (debug)
++ printk("RING on %d/%d!\n", wc->span.spanno, card + 1);
++ if (!wc->mod[card].fxo.offhook)
++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING);
++ wc->mod[card].fxo.ring = 1;
++ wc->mod[card].fxo.readcid = 1;
++ }
++ if (wc->mod[card].fxo.ring && !wc->mod[card].fxo.pegcount) {
++ /* No more ring */
++ if (debug)
++ printk("NO RING on %d/%d!\n", wc->span.spanno, card + 1);
++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK);
++ wc->mod[card].fxo.ring = 0;
++ wc->mod[card].fxo.cidtimer = wc->intcount;
++ wc->mod[card].fxo.readcid = 0;
++ }
++ }
++}
++#endif
++static inline void ystdm_dtmfcheck_fakepolarity(struct ystdm *wc, int card, int x)
++{
++ int sample;
++ /* only look for sound on the line if dtmf flag is on, it is an fxo card and line is onhook */
++ if (!dtmf || !(wc->cardflag & (1 << card)) || !(wc->modtype[card] == MOD_TYPE_FXO) || wc->mod[card].fxo.offhook )
++ return;
++
++ /* don't look for noise if we're already processing it, or there is a ringing tone */
++ if(!wc->mod[card].fxo.readcid && !wc->mod[card].fxo.wasringing &&
++ wc->intcount > wc->mod[card].fxo.cidtimer + 400 ) {
++ sample = DAHDI_XLAW(wc->chans[card]->readchunk[x], (wc->chans[card]));
++ if (sample > 16000 || sample < -16000) {
++ wc->mod[card].fxo.readcid = 1;
++ wc->mod[card].fxo.cidtimer = wc->intcount;
++ if (debug) printk("DTMF CLIP on %i\n",card+1);
++ dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY);
++ }
++ } else if(wc->mod[card].fxo.readcid && wc->intcount > wc->mod[card].fxo.cidtimer + 2000) {
++ /* reset flags if it's been a while */
++ wc->mod[card].fxo.cidtimer = wc->intcount;
++ wc->mod[card].fxo.readcid = 0;
++ }
++}
++static inline void ystdm_receiveprep(struct ystdm *wc, unsigned char ints)
++{
++ volatile unsigned int *readchunk;
++ int x;
++ int y;
++
++ if (ints & 0x08)
++ readchunk = wc->readchunk + DAHDI_CHUNKSIZE * (NUM_CARDS / 4);
++ else
++ /* Read is at interrupt address. Valid data is available at normal offset */
++ readchunk = wc->readchunk;
++ for (x=0;x<DAHDI_CHUNKSIZE;x++) {
++#ifdef __BIG_ENDIAN
++ if (wc->cardflag & (1 << 15))
++ wc->chans[15]->readchunk[x] = (readchunk[4 * x]) & 0xff;
++ if (wc->cardflag & (1 << 14))
++ wc->chans[14]->readchunk[x] = (readchunk[4 * x] >> 8) & 0xff;
++ if (wc->cardflag & (1 << 13))
++ wc->chans[13]->readchunk[x] = (readchunk[4 * x] >> 16) & 0xff;
++ if (wc->cardflag & (1 << 12))
++ wc->chans[12]->readchunk[x] = (readchunk[4 * x] >> 24) & 0xff;
++
++ if (wc->cardflag & (1 << 11))
++ wc->chans[11]->readchunk[x] = (readchunk[4 * x + 3]) & 0xff;
++ if (wc->cardflag & (1 << 10))
++ wc->chans[10]->readchunk[x] = (readchunk[4 * x + 3] >> 8) & 0xff;
++ if (wc->cardflag & (1 << 9))
++ wc->chans[9]->readchunk[x] = (readchunk[4 * x + 3] >> 16) & 0xff;
++ if (wc->cardflag & (1 << 8))
++ wc->chans[8]->readchunk[x] = (readchunk[4 * x + 3] >> 24) & 0xff;
++
++ if (wc->cardflag & (1 << 7))
++ wc->chans[7]->readchunk[x] = (readchunk[4 * x + 2]) & 0xff;
++ if (wc->cardflag & (1 << 6))
++ wc->chans[6]->readchunk[x] = (readchunk[4 * x + 2] >> 8) & 0xff;
++ if (wc->cardflag & (1 << 5))
++ wc->chans[5]->readchunk[x] = (readchunk[4 * x + 2] >> 16) & 0xff;
++ if (wc->cardflag & (1 << 4))
++ wc->chans[4]->readchunk[x] = (readchunk[4 * x + 2] >> 24) & 0xff;
++
++ if (wc->cardflag & (1 << 3))
++ wc->chans[3]->readchunk[x] = (readchunk[4 * x + 1]) & 0xff;
++ if (wc->cardflag & (1 << 2))
++ wc->chans[2]->readchunk[x] = (readchunk[4 * x + 1] >> 8) & 0xff;
++ if (wc->cardflag & (1 << 1))
++ wc->chans[1]->readchunk[x] = (readchunk[4 * x + 1] >> 16) & 0xff;
++ if (wc->cardflag & (1 << 0))
++ wc->chans[0]->readchunk[x] = (readchunk[4 * x + 1] >> 24) & 0xff;
++#else
++ if (wc->cardflag & (1 << 15))
++ wc->chans[15]->readchunk[x] = (readchunk[4 * x] >> 24) & 0xff;
++ if (wc->cardflag & (1 << 14))
++ wc->chans[14]->readchunk[x] = (readchunk[4 * x] >> 16) & 0xff;
++ if (wc->cardflag & (1 << 13))
++ wc->chans[13]->readchunk[x] = (readchunk[4 * x] >> 8) & 0xff;
++ if (wc->cardflag & (1 << 12))
++ wc->chans[12]->readchunk[x] = (readchunk[4 * x]) & 0xff;
++
++ if (wc->cardflag & (1 << 11))
++ wc->chans[11]->readchunk[x] = (readchunk[4 * x + 3] >> 24) & 0xff;
++ if (wc->cardflag & (1 << 10))
++ wc->chans[10]->readchunk[x] = (readchunk[4 * x + 3] >> 16) & 0xff;
++ if (wc->cardflag & (1 << 9))
++ wc->chans[9]->readchunk[x] = (readchunk[4 * x + 3] >> 8) & 0xff;
++ if (wc->cardflag & (1 << 8))
++ wc->chans[8]->readchunk[x] = (readchunk[4 * x + 3]) & 0xff;
++
++ if (wc->cardflag & (1 << 7))
++ wc->chans[7]->readchunk[x] = (readchunk[4 * x + 2] >> 24) & 0xff;
++ if (wc->cardflag & (1 << 6))
++ wc->chans[6]->readchunk[x] = (readchunk[4 * x + 2] >> 16) & 0xff;
++ if (wc->cardflag & (1 << 5))
++ wc->chans[5]->readchunk[x] = (readchunk[4 * x + 2] >> 8) & 0xff;
++ if (wc->cardflag & (1 << 4))
++ wc->chans[4]->readchunk[x] = (readchunk[4 * x + 2]) & 0xff;
++
++ if (wc->cardflag & (1 << 3))
++ wc->chans[3]->readchunk[x] = (readchunk[4 * x + 1] >> 24) & 0xff;
++ if (wc->cardflag & (1 << 2))
++ wc->chans[2]->readchunk[x] = (readchunk[4 * x + 1] >> 16) & 0xff;
++ if (wc->cardflag & (1 << 1))
++ wc->chans[1]->readchunk[x] = (readchunk[4 * x + 1] >> 8) & 0xff;
++ if (wc->cardflag & (1 << 0))
++ wc->chans[0]->readchunk[x] = (readchunk[4 * x + 1]) & 0xff;
++
++#endif
++ for(y = 0; y < NUM_CARDS; y ++)
++ ystdm_dtmfcheck_fakepolarity(wc,y,x);
++ }
++#ifdef AUDIO_RINGCHECK
++ for (x=0;x<wc->cards;x++)
++ ring_check(wc, x);
++#endif
++ /* XXX We're wasting 8 taps. We should get closer :( */
++ for (x = 0; x < NUM_CARDS; x++) {
++ if (wc->cardflag & (1 << x))
++ dahdi_ec_chunk(wc->chans[x], wc->chans[x]->readchunk, wc->chans[x]->writechunk);
++ }
++ dahdi_receive(&wc->span);
++}
++
++static void ystdm_stop_dma(struct ystdm *wc);
++static void ystdm_reset_tdm(struct ystdm *wc);
++static void ystdm_restart_dma(struct ystdm *wc);
++
++static inline void __write_8bits(struct ystdm *wc, unsigned char bits)
++{
++/* Out BIT_CS --\________________________________/---- */
++/* Out BIT_SCLK ---\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/------ */
++/* Out BIT_SDI ---\___/---\___/---\___/---\___/-------- */
++/* Data Bit 7 6 5 4 3 2 1 0 */
++/* Data written 0 1 0 1 0 1 0 1 */
++ /* Drop chip select */
++ int x;
++ 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);
++
++}
++
++static inline void __reset_spi(struct ystdm *wc)
++{
++ /* 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);
++
++}
++
++static inline unsigned char __read_8bits(struct ystdm *wc)
++{
++/* Out BIT_CS --\________________________________________/----*/
++/* Out BIT_SCLK ---\_/--\_/--\_/--\_/--\_/--\_/--\_/--\_/-------*/
++/* In BIT_SDO ????/1111\0000/1111\0000/1111\0000/1111\0000/???*/
++/* Data bit 7 6 5 4 3 2 1 0 */
++/* Data Read 1 0 1 0 1 0 1 0 */
++
++/* Note: Clock High time is 2x Low time, due to input read */
++
++ unsigned char res=0, c;
++ int x;
++ /* Drop chip select */
++ wc->ios &= ~BIT_CS;
++ outb(wc->ios, wc->ioaddr + WC_AUXD);
++ for (x=0;x<8;x++) {
++ res <<= 1;
++ /* Drop SCLK */
++ wc->ios &= ~BIT_SCLK;
++ outb(wc->ios, wc->ioaddr + WC_AUXD);
++ /* Now raise SCLK high again */
++ 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;
++ }
++ /* Finally raise CS back high again */
++ wc->ios |= BIT_CS;
++ outb(wc->ios, wc->ioaddr + WC_AUXD);
++
++ /* And return our result */
++ return res;
++}
++
++static void __ystdm_setcreg(struct ystdm *wc, unsigned char reg, unsigned char val)
++{
++ outb(val, wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2));
++}
++
++static unsigned char __ystdm_getcreg(struct ystdm *wc, unsigned char reg)
++{
++ return inb(wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2));
++}
++
++static inline void __ystdm_setcard(struct ystdm *wc, int card)
++{
++ if (wc->curcard == card)
++ return;
++ if (card < NUM_CARDS/2) {
++ __ystdm_setcreg(wc, WC_CS1, 0);
++ __ystdm_setcreg(wc, WC_CS, (1 << card));
++ } else {
++ __ystdm_setcreg(wc, WC_CS, 0);
++ __ystdm_setcreg(wc, WC_CS1, (1 << (card-8)));
++ }
++ wc->curcard = card;
++}
++
++static void __ystdm_setreg(struct ystdm *wc, int card, unsigned char reg, unsigned char value)
++{
++ __ystdm_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 ystdm_setreg(struct ystdm *wc, int card, unsigned char reg, unsigned char value)
++{
++ unsigned long flags;
++ spin_lock_irqsave(&wc->lock, flags);
++ __ystdm_setreg(wc, card, reg, value);
++ spin_unlock_irqrestore(&wc->lock, flags);
++}
++
++static unsigned char __ystdm_getreg(struct ystdm *wc, int card, unsigned char reg)
++{
++ __ystdm_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 ystdm *wc, int card)
++{
++ unsigned long flags;
++ spin_lock_irqsave(&wc->lock, flags);
++ __ystdm_setcard(wc, card);
++ __reset_spi(wc);
++ __reset_spi(wc);
++ spin_unlock_irqrestore(&wc->lock, flags);
++}
++
++static unsigned char ystdm_getreg(struct ystdm *wc, int card, unsigned char reg)
++{
++ unsigned long flags;
++ unsigned char res;
++ spin_lock_irqsave(&wc->lock, flags);
++ res = __ystdm_getreg(wc, card, reg);
++ spin_unlock_irqrestore(&wc->lock, flags);
++ return res;
++}
++
++static int __wait_access(struct ystdm *wc, int card)
++{
++ unsigned char data = 0;
++
++ int count = 0;
++
++ #define MAX 6000 /* attempts */
++
++ /* Wait for indirect access */
++ while (count++ < MAX)
++ {
++ data = __ystdm_getreg(wc, card, I_STATUS);
++
++ if (!data)
++ return 0;
++
++ }
++
++ if(count > (MAX-1)) printk(" ##### Loop error (%02x) #####\n", data);
++
++ return 0;
++}
++
++static unsigned char translate_3215(unsigned char address)
++{
++ int x;
++ for (x=0;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 ystdm_proslic_setreg_indirect(struct ystdm *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)) {
++ __ystdm_setreg(wc, card, IDA_LO,(unsigned char)(data & 0xFF));
++ __ystdm_setreg(wc, card, IDA_HI,(unsigned char)((data & 0xFF00)>>8));
++ __ystdm_setreg(wc, card, IAA,address);
++ res = 0;
++ };
++ spin_unlock_irqrestore(&wc->lock, flags);
++ return res;
++}
++
++static int ystdm_proslic_getreg_indirect(struct ystdm *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)) {
++ __ystdm_setreg(wc, card, IAA, address);
++ if (!__wait_access(wc, card)) {
++ unsigned char data1, data2;
++ data1 = __ystdm_getreg(wc, card, IDA_LO);
++ data2 = __ystdm_getreg(wc, card, IDA_HI);
++ res = data1 | (data2 << 8);
++ } else
++ p = "Failed to wait inside\n";
++ } else
++ p = "failed to wait\n";
++ spin_unlock_irqrestore(&wc->lock, flags);
++ if (p)
++ printk(p);
++ return res;
++}
++
++static int ystdm_proslic_init_indirect_regs(struct ystdm *wc, int card)
++{
++ unsigned char i;
++
++ for (i=0; i<sizeof(indirect_regs) / sizeof(indirect_regs[0]); i++)
++ {
++ if(ystdm_proslic_setreg_indirect(wc, card, indirect_regs[i].address,indirect_regs[i].initial))
++ return -1;
++ }
++
++ return 0;
++}
++
++static int ystdm_proslic_verify_indirect_regs(struct ystdm *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 = ystdm_proslic_getreg_indirect(wc, card, (unsigned char) indirect_regs[i].address)) < 0) {
++ printk("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("!!!!!!! %s iREG %X = %X should be %X\n",
++ indirect_regs[i].name,indirect_regs[i].address,j,initial );
++ passed = 0;
++ }
++ }
++
++ if (passed) {
++ if (debug)
++ printk("Init Indirect Registers completed successfully.\n");
++ } else {
++ printk(" !!!!! Init Indirect Registers UNSUCCESSFULLY.\n");
++ return -1;
++ }
++ return 0;
++}
++
++static inline void ystdm_proslic_recheck_sanity(struct ystdm *wc, int card)
++{
++ struct fxs *const fxs = &wc->mod[card].fxs;
++ int res;
++ /* Check loopback */
++ res = wc->reg1shadow[card];
++ if (!res && (res != fxs->lasttxhook)) {
++ res = ystdm_getreg(wc, card, 8);
++ if (res) {
++ printk(KERN_NOTICE "Ouch, part reset, quickly restoring reality (%d)\n", card);
++ ystdm_init_proslic(wc, card, 1, 0, 1);
++ } else {
++ if (fxs->palarms++ < MAX_ALARMS) {
++ printk(KERN_NOTICE "Power alarm on module %d, resetting!\n", card + 1);
++ if (fxs->lasttxhook == SLIC_LF_RINGING)
++ fxs->lasttxhook = SLIC_LF_ACTIVE_FWD;
++ ystdm_setreg(wc, card, 64, fxs->lasttxhook);
++ } else {
++ if (fxs->palarms == MAX_ALARMS)
++ printk(KERN_NOTICE "Too many power alarms on card %d, NOT resetting!\n", card + 1);
++ }
++ }
++ }
++}
++
++static inline void ystdm_voicedaa_check_hook(struct ystdm *wc, int card)
++{
++#define MS_PER_CHECK_HOOK 16
++
++#ifndef AUDIO_RINGCHECK
++ unsigned char res;
++#endif
++ signed char b;
++ int poopy = 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("Poopy (%02x) on card %d!\n", b, card + 1);
++ poopy++;
++ }
++ b &= 0x9b;
++ if (fxo->offhook) {
++ if (b != 0x9)
++ ystdm_setreg(wc, card, 5, 0x9);
++ } else {
++ if (b != 0x8)
++ ystdm_setreg(wc, card, 5, 0x8);
++ }
++ if (poopy)
++ return;
++ if (!fxo->offhook) {
++ if (fwringdetect) {
++ res = wc->reg0shadow[card] & 0x60;
++ if (fxo->ringdebounce--) {
++ if (res && (res != fxo->lastrdtx) &&
++ (fxo->battery == BATTERY_PRESENT)) {
++ if (!fxo->wasringing) {
++ fxo->wasringing = 1;
++ if (debug)
++ printk("RING on %d/%d!\n", wc->span.spanno, card + 1);
++ 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("NO RING on %d/%d!\n", wc->span.spanno, card + 1);
++ 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;
++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING);
++ if (debug)
++ printk("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;
++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK);
++ if (debug)
++ printk("NO RING on %d/%d!\n", wc->span.spanno, card + 1);
++ }
++ fxo->ringdebounce = 0;
++ }
++ }
++ }
++ }
++
++ b = wc->reg1shadow[card];
++
++ if (fxovoltage) {
++ static int count = 0;
++ if (!(count++ % 100)) {
++ printk(KERN_DEBUG "Card %d: Voltage: %d Debounce %d\n", card + 1, b, fxo->battdebounce);
++ }
++ }
++
++ if (unlikely(DAHDI_RXSIG_INITIAL == wc->chans[card]->rxhooksig)) {
++ /*
++ * dahdi-base will set DAHDI_RXSIG_INITIAL after a
++ * DAHDI_STARTUP or DAHDI_CHANCONFIG ioctl so that new events
++ * will be queued on the channel with the current received
++ * hook state. Channels that use robbed-bit signalling always
++ * report the current received state via the dahdi_rbsbits
++ * call. Since we only call dahdi_hooksig when we've detected
++ * a change to report, let's forget our current state in order
++ * to force us to report it again via dahdi_hooksig.
++ *
++ */
++ fxo->battery = BATTERY_UNKNOWN;
++ }
++
++if (DAHDI_RXSIG_INITIAL == wc->chans[card]->rxhooksig) {
++ /* If we've been set to the initial state, let's reset the
++ * battery state to unknown so that we will reset the
++ * current state of the battery and call dahdi_hooksig. */
++ fxo->battery = BATTERY_UNKNOWN;
++ } /* add by David at 2009.09.10 */
++
++ 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("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("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("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("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 ? DAHDI_ALARM_NONE : DAHDI_ALARM_RED);
++ }
++ }
++
++ if (fxo->polaritydebounce) {
++ if (--fxo->polaritydebounce == 0) {
++ if (fxo->lastpol != fxo->polarity) {
++ if (debug)
++ printk("%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 void ystdm_fxs_hooksig(struct ystdm *wc, const int card, enum dahdi_txsig txsig)
++{
++ struct fxs *const fxs = &wc->mod[card].fxs;
++ switch (txsig) {
++ case DAHDI_TXSIG_ONHOOK:
++ switch (wc->span.chans[card]->sig) {
++ case DAHDI_SIG_FXOKS:
++ case DAHDI_SIG_FXOLS:
++ /* Can't change Ring Generator during OHT */
++ if (!fxs->ohttimer) {
++ ystdm_set_ring_generator_mode(wc,
++ card, fxs->vmwi_hvac);
++ fxs->lasttxhook = fxs->vmwi_hvac ?
++ SLIC_LF_RINGING :
++ fxs->idletxhookstate;
++ } else {
++ fxs->lasttxhook = fxs->idletxhookstate;
++ }
++ break;
++ case DAHDI_SIG_EM:
++ fxs->lasttxhook = fxs->idletxhookstate;
++ break;
++ case DAHDI_SIG_FXOGS:
++ fxs->lasttxhook = SLIC_LF_TIP_OPEN;
++ break;
++ }
++ break;
++ case DAHDI_TXSIG_OFFHOOK:
++ switch (wc->span.chans[card]->sig) {
++ case DAHDI_SIG_EM:
++ fxs->lasttxhook = SLIC_LF_ACTIVE_REV;
++ break;
++ default:
++ fxs->lasttxhook = fxs->idletxhookstate;
++ break;
++ }
++ break;
++ case DAHDI_TXSIG_START:
++ /* Set ringer mode */
++ ystdm_set_ring_generator_mode(wc, card, 0);
++ fxs->lasttxhook = SLIC_LF_RINGING;
++ break;
++ case DAHDI_TXSIG_KEWL:
++ fxs->lasttxhook = SLIC_LF_OPEN;
++ break;
++ default:
++ printk(KERN_NOTICE "ystdm: Can't set tx state to %d\n", txsig);
++ return;
++ }
++ if (debug) {
++ printk(KERN_DEBUG
++ "Setting FXS hook state to %d (%02x)\n",
++ txsig, fxs->lasttxhook);
++ }
++ ystdm_setreg(wc, card, LINE_STATE, fxs->lasttxhook);
++}
++
++static inline void ystdm_proslic_check_hook(struct ystdm *wc, int card)
++{
++ struct fxs *const fxs = &wc->mod[card].fxs;
++ char res;
++ int hook;
++
++ /* For some reason we have to debounce the
++ hook detector. */
++
++ res = wc->reg0shadow[card];
++ hook = (res & 1);
++ if (hook != fxs->lastrxhook) {
++ /* Reset the debounce (must be multiple of 4ms) */
++ fxs->debounce = dialdebounce * 4;
++#if 0
++ printk(KERN_DEBUG "Resetting debounce card %d hook %d, %d\n",
++ card, hook, fxs->debounce);
++#endif
++ } else {
++ if (fxs->debounce > 0) {
++ fxs->debounce -= 16 * DAHDI_CHUNKSIZE;
++#if 0
++ printk(KERN_DEBUG "Sustaining hook %d, %d\n",
++ hook, fxs->debounce);
++#endif
++ if (!fxs->debounce) {
++#if 0
++ printk(KERN_DEBUG "Counted down debounce, newhook: %d...\n", hook);
++#endif
++ fxs->debouncehook = hook;
++ }
++ if (!fxs->oldrxhook && fxs->debouncehook) {
++ /* Off hook */
++#if 1
++ if (debug)
++#endif
++ printk(KERN_DEBUG "ystdm: Card %d Going off hook\n", card);
++
++ switch (fxs->lasttxhook) {
++ case SLIC_LF_RINGING:
++ case SLIC_LF_OHTRAN_FWD:
++ case SLIC_LF_OHTRAN_REV:
++ /* just detected OffHook, during
++ * Ringing or OnHookTransfer */
++ fxs->idletxhookstate =
++ POLARITY_XOR ?
++ SLIC_LF_ACTIVE_REV :
++ SLIC_LF_ACTIVE_FWD;
++ break;
++ }
++
++ ystdm_fxs_hooksig(wc, card, DAHDI_TXSIG_OFFHOOK);
++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK);
++ if (robust)
++ ystdm_init_proslic(wc, card, 1, 0, 1);
++ fxs->oldrxhook = 1;
++
++ } else if (fxs->oldrxhook && !fxs->debouncehook) {
++ /* On hook */
++#if 1
++ if (debug)
++#endif
++ printk(KERN_DEBUG "ystdm: Card %d Going on hook\n", card);
++ ystdm_fxs_hooksig(wc, card, DAHDI_TXSIG_ONHOOK);
++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK);
++ fxs->oldrxhook = 0;
++ }
++ }
++ }
++ fxs->lastrxhook = hook;
++}
++
++DAHDI_IRQ_HANDLER(ystdm_interrupt)
++{
++ struct ystdm *wc = dev_id;
++ unsigned char ints;
++ int x;
++ int mode;
++
++ ints = inb(wc->ioaddr + WC_INTSTAT);
++ outb(ints, wc->ioaddr + WC_INTSTAT);
++
++ if (!ints)
++ return IRQ_NONE;
++
++ outb(ints, wc->ioaddr + WC_INTSTAT);
++
++ if (ints & 0x10) {
++ /* Stop DMA, wait for watchdog */
++ printk("TDM PCI Master abort\n");
++ ystdm_stop_dma(wc);
++
++ return IRQ_RETVAL(1);
++
++ }
++
++ if (ints & 0x20) {
++ printk("PCI Target abort\n");
++ return IRQ_RETVAL(1);
++ }
++
++ for (x=0;x<NUM_CARDS;x++) {
++ if (wc->cardflag & (1 << x) &&
++ (wc->modtype[x] == MOD_TYPE_FXS)) {
++ struct fxs *const fxs = &wc->mod[x].fxs;
++ if (fxs->lasttxhook == SLIC_LF_RINGING &&
++ !fxs->neonringing) {
++ /* RINGing, prepare for OHT */
++ fxs->ohttimer = OHT_TIMER << 3;
++
++ /* logical XOR 3 variables
++ module parameter 'reversepolarity', global reverse all FXS lines.
++ ioctl channel variable fxs 'reversepolarity', Line Reversal Alert Signal if required.
++ ioctl channel variable fxs 'vmwi_lrev', VMWI pending.
++ */
++
++ /* OHT mode when idle */
++ fxs->idletxhookstate = POLARITY_XOR ?
++ SLIC_LF_OHTRAN_REV :
++ SLIC_LF_OHTRAN_FWD;
++ } else if (fxs->ohttimer) {
++ /* check if still OnHook */
++ if (!fxs->oldrxhook) {
++ fxs->ohttimer -= DAHDI_CHUNKSIZE;
++ if (!fxs->ohttimer) {
++ fxs->idletxhookstate = POLARITY_XOR ? SLIC_LF_ACTIVE_REV : SLIC_LF_ACTIVE_FWD; /* Switch to Active, Rev or Fwd */
++ /* if currently OHT */
++ if ((fxs->lasttxhook == SLIC_LF_OHTRAN_FWD) || (fxs->lasttxhook == SLIC_LF_OHTRAN_REV)) {
++ if (fxs->vmwi_hvac) {
++ /* force idle polarity Forward if ringing */
++ fxs->idletxhookstate = SLIC_LF_ACTIVE_FWD;
++ /* Set ring generator for neon */
++ ystdm_set_ring_generator_mode(wc, x, 1);
++ fxs->lasttxhook = SLIC_LF_RINGING;
++ } else {
++ fxs->lasttxhook = fxs->idletxhookstate;
++ }
++ /* Apply the change as appropriate */
++ ystdm_setreg(wc, x, LINE_STATE, fxs->lasttxhook);
++ }
++ }
++ } else {
++ fxs->ohttimer = 0;
++ /* Switch to Active, Rev or Fwd */
++ fxs->idletxhookstate = POLARITY_XOR ? SLIC_LF_ACTIVE_REV : SLIC_LF_ACTIVE_FWD;
++ }
++ }
++ }
++ }
++
++ if (ints & 0x0f) {
++ wc->intcount++;
++ x = wc->intcount & 0xf;
++ mode = wc->intcount & 0x30;
++ if (wc->cardflag & (1 << x)) {
++ switch(mode) {
++ case 0:
++ /* Rest */
++ break;
++ case 16:
++ /* Read first shadow reg */
++ if (wc->modtype[x] == MOD_TYPE_FXS)
++ wc->reg0shadow[x] = ystdm_getreg(wc, x, 68);
++ else if (wc->modtype[x] == MOD_TYPE_FXO)
++ wc->reg0shadow[x] = ystdm_getreg(wc, x, 5);
++ break;
++ case 32:
++ /* Read second shadow reg */
++ if (wc->modtype[x] == MOD_TYPE_FXS)
++ wc->reg1shadow[x] = ystdm_getreg(wc, x, LINE_STATE);
++ else if (wc->modtype[x] == MOD_TYPE_FXO)
++ wc->reg1shadow[x] = ystdm_getreg(wc, x, 29);
++ break;
++ case 48:
++ /* Perform processing */
++ if (wc->modtype[x] == MOD_TYPE_FXS) {
++ ystdm_proslic_check_hook(wc, x);
++ if (!(wc->intcount & 0xf0)) {
++ ystdm_proslic_recheck_sanity(wc, x);
++ }
++ } else if (wc->modtype[x] == MOD_TYPE_FXO) {
++ ystdm_voicedaa_check_hook(wc, x);
++ }
++ break;
++ }
++ }
++ if (!(wc->intcount % 10000)) {
++ /* Accept an alarm once per 10 seconds */
++ for (x=0;x<NUM_CARDS;x++)
++ if (wc->modtype[x] == MOD_TYPE_FXS) {
++ if (wc->mod[x].fxs.palarms)
++ wc->mod[x].fxs.palarms--;
++ }
++ }
++ ystdm_receiveprep(wc, ints);
++ ystdm_transmitprep(wc, ints);
++ }
++ return IRQ_RETVAL(1);
++
++}
++
++static int ystdm_voicedaa_insane(struct ystdm *wc, int card)
++{
++ int blah;
++ blah = ystdm_getreg(wc, card, 2);
++ if (blah != 0x3)
++ return -2;
++ blah = ystdm_getreg(wc, card, 11);
++ if (debug)
++ printk("VoiceDAA System: %02x\n", blah & 0xf);
++ return 0;
++}
++
++static int ystdm_proslic_insane(struct ystdm *wc, int card)
++{
++ int blah,insane_report;
++ insane_report=0;
++
++ blah = ystdm_getreg(wc, card, 0);
++ if (debug)
++ printk("ProSLIC on module %d, product %d, version %d\n", card, (blah & 0x30) >> 4, (blah & 0xf));
++
++#if 0
++ if ((blah & 0x30) >> 4) {
++ printk("ProSLIC on module %d is not a 3210.\n", card);
++ return -1;
++ }
++#endif
++ if (((blah & 0xf) == 0) || ((blah & 0xf) == 0xf)) {
++ /* SLIC not loaded */
++ return -1;
++ }
++ if ((blah & 0xf) < 2) {
++ printk("ProSLIC 3210 version %d is too old\n", blah & 0xf);
++ return -1;
++ }
++ if ((blah & 0xf) == 2) {
++ /* ProSLIC 3215, not a 3210 */
++ wc->flags[card] |= FLAG_3215;
++ }
++ blah = ystdm_getreg(wc, card, 8);
++ if (blah != 0x2) {
++ printk("ProSLIC on module %d insane (1) %d should be 2\n", card, blah);
++ return -1;
++ } else if ( insane_report)
++ printk("ProSLIC on module %d Reg 8 Reads %d Expected is 0x2\n",card,blah);
++
++ blah = ystdm_getreg(wc, card, 64);
++ if (blah != 0x0) {
++ printk("ProSLIC on module %d insane (2)\n", card);
++ return -1;
++ } else if ( insane_report)
++ printk("ProSLIC on module %d Reg 64 Reads %d Expected is 0x0\n",card,blah);
++
++ blah = ystdm_getreg(wc, card, 11);
++ if (blah != 0x33) {
++ printk("ProSLIC on module %d insane (3)\n", card);
++ return -1;
++ } else if ( insane_report)
++ printk("ProSLIC on module %d Reg 11 Reads %d Expected is 0x33\n",card,blah);
++
++ /* Just be sure it's setup right. */
++ ystdm_setreg(wc, card, 30, 0);
++
++ if (debug)
++ printk("ProSLIC on module %d seems sane.\n", card);
++ return 0;
++}
++
++static int ystdm_proslic_powerleak_test(struct ystdm *wc, int card)
++{
++ unsigned long origjiffies;
++ unsigned char vbat;
++
++ /* Turn off linefeed */
++ ystdm_setreg(wc, card, 64, 0);
++
++ /* Power down */
++ ystdm_setreg(wc, card, 14, 0x10);
++
++ /* Wait for one second */
++ origjiffies = jiffies;
++
++ while((vbat = ystdm_getreg(wc, card, 82)) > 0x6) {
++ if ((jiffies - origjiffies) >= (HZ/2))
++ break;;
++ }
++
++ if (vbat < 0x06) {
++ printk("Excessive leakage detected on module %d: %d volts (%02x) after %d ms\n", card,
++ 376 * vbat / 1000, vbat, (int)((jiffies - origjiffies) * 1000 / HZ));
++ return -1;
++ } else if (debug) {
++ printk("Post-leakage voltage: %d volts\n", 376 * vbat / 1000);
++ }
++ return 0;
++}
++
++static int ystdm_powerup_proslic(struct ystdm *wc, int card, int fast)
++{
++ unsigned char vbat;
++ unsigned long origjiffies;
++ int lim;
++
++ /* Set period of DC-DC converter to 1/64 khz */
++ ystdm_setreg(wc, card, 92, 0xff /* was 0xff */);
++
++ /* Wait for VBat to powerup */
++ origjiffies = jiffies;
++
++ /* Disable powerdown */
++ ystdm_setreg(wc, card, 14, 0);
++
++ /* If fast, don't bother checking anymore */
++ if (fast)
++ return 0;
++
++ while((vbat = ystdm_getreg(wc, card, 82)) < 0xc0) {
++ /* Wait no more than 500ms */
++ if ((jiffies - origjiffies) > HZ/2) {
++ break;
++ }
++ }
++
++ if (vbat < 0xc0) {
++ if (wc->proslic_power == PROSLIC_POWER_UNKNOWN)
++ printk("ProSLIC on module %d failed to powerup within %d ms (%d mV only)\n\n -- DID YOU REMEMBER TO PLUG IN THE HD POWER CABLE TO THE YSTDM16xx??\n",
++ card, (int)(((jiffies - origjiffies) * 1000 / HZ)),
++ vbat * 375);
++ wc->proslic_power = PROSLIC_POWER_WARNED;
++ return -1;
++ } else if (debug) {
++ printk("ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n",
++ card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ)));
++ }
++ wc->proslic_power = PROSLIC_POWER_ON;
++
++ /* Proslic max allowed loop current, reg 71 LOOP_I_LIMIT */
++ /* If out of range, just set it to the default value */
++ lim = (loopcurrent - 20) / 3;
++ if ( loopcurrent > 41 ) {
++ lim = 0;
++ if (debug)
++ printk("Loop current out of range! Setting to default 20mA!\n");
++ }
++ else if (debug)
++ printk("Loop current set to %dmA!\n",(lim*3)+20);
++ ystdm_setreg(wc,card,LOOP_I_LIMIT,lim);
++
++ /* Engage DC-DC converter */
++ ystdm_setreg(wc, card, 93, 0x19 /* was 0x19 */);
++#if 0
++ origjiffies = jiffies;
++ while(0x80 & ystdm_getreg(wc, card, 93)) {
++ if ((jiffies - origjiffies) > 2 * HZ) {
++ printk("Timeout waiting for DC-DC calibration on module %d\n", card);
++ return -1;
++ }
++ }
++
++#if 0
++ /* Wait a full two seconds */
++ while((jiffies - origjiffies) < 2 * HZ);
++
++ /* Just check to be sure */
++ vbat = ystdm_getreg(wc, card, 82);
++ printk("ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n",
++ card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ)));
++#endif
++#endif
++ return 0;
++
++}
++
++static int ystdm_proslic_manual_calibrate(struct ystdm *wc, int card){
++ unsigned long origjiffies;
++ unsigned char i;
++
++ ystdm_setreg(wc, card, 21, 0);//(0) Disable all interupts in DR21
++ ystdm_setreg(wc, card, 22, 0);//(0)Disable all interupts in DR21
++ ystdm_setreg(wc, card, 23, 0);//(0)Disable all interupts in DR21
++ ystdm_setreg(wc, card, 64, 0);//(0)
++
++ ystdm_setreg(wc, card, 97, 0x18); //(0x18)Calibrations without the ADC and DAC offset and without common mode calibration.
++ ystdm_setreg(wc, card, 96, 0x47); //(0x47) Calibrate common mode and differential DAC mode DAC + ILIM
++
++ origjiffies=jiffies;
++ while( ystdm_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);
++ ystdm_proslic_setreg_indirect(wc, card, 88,0);
++ ystdm_proslic_setreg_indirect(wc, card, 89,0);
++ ystdm_proslic_setreg_indirect(wc, card, 90,0);
++ ystdm_proslic_setreg_indirect(wc, card, 91,0);
++ ystdm_proslic_setreg_indirect(wc, card, 92,0);
++ ystdm_proslic_setreg_indirect(wc, card, 93,0);
++
++ ystdm_setreg(wc, card, 98, 0x10); // This is necessary if the calibration occurs other than at reset time
++ ystdm_setreg(wc, card, 99, 0x10);
++
++ for ( i=0x1f; i>0; i--)
++ {
++ ystdm_setreg(wc, card, 98, i);
++ origjiffies=jiffies;
++ while((jiffies-origjiffies)<4);
++ if((ystdm_getreg(wc, card, 88)) == 0)
++ break;
++ } // for
++
++ for ( i=0x1f; i>0; i--)
++ {
++ ystdm_setreg(wc, card, 99, i);
++ origjiffies=jiffies;
++ while((jiffies-origjiffies)<4);
++ if((ystdm_getreg(wc, card, 89)) == 0)
++ break;
++ }//for
++
++/*******************************The preceding is the manual gain mismatch calibration****************************/
++/**********************************The following is the longitudinal Balance Cal***********************************/
++ ystdm_setreg(wc,card,64,1);
++ while((jiffies-origjiffies)<10); // Sleep 100?
++
++ ystdm_setreg(wc, card, 64, 0);
++ ystdm_setreg(wc, card, 23, 0x4); // enable interrupt for the balance Cal
++ ystdm_setreg(wc, card, 97, 0x1); // this is a singular calibration bit for longitudinal calibration
++ ystdm_setreg(wc, card, 96, 0x40);
++
++ ystdm_getreg(wc, card, 96); /* Read Reg 96 just cause */
++
++ ystdm_setreg(wc, card, 21, 0xFF);
++ ystdm_setreg(wc, card, 22, 0xFF);
++ ystdm_setreg(wc, card, 23, 0xFF);
++
++ /**The preceding is the longitudinal Balance Cal***/
++ return(0);
++
++}
++#if 1
++static int ystdm_proslic_calibrate(struct ystdm *wc, int card)
++{
++ unsigned long origjiffies;
++ int x;
++ /* Perform all calibrations */
++ ystdm_setreg(wc, card, 97, 0x1f);
++
++ /* Begin, no speedup */
++ ystdm_setreg(wc, card, 96, 0x5f);
++
++ /* Wait for it to finish */
++ origjiffies = jiffies;
++ while(ystdm_getreg(wc, card, 96)) {
++ if ((jiffies - origjiffies) > 2 * HZ) {
++ printk("Timeout waiting for calibration of module %d\n", card);
++ return -1;
++ }
++ }
++
++ if (debug) {
++ /* Print calibration parameters */
++ printk("Calibration Vector Regs 98 - 107: \n");
++ for (x=98;x<108;x++) {
++ printk("%d: %02x\n", x, ystdm_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 ystdm_set_hwgain(struct ystdm *wc, int card, __s32 gain, __u32 tx)
++{
++ if (!(wc->modtype[card] == MOD_TYPE_FXO)) {
++ printk("Cannot adjust gain. Unsupported module type!\n");
++ return -1;
++ }
++ if (tx) {
++ if (debug)
++ printk("setting FXO tx gain for card=%d to %d\n", card, gain);
++ if (gain >= -150 && gain <= 0) {
++ ystdm_setreg(wc, card, 38, 16 + (gain/-10));
++ ystdm_setreg(wc, card, 40, 16 + (-gain%10));
++ } else if (gain <= 120 && gain > 0) {
++ ystdm_setreg(wc, card, 38, gain/10);
++ ystdm_setreg(wc, card, 40, (gain%10));
++ } else {
++ printk("FXO tx gain is out of range (%d)\n", gain);
++ return -1;
++ }
++ } else { /* rx */
++ if (debug)
++ printk("setting FXO rx gain for card=%d to %d\n", card, gain);
++ if (gain >= -150 && gain <= 0) {
++ ystdm_setreg(wc, card, 39, 16+ (gain/-10));
++ ystdm_setreg(wc, card, 41, 16 + (-gain%10));
++ } else if (gain <= 120 && gain > 0) {
++ ystdm_setreg(wc, card, 39, gain/10);
++ ystdm_setreg(wc, card, 41, (gain%10));
++ } else {
++ printk("FXO rx gain is out of range (%d)\n", gain);
++ return -1;
++ }
++ }
++
++ return 0;
++}
++
++
++static int set_vmwi(struct ystdm * wc, int chan_idx)
++{
++ struct fxs *const fxs = &wc->mod[chan_idx].fxs;
++ if (fxs->vmwi_active_messages) {
++ fxs->vmwi_lrev =
++ (fxs->vmwisetting.vmwi_type & DAHDI_VMWI_LREV) ? 1 : 0;
++ fxs->vmwi_hvdc =
++ (fxs->vmwisetting.vmwi_type & DAHDI_VMWI_HVDC) ? 1 : 0;
++ fxs->vmwi_hvac =
++ (fxs->vmwisetting.vmwi_type & DAHDI_VMWI_HVAC) ? 1 : 0;
++ } else {
++ fxs->vmwi_lrev = 0;
++ fxs->vmwi_hvdc = 0;
++ fxs->vmwi_hvac = 0;
++ }
++
++ if (debug) {
++ printk(KERN_DEBUG "Setting VMWI on channel %d, messages=%d, "
++ "lrev=%d, hvdc=%d, hvac=%d\n",
++ chan_idx,
++ fxs->vmwi_active_messages,
++ fxs->vmwi_lrev,
++ fxs->vmwi_hvdc,
++ fxs->vmwi_hvac
++ );
++ }
++ if (fxs->vmwi_hvac) {
++ /* Can't change ring generator while in On Hook Transfer mode*/
++ if (!fxs->ohttimer) {
++ if (POLARITY_XOR)
++ fxs->idletxhookstate |= SLIC_LF_REVMASK;
++ else
++ fxs->idletxhookstate &= ~SLIC_LF_REVMASK;
++ /* Set ring generator for neon */
++ ystdm_set_ring_generator_mode(wc, chan_idx, 1);
++ /* Activate ring to send neon pulses */
++ fxs->lasttxhook = SLIC_LF_RINGING;
++ ystdm_setreg(wc, chan_idx, LINE_STATE, fxs->lasttxhook);
++ }
++ } else {
++ if (fxs->neonringing) {
++ /* Set ring generator for normal ringer */
++ ystdm_set_ring_generator_mode(wc, chan_idx, 0);
++ /* ACTIVE, polarity determined later */
++ fxs->lasttxhook = SLIC_LF_ACTIVE_FWD;
++ } else if ((fxs->lasttxhook == SLIC_LF_RINGING) ||
++ (fxs->lasttxhook == SLIC_LF_OPEN)) {
++ /* Can't change polarity while ringing or when open,
++ set idlehookstate instead */
++ if (POLARITY_XOR)
++ fxs->idletxhookstate |= SLIC_LF_REVMASK;
++ else
++ fxs->idletxhookstate &= ~SLIC_LF_REVMASK;
++
++ printk(KERN_DEBUG "Unable to change polarity on channel"
++ "%d, lasttxhook=0x%X\n",
++ chan_idx,
++ fxs->lasttxhook
++ );
++ return 0;
++ }
++ if (POLARITY_XOR) {
++ fxs->idletxhookstate |= SLIC_LF_REVMASK;
++ fxs->lasttxhook |= SLIC_LF_REVMASK;
++ } else {
++ fxs->idletxhookstate &= ~SLIC_LF_REVMASK;
++ fxs->lasttxhook &= ~SLIC_LF_REVMASK;
++ }
++ ystdm_setreg(wc, chan_idx, LINE_STATE, fxs->lasttxhook);
++ }
++ return 0;
++}
++
++
++static int ystdm_init_voicedaa(struct ystdm *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 && ystdm_voicedaa_insane(wc, card))
++ return -2;
++
++ /* Software reset */
++ ystdm_setreg(wc, card, 1, 0x80);
++
++ /* Wait just a bit */
++ wait_just_a_bit(HZ/10);
++
++ /* Enable PCM, ulaw */
++ if (alawoverride){
++ ystdm_setreg(wc, card, 33, 0x20);
++ } else {
++ ystdm_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);
++ ystdm_setreg(wc, card, 16, reg16);
++
++ if(fwringdetect) {
++ /* Enable ring detector full-wave rectifier mode */
++ ystdm_setreg(wc, card, 18, 2);
++ ystdm_setreg(wc, card, 24, 0);
++ } else {
++ /* Set to the device defaults */
++ ystdm_setreg(wc, card, 18, 0);
++ ystdm_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);
++ ystdm_setreg(wc, card, 26, reg26);
++
++ /* Set AC Impedence */
++ reg30 = (fxo_modes[_opermode].acim);
++ ystdm_setreg(wc, card, 30, reg30);
++
++ /* Misc. DAA parameters */
++ if (fastpickup)
++ reg31 = 0xe3;
++ else
++ reg31 = 0xa3;
++
++ reg31 |= (fxo_modes[_opermode].ohs2 << 3);
++ ystdm_setreg(wc, card, 31, reg31);
++
++ /* Set Transmit/Receive timeslot */
++ if (card < NUM_CARDS/4) {
++ ystdm_setreg(wc, card, 34, (3-card) * 8);
++ ystdm_setreg(wc, card, 35, 0x00);
++ ystdm_setreg(wc, card, 36, (3-card) * 8);
++ ystdm_setreg(wc, card, 37, 0x00);
++ } else if (card < NUM_CARDS/2) {
++ ystdm_setreg(wc, card, 34, (15-card) * 8);
++ ystdm_setreg(wc, card, 35, 0x00);
++ ystdm_setreg(wc, card, 36, (15-card) * 8);
++ ystdm_setreg(wc, card, 37, 0x00);
++ } else if (card < (NUM_CARDS*3)/4) {
++ ystdm_setreg(wc, card, 34, (27-card) * 8);
++ ystdm_setreg(wc, card, 35, 0x00);
++ ystdm_setreg(wc, card, 36, (27-card) * 8);
++ ystdm_setreg(wc, card, 37, 0x00);
++ } else {
++ ystdm_setreg(wc, card, 34, (39-card) * 8);
++ ystdm_setreg(wc, card, 35, 0x00);
++ ystdm_setreg(wc, card, 36, (39-card) * 8);
++ ystdm_setreg(wc, card, 37, 0x00);
++ }
++
++ /* Enable ISO-Cap */
++ ystdm_setreg(wc, card, 6, 0x00);
++ if (fastpickup)
++ ystdm_setreg(wc, card, 17, ystdm_getreg(wc, card, 17) | 0x20);
++
++ /* Wait 1000ms for ISO-cap to come up */
++ newjiffies = jiffies;
++ newjiffies += 2 * HZ;
++ while((jiffies < newjiffies) && !(ystdm_getreg(wc, card, 11) & 0xf0))
++ wait_just_a_bit(HZ/10);
++
++ if (!(ystdm_getreg(wc, card, 11) & 0xf0)) {
++ printk("VoiceDAA did not bring up ISO link properly!\n");
++ return -1;
++ }
++ if (debug)
++ printk("ISO-Cap is now up, line side: %02x rev %02x\n",
++ ystdm_getreg(wc, card, 11) >> 4,
++ (ystdm_getreg(wc, card, 13) >> 2) & 0xf);
++ /* Enable on-hook line monitor */
++ ystdm_setreg(wc, card, 5, 0x08);
++ /* Take values for fxotxgain and fxorxgain and apply them to module */
++ if (fxotxgain)
++ ystdm_set_hwgain(wc, card, fxotxgain, 1);
++ else
++ ystdm_set_hwgain(wc, card, 0, 1);
++ if (fxorxgain)
++ ystdm_set_hwgain(wc, card, fxorxgain, 0);
++ else
++ ystdm_set_hwgain(wc, card, 20, 0);
++
++ /* NZ -- crank the tx gain up by 7 dB */
++ if (!strcmp(fxo_modes[_opermode].name, "NEWZEALAND")) {
++ printk("Adjusting gain\n");
++ ystdm_set_hwgain(wc, card, 7, 1);
++
++ }
++ /* KR -- crank the rv gain up by 9 dB */
++ if (!strcmp(fxo_modes[_opermode].name, "SOUTHKOREA")) {
++ printk("Adjusting gain\n");
++ ystdm_setreg(wc, card, 39, 0x9);
++ }
++ if(debug)
++ printk("DEBUG fxotxgain:%i.%i fxorxgain:%i.%i\n", (ystdm_getreg(wc, card, 38)/16)?-(ystdm_getreg(wc, card, 38) - 16) : ystdm_getreg(wc, card, 38), (ystdm_getreg(wc, card, 40)/16)? -(ystdm_getreg(wc, card, 40) - 16):ystdm_getreg(wc, card, 40), (ystdm_getreg(wc, card, 39)/16)? -(ystdm_getreg(wc, card, 39) - 16) : ystdm_getreg(wc, card, 39),(ystdm_getreg(wc, card, 41)/16)?-(ystdm_getreg(wc, card, 41) - 16):ystdm_getreg(wc, card, 41));
++
++ return 0;
++
++}
++
++static int ystdm_init_proslic(struct ystdm *wc, int card, int fast, int manual, int sane)
++{
++
++ unsigned short tmp[5];
++ unsigned char r19,r9;
++ int x;
++ int fxsmode=0;
++ struct fxs *const fxs = &wc->mod[card].fxs;
++
++ /* Sanity check the ProSLIC */
++ if (!sane && ystdm_proslic_insane(wc, card))
++ return -2;
++
++ /* default messages to none and method to FSK */
++ memset(&fxs->vmwisetting, 0, sizeof(fxs->vmwisetting));
++ fxs->vmwi_lrev = 0;
++ fxs->vmwi_hvdc = 0;
++ fxs->vmwi_hvac = 0;
++
++ /* By default, don't send on hook */
++ if (!reversepolarity != !fxs->reversepolarity)
++ fxs->idletxhookstate = SLIC_LF_ACTIVE_REV;
++ else
++ fxs->idletxhookstate = SLIC_LF_ACTIVE_FWD;
++
++ /* Sanity check the ProSLIC */
++
++ if (sane) {
++ /* Make sure we turn off the DC->DC converter to prevent anything from blowing up */
++ ystdm_setreg(wc, card, 14, 0x10);
++ }
++
++ if (ystdm_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 */
++ ystdm_proslic_setreg_indirect(wc, card, 97,0);
++
++ /* Clear digital loopback */
++ ystdm_setreg(wc, card, 8, 0);
++
++ /* Revision C optimization */
++ ystdm_setreg(wc, card, 108, 0xeb);
++
++ /* Disable automatic VBat switching for safety to prevent
++ Q7 from accidently turning on and burning out. */
++ ystdm_setreg(wc, card, 67, 0x07);
++
++ /* Turn off Q7 */
++ ystdm_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] = ystdm_proslic_getreg_indirect(wc, card, x + 35);
++ ystdm_proslic_setreg_indirect(wc, card, x + 35, 0x8000);
++ }
++
++ /* Power up the DC-DC converter */
++ if (ystdm_powerup_proslic(wc, card, fast)) {
++ printk("Unable to do INITIAL ProSLIC powerup on module %d\n", card);
++ return -1;
++ }
++
++ if (!fast) {
++
++ /* Check for power leaks */
++ if (ystdm_proslic_powerleak_test(wc, card)) {
++ printk("ProSLIC module %d failed leakage test. Check for short circuit\n", card);
++ }
++ /* Power up again */
++ if (ystdm_powerup_proslic(wc, card, fast)) {
++ printk("Unable to do FINAL ProSLIC powerup on module %d\n", card);
++ return -1;
++ }
++#ifndef NO_CALIBRATION
++ /* Perform calibration */
++ if(manual) {
++ if (ystdm_proslic_manual_calibrate(wc, card)) {
++ //printk("Proslic failed on Manual Calibration\n");
++ if (ystdm_proslic_manual_calibrate(wc, card)) {
++ printk("Proslic Failed on Second Attempt to Calibrate Manually. (Try -DNO_CALIBRATION in Makefile)\n");
++ return -1;
++ }
++ printk("Proslic Passed Manual Calibration on Second Attempt\n");
++ }
++ }
++ else {
++ if(ystdm_proslic_calibrate(wc, card)) {
++ //printk("ProSlic died on Auto Calibration.\n");
++ if (ystdm_proslic_calibrate(wc, card)) {
++ printk("Proslic Failed on Second Attempt to Auto Calibrate\n");
++ return -1;
++ }
++ printk("Proslic Passed Auto Calibration on Second Attempt\n");
++ }
++ }
++ /* Perform DC-DC calibration */
++ ystdm_setreg(wc, card, 93, 0x99);
++ r19 = ystdm_getreg(wc, card, 107);
++ if ((r19 < 0x2) || (r19 > 0xd)) {
++ printk("DC-DC cal has a surprising direct 107 of 0x%02x!\n", r19);
++ ystdm_setreg(wc, card, 107, 0x8);
++ }
++
++ /* Save calibration vectors */
++ for (x=0;x<NUM_CAL_REGS;x++)
++ fxs->calregs.vals[x] = ystdm_getreg(wc, card, 96 + x);
++#endif
++
++ } else {
++ /* Restore calibration registers */
++ for (x=0;x<NUM_CAL_REGS;x++)
++ ystdm_setreg(wc, card, 96 + x, fxs->calregs.vals[x]);
++ }
++ /* Calibration complete, restore original values */
++ for (x=0;x<5;x++) {
++ ystdm_proslic_setreg_indirect(wc, card, x + 35, tmp[x]);
++ }
++
++ if (ystdm_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" */
++ ystdm_setreg(wc, card, 67, 0x0e);
++ blah = ystdm_getreg(wc, card, 67);
++#endif
++
++#if 0
++ if (ystdm_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)
++ ystdm_setreg(wc, card, 1, 0x20);
++ else
++ ystdm_setreg(wc, card, 1, 0x28);
++ // U-Law 8-bit interface
++ if (card < NUM_CARDS/4) {
++ ystdm_setreg(wc, card, 2, (3-card) * 8); // Tx Start count low byte 0
++ ystdm_setreg(wc, card, 3, 0); // Tx Start count high byte 0
++ ystdm_setreg(wc, card, 4, (3-card) * 8); // Rx Start count low byte 0
++ ystdm_setreg(wc, card, 5, 0); // Rx Start count high byte 0
++ } else if (card < NUM_CARDS/2) {
++ ystdm_setreg(wc, card, 2, (15-card) * 8); // Tx Start count low byte 0
++ ystdm_setreg(wc, card, 3, 0); // Tx Start count high byte 0
++ ystdm_setreg(wc, card, 4, (15-card) * 8); // Rx Start count low byte 0
++ ystdm_setreg(wc, card, 5, 0); // Rx Start count high byte 0
++ } else if (card < (NUM_CARDS*3)/4) {
++ ystdm_setreg(wc, card, 2, (27-card) * 8); // Tx Start count low byte 0
++ ystdm_setreg(wc, card, 3, 0); // Tx Start count high byte 0
++ ystdm_setreg(wc, card, 4, (27-card) * 8); // Rx Start count low byte 0
++ ystdm_setreg(wc, card, 5, 0); // Rx Start count high byte 0
++ } else {
++ ystdm_setreg(wc, card, 2, (39-card) * 8); // Tx Start count low byte 0
++ ystdm_setreg(wc, card, 3, 0); // Tx Start count high byte 0
++ ystdm_setreg(wc, card, 4, (39-card) * 8); // Rx Start count low byte 0
++ ystdm_setreg(wc, card, 5, 0); // Rx Start count high byte 0
++ }
++ ystdm_setreg(wc, card, 18, 0xff); // clear all interrupt
++ ystdm_setreg(wc, card, 19, 0xff);
++ ystdm_setreg(wc, card, 20, 0xff);
++ ystdm_setreg(wc, card, 73, 0x04);
++ if (fxshonormode) {
++ fxsmode = acim2tiss[fxo_modes[_opermode].acim];
++ ystdm_setreg(wc, card, 10, 0x08 | fxsmode);
++ }
++ if (lowpower)
++ ystdm_setreg(wc, card, 72, 0x10);
++
++#if 0
++ ystdm_setreg(wc, card, 21, 0x00); // enable interrupt
++ ystdm_setreg(wc, card, 22, 0x02); // Loop detection interrupt
++ ystdm_setreg(wc, card, 23, 0x01); // DTMF detection interrupt
++#endif
++
++#if 0
++ /* Enable loopback */
++ ystdm_setreg(wc, card, 8, 0x2);
++ ystdm_setreg(wc, card, 14, 0x0);
++ ystdm_setreg(wc, card, 64, 0x0);
++ ystdm_setreg(wc, card, 1, 0x08);
++#endif
++ if (ystdm_init_ring_generator_mode(wc, card)) {
++ return -1;
++ }
++ if(fxstxgain || fxsrxgain) {
++ r9 = ystdm_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;
++ }
++ ystdm_setreg(wc,card,9,r9);
++ }
++
++ if(debug)
++ printk("DEBUG: fxstxgain:%s fxsrxgain:%s\n",((ystdm_getreg(wc, card, 9)/8) == 1)?"3.5":(((ystdm_getreg(wc,card,9)/4) == 1)?"-3.5":"0.0"),((ystdm_getreg(wc, card, 9)/2) == 1)?"3.5":((ystdm_getreg(wc,card,9)%2)?"-3.5":"0.0"));
++
++ fxs->lasttxhook = fxs->idletxhookstate;
++ ystdm_setreg(wc, card, LINE_STATE, fxs->lasttxhook);
++
++ /* Analog Transmit Path Gain = 3.5dB; Analog Receive Path Gain = 3.5dB. */
++ /* ystdm_setreg(wc, card, 9, 0x0a); */
++ return 0;
++}
++
++static int ystdm_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data)
++{
++ struct ystdm_stats stats;
++ struct ystdm_regs regs;
++ struct ystdm_regop regop;
++ struct ystdm_echo_coefs echoregs;
++ struct dahdi_hwgain hwgain;
++ struct ystdm *wc = chan->pvt;
++ struct fxs *const fxs = &wc->mod[chan->chanpos - 1].fxs;
++ 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;
++ fxs->ohttimer = x << 3;
++
++ /* Active mode when idle */
++ fxs->idletxhookstate = POLARITY_XOR ?
++ SLIC_LF_ACTIVE_REV : SLIC_LF_ACTIVE_FWD;
++ if (fxs->neonringing) {
++ /* keep same Forward polarity */
++ fxs->lasttxhook = SLIC_LF_OHTRAN_FWD;
++ printk(KERN_INFO "ioctl: Start OnHookTrans, card %d\n",
++ chan->chanpos - 1);
++ ystdm_setreg(wc, chan->chanpos - 1,
++ LINE_STATE, fxs->lasttxhook);
++ } else if (fxs->lasttxhook == SLIC_LF_ACTIVE_FWD ||
++ fxs->lasttxhook == SLIC_LF_ACTIVE_REV) {
++ /* Apply the change if appropriate */
++ fxs->lasttxhook = POLARITY_XOR ?
++ SLIC_LF_OHTRAN_REV : SLIC_LF_OHTRAN_FWD;
++ printk(KERN_INFO "ioctl: Start OnHookTrans, card %d\n",
++ chan->chanpos - 1);
++ ystdm_setreg(wc, chan->chanpos - 1,
++ LINE_STATE, fxs->lasttxhook);
++ }
++ break;
++ case DAHDI_SETPOLARITY:
++ if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS)
++ return -EINVAL;
++ if (get_user(x, (__user int *) data))
++ return -EFAULT;
++ /* Can't change polarity while ringing or when open */
++ if ((fxs->lasttxhook == SLIC_LF_RINGING) ||
++ (fxs->lasttxhook == SLIC_LF_OPEN))
++ return -EINVAL;
++
++ fxs->reversepolarity = x;
++ if (POLARITY_XOR) {
++ fxs->lasttxhook |= SLIC_LF_REVMASK;
++ printk(KERN_INFO "ioctl: Reverse Polarity, card %d\n",
++ chan->chanpos - 1);
++ }
++ else {
++ fxs->lasttxhook &= ~SLIC_LF_REVMASK;
++ printk(KERN_INFO "ioctl: Normal Polarity, card %d\n",
++ chan->chanpos - 1);
++ }
++
++ ystdm_setreg(wc, chan->chanpos - 1,
++ LINE_STATE, fxs->lasttxhook);
++ break;
++ case DAHDI_VMWI_CONFIG:
++ if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS)
++ return -EINVAL;
++ if (copy_from_user(&(fxs->vmwisetting), (__user void *) data,
++ sizeof(fxs->vmwisetting)))
++ return -EFAULT;
++ set_vmwi(wc, chan->chanpos - 1);
++ break;
++ case DAHDI_VMWI:
++ if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS)
++ return -EINVAL;
++ if (get_user(x, (__user int *) data))
++ return -EFAULT;
++ if (0 > x)
++ return -EFAULT;
++ fxs->vmwi_active_messages = x;
++ set_vmwi(wc, chan->chanpos - 1);
++ break;
++ case WCTDM_GET_STATS:
++ if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) {
++ stats.tipvolt = ystdm_getreg(wc, chan->chanpos - 1, 80) * -376;
++ stats.ringvolt = ystdm_getreg(wc, chan->chanpos - 1, 81) * -376;
++ stats.batvolt = ystdm_getreg(wc, chan->chanpos - 1, 82) * -376;
++ } else if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) {
++ stats.tipvolt = (signed char)ystdm_getreg(wc, chan->chanpos - 1, 29) * 1000;
++ stats.ringvolt = (signed char)ystdm_getreg(wc, chan->chanpos - 1, 29) * 1000;
++ stats.batvolt = (signed char)ystdm_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] = ystdm_proslic_getreg_indirect(wc, chan->chanpos -1, x);
++ for (x=0;x<NUM_REGS;x++)
++ regs.direct[x] = ystdm_getreg(wc, chan->chanpos - 1, x);
++ } else {
++ memset(&regs, 0, sizeof(regs));
++ for (x=0;x<NUM_FXO_REGS;x++)
++ regs.direct[x] = ystdm_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("Setting indirect %d to 0x%04x on %d\n", regop.reg, regop.val, chan->chanpos);
++ ystdm_proslic_setreg_indirect(wc, chan->chanpos - 1, regop.reg, regop.val);
++ } else {
++ regop.val &= 0xff;
++ printk("Setting direct %d to %04x on %d\n", regop.reg, regop.val, chan->chanpos);
++ ystdm_setreg(wc, chan->chanpos - 1, regop.reg, regop.val);
++ }
++ break;
++ case WCTDM_SET_ECHOTUNE:
++ printk("-- 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 */
++ ystdm_setreg(wc, chan->chanpos - 1, 30, echoregs.acim);
++
++ /* Set the digital echo canceller registers */
++ ystdm_setreg(wc, chan->chanpos - 1, 45, echoregs.coef1);
++ ystdm_setreg(wc, chan->chanpos - 1, 46, echoregs.coef2);
++ ystdm_setreg(wc, chan->chanpos - 1, 47, echoregs.coef3);
++ ystdm_setreg(wc, chan->chanpos - 1, 48, echoregs.coef4);
++ ystdm_setreg(wc, chan->chanpos - 1, 49, echoregs.coef5);
++ ystdm_setreg(wc, chan->chanpos - 1, 50, echoregs.coef6);
++ ystdm_setreg(wc, chan->chanpos - 1, 51, echoregs.coef7);
++ ystdm_setreg(wc, chan->chanpos - 1, 52, echoregs.coef8);
++
++ printk("-- 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;
++
++ ystdm_set_hwgain(wc, chan->chanpos-1, hwgain.newgain, hwgain.tx);
++
++ if (debug)
++ printk("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 ystdm_open(struct dahdi_chan *chan)
++{
++ struct ystdm *wc = chan->pvt;
++ if (!(wc->cardflag & (1 << (chan->chanpos - 1))))
++ return -ENODEV;
++ if (wc->dead)
++ return -ENODEV;
++ wc->usecount++;
++ return 0;
++}
++
++static inline struct ystdm *ystdm_from_span(struct dahdi_span *span)
++{
++ return container_of(span, struct ystdm, span);
++}
++
++static int ystdm_watchdog(struct dahdi_span *span, int event)
++{
++ printk("TDM: Restarting DMA\n");
++ ystdm_restart_dma(ystdm_from_span(span));
++ return 0;
++}
++
++static int ystdm_close(struct dahdi_chan *chan)
++{
++ struct ystdm *wc = chan->pvt;
++ struct fxs *const fxs = &wc->mod[chan->chanpos - 1].fxs;
++ wc->usecount--;
++ if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) {
++ int idlehookstate;
++ idlehookstate = POLARITY_XOR ?
++ SLIC_LF_ACTIVE_REV :
++ SLIC_LF_ACTIVE_FWD;
++ fxs->idletxhookstate = idlehookstate;
++ }
++ /* If we're dead, release us now */
++ if (!wc->usecount && wc->dead)
++ ystdm_release(wc);
++ return 0;
++}
++
++static int ystdm_init_ring_generator_mode(struct ystdm *wc, int card)
++{
++ ystdm_setreg(wc, card, 34, 0x00); /* Ringing Osc. Control */
++
++ /* neon trapezoid timers */
++ ystdm_setreg(wc, card, 48, 0xe0); /* Active Timer low byte */
++ ystdm_setreg(wc, card, 49, 0x01); /* Active Timer high byte */
++ ystdm_setreg(wc, card, 50, 0xF0); /* Inactive Timer low byte */
++ ystdm_setreg(wc, card, 51, 0x05); /* Inactive Timer high byte */
++
++ ystdm_set_ring_generator_mode(wc, card, 0);
++
++ return 0;
++}
++
++static int ystdm_set_ring_generator_mode(struct ystdm *wc, int card, int mode)
++{
++ int reg20, reg21, reg74; /* RCO, RNGX, VBATH */
++ struct fxs *const fxs = &wc->mod[card].fxs;
++
++ fxs->neonringing = mode; /* track ring generator mode */
++
++ if (mode) { /* Neon */
++ if (debug)
++ printk(KERN_DEBUG "NEON ring on chan %d, "
++ "lasttxhook was 0x%x\n", card, fxs->lasttxhook);
++ /* Must be in FORWARD ACTIVE before setting ringer */
++ fxs->lasttxhook = SLIC_LF_ACTIVE_FWD;
++ ystdm_setreg(wc, card, LINE_STATE, fxs->lasttxhook);
++
++ ystdm_proslic_setreg_indirect(wc, card, 22,
++ NEON_MWI_RNGY_PULSEWIDTH);
++ ystdm_proslic_setreg_indirect(wc, card, 21,
++ 0x7bef); /* RNGX (91.5Vpk) */
++ ystdm_proslic_setreg_indirect(wc, card, 20,
++ 0x009f); /* RCO (RNGX, t rise)*/
++
++ ystdm_setreg(wc, card, 34, 0x19); /* Ringing Osc. Control */
++ ystdm_setreg(wc, card, 74, 0x3f); /* VBATH 94.5V */
++ ystdm_proslic_setreg_indirect(wc, card, 29, 0x4600); /* RPTP */
++ /* A write of 0x04 to register 64 will turn on the VM led */
++ } else {
++ ystdm_setreg(wc, card, 34, 0x00); /* Ringing Osc. Control */
++ /* RNGY Initial Phase */
++ ystdm_proslic_setreg_indirect(wc, card, 22, 0x0000);
++ ystdm_proslic_setreg_indirect(wc, card, 29, 0x3600); /* RPTP */
++ /* A write of 0x04 to register 64 will turn on the ringer */
++
++ if (fastringer) {
++ /* Speed up Ringer */
++ reg20 = 0x7e6d;
++ reg74 = 0x32; /* Default */
++ /* Beef up Ringing voltage to 89V */
++ if (boostringer) {
++ reg74 = 0x3f;
++ reg21 = 0x0247; /* RNGX */
++ if (debug)
++ printk(KERN_DEBUG "Boosting fast ringer"
++ " on chan %d (89V peak)\n",
++ card);
++ } else if (lowpower) {
++ reg21 = 0x014b; /* RNGX */
++ if (debug)
++ printk(KERN_DEBUG "Reducing fast ring "
++ "power on chan %d (50V peak)\n",
++ card);
++ } else if (fxshonormode &&
++ fxo_modes[_opermode].ring_x) {
++ reg21 = fxo_modes[_opermode].ring_x;
++ if (debug)
++ printk(KERN_DEBUG "fxshonormode: fast "
++ "ring_x power on chan %d\n",
++ card);
++ } else {
++ reg21 = 0x01b9;
++ if (debug)
++ printk(KERN_DEBUG "Speeding up ringer "
++ "on chan %d (25Hz)\n",
++ card);
++ }
++ /* VBATH */
++ ystdm_setreg(wc, card, 74, reg74);
++ /*RCO*/
++ ystdm_proslic_setreg_indirect(wc, card, 20, reg20);
++ /*RNGX*/
++ ystdm_proslic_setreg_indirect(wc, card, 21, reg21);
++
++ } else {
++ /* Ringer Speed */
++ if (fxshonormode && fxo_modes[_opermode].ring_osc) {
++ reg20 = fxo_modes[_opermode].ring_osc;
++ if (debug)
++ printk(KERN_DEBUG "fxshonormode: "
++ "ring_osc speed on chan %d\n",
++ card);
++ } else {
++ reg20 = 0x7ef0; /* Default */
++ }
++
++ reg74 = 0x32; /* Default */
++ /* Beef up Ringing voltage to 89V */
++ if (boostringer) {
++ reg74 = 0x3f;
++ reg21 = 0x1d1;
++ if (debug)
++ printk(KERN_DEBUG "Boosting ringer on "
++ "chan %d (89V peak)\n",
++ card);
++ } else if (lowpower) {
++ reg21 = 0x108;
++ if (debug)
++ printk(KERN_DEBUG "Reducing ring power "
++ "on chan %d (50V peak)\n",
++ card);
++ } else if (fxshonormode &&
++ fxo_modes[_opermode].ring_x) {
++ reg21 = fxo_modes[_opermode].ring_x;
++ if (debug)
++ printk(KERN_DEBUG "fxshonormode: ring_x"
++ " power on chan %d\n",
++ card);
++ } else {
++ reg21 = 0x160;
++ if (debug)
++ printk(KERN_DEBUG "Normal ring power on"
++ " chan %d\n",
++ card);
++ }
++ /* VBATH */
++ ystdm_setreg(wc, card, 74, reg74);
++ /* RCO */
++ ystdm_proslic_setreg_indirect(wc, card, 20, reg20);
++ /* RNGX */
++ ystdm_proslic_setreg_indirect(wc, card, 21, reg21);
++ }
++ }
++ return 0;
++}
++
++static int ystdm_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig)
++{
++ struct ystdm *wc = chan->pvt;
++ int chan_entry = chan->chanpos - 1;
++ if (wc->modtype[chan_entry] == MOD_TYPE_FXO) {
++ /* XXX Enable hooksig for FXO XXX */
++ switch(txsig) {
++ case DAHDI_TXSIG_START:
++ case DAHDI_TXSIG_OFFHOOK:
++ wc->mod[chan_entry].fxo.offhook = 1;
++ ystdm_setreg(wc, chan_entry, 5, 0x9);
++ break;
++ case DAHDI_TXSIG_ONHOOK:
++ wc->mod[chan_entry].fxo.offhook = 0;
++ ystdm_setreg(wc, chan_entry, 5, 0x8);
++ break;
++ default:
++ printk("wcfxo: Can't set tx state to %d\n", txsig);
++ }
++ } else {
++ ystdm_fxs_hooksig(wc, chan_entry, txsig);
++ }
++ return 0;
++}
++
++static const struct dahdi_span_ops ystdm_span_ops = {
++ .owner = THIS_MODULE,
++ .hooksig = ystdm_hooksig,
++ .open = ystdm_open,
++ .close = ystdm_close,
++ .ioctl = ystdm_ioctl,
++ .watchdog = ystdm_watchdog,
++};
++
++static int ystdm_initialize(struct ystdm *wc)
++{
++ int x;
++
++ wc->ddev = dahdi_create_device();
++ if (!wc->ddev)
++ return -ENOMEM;
++
++ /* Zapata stuff */
++ sprintf(wc->span.name, "WCTDM/%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 = "YEASTAR";
++ wc->ddev->devicetype = wc->variety;
++
++ if (alawoverride) {
++ printk("ALAW override parameter detected. Device will be operating in ALAW\n");
++ wc->span.deflaw = DAHDI_LAW_ALAW;
++ } else {
++ wc->span.deflaw = DAHDI_LAW_MULAW;
++ }
++ for (x = 0; x < NUM_CARDS; x++) {
++ sprintf(wc->chans[x]->name, "WCTDM/%d/%d", wc->pos, x);
++ wc->chans[x]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR;
++ wc->chans[x]->sigcap |= DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR;
++ wc->chans[x]->chanpos = x+1;
++ wc->chans[x]->pvt = wc;
++ }
++
++ wc->span.chans = wc->chans;
++ wc->span.channels = NUM_CARDS;
++ wc->span.flags = DAHDI_FLAG_RBS;
++ wc->span.ops = &ystdm_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 span with DAHDI\n");
++ kfree(wc->ddev->location);
++ dahdi_free_device(wc->ddev);
++ wc->ddev = NULL;
++ return -1;
++ }
++ return 0;
++}
++
++static void ystdm_post_initialize(struct ystdm *wc)
++{
++ int x;
++ /* Finalize signalling */
++ for (x = 0; x < NUM_CARDS; x++) {
++ if (wc->cardflag & (1 << x)) {
++ if (wc->modtype[x] == MOD_TYPE_FXO)
++ wc->chans[x]->sigcap = 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 ystdm_hardware_init(struct ystdm *wc)
++{
++ /* Hardware stuff */
++ unsigned char ver;
++ unsigned char x,y;
++ unsigned char ol = 0, sl = 0;
++ unsigned char ol2 = 0, sl2 = 0;
++ int failed;
++
++ /* Signal Reset */
++ outb(0x01, wc->ioaddr + WC_CNTL);
++
++ /* Check Freshmaker chip */
++ x=inb(wc->ioaddr + WC_CNTL);
++ ver = __ystdm_getcreg(wc, WC_VER);
++ failed = 0;
++ if (ver != 0x59) {
++ printk("Freshmaker version: %02x\n", ver);
++ for (x=0;x<255;x++) {
++ /* Test registers */
++ if (ver >= 0x70) {
++ __ystdm_setcreg(wc, WC_CS, x);
++ y = __ystdm_getcreg(wc, WC_CS);
++ } else {
++ __ystdm_setcreg(wc, WC_TEST, x);
++ y = __ystdm_getcreg(wc, WC_TEST);
++ }
++ if (x != y) {
++ printk("%02x != %02x\n", x, y);
++ failed++;
++ }
++ }
++ if (!failed) {
++ printk("Freshmaker passed register test\n");
++ } else {
++ printk("Freshmaker failed register test\n");
++ return -1;
++ }
++ } else {
++ printk("No freshmaker chip\n");
++ }
++
++ /* 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 | BIT_SYNC;
++
++ outb(wc->ios, wc->ioaddr + WC_AUXD);
++
++ /* Set all to outputs except AUX 5, which is an input */
++ outb(0xdf, wc->ioaddr + WC_AUXC);
++
++ /* 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);
++
++ /* 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(0x0, wc->ioaddr + WC_FSCDELAY);
++
++ /* Setup DMA Addresses */
++ outl(wc->writedma, wc->ioaddr + WC_DMAWS); /* Write start */
++ outl(wc->writedma + DAHDI_CHUNKSIZE * NUM_CARDS - 4, wc->ioaddr + WC_DMAWI); /* Middle (interrupt) */
++ outl(wc->writedma + 2 * DAHDI_CHUNKSIZE * NUM_CARDS - 4, wc->ioaddr + WC_DMAWE); /* End */
++
++ outl(wc->readdma, wc->ioaddr + WC_DMARS); /* Read start */
++ outl(wc->readdma + DAHDI_CHUNKSIZE * NUM_CARDS - 4, wc->ioaddr + WC_DMARI); /* Middle (interrupt) */
++ outl(wc->readdma + 2 * DAHDI_CHUNKSIZE * NUM_CARDS - 4, wc->ioaddr + WC_DMARE); /* End */
++
++ /* Clear interrupts */
++ outb(0xff, wc->ioaddr + WC_INTSTAT);
++
++ /* Wait 1/4 of a second more */
++ wait_just_a_bit(HZ/4);
++
++ for (x = 0; x < NUM_CARDS; x++) {
++ int sane=0,ret=0,readi=0;
++#if 1
++ /* Init with Auto Calibration */
++ if (!(ret=ystdm_init_proslic(wc, x, 0, 0, sane))) {
++ wc->cardflag |= (1 << x);
++ if(x < 8)
++ sl |= (1 << x);
++ else
++ sl2 |= (1 << (x - 8));
++ if (debug) {
++ readi = ystdm_getreg(wc,x,LOOP_I_LIMIT);
++ printk("Proslic module %d loop current is %dmA\n",x,((readi*3)+20));
++ }
++ printk("Module %d: Installed -- AUTO FXS/DPO\n",x);
++ } else {
++ if(ret!=-2) {
++ sane=1;
++ /* Init with Manual Calibration */
++ if (!ystdm_init_proslic(wc, x, 0, 1, sane)) {
++ wc->cardflag |= (1 << x);
++ if(x < 8)
++ sl |= (1 << x);
++ else
++ sl2 |= (1 << (x - 8));
++ if (debug) {
++ readi = ystdm_getreg(wc,x,LOOP_I_LIMIT);
++ printk("Proslic module %d loop current is %dmA\n",x,((readi*3)+20));
++ }
++ printk("Module %d: Installed -- MANUAL FXS\n",x);
++ } else {
++ printk("Module %d: FAILED FXS (%s)\n", x, fxshonormode ? fxo_modes[_opermode].name : "FCC");
++ wc->chans[x]->sigcap = __DAHDI_SIG_FXO | DAHDI_SIG_BROKEN;
++ }
++ } else if (!(ret = ystdm_init_voicedaa(wc, x, 0, 0, sane))) {
++ wc->cardflag |= (1 << x);
++ if(x < 8)
++ ol |= (1 << x);
++ else
++ ol2 |= (1 << (x - 8));
++ printk("Module %d: Installed -- AUTO FXO (%s mode)\n",x, fxo_modes[_opermode].name);
++ } else
++ printk("Module %d: Not installed\n", x);
++ }
++#endif
++ }
++
++ /* Return error if nothing initialized okay. */
++ if (!wc->cardflag && !timingonly)
++ return -1;
++ if(ver == 0x88)
++ __ystdm_setcreg(wc, WC_SYNC, wc->cardflag);
++ else{
++ __ystdm_setcreg(wc, WC_SYNC, sl);
++ __ystdm_setcreg(wc, YS_SLC, ol);
++ __ystdm_setcreg(wc, YS_DCH, sl2);
++ __ystdm_setcreg(wc, YS_E0H, ol2);
++ }
++ return 0;
++}
++
++static void ystdm_enable_interrupts(struct ystdm *wc)
++{
++ /* Enable interrupts (we care about all of them) */
++ outb(0x3f, wc->ioaddr + WC_MASK0);
++ /* No external interrupts */
++ outb(0x00, wc->ioaddr + WC_MASK1);
++}
++
++static void ystdm_restart_dma(struct ystdm *wc)
++{
++ /* Reset Master and TDM */
++ outb(0x01, wc->ioaddr + WC_CNTL);
++ outb(0x01, wc->ioaddr + WC_OPER);
++}
++
++static void ystdm_start_dma(struct ystdm *wc)
++{
++ /* Reset Master and TDM */
++ unsigned char x,y;
++ outb(0x0f, wc->ioaddr + WC_CNTL);
++ wc->ios &= ~BIT_SYNC;
++ outb(wc->ios, wc->ioaddr + WC_AUXD);
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(1);
++ wc->ios |= BIT_SYNC;
++ outb(wc->ios, wc->ioaddr + WC_AUXD);
++ outb(0x01, wc->ioaddr + WC_CNTL);
++ outb(0x01, wc->ioaddr + WC_OPER);
++ y = __ystdm_getcreg(wc, WC_TEST);
++ x = y | 0x01;
++ __ystdm_setcreg(wc, WC_TEST, x);
++}
++
++static void ystdm_stop_dma(struct ystdm *wc)
++{
++ unsigned char x,y;
++ wc->ios &= ~BIT_SYNC;
++ outb(wc->ios, wc->ioaddr + WC_AUXD);
++ outb(0x00, wc->ioaddr + WC_OPER);
++ y = __ystdm_getcreg(wc, WC_TEST);
++ x = y & 0xFE;
++ __ystdm_setcreg(wc, WC_TEST, x);
++}
++
++static void ystdm_reset_tdm(struct ystdm *wc)
++{
++ /* Reset TDM */
++ outb(0x0f, wc->ioaddr + WC_CNTL);
++}
++
++static void ystdm_disable_interrupts(struct ystdm *wc)
++{
++ outb(0x00, wc->ioaddr + WC_MASK0);
++ outb(0x00, wc->ioaddr + WC_MASK1);
++}
++
++static int __devinit ystdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
++{
++ int res;
++ struct ystdm *wc;
++ struct ystdm_desc *d = (struct ystdm_desc *)ent->driver_data;
++ int x;
++ int y;
++
++
++
++ for (x=0;x<WC_MAX_IFACES;x++)
++ if (!ifaces[x]) break;
++ if (x >= WC_MAX_IFACES) {
++ printk("Too many interfaces\n");
++ return -EIO;
++ }
++
++ if (pci_enable_device(pdev)) {
++ res = -EIO;
++ } else {
++ wc = kmalloc(sizeof(struct ystdm), GFP_KERNEL);
++ if (wc) {
++ int cardcount = 0;
++
++ ifaces[x] = wc;
++ memset(wc, 0, sizeof(struct ystdm));
++ 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->dev = pdev;
++ wc->pos = x;
++ wc->variety = d->name;
++ for (y=0;y<NUM_CARDS;y++)
++ wc->flags[y] = d->flags;
++ /* Keep track of whether we need to free the region */
++ if (request_region(wc->ioaddr, 0xff, "ystdm"))
++ wc->freeregion = 1;
++
++ /* Allocate enough memory for two zt chunks, receive and transmit. Each sample uses
++ 32 bits. Allocate an extra set just for control too */
++ wc->writechunk = pci_alloc_consistent(pdev, DAHDI_MAX_CHUNKSIZE * 2 * 2 * 2 * NUM_CARDS, &wc->writedma);
++ if (!wc->writechunk) {
++ printk("ystdm: Unable to allocate DMA-able memory\n");
++ if (wc->freeregion)
++ release_region(wc->ioaddr, 0xff);
++ return -ENOMEM;
++ }
++
++ wc->readchunk = wc->writechunk + 2 * DAHDI_MAX_CHUNKSIZE * (NUM_CARDS / 4); /* in doublewords */
++ wc->readdma = wc->writedma + 2 * DAHDI_MAX_CHUNKSIZE * (NUM_CARDS / 1); /* in bytes */
++
++ if (ystdm_initialize(wc)) {
++ printk("ystdm: 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)
++ release_region(wc->ioaddr, 0xff);
++ pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * 2 * 2 * 2 * NUM_CARDS, (void *)wc->writechunk, wc->writedma);
++ kfree(wc);
++ return -EIO;
++ }
++
++ /* Enable bus mastering */
++ pci_set_master(pdev);
++
++ /* Keep track of which device we are */
++ pci_set_drvdata(pdev, wc);
++
++ if (request_irq(pdev->irq, ystdm_interrupt, DAHDI_IRQ_SHARED, "ystdm", wc)) {
++ printk("ystdm: Unable to request IRQ %d\n", pdev->irq);
++ if (wc->freeregion)
++ release_region(wc->ioaddr, 0xff);
++ pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * 2 * 2 * 2 * NUM_CARDS, (void *)wc->writechunk, wc->writedma);
++ pci_set_drvdata(pdev, NULL);
++ kfree(wc);
++ return -EIO;
++ }
++
++
++ if (ystdm_hardware_init(wc)) {
++ unsigned char x;
++
++ /* Set Reset Low */
++ x=inb(wc->ioaddr + WC_CNTL);
++ outb((~0x1)&x, wc->ioaddr + WC_CNTL);
++ /* Free Resources */
++ free_irq(pdev->irq, wc);
++ if (wc->freeregion)
++ release_region(wc->ioaddr, 0xff);
++ pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * 2 * 2 * 2 * NUM_CARDS, (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;
++
++ }
++
++ ystdm_post_initialize(wc);
++
++ /* Enable interrupts */
++ ystdm_enable_interrupts(wc);
++ /* Initialize Write/Buffers to all blank data */
++ memset((void *)wc->writechunk,0,DAHDI_MAX_CHUNKSIZE * 2 * 2 * NUM_CARDS);
++
++ /* Start DMA */
++ ystdm_start_dma(wc);
++
++ for (x = 0; x < NUM_CARDS; x++) {
++ if (wc->cardflag & (1 << x))
++ cardcount++;
++ }
++
++ printk("Found a YSTDM16xx: %s (%d modules)\n", wc->variety, cardcount);
++ res = 0;
++ } else
++ res = -ENOMEM;
++ }
++ return res;
++}
++
++static void ystdm_release(struct ystdm *wc)
++{
++ dahdi_unregister_device(wc->ddev);
++ if (wc->freeregion)
++ release_region(wc->ioaddr, 0xff);
++ kfree(wc->ddev->location);
++ dahdi_free_device(wc->ddev);
++ kfree(wc);
++ printk("Freed a Wildcard\n");
++}
++
++static void __devexit ystdm_remove_one(struct pci_dev *pdev)
++{
++ struct ystdm *wc = pci_get_drvdata(pdev);
++ if (wc) {
++
++ /* Stop any DMA */
++ ystdm_stop_dma(wc);
++ ystdm_reset_tdm(wc);
++
++ /* In case hardware is still there */
++ ystdm_disable_interrupts(wc);
++
++ /* Immediately free resources */
++ pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * 2 * 2 * 2 * NUM_CARDS, (void *)wc->writechunk, wc->writedma);
++ free_irq(pdev->irq, wc);
++
++ /* Reset PCI chip and registers */
++ outb(0x0e, wc->ioaddr + WC_CNTL);
++
++ /* Release span, possibly delayed */
++ if (!wc->usecount)
++ ystdm_release(wc);
++ else
++ wc->dead = 1;
++ }
++}
++
++static DEFINE_PCI_DEVICE_TABLE(ystdm_pci_tbl) = {
++ { 0xe159, 0x0001, 0x6151, PCI_ANY_ID, 0, 0, (unsigned long) &ystdme },
++ { 0 }
++};
++
++MODULE_DEVICE_TABLE(pci, ystdm_pci_tbl);
++
++static int ystdm_suspend(struct pci_dev *pdev, pm_message_t state)
++{
++ return -ENOSYS;
++}
++
++static struct pci_driver ystdm_driver = {
++ .name = "ystdm16xx",
++ .probe = ystdm_init_one,
++ .remove = __devexit_p(ystdm_remove_one),
++ .suspend = ystdm_suspend,
++ .id_table = ystdm_pci_tbl,
++};
++
++static int __init ystdm_init(void)
++{
++ int res;
++ int x;
++
++ for (x=0;x<(sizeof(fxo_modes) / sizeof(fxo_modes[0])); x++) {
++ if (!strcmp(fxo_modes[x].name, opermode))
++ break;
++ }
++ if (x < sizeof(fxo_modes) / sizeof(fxo_modes[0])) {
++ _opermode = x;
++ } else {
++ printk("Invalid/unknown operating mode '%s' specified. Please choose one of:\n", opermode);
++ for (x = 0; x < sizeof(fxo_modes) / sizeof(fxo_modes[0]); x++)
++ printk(" %s\n", fxo_modes[x].name);
++ printk("Note this option is CASE SENSITIVE!\n");
++ return -ENODEV;
++ }
++ if (!strcmp(opermode, "AUSTRALIA")) {
++ boostringer = 1;
++ fxshonormode = 1;
++ }
++
++ /* for the voicedaa_check_hook defaults, if the user has not overridden
++ them by specifying them as module parameters, then get the values
++ from the selected operating mode
++ */
++ 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(&ystdm_driver);
++ if (res)
++ return -ENODEV;
++ return 0;
++}
++
++static void __exit ystdm_cleanup(void)
++{
++ pci_unregister_driver(&ystdm_driver);
++}
++
++module_param(debug, int, 0600);
++module_param(fxovoltage, 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(battalarm, uint, 0600);
++module_param(battthresh, uint, 0600);
++module_param(ringdebounce, int, 0600);
++module_param(dialdebounce, int, 0600);
++module_param(fwringdetect, int, 0600);
++module_param(alawoverride, int, 0600);
++module_param(fastpickup, int, 0600);
++module_param(fxotxgain, int, 0600);
++module_param(fxorxgain, int, 0600);
++module_param(fxstxgain, int, 0600);
++module_param(fxsrxgain, int, 0600);
++module_param(dtmf, int, 0600);
++
++MODULE_DESCRIPTION("YSTDM16xx Yeastar Driver");
++MODULE_AUTHOR("yeastar <support@yeastar.com>");
++MODULE_ALIAS("ystdm16xx");
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("GPL v2");
++#endif
++
++module_init(ystdm_init);
++module_exit(ystdm_cleanup);
+diff -Nur dahdi-linux-2.10.0.1/drivers/dahdi/ystdm8xx.c dahdi-linux-2.10.0.1-yeastar/drivers/dahdi/ystdm8xx.c
+--- dahdi-linux-2.10.0.1/drivers/dahdi/ystdm8xx.c 1970-01-01 01:00:00.000000000 +0100
++++ dahdi-linux-2.10.0.1-yeastar/drivers/dahdi/ystdm8xx.c 2015-02-10 15:33:19.363713850 +0100
+@@ -0,0 +1,3065 @@
++/*
++ * Yeastar YSTDM8xx TDM FXS/FXO Interface Driver for Zapata Telephony interface
++ *
++ * Derived from wctdm.c written by Mark Spencer <markster@linux-support.net>
++ * Matthew Fredrickson <creslin@linux-support.net>
++ *
++ * Copyright (C) 2006, Yeastar Technology Co.,Ltd. <support@yeastar.com>
++ * Copyright (C) 2001, Linux Support Services, Inc.
++ *
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * 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/init.h>
++
++#include <linux/pci.h>
++#include <linux/interrupt.h>
++#include <linux/moduleparam.h>
++#include <linux/sched.h>
++#include <linux/ioctl.h>
++#include <asm/io.h>
++#include "proslic.h"
++/*
++ * 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;
++#define POLARITY_XOR (\
++ (reversepolarity != 0) ^ (fxs->reversepolarity != 0) ^\
++ (fxs->vmwi_lrev != 0) ^\
++ ((fxs->vmwisetting.vmwi_type & DAHDI_VMWI_HVAC) != 0))
++
++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",0x4000}, // playback volume set lower
++{27,14,"XMIT_DIGITAL_GAIN",0x3000},
++//{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 "fxo_modes.h"
++
++#define NUM_FXO_REGS 60
++
++#define WC_MAX_IFACES 128
++
++#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_SYNC 0x0
++#define WC_TEST 0x1
++#define WC_CS 0x2
++#define WC_VER 0x3
++#define YS_SLC 0x4
++
++#define BIT_SYNC (1 << 0)
++#define BIT_CS (1 << 2)
++#define BIT_SCLK (1 << 3)
++#define BIT_SDI (1 << 4)
++#define BIT_SDO (1 << 5)
++
++#define FLAG_EMPTY 0
++#define FLAG_WRITE 1
++#define FLAG_READ 2
++
++/* the constants below control the 'debounce' periods enforced by the
++ check_hook routines; these routines are called once every 4 interrupts
++ (the interrupt cycles around the four modules), so the periods are
++ specified in _4 millisecond_ increments
++*/
++#define 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 */
++
++/* NEON MWI pulse width - Make larger for longer period time
++ * For more information on NEON MWI generation using the proslic
++ * refer to Silicon Labs App Note "AN33-SI321X NEON FLASHING"
++ * RNGY = RNGY 1/2 * Period * 8000
++ */
++#define NEON_MWI_RNGY_PULSEWIDTH 0x3e8 /*=> period of 250 mS */
++
++#define FLAG_3215 (1 << 0)
++
++#define NUM_CARDS 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,
++};
++
++#define NUM_REGS 109
++#define NUM_INDIRECT_REGS 105
++
++struct ystdm_stats {
++ int tipvolt; /* TIP voltage (mV) */
++ int ringvolt; /* RING voltage (mV) */
++ int batvolt; /* VBAT voltage (mV) */
++};
++
++struct ystdm_regs {
++ unsigned char direct[NUM_REGS];
++ unsigned short indirect[NUM_INDIRECT_REGS];
++};
++
++struct ystdm_regop {
++ int indirect;
++ unsigned char reg;
++ unsigned short val;
++};
++
++struct ystdm_echo_coefs {
++ unsigned char acim;
++ unsigned char coef1;
++ unsigned char coef2;
++ unsigned char coef3;
++ unsigned char coef4;
++ unsigned char coef5;
++ unsigned char coef6;
++ unsigned char coef7;
++ unsigned char coef8;
++};
++
++#define WCTDM_GET_STATS _IOR (DAHDI_CODE, 60, struct ystdm_stats)
++#define WCTDM_GET_REGS _IOR (DAHDI_CODE, 61, struct ystdm_regs)
++#define WCTDM_SET_REG _IOW (DAHDI_CODE, 62, struct ystdm_regop)
++#define WCTDM_SET_ECHOTUNE _IOW (DAHDI_CODE, 63, struct ystdm_echo_coefs)
++
++struct ystdm {
++ 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[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;
++ int readcid;
++ unsigned int cidtimer;
++ } fxo;
++ struct fxs {
++ int oldrxhook;
++ int debouncehook;
++ int lastrxhook;
++ int debounce;
++ int ohttimer;
++ int idletxhookstate; /* IDLE changing hook state */
++ int lasttxhook;
++ int palarms;
++ int reversepolarity; /* Reverse Line */
++ int mwisendtype;
++ struct dahdi_vmwi_info vmwisetting;
++ int vmwi_active_messages;
++ u32 vmwi_lrev:1; /* MWI Line Reversal*/
++ u32 vmwi_hvdc:1; /* MWI High Voltage DC Idle line */
++ u32 vmwi_hvac:1; /* MWI Neon High Voltage AC Idle line */
++ u32 neonringing:1; /* Ring Generator is set for NEON */
++ struct calregs calregs;
++ } fxs;
++ } mod[NUM_CARDS];
++
++ /* Receive hook state and debouncing */
++ int modtype[NUM_CARDS];
++ unsigned char reg0shadow[NUM_CARDS];
++ unsigned char reg1shadow[NUM_CARDS];
++
++ unsigned long ioaddr;
++ 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[NUM_CARDS];
++ struct dahdi_chan *chans[NUM_CARDS];
++};
++
++
++struct ystdm_desc {
++ char *name;
++ int flags;
++};
++
++static struct ystdm_desc ystdme = { "YSTDM8xx REV E", 0 };
++static int acim2tiss[16] = { 0x0, 0x1, 0x4, 0x5, 0x7, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x2, 0x0, 0x3 };
++
++static struct ystdm *ifaces[WC_MAX_IFACES];
++
++static void ystdm_release(struct ystdm *wc);
++
++static unsigned int fxovoltage;
++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 dtmf = 0;
++static int fastpickup = 0;
++static int fxotxgain = 0;
++static int fxorxgain = 0;
++static int fxstxgain = 0;
++static int fxsrxgain = 0;
++
++static int ystdm_init_proslic(struct ystdm *wc, int card, int fast , int manual, int sane);
++static int ystdm_init_ring_generator_mode(struct ystdm *wc, int card);
++static int ystdm_set_ring_generator_mode(struct ystdm *wc, int card, int mode);
++
++static inline void ystdm_transmitprep(struct ystdm *wc, unsigned char ints)
++{
++ volatile unsigned int *writechunk;
++ int x;
++ if (ints & 0x01)
++ /* Write is at interrupt address. Start writing from normal offset */
++ writechunk = wc->writechunk;
++ else
++ writechunk = wc->writechunk + DAHDI_CHUNKSIZE * (NUM_CARDS / 4);
++ /* Calculate Transmission */
++ dahdi_transmit(&wc->span);
++
++ for (x=0;x<DAHDI_CHUNKSIZE;x++) {
++ /* Send a sample, as a 32-bit word */
++ writechunk[2 * x] = 0;
++ writechunk[2 * x + 1] = 0;
++#ifdef __BIG_ENDIAN
++ if (wc->cardflag & (1 << 7))
++ writechunk[2 * x] |= (wc->chans[7]->writechunk[x]);
++ if (wc->cardflag & (1 << 6))
++ writechunk[2 * x] |= (wc->chans[6]->writechunk[x] << 8);
++ if (wc->cardflag & (1 << 5))
++ writechunk[2 * x] |= (wc->chans[5]->writechunk[x] << 16);
++ if (wc->cardflag & (1 << 4))
++ writechunk[2 * x] |= (wc->chans[4]->writechunk[x] << 24);
++
++ if (wc->cardflag & (1 << 3))
++ writechunk[2 * x + 1] |= (wc->chans[3]->writechunk[x]);
++ if (wc->cardflag & (1 << 2))
++ writechunk[2 * x + 1] |= (wc->chans[2]->writechunk[x] << 8);
++ if (wc->cardflag & (1 << 1))
++ writechunk[2 * x + 1] |= (wc->chans[1]->writechunk[x] << 16);
++ if (wc->cardflag & (1 << 0))
++ writechunk[2 * x + 1] |= (wc->chans[0]->writechunk[x] << 24);
++#else
++ if (wc->cardflag & (1 << 7))
++ writechunk[2 * x] |= (wc->chans[7]->writechunk[x] << 24);
++ if (wc->cardflag & (1 << 6))
++ writechunk[2 * x] |= (wc->chans[6]->writechunk[x] << 16);
++ if (wc->cardflag & (1 << 5))
++ writechunk[2 * x] |= (wc->chans[5]->writechunk[x] << 8);
++ if (wc->cardflag & (1 << 4))
++ writechunk[2 * x] |= (wc->chans[4]->writechunk[x]);
++
++ if (wc->cardflag & (1 << 3))
++ writechunk[2 * x + 1] |= (wc->chans[3]->writechunk[x] << 24);
++ if (wc->cardflag & (1 << 2))
++ writechunk[2 * x + 1] |= (wc->chans[2]->writechunk[x] << 16);
++ if (wc->cardflag & (1 << 1))
++ writechunk[2 * x + 1] |= (wc->chans[1]->writechunk[x] << 8);
++ if (wc->cardflag & (1 << 0))
++ writechunk[2 * x + 1] |= (wc->chans[0]->writechunk[x]);
++#endif
++ }
++
++}
++
++#ifdef AUDIO_RINGCHECK
++static inline void ring_check(struct ystdm *wc, int card)
++{
++ int x;
++ short sample;
++ if (wc->modtype[card] != MOD_TYPE_FXO)
++ return;< if (fxovoltage) {
++< static int count = 0;
++< if (!(count++ % 100)) {
++< printk(KERN_DEBUG "Card %d: Voltage: %d Debounce %d\n", card + 1, b, fxo->battdebounce);
++< }
++< }
++
++ 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;
++ wc->mod[card].fxo.readcid = 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;
++ wc->mod[card].fxo.cidtimer = wc->intcount;
++ wc->mod[card].fxo.readcid = 0;
++ }
++ }
++}
++#endif
++static inline void ystdm_dtmfcheck_fakepolarity(struct ystdm *wc, int card, int x)
++{
++ int sample;
++ /* only look for sound on the line if dtmf flag is on, it is an fxo card and line is onhook */
++ if (!dtmf || !(wc->cardflag & (1 << card)) || !(wc->modtype[card] == MOD_TYPE_FXO) || wc->mod[card].fxo.offhook )
++ return;
++
++ /* don't look for noise if we're already processing it, or there is a ringing tone */
++ if(!wc->mod[card].fxo.readcid && !wc->mod[card].fxo.wasringing &&
++ wc->intcount > wc->mod[card].fxo.cidtimer + 400 ) {
++ sample = DAHDI_XLAW(wc->chans[card]->readchunk[x], (wc->chans[card]));
++ if (sample > 16000 || sample < -16000) {
++ wc->mod[card].fxo.readcid = 1;
++ wc->mod[card].fxo.cidtimer = wc->intcount;
++ if (debug) printk("DTMF CLIP on %i\n",card+1);
++ dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY);
++ }
++ } else if(wc->mod[card].fxo.readcid && wc->intcount > wc->mod[card].fxo.cidtimer + 2000) {
++ /* reset flags if it's been a while */
++ wc->mod[card].fxo.cidtimer = wc->intcount;
++ wc->mod[card].fxo.readcid = 0;
++ }
++}
++static inline void ystdm_receiveprep(struct ystdm *wc, unsigned char ints)
++{
++ volatile unsigned int *readchunk;
++ int x;
++
++ if (ints & 0x08)
++ readchunk = wc->readchunk + DAHDI_CHUNKSIZE * (NUM_CARDS / 4);
++ else
++ /* Read is at interrupt address. Valid data is available at normal offset */
++ readchunk = wc->readchunk;
++ for (x=0;x<DAHDI_CHUNKSIZE;x++) {
++#ifdef __BIG_ENDIAN
++ if (wc->cardflag & (1 << 7))
++ wc->chans[7]->readchunk[x] = (readchunk[2 * x + 1]) & 0xff;
++ if (wc->cardflag & (1 << 6))
++ wc->chans[6]->readchunk[x] = (readchunk[2 * x + 1] >> 8) & 0xff;
++ if (wc->cardflag & (1 << 5))
++ wc->chans[5]->readchunk[x] = (readchunk[2 * x + 1] >> 16) & 0xff;
++ if (wc->cardflag & (1 << 4))
++ wc->chans[4]->readchunk[x] = (readchunk[2 * x + 1] >> 24) & 0xff;
++
++ if (wc->cardflag & (1 << 3))
++ wc->chans[3]->readchunk[x] = (readchunk[2 * x]) & 0xff;
++ if (wc->cardflag & (1 << 2))
++ wc->chans[2]->readchunk[x] = (readchunk[2 * x] >> 8) & 0xff;
++ if (wc->cardflag & (1 << 1))
++ wc->chans[1]->readchunk[x] = (readchunk[2 * x] >> 16) & 0xff;
++ if (wc->cardflag & (1 << 0))
++ wc->chans[0]->readchunk[x] = (readchunk[2 * x] >> 24) & 0xff;
++#else
++ if (wc->cardflag & (1 << 7))
++ wc->chans[7]->readchunk[x] = (readchunk[2 * x + 1] >> 24) & 0xff;
++ if (wc->cardflag & (1 << 6))
++ wc->chans[6]->readchunk[x] = (readchunk[2 * x + 1] >> 16) & 0xff;
++ if (wc->cardflag & (1 << 5))
++ wc->chans[5]->readchunk[x] = (readchunk[2 * x + 1] >> 8) & 0xff;
++ if (wc->cardflag & (1 << 4))
++ wc->chans[4]->readchunk[x] = (readchunk[2 * x + 1]) & 0xff;
++
++ if (wc->cardflag & (1 << 3))
++ wc->chans[3]->readchunk[x] = (readchunk[2 * x] >> 24) & 0xff;
++ if (wc->cardflag & (1 << 2))
++ wc->chans[2]->readchunk[x] = (readchunk[2 * x] >> 16) & 0xff;
++ if (wc->cardflag & (1 << 1))
++ wc->chans[1]->readchunk[x] = (readchunk[2 * x] >> 8) & 0xff;
++ if (wc->cardflag & (1 << 0))
++ wc->chans[0]->readchunk[x] = (readchunk[2 * x]) & 0xff;
++#endif
++
++ /*ystdm_dtmfcheck_fakepolarity(wc,0,x);
++ ystdm_dtmfcheck_fakepolarity(wc,1,x);
++ ystdm_dtmfcheck_fakepolarity(wc,2,x);
++ ystdm_dtmfcheck_fakepolarity(wc,3,x);
++ ystdm_dtmfcheck_fakepolarity(wc,4,x);
++ ystdm_dtmfcheck_fakepolarity(wc,5,x);
++ ystdm_dtmfcheck_fakepolarity(wc,6,x);
++ ystdm_dtmfcheck_fakepolarity(wc,7,x);*/
++ }
++#ifdef AUDIO_RINGCHECK
++ for (x=0;x<wc->cards;x++)
++ ring_check(wc, x);
++#endif
++ /* XXX We're wasting 8 taps. We should get closer :( */
++ for (x = 0; x < NUM_CARDS; x++) {
++ if (wc->cardflag & (1 << x))
++ dahdi_ec_chunk(wc->chans[x], wc->chans[x]->readchunk, wc->chans[x]->writechunk);
++ }
++ dahdi_receive(&wc->span);
++}
++
++static void ystdm_stop_dma(struct ystdm *wc);
++static void ystdm_reset_tdm(struct ystdm *wc);
++static void ystdm_restart_dma(struct ystdm *wc);
++
++static inline void __write_8bits(struct ystdm *wc, unsigned char bits)
++{
++/* Out BIT_CS --\________________________________/---- */
++/* Out BIT_SCLK ---\_/-\_/-\_/-\_/-\_/-\_/-\_/-\_/------ */
++/* Out BIT_SDI ---\___/---\___/---\___/---\___/-------- */
++/* Data Bit 7 6 5 4 3 2 1 0 */
++/* Data written 0 1 0 1 0 1 0 1 */
++
++ int x;
++ /* Drop chip select */
++ 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);
++
++}
++
++static inline void __reset_spi(struct ystdm *wc)
++{
++ /* 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);
++
++}
++
++static inline unsigned char __read_8bits(struct ystdm *wc)
++{
++/* Out BIT_CS --\________________________________________/----*/
++/* Out BIT_SCLK ---\_/--\_/--\_/--\_/--\_/--\_/--\_/--\_/-------*/
++/* In BIT_SDO ????/1111\0000/1111\0000/1111\0000/1111\0000/???*/
++/* Data bit 7 6 5 4 3 2 1 0 */
++/* Data Read 1 0 1 0 1 0 1 0 */
++
++/* Note: Clock High time is 2x Low time, due to input read */
++
++ unsigned char res=0, c;
++ int x;
++ /* Drop chip select */
++ wc->ios &= ~BIT_CS;
++ outb(wc->ios, wc->ioaddr + WC_AUXD);
++ for (x=0;x<8;x++) {
++ res <<= 1;
++ /* Drop SCLK */
++ wc->ios &= ~BIT_SCLK;
++ outb(wc->ios, wc->ioaddr + WC_AUXD);
++ /* Now raise SCLK high again */
++ 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;
++ }
++ /* Finally raise CS back high again */
++ wc->ios |= BIT_CS;
++ outb(wc->ios, wc->ioaddr + WC_AUXD);
++
++ /* And return our result */
++ return res;
++}
++
++static void __ystdm_setcreg(struct ystdm *wc, unsigned char reg, unsigned char val)
++{
++ outb(val, wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2));
++}
++
++static unsigned char __ystdm_getcreg(struct ystdm *wc, unsigned char reg)
++{
++ return inb(wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2));
++}
++
++static inline void __ystdm_setcard(struct ystdm *wc, int card)
++{
++ if (wc->curcard != card) {
++ __ystdm_setcreg(wc, WC_CS, (1 << card));
++ wc->curcard = card;
++ }
++}
++
++static void __ystdm_setreg(struct ystdm *wc, int card, unsigned char reg, unsigned char value)
++{
++ __ystdm_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 ystdm_setreg(struct ystdm *wc, int card, unsigned char reg, unsigned char value)
++{
++ unsigned long flags;
++ spin_lock_irqsave(&wc->lock, flags);
++ __ystdm_setreg(wc, card, reg, value);
++ spin_unlock_irqrestore(&wc->lock, flags);
++}
++
++static unsigned char __ystdm_getreg(struct ystdm *wc, int card, unsigned char reg)
++{
++ __ystdm_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 ystdm *wc, int card)
++{
++ unsigned long flags;
++ spin_lock_irqsave(&wc->lock, flags);
++ __ystdm_setcard(wc, card);
++ __reset_spi(wc);
++ __reset_spi(wc);
++ spin_unlock_irqrestore(&wc->lock, flags);
++}
++
++static unsigned char ystdm_getreg(struct ystdm *wc, int card, unsigned char reg)
++{
++ unsigned long flags;
++ unsigned char res;
++ spin_lock_irqsave(&wc->lock, flags);
++ res = __ystdm_getreg(wc, card, reg);
++ spin_unlock_irqrestore(&wc->lock, flags);
++ return res;
++}
++
++static int __wait_access(struct ystdm *wc, int card)
++{
++ unsigned char data = 0;
++
++ int count = 0;
++
++ #define MAX 6000 /* attempts */
++
++
++
++ /* Wait for indirect access */
++ while (count++ < MAX)
++ {
++ data = __ystdm_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 ystdm_proslic_setreg_indirect(struct ystdm *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)) {
++ __ystdm_setreg(wc, card, IDA_LO,(unsigned char)(data & 0xFF));
++ __ystdm_setreg(wc, card, IDA_HI,(unsigned char)((data & 0xFF00)>>8));
++ __ystdm_setreg(wc, card, IAA,address);
++ res = 0;
++ };
++ spin_unlock_irqrestore(&wc->lock, flags);
++ return res;
++}
++
++static int ystdm_proslic_getreg_indirect(struct ystdm *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)) {
++ __ystdm_setreg(wc, card, IAA, address);
++ if (!__wait_access(wc, card)) {
++ unsigned char data1, data2;
++ data1 = __ystdm_getreg(wc, card, IDA_LO);
++ data2 = __ystdm_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 ystdm_proslic_init_indirect_regs(struct ystdm *wc, int card)
++{
++ unsigned char i;
++
++ for (i=0; i<sizeof(indirect_regs) / sizeof(indirect_regs[0]); i++)
++ {
++ if(ystdm_proslic_setreg_indirect(wc, card, indirect_regs[i].address,indirect_regs[i].initial))
++ return -1;
++ }
++
++ return 0;
++}
++
++static int ystdm_proslic_verify_indirect_regs(struct ystdm *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 = ystdm_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 ystdm_proslic_recheck_sanity(struct ystdm *wc, int card)
++{
++ struct fxs *const fxs = &wc->mod[card].fxs;
++ int res;
++ /* Check loopback */
++ res = wc->reg1shadow[card];
++ if (!res && (res != fxs->lasttxhook)) {
++ res = ystdm_getreg(wc, card, 8);
++ if (res) {
++ printk(KERN_NOTICE "Ouch, part reset, quickly restoring reality (%d)\n", card);
++ ystdm_init_proslic(wc, card, 1, 0, 1);
++ } else {
++ if (fxs->palarms++ < MAX_ALARMS) {
++ printk(KERN_NOTICE "Power alarm on module %d, resetting!\n", card + 1);
++ if (fxs->lasttxhook == SLIC_LF_RINGING)
++ fxs->lasttxhook = SLIC_LF_ACTIVE_FWD;
++ ystdm_setreg(wc, card, 64, fxs->lasttxhook);
++ } else {
++ if (fxs->palarms == MAX_ALARMS)
++ printk(KERN_NOTICE "Too many power alarms on card %d, NOT resetting!\n", card + 1);
++ }
++ }
++ }
++}
++
++static inline void ystdm_voicedaa_check_hook(struct ystdm *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)
++ ystdm_setreg(wc, card, 5, 0x9);
++ } else {
++ if (b != 0x8)
++ ystdm_setreg(wc, card, 5, 0x8);
++ }
++ if (errors)
++ return;
++ if (!fxo->offhook) {
++ 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);
++ 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);
++ 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;
++ 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;
++ 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 (fxovoltage) {
++ static int count = 0;
++ if (!(count++ % 100)) {
++ printk(KERN_DEBUG "Card %d: Voltage: %d Debounce %d\n", card + 1, b, fxo->battdebounce);
++ }
++ }
++
++ if (unlikely(DAHDI_RXSIG_INITIAL == wc->chans[card]->rxhooksig)) {
++ /*
++ * dahdi-base will set DAHDI_RXSIG_INITIAL after a
++ * DAHDI_STARTUP or DAHDI_CHANCONFIG ioctl so that new events
++ * will be queued on the channel with the current received
++ * hook state. Channels that use robbed-bit signalling always
++ * report the current received state via the dahdi_rbsbits
++ * call. Since we only call dahdi_hooksig when we've detected
++ * a change to report, let's forget our current state in order
++ * to force us to report it again via dahdi_hooksig.
++ *
++ */
++ fxo->battery = BATTERY_UNKNOWN;
++ }
++
++if (DAHDI_RXSIG_INITIAL == wc->chans[card]->rxhooksig) {
++ /* If we've been set to the initial state, let's reset the
++ * battery state to unknown so that we will reset the
++ * current state of the battery and call dahdi_hooksig. */
++ fxo->battery = BATTERY_UNKNOWN;
++ } /* add by David at 2009.09.10 */
++
++
++ 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");
++ dahdi_alarm_channel(&wc->chans[card], DAHDI_ALARM_RED); //add by david
++
++#ifdef ZERO_BATT_RING
++ wc->onhook++;
++#endif
++ }
++#else
++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK);
++ dahdi_alarm_channel(wc->chans[card], DAHDI_ALARM_RED); //add by david
++ /* 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);
++ dahdi_alarm_channel(wc->chans[card], DAHDI_ALARM_NONE); //add by david
++
++#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 void ystdm_fxs_hooksig(struct ystdm *wc, const int card, enum dahdi_txsig txsig)
++{
++ struct fxs *const fxs = &wc->mod[card].fxs;
++ switch (txsig) {
++ case DAHDI_TXSIG_ONHOOK:
++ switch (wc->span.chans[card]->sig) {
++ case DAHDI_SIG_FXOKS:
++ case DAHDI_SIG_FXOLS:
++ /* Can't change Ring Generator during OHT */
++ if (!fxs->ohttimer) {
++ ystdm_set_ring_generator_mode(wc,
++ card, fxs->vmwi_hvac);
++ fxs->lasttxhook = fxs->vmwi_hvac ?
++ SLIC_LF_RINGING :
++ fxs->idletxhookstate;
++ } else {
++ fxs->lasttxhook = fxs->idletxhookstate;
++ }
++ break;
++ case DAHDI_SIG_EM:
++ fxs->lasttxhook = fxs->idletxhookstate;
++ break;
++ case DAHDI_SIG_FXOGS:
++ fxs->lasttxhook = SLIC_LF_TIP_OPEN;
++ break;
++ }
++ break;
++ case DAHDI_TXSIG_OFFHOOK:
++ switch (wc->span.chans[card]->sig) {
++ case DAHDI_SIG_EM:
++ fxs->lasttxhook = SLIC_LF_ACTIVE_REV;
++ break;
++ default:
++ fxs->lasttxhook = fxs->idletxhookstate;
++ break;
++ }
++ break;
++ case DAHDI_TXSIG_START:
++ /* Set ringer mode */
++ ystdm_set_ring_generator_mode(wc, card, 0);
++ fxs->lasttxhook = SLIC_LF_RINGING;
++ break;
++ case DAHDI_TXSIG_KEWL:
++ fxs->lasttxhook = SLIC_LF_OPEN;
++ break;
++ default:
++ printk(KERN_NOTICE "ystdm: Can't set tx state to %d\n", txsig);
++ return;
++ }
++ if (debug) {
++ printk(KERN_DEBUG
++ "Setting FXS hook state to %d (%02x)\n",
++ txsig, fxs->lasttxhook);
++ }
++ ystdm_setreg(wc, card, LINE_STATE, fxs->lasttxhook);
++}
++
++
++static inline void ystdm_proslic_check_hook(struct ystdm *wc, int card)
++{
++ struct fxs *const fxs = &wc->mod[card].fxs;
++ char res;
++ int hook;
++
++ /* For some reason we have to debounce the
++ hook detector. */
++
++ res = wc->reg0shadow[card];
++ hook = (res & 1);
++ if (hook != fxs->lastrxhook) {
++ /* Reset the debounce (must be multiple of 4ms) */
++ fxs->debounce = dialdebounce * 4;
++#if 0
++ printk(KERN_DEBUG "Resetting debounce card %d hook %d, %d\n",
++ card, hook, fxs->debounce);
++#endif
++ } else {
++ if (fxs->debounce > 0) {
++ fxs->debounce -= 16 * DAHDI_CHUNKSIZE;
++#if 0
++ printk(KERN_DEBUG "Sustaining hook %d, %d\n",
++ hook, fxs->debounce);
++#endif
++ if (!fxs->debounce) {
++#if 0
++ printk(KERN_DEBUG "Counted down debounce, newhook: %d...\n", hook);
++#endif
++ fxs->debouncehook = hook;
++ }
++ if (!fxs->oldrxhook && fxs->debouncehook) {
++ /* Off hook */
++#if 1
++ if (debug)
++#endif
++ printk(KERN_DEBUG "ystdm: Card %d Going off hook\n", card);
++
++ switch (fxs->lasttxhook) {
++ case SLIC_LF_RINGING:
++ case SLIC_LF_OHTRAN_FWD:
++ case SLIC_LF_OHTRAN_REV:
++ /* just detected OffHook, during
++ * Ringing or OnHookTransfer */
++ fxs->idletxhookstate =
++ POLARITY_XOR ?
++ SLIC_LF_ACTIVE_REV :
++ SLIC_LF_ACTIVE_FWD;
++ break;
++ }
++
++ ystdm_fxs_hooksig(wc, card, DAHDI_TXSIG_OFFHOOK);
++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK);
++ if (robust)
++ ystdm_init_proslic(wc, card, 1, 0, 1);
++ fxs->oldrxhook = 1;
++
++ } else if (fxs->oldrxhook && !fxs->debouncehook) {
++ /* On hook */
++#if 1
++ if (debug)
++#endif
++ printk(KERN_DEBUG "ystdm: Card %d Going on hook\n", card);
++ ystdm_fxs_hooksig(wc, card, DAHDI_TXSIG_ONHOOK);
++ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK);
++ fxs->oldrxhook = 0;
++ }
++ }
++ }
++ fxs->lastrxhook = hook;
++}
++
++DAHDI_IRQ_HANDLER(ystdm_interrupt)
++{
++ struct ystdm *wc = dev_id;
++ unsigned char ints;
++ int x;
++ int mode;
++
++ ints = inb(wc->ioaddr + WC_INTSTAT);
++ outb(ints, 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");
++ ystdm_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<NUM_CARDS;x++) {
++ if (wc->cardflag & (1 << x) &&
++ (wc->modtype[x] == MOD_TYPE_FXS)) {
++ struct fxs *const fxs = &wc->mod[x].fxs;
++ if (fxs->lasttxhook == SLIC_LF_RINGING &&
++ !fxs->neonringing) {
++ /* RINGing, prepare for OHT */
++ fxs->ohttimer = OHT_TIMER << 3;
++
++ /* logical XOR 3 variables
++ module parameter 'reversepolarity', global reverse all FXS lines.
++ ioctl channel variable fxs 'reversepolarity', Line Reversal Alert Signal if required.
++ ioctl channel variable fxs 'vmwi_lrev', VMWI pending.
++ */
++
++ /* OHT mode when idle */
++ fxs->idletxhookstate = POLARITY_XOR ?
++ SLIC_LF_OHTRAN_REV :
++ SLIC_LF_OHTRAN_FWD;
++ } else if (fxs->ohttimer) {
++ /* check if still OnHook */
++ if (!fxs->oldrxhook) {
++ fxs->ohttimer -= DAHDI_CHUNKSIZE;
++ if (!fxs->ohttimer) {
++ fxs->idletxhookstate = POLARITY_XOR ? SLIC_LF_ACTIVE_REV : SLIC_LF_ACTIVE_FWD; /* Switch to Active, Rev or Fwd */
++ /* if currently OHT */
++ if ((fxs->lasttxhook == SLIC_LF_OHTRAN_FWD) || (fxs->lasttxhook == SLIC_LF_OHTRAN_REV)) {
++ if (fxs->vmwi_hvac) {
++ /* force idle polarity Forward if ringing */
++ fxs->idletxhookstate = SLIC_LF_ACTIVE_FWD;
++ /* Set ring generator for neon */
++ ystdm_set_ring_generator_mode(wc, x, 1);
++ fxs->lasttxhook = SLIC_LF_RINGING;
++ } else {
++ fxs->lasttxhook = fxs->idletxhookstate;
++ }
++ /* Apply the change as appropriate */
++ ystdm_setreg(wc, x, LINE_STATE, fxs->lasttxhook);
++ }
++ }
++ } else {
++ fxs->ohttimer = 0;
++ /* Switch to Active, Rev or Fwd */
++ fxs->idletxhookstate = POLARITY_XOR ? SLIC_LF_ACTIVE_REV : SLIC_LF_ACTIVE_FWD;
++ }
++ }
++ }
++ }
++
++ if (ints & 0x0f) {
++ wc->intcount++;
++ x = wc->intcount & 0x7;
++ mode = wc->intcount & 0x18;
++ if (wc->cardflag & (1 << x)) {
++ switch(mode) {
++ case 0:
++ /* Rest */
++ break;
++ case 8:
++ /* Read first shadow reg */
++ if (wc->modtype[x] == MOD_TYPE_FXS)
++ wc->reg0shadow[x] = ystdm_getreg(wc, x, 68);
++ else if (wc->modtype[x] == MOD_TYPE_FXO)
++ wc->reg0shadow[x] = ystdm_getreg(wc, x, 5);
++ break;
++ case 16:
++ /* Read second shadow reg */
++ if (wc->modtype[x] == MOD_TYPE_FXS)
++ wc->reg1shadow[x] = ystdm_getreg(wc, x, LINE_STATE);
++ else if (wc->modtype[x] == MOD_TYPE_FXO)
++ wc->reg1shadow[x] = ystdm_getreg(wc, x, 29);
++ break;
++ case 24:
++ /* Perform processing */
++ if (wc->modtype[x] == MOD_TYPE_FXS) {
++ ystdm_proslic_check_hook(wc, x);
++ if (!(wc->intcount & 0xf0)) {
++ ystdm_proslic_recheck_sanity(wc, x);
++ }
++ } else if (wc->modtype[x] == MOD_TYPE_FXO) {
++ ystdm_voicedaa_check_hook(wc, x);
++ }
++ break;
++ }
++ }
++ if (!(wc->intcount % 10000)) {
++ /* Accept an alarm once per 10 seconds */
++ for (x=0;x<NUM_CARDS;x++)
++ if (wc->modtype[x] == MOD_TYPE_FXS) {
++ if (wc->mod[x].fxs.palarms)
++ wc->mod[x].fxs.palarms--;
++ }
++ }
++ ystdm_receiveprep(wc, ints);
++ ystdm_transmitprep(wc, ints);
++ }
++
++ return IRQ_RETVAL(1);
++
++}
++
++static int ystdm_voicedaa_insane(struct ystdm *wc, int card)
++{
++ int blah;
++ blah = ystdm_getreg(wc, card, 2);
++ if (blah != 0x3)
++ return -2;
++ blah = ystdm_getreg(wc, card, 11);
++ if (debug)
++ printk(KERN_DEBUG "VoiceDAA System: %02x\n", blah & 0xf);
++ return 0;
++}
++
++static int ystdm_proslic_insane(struct ystdm *wc, int card)
++{
++ int blah,insane_report;
++ insane_report=0;
++
++ blah = ystdm_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 ((blah & 0xf) == 2) {
++ /* ProSLIC 3215, not a 3210 */
++ wc->flags[card] |= FLAG_3215;
++ }
++ blah = ystdm_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 = ystdm_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 = ystdm_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. */
++ ystdm_setreg(wc, card, 30, 0);
++
++ if (debug)
++ printk(KERN_DEBUG "ProSLIC on module %d seems sane.\n", card);
++ return 0;
++}
++
++static int ystdm_proslic_powerleak_test(struct ystdm *wc, int card)
++{
++ unsigned long origjiffies;
++ unsigned char vbat;
++
++ /* Turn off linefeed */
++ ystdm_setreg(wc, card, 64, 0);
++
++ /* Power down */
++ ystdm_setreg(wc, card, 14, 0x10);
++
++ /* Wait for one second */
++ origjiffies = jiffies;
++
++ while((vbat = ystdm_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 ystdm_powerup_proslic(struct ystdm *wc, int card, int fast)
++{
++ unsigned char vbat;
++ unsigned long origjiffies;
++ int lim;
++
++ /* Set period of DC-DC converter to 1/64 khz */
++ ystdm_setreg(wc, card, 92, 0xff /* was 0xff */);
++
++ /* Wait for VBat to powerup */
++ origjiffies = jiffies;
++
++ /* Disable powerdown */
++ ystdm_setreg(wc, card, 14, 0);
++
++ /* If fast, don't bother checking anymore */
++ if (fast)
++ return 0;
++
++ while((vbat = ystdm_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 TDM400P??\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);
++ ystdm_setreg(wc,card,LOOP_I_LIMIT,lim);
++
++ /* Engage DC-DC converter */
++ ystdm_setreg(wc, card, 93, 0x19 /* was 0x19 */);
++#if 0
++ origjiffies = jiffies;
++ while(0x80 & ystdm_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 = ystdm_getreg(wc, card, 82);
++ printk("ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n",
++ card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ)));
++#endif
++#endif
++ return 0;
++
++}
++
++static int ystdm_proslic_manual_calibrate(struct ystdm *wc, int card){
++ unsigned long origjiffies;
++ unsigned char i;
++
++ ystdm_setreg(wc, card, 21, 0);//(0) Disable all interupts in DR21
++ ystdm_setreg(wc, card, 22, 0);//(0)Disable all interupts in DR21
++ ystdm_setreg(wc, card, 23, 0);//(0)Disable all interupts in DR21
++ ystdm_setreg(wc, card, 64, 0);//(0)
++
++ ystdm_setreg(wc, card, 97, 0x18); //(0x18)Calibrations without the ADC and DAC offset and without common mode calibration.
++ ystdm_setreg(wc, card, 96, 0x47); //(0x47) Calibrate common mode and differential DAC mode DAC + ILIM
++
++ origjiffies=jiffies;
++ while( ystdm_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);
++ ystdm_proslic_setreg_indirect(wc, card, 88,0);
++ ystdm_proslic_setreg_indirect(wc, card, 89,0);
++ ystdm_proslic_setreg_indirect(wc, card, 90,0);
++ ystdm_proslic_setreg_indirect(wc, card, 91,0);
++ ystdm_proslic_setreg_indirect(wc, card, 92,0);
++ ystdm_proslic_setreg_indirect(wc, card, 93,0);
++
++ ystdm_setreg(wc, card, 98, 0x10); // This is necessary if the calibration occurs other than at reset time
++ ystdm_setreg(wc, card, 99, 0x10);
++
++ for ( i=0x1f; i>0; i--)
++ {
++ ystdm_setreg(wc, card, 98, i);
++ origjiffies=jiffies;
++ while((jiffies-origjiffies)<4);
++ if((ystdm_getreg(wc, card, 88)) == 0)
++ break;
++ } // for
++
++ for ( i=0x1f; i>0; i--)
++ {
++ ystdm_setreg(wc, card, 99, i);
++ origjiffies=jiffies;
++ while((jiffies-origjiffies)<4);
++ if((ystdm_getreg(wc, card, 89)) == 0)
++ break;
++ }//for
++
++/*******************************The preceding is the manual gain mismatch calibration****************************/
++/**********************************The following is the longitudinal Balance Cal***********************************/
++ ystdm_setreg(wc,card,64,1);
++ while((jiffies-origjiffies)<10); // Sleep 100?
++
++ ystdm_setreg(wc, card, 64, 0);
++ ystdm_setreg(wc, card, 23, 0x4); // enable interrupt for the balance Cal
++ ystdm_setreg(wc, card, 97, 0x1); // this is a singular calibration bit for longitudinal calibration
++ ystdm_setreg(wc, card, 96, 0x40);
++
++ ystdm_getreg(wc, card, 96); /* Read Reg 96 just cause */
++
++ ystdm_setreg(wc, card, 21, 0xFF);
++ ystdm_setreg(wc, card, 22, 0xFF);
++ ystdm_setreg(wc, card, 23, 0xFF);
++
++ /**The preceding is the longitudinal Balance Cal***/
++ return(0);
++
++}
++#if 1
++static int ystdm_proslic_calibrate(struct ystdm *wc, int card)
++{
++ unsigned long origjiffies;
++ int x;
++ /* Perform all calibrations */
++ ystdm_setreg(wc, card, 97, 0x1f);
++
++ /* Begin, no speedup */
++ ystdm_setreg(wc, card, 96, 0x5f);
++
++ /* Wait for it to finish */
++ origjiffies = jiffies;
++ while(ystdm_getreg(wc, card, 96)) {
++ if ((jiffies - origjiffies) > 2 * HZ) {
++ printk("Timeout waiting for calibration of module %d\n", card);
++ return -1;
++ }
++ }
++
++ if (debug) {
++ /* Print calibration parameters */
++ printk("Calibration Vector Regs 98 - 107: \n");
++ for (x=98;x<108;x++) {
++ printk("%d: %02x\n", x, ystdm_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 ystdm_set_hwgain(struct ystdm *wc, int card, __s32 gain, __u32 tx)
++{
++ if (!(wc->modtype[card] == MOD_TYPE_FXO)) {
++ printk("Cannot adjust gain. Unsupported module type!\n");
++ return -1;
++ }
++ if (tx) {
++ if (debug)
++ printk("setting FXO tx gain for card=%d to %d\n", card, gain);
++ if (gain >= -150 && gain <= 0) {
++ ystdm_setreg(wc, card, 38, 16 + (gain/-10));
++ ystdm_setreg(wc, card, 40, 16 + (-gain%10));
++ } else if (gain <= 120 && gain > 0) {
++ ystdm_setreg(wc, card, 38, gain/10);
++ ystdm_setreg(wc, card, 40, (gain%10));
++ } else {
++ printk("FXO tx gain is out of range (%d)\n", gain);
++ return -1;
++ }
++ } else { /* rx */
++ if (debug)
++ printk("setting FXO rx gain for card=%d to %d\n", card, gain);
++ if (gain >= -150 && gain <= 0) {
++ ystdm_setreg(wc, card, 39, 16+ (gain/-10));
++ ystdm_setreg(wc, card, 41, 16 + (-gain%10));
++ } else if (gain <= 120 && gain > 0) {
++ ystdm_setreg(wc, card, 39, gain/10);
++ ystdm_setreg(wc, card, 41, (gain%10));
++ } else {
++ printk("FXO rx gain is out of range (%d)\n", gain);
++ return -1;
++ }
++ }
++
++ return 0;
++}
++
++
++static int set_vmwi(struct ystdm * wc, int chan_idx)
++{
++ struct fxs *const fxs = &wc->mod[chan_idx].fxs;
++ if (fxs->vmwi_active_messages) {
++ fxs->vmwi_lrev =
++ (fxs->vmwisetting.vmwi_type & DAHDI_VMWI_LREV) ? 1 : 0;
++ fxs->vmwi_hvdc =
++ (fxs->vmwisetting.vmwi_type & DAHDI_VMWI_HVDC) ? 1 : 0;
++ fxs->vmwi_hvac =
++ (fxs->vmwisetting.vmwi_type & DAHDI_VMWI_HVAC) ? 1 : 0;
++ } else {
++ fxs->vmwi_lrev = 0;
++ fxs->vmwi_hvdc = 0;
++ fxs->vmwi_hvac = 0;
++ }
++
++ if (debug) {
++ printk(KERN_DEBUG "Setting VMWI on channel %d, messages=%d, "
++ "lrev=%d, hvdc=%d, hvac=%d\n",
++ chan_idx,
++ fxs->vmwi_active_messages,
++ fxs->vmwi_lrev,
++ fxs->vmwi_hvdc,
++ fxs->vmwi_hvac
++ );
++ }
++ if (fxs->vmwi_hvac) {
++ /* Can't change ring generator while in On Hook Transfer mode*/
++ if (!fxs->ohttimer) {
++ if (POLARITY_XOR)
++ fxs->idletxhookstate |= SLIC_LF_REVMASK;
++ else
++ fxs->idletxhookstate &= ~SLIC_LF_REVMASK;
++ /* Set ring generator for neon */
++ ystdm_set_ring_generator_mode(wc, chan_idx, 1);
++ /* Activate ring to send neon pulses */
++ fxs->lasttxhook = SLIC_LF_RINGING;
++ ystdm_setreg(wc, chan_idx, LINE_STATE, fxs->lasttxhook);
++ }
++ } else {
++ if (fxs->neonringing) {
++ /* Set ring generator for normal ringer */
++ ystdm_set_ring_generator_mode(wc, chan_idx, 0);
++ /* ACTIVE, polarity determined later */
++ fxs->lasttxhook = SLIC_LF_ACTIVE_FWD;
++ } else if ((fxs->lasttxhook == SLIC_LF_RINGING) ||
++ (fxs->lasttxhook == SLIC_LF_OPEN)) {
++ /* Can't change polarity while ringing or when open,
++ set idlehookstate instead */
++ if (POLARITY_XOR)
++ fxs->idletxhookstate |= SLIC_LF_REVMASK;
++ else
++ fxs->idletxhookstate &= ~SLIC_LF_REVMASK;
++
++ printk(KERN_DEBUG "Unable to change polarity on channel"
++ "%d, lasttxhook=0x%X\n",
++ chan_idx,
++ fxs->lasttxhook
++ );
++ return 0;
++ }
++ if (POLARITY_XOR) {
++ fxs->idletxhookstate |= SLIC_LF_REVMASK;
++ fxs->lasttxhook |= SLIC_LF_REVMASK;
++ } else {
++ fxs->idletxhookstate &= ~SLIC_LF_REVMASK;
++ fxs->lasttxhook &= ~SLIC_LF_REVMASK;
++ }
++ ystdm_setreg(wc, chan_idx, LINE_STATE, fxs->lasttxhook);
++ }
++ return 0;
++}
++
++static int ystdm_init_voicedaa(struct ystdm *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 && ystdm_voicedaa_insane(wc, card))
++ return -2;
++
++ /* Software reset */
++ ystdm_setreg(wc, card, 1, 0x80);
++
++ /* Wait just a bit */
++ wait_just_a_bit(HZ/10);
++
++ /* Enable PCM, ulaw */
++ if (alawoverride){
++ ystdm_setreg(wc, card, 33, 0x20);
++ } else {
++ ystdm_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);
++ ystdm_setreg(wc, card, 16, reg16);
++
++ if(fwringdetect) {
++ /* Enable ring detector full-wave rectifier mode */
++ ystdm_setreg(wc, card, 18, 2);
++ ystdm_setreg(wc, card, 24, 0);
++ } else {
++ /* Set to the device defaults */
++ ystdm_setreg(wc, card, 18, 0);
++ ystdm_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);
++ ystdm_setreg(wc, card, 26, reg26);
++
++ /* Set AC Impedence */
++ reg30 = (fxo_modes[_opermode].acim);
++ ystdm_setreg(wc, card, 30, reg30);
++
++ /* Misc. DAA parameters */
++ if (fastpickup)
++ reg31 = 0xe3;
++ else
++ reg31 = 0xa3;
++
++ reg31 |= (fxo_modes[_opermode].ohs2 << 3);
++ ystdm_setreg(wc, card, 31, reg31);
++
++ /* Set Transmit/Receive timeslot */
++ if (card < NUM_CARDS/2) {
++ ystdm_setreg(wc, card, 34, (3-card) * 8);
++ ystdm_setreg(wc, card, 35, 0x00);
++ ystdm_setreg(wc, card, 36, (3-card) * 8);
++ ystdm_setreg(wc, card, 37, 0x00);
++ } else {
++ ystdm_setreg(wc, card, 34, (3-(card-NUM_CARDS/2)+16) * 8);
++ ystdm_setreg(wc, card, 35, 0x00);
++ ystdm_setreg(wc, card, 36, (3-(card-NUM_CARDS/2)+16) * 8);
++ ystdm_setreg(wc, card, 37, 0x00);
++ }
++
++ /* Enable ISO-Cap */
++ ystdm_setreg(wc, card, 6, 0x00);
++ if (fastpickup)
++ ystdm_setreg(wc, card, 17, ystdm_getreg(wc, card, 17) | 0x20);
++
++ /* Wait 1000ms for ISO-cap to come up */
++ newjiffies = jiffies;
++ newjiffies += 2 * HZ;
++ while((jiffies < newjiffies) && !(ystdm_getreg(wc, card, 11) & 0xf0))
++ wait_just_a_bit(HZ/10);
++
++ if (!(ystdm_getreg(wc, card, 11) & 0xf0)) {
++ printk("VoiceDAA did not bring up ISO link properly!\n");
++ return -1;
++ }
++ if (debug)
++ printk("ISO-Cap is now up, line side: %02x rev %02x\n",
++ ystdm_getreg(wc, card, 11) >> 4,
++ (ystdm_getreg(wc, card, 13) >> 2) & 0xf);
++ /* Enable on-hook line monitor */
++ ystdm_setreg(wc, card, 5, 0x08);
++ /* Take values for fxotxgain and fxorxgain and apply them to module */
++ if (fxotxgain)
++ ystdm_set_hwgain(wc, card, fxotxgain, 1);
++ else
++ ystdm_set_hwgain(wc, card, 0, 1);
++ if (fxorxgain)
++ ystdm_set_hwgain(wc, card, fxorxgain, 0);
++ else
++ ystdm_set_hwgain(wc, card, 20, 0);
++
++ /* NZ -- crank the tx gain up by 7 dB */
++ if (!strcmp(fxo_modes[_opermode].name, "NEWZEALAND")) {
++ printk("Adjusting gain\n");
++ ystdm_set_hwgain(wc, card, 7, 1);
++
++ }
++ /* KR -- crank the rv gain up by 9 dB */
++ if (!strcmp(fxo_modes[_opermode].name, "SOUTHKOREA")) {
++ printk("Adjusting gain\n");
++ ystdm_setreg(wc, card, 39, 0x9);
++ }
++ if(debug)
++ printk("DEBUG fxotxgain:%i.%i fxorxgain:%i.%i\n", (ystdm_getreg(wc, card, 38)/16)?-(ystdm_getreg(wc, card, 38) - 16) : ystdm_getreg(wc, card, 38), (ystdm_getreg(wc, card, 40)/16)? -(ystdm_getreg(wc, card, 40) - 16):ystdm_getreg(wc, card, 40), (ystdm_getreg(wc, card, 39)/16)? -(ystdm_getreg(wc, card, 39) - 16) : ystdm_getreg(wc, card, 39),(ystdm_getreg(wc, card, 41)/16)?-(ystdm_getreg(wc, card, 41) - 16):ystdm_getreg(wc, card, 41));
++
++ return 0;
++
++}
++
++static int ystdm_init_proslic(struct ystdm *wc, int card, int fast, int manual, int sane)
++{
++
++ unsigned short tmp[5];
++ unsigned char r19,r9;
++ int x;
++ int fxsmode=0;
++ struct fxs *const fxs = &wc->mod[card].fxs;
++
++ /* Sanity check the ProSLIC */
++ if (!sane && ystdm_proslic_insane(wc, card))
++ return -2;
++
++ /* default messages to none and method to FSK */
++ memset(&fxs->vmwisetting, 0, sizeof(fxs->vmwisetting));
++ fxs->vmwi_lrev = 0;
++ fxs->vmwi_hvdc = 0;
++ fxs->vmwi_hvac = 0;
++
++ /* By default, don't send on hook */
++ if (!reversepolarity != !fxs->reversepolarity)
++ fxs->idletxhookstate = SLIC_LF_ACTIVE_REV;
++ else
++ fxs->idletxhookstate = SLIC_LF_ACTIVE_FWD;
++
++ /* Sanity check the ProSLIC */
++ //if (!sane && ystdm_proslic_insane(wc, card))
++ // return -2;
++
++ if (sane) {
++ /* Make sure we turn off the DC->DC converter to prevent anything from blowing up */
++ ystdm_setreg(wc, card, 14, 0x10);
++ }
++
++ if (ystdm_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 */
++ ystdm_proslic_setreg_indirect(wc, card, 97,0);
++
++ /* Clear digital loopback */
++ ystdm_setreg(wc, card, 8, 0);
++
++ /* Revision C optimization */
++ ystdm_setreg(wc, card, 108, 0xeb);
++
++ /* Disable automatic VBat switching for safety to prevent
++ Q7 from accidently turning on and burning out. */
++ ystdm_setreg(wc, card, 67, 0x07);
++
++ /* Turn off Q7 */
++ ystdm_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] = ystdm_proslic_getreg_indirect(wc, card, x + 35);
++ ystdm_proslic_setreg_indirect(wc, card, x + 35, 0x8000);
++ }
++
++ /* Power up the DC-DC converter */
++ if (ystdm_powerup_proslic(wc, card, fast)) {
++ printk("Unable to do INITIAL ProSLIC powerup on module %d\n", card);
++ return -1;
++ }
++
++ if (!fast) {
++
++ /* Check for power leaks */
++ if (ystdm_proslic_powerleak_test(wc, card)) {
++ printk("ProSLIC module %d failed leakage test. Check for short circuit\n", card);
++ }
++ /* Power up again */
++ if (ystdm_powerup_proslic(wc, card, fast)) {
++ printk("Unable to do FINAL ProSLIC powerup on module %d\n", card);
++ return -1;
++ }
++#ifndef NO_CALIBRATION
++ /* Perform calibration */
++ if(manual) {
++ if (ystdm_proslic_manual_calibrate(wc, card)) {
++ //printk("Proslic failed on Manual Calibration\n");
++ if (ystdm_proslic_manual_calibrate(wc, card)) {
++ printk("Proslic Failed on Second Attempt to Calibrate Manually. (Try -DNO_CALIBRATION in Makefile)\n");
++ return -1;
++ }
++ printk("Proslic Passed Manual Calibration on Second Attempt\n");
++ }
++ }
++ else {
++ if(ystdm_proslic_calibrate(wc, card)) {
++ //printk("ProSlic died on Auto Calibration.\n");
++ if (ystdm_proslic_calibrate(wc, card)) {
++ printk("Proslic Failed on Second Attempt to Auto Calibrate\n");
++ return -1;
++ }
++ printk("Proslic Passed Auto Calibration on Second Attempt\n");
++ }
++ }
++ /* Perform DC-DC calibration */
++ ystdm_setreg(wc, card, 93, 0x99);
++ r19 = ystdm_getreg(wc, card, 107);
++ if ((r19 < 0x2) || (r19 > 0xd)) {
++ printk("DC-DC cal has a surprising direct 107 of 0x%02x!\n", r19);
++ ystdm_setreg(wc, card, 107, 0x8);
++ }
++
++ /* Save calibration vectors */
++ for (x=0;x<NUM_CAL_REGS;x++)
++ fxs->calregs.vals[x] = ystdm_getreg(wc, card, 96 + x);
++#endif
++
++ } else {
++ /* Restore calibration registers */
++ for (x=0;x<NUM_CAL_REGS;x++)
++ ystdm_setreg(wc, card, 96 + x, fxs->calregs.vals[x]);
++ }
++ /* Calibration complete, restore original values */
++ for (x=0;x<5;x++) {
++ ystdm_proslic_setreg_indirect(wc, card, x + 35, tmp[x]);
++ }
++
++ if (ystdm_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" */
++ ystdm_setreg(wc, card, 67, 0x0e);
++ blah = ystdm_getreg(wc, card, 67);
++#endif
++
++#if 0
++ if (ystdm_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)
++ ystdm_setreg(wc, card, 1, 0x20);
++ else
++ ystdm_setreg(wc, card, 1, 0x28);
++ // U-Law 8-bit interface
++ if (card < NUM_CARDS/2) {
++ ystdm_setreg(wc, card, 2, (3-card) * 8); // Tx Start count low byte 0
++ ystdm_setreg(wc, card, 3, 0); // Tx Start count high byte 0
++ ystdm_setreg(wc, card, 4, (3-card) * 8); // Rx Start count low byte 0
++ ystdm_setreg(wc, card, 5, 0); // Rx Start count high byte 0
++ } else {
++ ystdm_setreg(wc, card, 2, (3-(card-NUM_CARDS/2)+16) * 8); // Tx Start count low byte 0
++ ystdm_setreg(wc, card, 3, 0); // Tx Start count high byte 0
++ ystdm_setreg(wc, card, 4, (3-(card-NUM_CARDS/2)+16) * 8); // Rx Start count low byte 0
++ ystdm_setreg(wc, card, 5, 0); // Rx Start count high byte 0
++ }
++ ystdm_setreg(wc, card, 18, 0xff); // clear all interrupt
++ ystdm_setreg(wc, card, 19, 0xff);
++ ystdm_setreg(wc, card, 20, 0xff);
++ ystdm_setreg(wc, card, 73, 0x04);
++ if (fxshonormode) {
++ fxsmode = acim2tiss[fxo_modes[_opermode].acim];
++ ystdm_setreg(wc, card, 10, 0x08 | fxsmode);
++ }
++ if (lowpower)
++ ystdm_setreg(wc, card, 72, 0x10);
++
++#if 0
++ ystdm_setreg(wc, card, 21, 0x00); // enable interrupt
++ ystdm_setreg(wc, card, 22, 0x02); // Loop detection interrupt
++ ystdm_setreg(wc, card, 23, 0x01); // DTMF detection interrupt
++#endif
++
++#if 0
++ /* Enable loopback */
++ ystdm_setreg(wc, card, 8, 0x2);
++ ystdm_setreg(wc, card, 14, 0x0);
++ ystdm_setreg(wc, card, 64, 0x0);
++ ystdm_setreg(wc, card, 1, 0x08);
++#endif
++ if (ystdm_init_ring_generator_mode(wc, card)) {
++ return -1;
++ }
++
++ if(fxstxgain || fxsrxgain) {
++ r9 = ystdm_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;
++ }
++ ystdm_setreg(wc,card,9,r9);
++ }
++
++ if(debug)
++ printk("DEBUG: fxstxgain:%s fxsrxgain:%s\n",((ystdm_getreg(wc, card, 9)/8) == 1)?"3.5":(((ystdm_getreg(wc,card,9)/4) == 1)?"-3.5":"0.0"),((ystdm_getreg(wc, card, 9)/2) == 1)?"3.5":((ystdm_getreg(wc,card,9)%2)?"-3.5":"0.0"));
++
++ fxs->lasttxhook = fxs->idletxhookstate;
++ ystdm_setreg(wc, card, LINE_STATE, fxs->lasttxhook);
++
++ /* Analog Transmit Path Gain = 3.5dB; Analog Receive Path Gain = 3.5dB. */
++ /* ystdm_setreg(wc, card, 9, 0x0a); */
++ return 0;
++}
++
++static int ystdm_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data)
++{
++ struct ystdm_stats stats;
++ struct ystdm_regs regs;
++ struct ystdm_regop regop;
++ struct ystdm_echo_coefs echoregs;
++ struct dahdi_hwgain hwgain;
++ struct ystdm *wc = chan->pvt;
++ struct fxs *const fxs = &wc->mod[chan->chanpos - 1].fxs;
++ 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;
++ fxs->ohttimer = x << 3;
++
++ /* Active mode when idle */
++ fxs->idletxhookstate = POLARITY_XOR ?
++ SLIC_LF_ACTIVE_REV : SLIC_LF_ACTIVE_FWD;
++ if (fxs->neonringing) {
++ /* keep same Forward polarity */
++ fxs->lasttxhook = SLIC_LF_OHTRAN_FWD;
++ printk(KERN_INFO "ioctl: Start OnHookTrans, card %d\n",
++ chan->chanpos - 1);
++ ystdm_setreg(wc, chan->chanpos - 1,
++ LINE_STATE, fxs->lasttxhook);
++ } else if (fxs->lasttxhook == SLIC_LF_ACTIVE_FWD ||
++ fxs->lasttxhook == SLIC_LF_ACTIVE_REV) {
++ /* Apply the change if appropriate */
++ fxs->lasttxhook = POLARITY_XOR ?
++ SLIC_LF_OHTRAN_REV : SLIC_LF_OHTRAN_FWD;
++ printk(KERN_INFO "ioctl: Start OnHookTrans, card %d\n",
++ chan->chanpos - 1);
++ ystdm_setreg(wc, chan->chanpos - 1,
++ LINE_STATE, fxs->lasttxhook);
++ }
++ break;
++ case DAHDI_SETPOLARITY:
++ if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS)
++ return -EINVAL;
++ if (get_user(x, (__user int *) data))
++ return -EFAULT;
++ /* Can't change polarity while ringing or when open */
++ if ((fxs->lasttxhook == SLIC_LF_RINGING) ||
++ (fxs->lasttxhook == SLIC_LF_OPEN))
++ return -EINVAL;
++
++ fxs->reversepolarity = x;
++ if (POLARITY_XOR) {
++ fxs->lasttxhook |= SLIC_LF_REVMASK;
++ printk(KERN_INFO "ioctl: Reverse Polarity, card %d\n",
++ chan->chanpos - 1);
++ }
++ else {
++ fxs->lasttxhook &= ~SLIC_LF_REVMASK;
++ printk(KERN_INFO "ioctl: Normal Polarity, card %d\n",
++ chan->chanpos - 1);
++ }
++
++ ystdm_setreg(wc, chan->chanpos - 1,
++ LINE_STATE, fxs->lasttxhook);
++ break;
++ case DAHDI_VMWI_CONFIG:
++ if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS)
++ return -EINVAL;
++ if (copy_from_user(&(fxs->vmwisetting), (__user void *) data,
++ sizeof(fxs->vmwisetting)))
++ return -EFAULT;
++ set_vmwi(wc, chan->chanpos - 1);
++ break;
++ case DAHDI_VMWI:
++ if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS)
++ return -EINVAL;
++ if (get_user(x, (__user int *) data))
++ return -EFAULT;
++ if (0 > x)
++ return -EFAULT;
++ fxs->vmwi_active_messages = x;
++ set_vmwi(wc, chan->chanpos - 1);
++ break;
++ case WCTDM_GET_STATS:
++ if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) {
++ stats.tipvolt = ystdm_getreg(wc, chan->chanpos - 1, 80) * -376;
++ stats.ringvolt = ystdm_getreg(wc, chan->chanpos - 1, 81) * -376;
++ stats.batvolt = ystdm_getreg(wc, chan->chanpos - 1, 82) * -376;
++ } else if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) {
++ stats.tipvolt = (signed char)ystdm_getreg(wc, chan->chanpos - 1, 29) * 1000;
++ stats.ringvolt = (signed char)ystdm_getreg(wc, chan->chanpos - 1, 29) * 1000;
++ stats.batvolt = (signed char)ystdm_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] = ystdm_proslic_getreg_indirect(wc, chan->chanpos -1, x);
++ for (x=0;x<NUM_REGS;x++)
++ regs.direct[x] = ystdm_getreg(wc, chan->chanpos - 1, x);
++ } else {
++ memset(&regs, 0, sizeof(regs));
++ for (x=0;x<NUM_FXO_REGS;x++)
++ regs.direct[x] = ystdm_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("Setting indirect %d to 0x%04x on %d\n", regop.reg, regop.val, chan->chanpos);
++ ystdm_proslic_setreg_indirect(wc, chan->chanpos - 1, regop.reg, regop.val);
++ } else {
++ regop.val &= 0xff;
++ printk("Setting direct %d to %04x on %d\n", regop.reg, regop.val, chan->chanpos);
++ ystdm_setreg(wc, chan->chanpos - 1, regop.reg, regop.val);
++ }
++ break;
++ case WCTDM_SET_ECHOTUNE:
++ printk("-- 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 */
++ ystdm_setreg(wc, chan->chanpos - 1, 30, echoregs.acim);
++
++ /* Set the digital echo canceller registers */
++ ystdm_setreg(wc, chan->chanpos - 1, 45, echoregs.coef1);
++ ystdm_setreg(wc, chan->chanpos - 1, 46, echoregs.coef2);
++ ystdm_setreg(wc, chan->chanpos - 1, 47, echoregs.coef3);
++ ystdm_setreg(wc, chan->chanpos - 1, 48, echoregs.coef4);
++ ystdm_setreg(wc, chan->chanpos - 1, 49, echoregs.coef5);
++ ystdm_setreg(wc, chan->chanpos - 1, 50, echoregs.coef6);
++ ystdm_setreg(wc, chan->chanpos - 1, 51, echoregs.coef7);
++ ystdm_setreg(wc, chan->chanpos - 1, 52, echoregs.coef8);
++
++ printk("-- 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;
++
++ ystdm_set_hwgain(wc, chan->chanpos-1, hwgain.newgain, hwgain.tx);
++
++ if (debug)
++ printk("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 ystdm_open(struct dahdi_chan *chan)
++{
++ struct ystdm *wc = chan->pvt;
++ if (!(wc->cardflag & (1 << (chan->chanpos - 1))))
++ return -ENODEV;
++ if (wc->dead)
++ return -ENODEV;
++ wc->usecount++;
++ return 0;
++}
++
++static inline struct ystdm *ystdm_from_span(struct dahdi_span *span)
++{
++ return container_of(span, struct ystdm, span);
++}
++
++static int ystdm_watchdog(struct dahdi_span *span, int event)
++{
++ printk("TDM: Restarting DMA\n");
++ ystdm_restart_dma(ystdm_from_span(span));
++ return 0;
++}
++
++static int ystdm_close(struct dahdi_chan *chan)
++{
++ struct ystdm *wc = chan->pvt;
++ struct fxs *const fxs = &wc->mod[chan->chanpos - 1].fxs;
++ wc->usecount--;
++ if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) {
++ int idlehookstate;
++ idlehookstate = POLARITY_XOR ?
++ SLIC_LF_ACTIVE_REV :
++ SLIC_LF_ACTIVE_FWD;
++ fxs->idletxhookstate = idlehookstate;
++ }
++ /* If we're dead, release us now */
++ if (!wc->usecount && wc->dead)
++ ystdm_release(wc);
++ return 0;
++}
++
++static int ystdm_init_ring_generator_mode(struct ystdm *wc, int card)
++{
++ ystdm_setreg(wc, card, 34, 0x00); /* Ringing Osc. Control */
++
++ /* neon trapezoid timers */
++ ystdm_setreg(wc, card, 48, 0xe0); /* Active Timer low byte */
++ ystdm_setreg(wc, card, 49, 0x01); /* Active Timer high byte */
++ ystdm_setreg(wc, card, 50, 0xF0); /* Inactive Timer low byte */
++ ystdm_setreg(wc, card, 51, 0x05); /* Inactive Timer high byte */
++
++ ystdm_set_ring_generator_mode(wc, card, 0);
++
++ return 0;
++}
++
++static int ystdm_set_ring_generator_mode(struct ystdm *wc, int card, int mode)
++{
++ int reg20, reg21, reg74; /* RCO, RNGX, VBATH */
++ struct fxs *const fxs = &wc->mod[card].fxs;
++
++ fxs->neonringing = mode; /* track ring generator mode */
++
++ if (mode) { /* Neon */
++ if (debug)
++ printk(KERN_DEBUG "NEON ring on chan %d, "
++ "lasttxhook was 0x%x\n", card, fxs->lasttxhook);
++ /* Must be in FORWARD ACTIVE before setting ringer */
++ fxs->lasttxhook = SLIC_LF_ACTIVE_FWD;
++ ystdm_setreg(wc, card, LINE_STATE, fxs->lasttxhook);
++
++ ystdm_proslic_setreg_indirect(wc, card, 22,
++ NEON_MWI_RNGY_PULSEWIDTH);
++ ystdm_proslic_setreg_indirect(wc, card, 21,
++ 0x7bef); /* RNGX (91.5Vpk) */
++ ystdm_proslic_setreg_indirect(wc, card, 20,
++ 0x009f); /* RCO (RNGX, t rise)*/
++
++ ystdm_setreg(wc, card, 34, 0x19); /* Ringing Osc. Control */
++ ystdm_setreg(wc, card, 74, 0x3f); /* VBATH 94.5V */
++ ystdm_proslic_setreg_indirect(wc, card, 29, 0x4600); /* RPTP */
++ /* A write of 0x04 to register 64 will turn on the VM led */
++ } else {
++ ystdm_setreg(wc, card, 34, 0x00); /* Ringing Osc. Control */
++ /* RNGY Initial Phase */
++ ystdm_proslic_setreg_indirect(wc, card, 22, 0x0000);
++ ystdm_proslic_setreg_indirect(wc, card, 29, 0x3600); /* RPTP */
++ /* A write of 0x04 to register 64 will turn on the ringer */
++
++ if (fastringer) {
++ /* Speed up Ringer */
++ reg20 = 0x7e6d;
++ reg74 = 0x32; /* Default */
++ /* Beef up Ringing voltage to 89V */
++ if (boostringer) {
++ reg74 = 0x3f;
++ reg21 = 0x0247; /* RNGX */
++ if (debug)
++ printk(KERN_DEBUG "Boosting fast ringer"
++ " on chan %d (89V peak)\n",
++ card);
++ } else if (lowpower) {
++ reg21 = 0x014b; /* RNGX */
++ if (debug)
++ printk(KERN_DEBUG "Reducing fast ring "
++ "power on chan %d (50V peak)\n",
++ card);
++ } else if (fxshonormode &&
++ fxo_modes[_opermode].ring_x) {
++ reg21 = fxo_modes[_opermode].ring_x;
++ if (debug)
++ printk(KERN_DEBUG "fxshonormode: fast "
++ "ring_x power on chan %d\n",
++ card);
++ } else {
++ reg21 = 0x01b9;
++ if (debug)
++ printk(KERN_DEBUG "Speeding up ringer "
++ "on chan %d (25Hz)\n",
++ card);
++ }
++ /* VBATH */
++ ystdm_setreg(wc, card, 74, reg74);
++ /*RCO*/
++ ystdm_proslic_setreg_indirect(wc, card, 20, reg20);
++ /*RNGX*/
++ ystdm_proslic_setreg_indirect(wc, card, 21, reg21);
++
++ } else {
++ /* Ringer Speed */
++ if (fxshonormode && fxo_modes[_opermode].ring_osc) {
++ reg20 = fxo_modes[_opermode].ring_osc;
++ if (debug)
++ printk(KERN_DEBUG "fxshonormode: "
++ "ring_osc speed on chan %d\n",
++ card);
++ } else {
++ reg20 = 0x7ef0; /* Default */
++ }
++
++ reg74 = 0x32; /* Default */
++ /* Beef up Ringing voltage to 89V */
++ if (boostringer) {
++ reg74 = 0x3f;
++ reg21 = 0x1d1;
++ if (debug)
++ printk(KERN_DEBUG "Boosting ringer on "
++ "chan %d (89V peak)\n",
++ card);
++ } else if (lowpower) {
++ reg21 = 0x108;
++ if (debug)
++ printk(KERN_DEBUG "Reducing ring power "
++ "on chan %d (50V peak)\n",
++ card);
++ } else if (fxshonormode &&
++ fxo_modes[_opermode].ring_x) {
++ reg21 = fxo_modes[_opermode].ring_x;
++ if (debug)
++ printk(KERN_DEBUG "fxshonormode: ring_x"
++ " power on chan %d\n",
++ card);
++ } else {
++ reg21 = 0x160;
++ if (debug)
++ printk(KERN_DEBUG "Normal ring power on"
++ " chan %d\n",
++ card);
++ }
++ /* VBATH */
++ ystdm_setreg(wc, card, 74, reg74);
++ /* RCO */
++ ystdm_proslic_setreg_indirect(wc, card, 20, reg20);
++ /* RNGX */
++ ystdm_proslic_setreg_indirect(wc, card, 21, reg21);
++ }
++ }
++ return 0;
++}
++
++static int ystdm_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig)
++{
++ struct ystdm *wc = chan->pvt;
++ int chan_entry = chan->chanpos - 1;
++ if (wc->modtype[chan_entry] == MOD_TYPE_FXO) {
++ /* XXX Enable hooksig for FXO XXX */
++ switch(txsig) {
++ case DAHDI_TXSIG_START:
++ case DAHDI_TXSIG_OFFHOOK:
++ wc->mod[chan_entry].fxo.offhook = 1;
++ ystdm_setreg(wc, chan_entry, 5, 0x9);
++ break;
++ case DAHDI_TXSIG_ONHOOK:
++ wc->mod[chan_entry].fxo.offhook = 0;
++ ystdm_setreg(wc, chan_entry, 5, 0x8);
++ break;
++ default:
++ printk("wcfxo: Can't set tx state to %d\n", txsig);
++ }
++ } else {
++ ystdm_fxs_hooksig(wc, chan_entry, txsig);
++ }
++ return 0;
++}
++
++static const struct dahdi_span_ops ystdm_span_ops = {
++ .owner = THIS_MODULE,
++ .hooksig = ystdm_hooksig,
++ .open = ystdm_open,
++ .close = ystdm_close,
++ .ioctl = ystdm_ioctl,
++ .watchdog = ystdm_watchdog,
++};
++
++static int ystdm_initialize(struct ystdm *wc)
++{
++ int x;
++
++ wc->ddev = dahdi_create_device();
++ if (!wc->ddev)
++ return -ENOMEM;
++
++ /* Zapata stuff */
++ sprintf(wc->span.name, "WCTDM/%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 = "YEASTAR";
++ wc->ddev->devicetype = wc->variety;
++
++
++ if (alawoverride) {
++ printk("ALAW override parameter detected. Device will be operating in ALAW\n");
++ wc->span.deflaw = DAHDI_LAW_ALAW;
++ } else {
++ wc->span.deflaw = DAHDI_LAW_MULAW;
++ }
++ for (x = 0; x < NUM_CARDS; x++) {
++ sprintf(wc->chans[x]->name, "WCTDM/%d/%d", wc->pos, x);
++ wc->chans[x]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR;
++ wc->chans[x]->sigcap |= DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR;
++ wc->chans[x]->chanpos = x+1;
++ wc->chans[x]->pvt = wc;
++ }
++
++ wc->span.chans = wc->chans;
++ wc->span.channels = NUM_CARDS;
++ wc->span.flags = DAHDI_FLAG_RBS;
++ wc->span.ops = &ystdm_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 span with DAHDI\n");
++ kfree(wc->ddev->location);
++ dahdi_free_device(wc->ddev);
++ wc->ddev = NULL;
++ return -1;
++ }
++
++ return 0;
++}
++
++static void ystdm_post_initialize(struct ystdm *wc)
++{
++ int x;
++ /* Finalize signalling */
++ for (x = 0; x < NUM_CARDS; x++) {
++ if (wc->cardflag & (1 << x)) {
++ if (wc->modtype[x] == MOD_TYPE_FXO)
++ wc->chans[x]->sigcap = 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 ystdm_hardware_init(struct ystdm *wc)
++{
++ /* Hardware stuff */
++ unsigned char ver;
++ unsigned char x,y;
++ unsigned char ol = 0, sl = 0;
++ int failed;
++
++ /* Signal Reset */
++ outb(0x01, wc->ioaddr + WC_CNTL);
++
++ /* Check Freshmaker chip */
++ x=inb(wc->ioaddr + WC_CNTL);
++ ver = __ystdm_getcreg(wc, WC_VER);
++ failed = 0;
++ if (ver != 0x59) {
++ printk("Freshmaker version: %02x\n", ver);
++ for (x=0;x<255;x++) {
++ /* Test registers */
++ if (ver >= 0x70) {
++ __ystdm_setcreg(wc, WC_CS, x);
++ y = __ystdm_getcreg(wc, WC_CS);
++ } else {
++ __ystdm_setcreg(wc, WC_TEST, x);
++ y = __ystdm_getcreg(wc, WC_TEST);
++ }
++ if (x != y) {
++ printk("%02x != %02x\n", x, y);
++ failed++;
++ }
++ }
++ if (!failed) {
++ printk("Freshmaker passed register test\n");
++ } else {
++ printk("Freshmaker failed register test\n");
++ return -1;
++ }
++ } else {
++ printk("No freshmaker chip\n");
++ }
++
++ /* 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 | BIT_SYNC;
++
++ outb(wc->ios, wc->ioaddr + WC_AUXD);
++
++ /* Set all to outputs except AUX 5, which is an input */
++ outb(0xdf, wc->ioaddr + WC_AUXC);
++
++ /* 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);
++
++ /* 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(0x0, wc->ioaddr + WC_FSCDELAY);
++
++ /* Setup DMA Addresses */
++ outl(wc->writedma, wc->ioaddr + WC_DMAWS); /* Write start */
++ outl(wc->writedma + DAHDI_CHUNKSIZE * NUM_CARDS - 4, wc->ioaddr + WC_DMAWI); /* Middle (interrupt) */
++ outl(wc->writedma + 2 * DAHDI_CHUNKSIZE * NUM_CARDS - 4, wc->ioaddr + WC_DMAWE); /* End */
++
++ outl(wc->readdma, wc->ioaddr + WC_DMARS); /* Read start */
++ outl(wc->readdma + DAHDI_CHUNKSIZE * NUM_CARDS - 4, wc->ioaddr + WC_DMARI); /* Middle (interrupt) */
++ outl(wc->readdma + 2 * DAHDI_CHUNKSIZE * NUM_CARDS - 4, wc->ioaddr + WC_DMARE); /* End */
++
++ /* Clear interrupts */
++ outb(0xff, wc->ioaddr + WC_INTSTAT);
++
++ /* Wait 1/4 of a second more */
++ wait_just_a_bit(HZ/4);
++
++ for (x = 0; x < NUM_CARDS; x++) {
++ int sane=0,ret=0,readi=0;
++#if 1
++ /* Init with Auto Calibration */
++ if (!(ret=ystdm_init_proslic(wc, x, 0, 0, sane))) {
++ wc->cardflag |= (1 << x);
++ sl |= (1 << x);
++ if (debug) {
++ readi = ystdm_getreg(wc,x,LOOP_I_LIMIT);
++ printk("Proslic module %d loop current is %dmA\n",x,
++ ((readi*3)+20));
++ }
++ printk("Module %d: Installed -- AUTO FXS/DPO\n",x);
++ } else {
++ if(ret!=-2) {
++ sane=1;
++ /* Init with Manual Calibration */
++ if (!ystdm_init_proslic(wc, x, 0, 1, sane)) {
++ wc->cardflag |= (1 << x);
++ sl |= (1 << x);
++ if (debug) {
++ readi = ystdm_getreg(wc,x,LOOP_I_LIMIT);
++ printk("Proslic module %d loop current is %dmA\n",x,
++ ((readi*3)+20));
++ }
++ printk("Module %d: Installed -- MANUAL FXS\n",x);
++ } else {
++ printk("Module %d: FAILED FXS (%s)\n", x, fxshonormode ? fxo_modes[_opermode].name : "FCC");
++ wc->chans[x]->sigcap = __DAHDI_SIG_FXO | DAHDI_SIG_BROKEN;
++ }
++ } else if (!(ret = ystdm_init_voicedaa(wc, x, 0, 0, sane))) {
++ wc->cardflag |= (1 << x);
++ ol |= (1 << x);
++ printk("Module %d: Installed -- AUTO FXO (%s mode)\n",x, fxo_modes[_opermode].name);
++ } else
++ printk("Module %d: Not installed\n", x);
++ }
++#endif
++ }
++
++ /* Return error if nothing initialized okay. */
++ if (!wc->cardflag && !timingonly)
++ return -1;
++ if(ver == 0x88)
++ __ystdm_setcreg(wc, WC_SYNC, wc->cardflag);
++ else{
++ __ystdm_setcreg(wc, WC_SYNC, ol);
++ __ystdm_setcreg(wc, YS_SLC, sl);
++ }
++ return 0;
++}
++
++static void ystdm_enable_interrupts(struct ystdm *wc)
++{
++ /* Enable interrupts (we care about all of them) */
++ outb(0x3f, wc->ioaddr + WC_MASK0);
++ /* No external interrupts */
++ outb(0x00, wc->ioaddr + WC_MASK1);
++}
++
++static void ystdm_restart_dma(struct ystdm *wc)
++{
++ /* Reset Master and TDM */
++ outb(0x01, wc->ioaddr + WC_CNTL);
++ outb(0x01, wc->ioaddr + WC_OPER);
++}
++
++static void ystdm_start_dma(struct ystdm *wc)
++{
++ /* Reset Master and TDM */
++ unsigned char x,y;
++ outb(0x0f, wc->ioaddr + WC_CNTL);
++ wc->ios &= ~BIT_SYNC;
++ outb(wc->ios, wc->ioaddr + WC_AUXD);
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(1);
++ wc->ios |= BIT_SYNC;
++ outb(wc->ios, wc->ioaddr + WC_AUXD);
++ outb(0x01, wc->ioaddr + WC_CNTL);
++ outb(0x01, wc->ioaddr + WC_OPER);
++ y = __ystdm_getcreg(wc, WC_TEST);
++ x = y | 0x01;
++ __ystdm_setcreg(wc, WC_TEST, x);
++}
++
++static void ystdm_stop_dma(struct ystdm *wc)
++{
++ unsigned char x,y;
++ wc->ios &= ~BIT_SYNC;
++ outb(wc->ios, wc->ioaddr + WC_AUXD);
++ outb(0x00, wc->ioaddr + WC_OPER);
++ y = __ystdm_getcreg(wc, WC_TEST);
++ x = y & 0xFE;
++ __ystdm_setcreg(wc, WC_TEST, x);
++}
++
++static void ystdm_reset_tdm(struct ystdm *wc)
++{
++ /* Reset TDM */
++ outb(0x0f, wc->ioaddr + WC_CNTL);
++}
++
++static void ystdm_disable_interrupts(struct ystdm *wc)
++{
++ outb(0x00, wc->ioaddr + WC_MASK0);
++ outb(0x00, wc->ioaddr + WC_MASK1);
++}
++
++static int __devinit ystdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
++{
++ int res;
++ struct ystdm *wc;
++ struct ystdm_desc *d = (struct ystdm_desc *)ent->driver_data;
++ int x;
++ int y;
++
++
++
++ for (x=0;x<WC_MAX_IFACES;x++)
++ if (!ifaces[x]) break;
++ if (x >= WC_MAX_IFACES) {
++ printk("Too many interfaces\n");
++ return -EIO;
++ }
++
++ if (pci_enable_device(pdev)) {
++ res = -EIO;
++ } else {
++ wc = kmalloc(sizeof(struct ystdm), GFP_KERNEL);
++ if (wc) {
++ int cardcount = 0;
++
++ ifaces[x] = wc;
++ memset(wc, 0, sizeof(struct ystdm));
++ 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->dev = pdev;
++ wc->pos = x;
++ wc->variety = d->name;
++ for (y=0;y<NUM_CARDS;y++)
++ wc->flags[y] = d->flags;
++ /* Keep track of whether we need to free the region */
++ if (request_region(wc->ioaddr, 0xff, "ystdm"))
++ wc->freeregion = 1;
++
++ /* Allocate enough memory for two zt chunks, receive and transmit. Each sample uses
++ 32 bits. Allocate an extra set just for control too */
++ wc->writechunk = pci_alloc_consistent(pdev, DAHDI_MAX_CHUNKSIZE * 2 * 2 * 2 * NUM_CARDS, &wc->writedma);
++ if (!wc->writechunk) {
++ printk("ystdm: Unable to allocate DMA-able memory\n");
++ if (wc->freeregion)
++ release_region(wc->ioaddr, 0xff);
++ return -ENOMEM;
++ }
++
++ wc->readchunk = wc->writechunk + 2 * DAHDI_MAX_CHUNKSIZE * (NUM_CARDS / 4); /* in doublewords */
++ wc->readdma = wc->writedma + 2 * DAHDI_MAX_CHUNKSIZE * (NUM_CARDS / 1); /* in bytes */
++
++ if (ystdm_initialize(wc)) {
++ printk("ystdm: 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)
++ release_region(wc->ioaddr, 0xff);
++ pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * 2 * 2 * 2 * NUM_CARDS, (void *)wc->writechunk, wc->writedma);
++ kfree(wc);
++ return -EIO;
++ }
++
++ /* Enable bus mastering */
++ pci_set_master(pdev);
++
++ /* Keep track of which device we are */
++ pci_set_drvdata(pdev, wc);
++
++ if (request_irq(pdev->irq, ystdm_interrupt, DAHDI_IRQ_SHARED, "ystdm", wc)) {
++ printk("ystdm: Unable to request IRQ %d\n", pdev->irq);
++ if (wc->freeregion)
++ release_region(wc->ioaddr, 0xff);
++ pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * 2 * 2 * 2 * NUM_CARDS, (void *)wc->writechunk, wc->writedma);
++ pci_set_drvdata(pdev, NULL);
++ kfree(wc);
++ return -EIO;
++ }
++
++
++ if (ystdm_hardware_init(wc)) {
++ unsigned char x;
++
++ /* Set Reset Low */
++ x=inb(wc->ioaddr + WC_CNTL);
++ outb((~0x1)&x, wc->ioaddr + WC_CNTL);
++ /* Free Resources */
++ free_irq(pdev->irq, wc);
++ if (wc->freeregion)
++ release_region(wc->ioaddr, 0xff);
++ pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * 2 * 2 * 2 * NUM_CARDS, (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;
++
++ }
++
++ ystdm_post_initialize(wc);
++
++ /* Enable interrupts */
++ ystdm_enable_interrupts(wc);
++ /* Initialize Write/Buffers to all blank data */
++ memset((void *)wc->writechunk,0,DAHDI_MAX_CHUNKSIZE * 2 * 2 * NUM_CARDS);
++
++ /* Start DMA */
++ ystdm_start_dma(wc);
++
++ for (x = 0; x < NUM_CARDS; x++) {
++ if (wc->cardflag & (1 << x))
++ cardcount++;
++ }
++
++ printk("Found a YSTDM8xx: %s (%d modules)\n", wc->variety, cardcount);
++ res = 0;
++ } else
++ res = -ENOMEM;
++ }
++ return res;
++}
++
++static void ystdm_release(struct ystdm *wc)
++{
++ dahdi_unregister_device(wc->ddev);
++ if (wc->freeregion)
++ release_region(wc->ioaddr, 0xff);
++
++ kfree(wc->ddev->location);
++ dahdi_free_device(wc->ddev);
++
++ kfree(wc);
++ printk("Freed a Wildcard\n");
++}
++
++static void __devexit ystdm_remove_one(struct pci_dev *pdev)
++{
++ struct ystdm *wc = pci_get_drvdata(pdev);
++ if (wc) {
++
++ /* Stop any DMA */
++ ystdm_stop_dma(wc);
++ ystdm_reset_tdm(wc);
++
++ /* In case hardware is still there */
++ ystdm_disable_interrupts(wc);
++
++ /* Immediately free resources */
++ pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * 2 * 2 * 2 * NUM_CARDS, (void *)wc->writechunk, wc->writedma);
++ free_irq(pdev->irq, wc);
++
++ /* Reset PCI chip and registers */
++ outb(0x0e, wc->ioaddr + WC_CNTL);
++
++ /* Release span, possibly delayed */
++ if (!wc->usecount)
++ ystdm_release(wc);
++ else
++ wc->dead = 1;
++ }
++}
++
++static DEFINE_PCI_DEVICE_TABLE(ystdm_pci_tbl) = {
++ { 0xe159, 0x0001, 0x2151, PCI_ANY_ID, 0, 0, (unsigned long) &ystdme },
++ { 0 }
++};
++
++MODULE_DEVICE_TABLE(pci, ystdm_pci_tbl);
++
++static int ystdm_suspend(struct pci_dev *pdev, pm_message_t state)
++{
++ return -ENOSYS;
++}
++
++static struct pci_driver ystdm_driver = {
++ .name = "ystdm8xx",
++ .probe = ystdm_init_one,
++ .remove = __devexit_p(ystdm_remove_one),
++ .suspend = ystdm_suspend,
++ .id_table = ystdm_pci_tbl,
++};
++
++static int __init ystdm_init(void)
++{
++ int res;
++ int x;
++
++ for (x=0;x<(sizeof(fxo_modes) / sizeof(fxo_modes[0])); x++) {
++ if (!strcmp(fxo_modes[x].name, opermode))
++ break;
++ }
++ if (x < sizeof(fxo_modes) / sizeof(fxo_modes[0])) {
++ _opermode = x;
++ } else {
++ printk("Invalid/unknown operating mode '%s' specified. Please choose one of:\n", opermode);
++ for (x = 0; x < sizeof(fxo_modes) / sizeof(fxo_modes[0]); x++)
++ printk(" %s\n", fxo_modes[x].name);
++ printk("Note this option is CASE SENSITIVE!\n");
++ return -ENODEV;
++ }
++ if (!strcmp(opermode, "AUSTRALIA")) {
++ boostringer = 1;
++ fxshonormode = 1;
++ }
++
++ /* for the voicedaa_check_hook defaults, if the user has not overridden
++ them by specifying them as module parameters, then get the values
++ from the selected operating mode
++ */
++ 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(&ystdm_driver);
++ if (res)
++ return -ENODEV;
++ return 0;
++}
++
++static void __exit ystdm_cleanup(void)
++{
++ pci_unregister_driver(&ystdm_driver);
++}
++
++module_param(debug, int, 0600);
++module_param(fxovoltage, 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(battalarm, uint, 0600);
++module_param(battthresh, uint, 0600);
++module_param(ringdebounce, int, 0600);
++module_param(dialdebounce, int, 0600);
++module_param(fwringdetect, int, 0600);
++module_param(alawoverride, int, 0600);
++module_param(fastpickup, int, 0600);
++module_param(fxotxgain, int, 0600);
++module_param(fxorxgain, int, 0600);
++module_param(fxstxgain, int, 0600);
++module_param(fxsrxgain, int, 0600);
++module_param(dtmf, int, 0600);
++
++MODULE_DESCRIPTION("YSTDM8xx Yeastar Driver");
++MODULE_AUTHOR("yeastar <support@yeastar.com>");
++MODULE_ALIAS("ystdm8xx");
++MODULE_LICENSE("GPL v2");
++
++module_init(ystdm_init);
++module_exit(ystdm_cleanup);
diff --git a/dahdi-linux.install b/dahdi-linux.install
new file mode 100644
index 000000000000..496f8f5ba066
--- /dev/null
+++ b/dahdi-linux.install
@@ -0,0 +1,15 @@
+# post install.
+post_install() {
+ depmod $(cat /usr/lib/modules/extramodules-4.0-ARCH/version)
+ udevadm control --reload-rules
+}
+
+# post upgrade.
+post_upgrade() {
+ post_install
+}
+
+# post remove.
+post_remove() {
+ post_install
+}