diff options
author | Maik Broemme | 2015-06-30 18:32:32 +0200 |
---|---|---|
committer | Maik Broemme | 2015-06-30 18:32:32 +0200 |
commit | 5d364f149da51e486ded92a4ea8ac5f4169ca12c (patch) | |
tree | c4b93642cd8912f4a52b2b101ad37e66e3a226a3 | |
download | aur-5d364f149da51e486ded92a4ea8ac5f4169ca12c.tar.gz |
Initial import
-rw-r--r-- | .SRCINFO | 35 | ||||
-rw-r--r-- | PKGBUILD | 71 | ||||
-rw-r--r-- | dahdi-linux-2.10.1-allo.patch | 7300 | ||||
-rw-r--r-- | dahdi-linux-2.10.1-openvox-1.patch | 6228 | ||||
-rw-r--r-- | dahdi-linux-2.10.1-openvox-2.patch | 5851 | ||||
-rw-r--r-- | dahdi-linux-2.10.1-openvox-3.patch | 7272 | ||||
-rw-r--r-- | dahdi-linux-2.10.1-yeastar.patch | 6269 | ||||
-rw-r--r-- | dahdi-linux.install | 15 |
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(®, (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(®, (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, ++ ®, 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, ++ ®s, 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, ®s)) { ++ 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(®s, 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, ®s, sizeof(regs))) ++ return -EFAULT; ++ break; ++ case WCTDM_SET_REG: ++ if (copy_from_user(®op, (__user void *)data, sizeof(regop))) ++ return -EFAULT; ++ if (regop.indirect) { ++ if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) ++ return -EINVAL; ++ printk(KERN_INFO "Setting indirect %d to 0x%04x on %d\n", regop.reg, regop.val, chan->chanpos); ++ wctdm_proslic_setreg_indirect(wc, chan->chanpos - 1, regop.reg, regop.val); ++ } else { ++ regop.val &= 0xff; ++ printk(KERN_INFO "Setting direct %d to %04x on %d\n", regop.reg, regop.val, chan->chanpos); ++ wctdm_setreg(wc, chan->chanpos - 1, regop.reg, regop.val); ++ } ++ break; ++ case WCTDM_SET_ECHOTUNE: ++ printk(KERN_INFO "-- Setting echo registers: \n"); ++ if (copy_from_user(&echoregs, (__user void *)data, sizeof(echoregs))) ++ return -EFAULT; ++ ++ if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) { ++ /* Set the ACIM register */ ++ wctdm_setreg(wc, chan->chanpos - 1, 30, (fxofullscale==1) ? (echoregs.acim|0x10) : echoregs.acim); ++ ++ /* Set the digital echo canceller registers */ ++ wctdm_setreg(wc, chan->chanpos - 1, 45, echoregs.coef1); ++ wctdm_setreg(wc, chan->chanpos - 1, 46, echoregs.coef2); ++ wctdm_setreg(wc, chan->chanpos - 1, 47, echoregs.coef3); ++ wctdm_setreg(wc, chan->chanpos - 1, 48, echoregs.coef4); ++ wctdm_setreg(wc, chan->chanpos - 1, 49, echoregs.coef5); ++ wctdm_setreg(wc, chan->chanpos - 1, 50, echoregs.coef6); ++ wctdm_setreg(wc, chan->chanpos - 1, 51, echoregs.coef7); ++ wctdm_setreg(wc, chan->chanpos - 1, 52, echoregs.coef8); ++ ++ printk(KERN_INFO "-- Set echo registers successfully\n"); ++ ++ break; ++ } else { ++ return -EINVAL; ++ ++ } ++ break; ++ case DAHDI_SET_HWGAIN: ++ if (copy_from_user(&hwgain, (__user void *) data, sizeof(hwgain))) ++ return -EFAULT; ++ ++ wctdm_set_hwgain(wc, chan->chanpos-1, hwgain.newgain, hwgain.tx); ++ ++ if (debug) ++ printk(KERN_DEBUG "Setting hwgain on channel %d to %d for %s direction\n", ++ chan->chanpos-1, hwgain.newgain, hwgain.tx ? "tx" : "rx"); ++ break; ++ default: ++ return -ENOTTY; ++ } ++ return 0; ++ ++} ++ ++static int wctdm_open(struct dahdi_chan *chan) ++{ ++ struct wctdm *wc = chan->pvt; ++ if (!(wc->cardflag & (1 << (chan->chanpos - 1)))) ++ return -ENODEV; ++ if (wc->dead) ++ return -ENODEV; ++ wc->usecount++; ++ ++ /*MOD_INC_USE_COUNT; */ ++ try_module_get(THIS_MODULE); ++ return 0; ++} ++ ++static 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(®s, 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, ®s, sizeof(regs))) { ++ return -EFAULT; ++ } ++ break; ++ case WCTDM_SET_REG: ++ if (copy_from_user(®op, (__user void *)data, sizeof(regop))) { ++ return -EFAULT; ++ } ++ if (regop.indirect) { ++ if (wc_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, ¶m->tone[j].busycount); ++ create_param_proc("threshold", subentry, ¶m->tone[j].threshold); ++ create_param_proc("ontime", subentry, ¶m->tone[j].ontime); ++ create_param_proc("offtime", subentry, ¶m->tone[j].offtime); ++ for (k = 0; k < TONE_FREQ_NUM; k++) { ++ sprintf(temp, "frequency%d", k + 1); ++ create_param_freq_proc(temp, subentry, ¶m->tone[j].fs[k]); ++ } ++ } ++ } ++ subentry = proc_mkdir("silent_detect", entry); ++ param->sd.subentry = subentry; ++ if (subentry) { ++ create_param_proc("detect_tx", subentry, ¶m->sd.detect_tx); ++ create_param_proc("detect_rx", subentry, ¶m->sd.detect_rx); ++ create_param_proc("length", subentry, ¶m->sd.length); ++ create_param_proc("threshold", subentry, ¶m->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(®, (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(®, (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, ++ ®, 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, ++ ®s, 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, ®s)) { ++ 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(®s, 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, ®s, sizeof(regs))) ++ return -EFAULT; ++ break; ++ case WCTDM_SET_REG: ++ if (copy_from_user(®op, (__user void *)data, sizeof(regop))) ++ return -EFAULT; ++ if (regop.indirect) { ++ if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) ++ return -EINVAL; ++ printk("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(®s, 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, ®s, sizeof(regs))) ++ return -EFAULT; ++ break; ++ case WCTDM_SET_REG: ++ if (copy_from_user(®op, (__user void *)data, sizeof(regop))) ++ return -EFAULT; ++ if (regop.indirect) { ++ if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) ++ return -EINVAL; ++ printk("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 +} |