[Pkg-voip-commits] r4698 - in /zaptel/trunk: debian/changelog debian/copyright debian/rules wcopenpci.c

tzafrir-guest at alioth.debian.org tzafrir-guest at alioth.debian.org
Sat Oct 6 22:33:36 UTC 2007


Author: tzafrir-guest
Date: Sat Oct  6 22:33:35 2007
New Revision: 4698

URL: http://svn.debian.org/wsvn/pkg-voip/?sc=1&rev=4698
Log:
Add VoiceTronix OpenPCI Zaptel driver.

Added:
    zaptel/trunk/wcopenpci.c
Modified:
    zaptel/trunk/debian/changelog
    zaptel/trunk/debian/copyright
    zaptel/trunk/debian/rules

Modified: zaptel/trunk/debian/changelog
URL: http://svn.debian.org/wsvn/pkg-voip/zaptel/trunk/debian/changelog?rev=4698&op=diff
==============================================================================
--- zaptel/trunk/debian/changelog (original)
+++ zaptel/trunk/debian/changelog Sat Oct  6 22:33:35 2007
@@ -1,8 +1,13 @@
 zaptel (1:1.4.5.1~dfsg-3) UNRELEASED; urgency=low
 
+  [ Kilian Krause ]
   * NOT RELEASED YET
 
- -- Kilian Krause <kilian at debian.org>  Sat, 22 Sep 2007 17:56:08 +0200
+  [ Tzafrir Cohen ]
+  * Add Voicetronix OpenPCI Zaptel driver (wcopenpci.c) .
+  * Explicitly delete depmod results (modules.dep at el.)
+
+ -- Tzafrir Cohen <tzafrir.cohen at xorcom.com>  Sat,  6 Oct 2007 23:10:56 +0200
 
 zaptel (1:1.4.5.1~dfsg-2) unstable; urgency=low
 

Modified: zaptel/trunk/debian/copyright
URL: http://svn.debian.org/wsvn/pkg-voip/zaptel/trunk/debian/copyright?rev=4698&op=diff
==============================================================================
--- zaptel/trunk/debian/copyright (original)
+++ zaptel/trunk/debian/copyright Sat Oct  6 22:33:35 2007
@@ -71,6 +71,11 @@
 http://www.openvox.com.cn/members_downloads.php
 (requires no login. Does require javascript)
 
+wcopenpci.c is from the vt zaptel distribution at
+http://www.voicetronix.com/Downloads/asterisk/ .
+Copyright (C) 2001, Linux Support Services, Inc.
+Copyright (C) 2005, 2006, VoiceTronix
+
 
 Files in oslec/ subdirectory and oslec patches:
 Part of the Open Source Line Echo Canceller (OSLEC) project:

Modified: zaptel/trunk/debian/rules
URL: http://svn.debian.org/wsvn/pkg-voip/zaptel/trunk/debian/rules?rev=4698&op=diff
==============================================================================
--- zaptel/trunk/debian/rules (original)
+++ zaptel/trunk/debian/rules Sat Oct  6 22:33:35 2007
@@ -41,7 +41,7 @@
 # the generated headers as part of the source:
 GENERATED_SOURCES := version.h radfw.h tor2fw.h tones.h 
 
-EXTRA_MODS=ds1x1f opvxa1200
+EXTRA_MODS=ds1x1f opvxa1200 wcopenpci
 
 BRISTUFF_MODULES=cwain qozap zaphfc ztgsm
 MOD_EXAMPLES_DIR:=modexamples

Added: zaptel/trunk/wcopenpci.c
URL: http://svn.debian.org/wsvn/pkg-voip/zaptel/trunk/wcopenpci.c?rev=4698&op=file
==============================================================================
--- zaptel/trunk/wcopenpci.c (added)
+++ zaptel/trunk/wcopenpci.c Sat Oct  6 22:33:35 2007
@@ -1,0 +1,2256 @@
+/*
+ * VoiceTronix OpenPCI Interface Driver for Zapata Telephony interface
+ *
+ * Written by Mark Spencer <markster at linux-support.net>
+ *            Matthew Fredrickson <creslin at linux-support.net>
+ *            Ben Kramer <ben at voicetronix.com.au>
+ *            Ron Lee <ron at voicetronix.com.au>
+ *
+ * Copyright (C) 2001, Linux Support Services, Inc.
+ * Copyright (C) 2005, 2006, VoiceTronix
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *
+ */
+
+/* Conditional debug options */
+#define VERBOSE_TIMING 0
+
+/* Driver constants */
+#define DRIVER_DESCRIPTION  "VoiceTronix OpenPCI zaptel driver"
+#define DRIVER_AUTHOR       "Mark Spencer <markster at digium.com>"\
+                            "VoiceTronix <support at voicetronix.com.au>"
+
+#define NAME      "wcopenpci"
+#define MAX_PORTS 8	    /* Maximum number of ports on each carrier */
+#define MAX_CARDS 8	    /* Maximum number of carriers per host */
+
+#define DEFAULT_COUNTRY  "AUSTRALIA"
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#ifdef STANDALONE_ZAPATA
+ #include "zaptel.h"
+#else
+ #include <linux/zaptel.h>
+#endif
+
+#include "version.h"
+#include "proslic.h"
+#include "wctdm.h"
+
+
+/* Compatibility helpers */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
+ #include <linux/interrupt.h>
+#else
+ typedef void irqreturn_t;
+ #define IRQ_NONE
+ #define IRQ_HANDLED
+ #define IRQ_RETVAL(x)
+ #define __devexit_p(x) x
+#endif
+
+// Centos4.3 uses a modified 2.6.9 kernel, with no indication that
+// it is different from the mainstream (or even Centos4.2 2.6.9)
+// kernel, so we must crowbar off the dunce-hat manually here.
+#if !defined CENTOS4_3 && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+ typedef int gfp_t;
+ static inline void *kzalloc( size_t n, gfp_t flags ){
+	void *p = kmalloc(n,flags);
+	if (p) memset(p, 0, n);
+	return p;
+ }
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
+ #define DEFINE_MUTEX(x)		DECLARE_MUTEX(x)
+ #define mutex_init(x)			init_MUTEX(x)
+ #define mutex_lock(x)			down(x)
+ #define mutex_lock_interruptible(x)	down_interruptible(x)
+ #define mutex_trylock(x)		down_trylock(x)
+ #define mutex_unlock(x)		up(x)
+#else
+ #include <linux/mutex.h>
+#endif
+
+
+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",0x2000},	// playback volume set lower
+{27,14,"XMIT_DIGITAL_GAIN",0x4000},
+{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}, // Recommmeded in AN47 for SOT89 or si3201)
+//{34,21,"PWR_ALARM_Q5Q6",0x150D}, // Recommened in AN47 for SOT223
+{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 struct fxo_mode {
+	char *name;
+	int ohs;
+	int ohs2;
+	int rz;
+	int rt;
+	int ilim;
+	int dcv;
+	int mini;
+	int acim;
+	int ring_osc;
+	int ring_x;
+} fxo_modes[] =
+{
+	{ "FCC", 0, 0, 0, 1, 0, 0x3, 0, 0, }, 	/* US, Canada */
+	{ "TBR21", 0, 0, 0, 0, 1, 0x3, 0, 0x2, 0x7e6c, 0x023a, },
+		/* Austria, Belgium, Denmark, Finland, France, Germany, 
+		   Greece, Iceland, Ireland, Italy, Luxembourg, Netherlands,
+		   Norway, Portugal, Spain, Sweden, Switzerland, and UK */
+	{ "ARGENTINA", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "AUSTRALIA", 1, 0, 0, 0, 0, 0, 0x3, 0x3, },
+	{ "AUSTRIA", 0, 1, 0, 0, 1, 0x3, 0, 0x3, },
+	{ "BAHRAIN", 0, 0, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "BELGIUM", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "BRAZIL", 0, 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "BULGARIA", 0, 0, 0, 0, 1, 0x3, 0x0, 0x3, },
+	{ "CANADA", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "CHILE", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "CHINA", 0, 0, 0, 0, 0, 0, 0x3, 0xf, },
+	{ "COLUMBIA", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "CROATIA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "CYRPUS", 0, 0, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "CZECH", 0, 0, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "DENMARK", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "ECUADOR", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "EGYPT", 0, 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "ELSALVADOR", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "FINLAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "FRANCE", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "GERMANY", 0, 1, 0, 0, 1, 0x3, 0, 0x3, },
+	{ "GREECE", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "GUAM", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "HONGKONG", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "HUNGARY", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "ICELAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "INDIA", 0, 0, 0, 0, 0, 0x3, 0, 0x4, },
+	{ "INDONESIA", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "IRELAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "ISRAEL", 0, 0, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "ITALY", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "JAPAN", 0, 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "JORDAN", 0, 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "KAZAKHSTAN", 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "KUWAIT", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "LATVIA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "LEBANON", 0, 0, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "LUXEMBOURG", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "MACAO", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "MALAYSIA", 0, 0, 0, 0, 0, 0, 0x3, 0, },	/* Current loop >= 20ma */
+	{ "MALTA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "MEXICO", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "MOROCCO", 0, 0, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "NETHERLANDS", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "NEWZEALAND", 0, 0, 0, 0, 0, 0x3, 0, 0x4, },
+	{ "NIGERIA", 0, 0, 0, 0, 0x1, 0x3, 0, 0x2, },
+	{ "NORWAY", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "OMAN", 0, 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "PAKISTAN", 0, 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "PERU", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "PHILIPPINES", 0, 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "POLAND", 0, 0, 1, 1, 0, 0x3, 0, 0, },
+	{ "PORTUGAL", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "ROMANIA", 0, 0, 0, 0, 0, 3, 0, 0, },
+	{ "RUSSIA", 0, 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "SAUDIARABIA", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "SINGAPORE", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "SLOVAKIA", 0, 0, 0, 0, 0, 0x3, 0, 0x3, },
+	{ "SLOVENIA", 0, 0, 0, 0, 0, 0x3, 0, 0x2, },
+	{ "SOUTHAFRICA", 1, 0, 1, 0, 0, 0x3, 0, 0x3, },
+	{ "SOUTHKOREA", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "SPAIN", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "SWEDEN", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "SWITZERLAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "SYRIA", 0, 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "TAIWAN", 0, 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "THAILAND", 0, 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "UAE", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "UK", 0, 1, 0, 0, 1, 0x3, 0, 0x5, },
+	{ "USA", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "YEMEN", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+};
+
+static struct ps_country_reg {
+	const char *country;
+	unsigned short value;
+} ps_country_regs[] = {
+	{"ARGENTINA",  0x8},
+	{"AUSTRALIA",  0xD},
+	{"AUSTRIA",    0xD},
+	{"BAHRAIN",    0xC},
+	{"BELGIUM",    0xC},
+	{"BRAZIL",     0x8},
+	{"BULGARIA",   0xD},
+	{"CANADA",     0x8},
+	{"CHILE",      0x8},
+	{"CHINA",      0xC},
+	{"COLOMBIA",   0x8},
+	{"CROATIA",    0xC},
+	{"CYPRUS",     0xC},
+	{"CZECH",      0xC},
+	{"DENMARK",    0xC},
+	{"ECUADOR",    0x8},
+	{"EGYPT",      0x8},
+	{"ELSALVADOR", 0x8},
+	{"FINLAND",    0xC},
+	{"FRANCE",     0xC},
+	{"GERMANY",    0xD},
+	{"GREECE",     0xC},
+	{"GUAM",       0x8},
+	{"HONGKONG",   0x8},
+	{"HUNGARY",    0x8},
+	{"ICELAND",    0xC},
+	{"INDIA",      0xF},
+	{"INDONESIA",  0x8},
+	{"IRELAND",    0xC},
+	{"ISRAEL",     0xC},
+	{"ITALY",      0xC},
+	{"JAPAN",      0x8},
+	{"JORDAN",     0x8},
+	{"KAZAKHSTAN", 0x8},
+	{"KUWAIT",     0x8},
+	{"LATVIA",     0xC},
+	{"LEBANON",    0xC},
+	{"LUXEMBOURG", 0xC},
+	{"MACAO",      0x8},
+	{"MALAYSIA",   0x8},
+	{"MALTA",      0xC},
+	{"MEXICO",     0x8},
+	{"MOROCCO",    0xC},
+	{"NETHERLANDS",0xC},
+	{"NEWZEALAND", 0xF},
+	{"NIGERIA",    0xC},
+	{"NORWAY",     0xC},
+	{"OMAN",       0x8},
+	{"PAKISTAN",   0x8},
+	{"PERU",       0x8},
+	{"PHILIPPINES",0x8},
+	{"POLAND",     0x8},
+	{"PORTUGAL",   0xC},
+	{"ROMANIA",    0x8},
+	{"RUSSIA",     0x8},
+	{"SAUDIARABIA",0x8},
+	{"SINGAPORE",  0x8},
+	{"SLOVAKIA",   0xE},
+	{"SLOVENIA",   0xE},
+	{"SOUTHAFRICA",0xE},
+	{"SOUTHKOREA", 0x8},
+	{"SPAIN",      0xC},
+	{"SWEDEN",     0xC},
+	{"SWITZERLAND",0xC},
+	{"SYRIA",      0x8},
+	{"TAIWAN",     0x8},
+	{"THAILAND",   0x8},
+	{"UAE",        0x8},
+	{"UK",         0xC},
+	{"USA",        0x8},
+	{"YEMEN",      0x8}
+};
+
+#define INOUT 2
+
+/* Allocate enough memory for two zt chunks, receive and transmit.  Each sample uses
+   32 bits.  Allocate an extra set just for control too */
+#define VT_PCIDMA_BLOCKSIZE (ZT_MAX_CHUNKSIZE * INOUT * MAX_PORTS * 2 * 2)
+#define VT_PCIDMA_MIDDLE    (ZT_MAX_CHUNKSIZE * MAX_PORTS - 4)
+#define VT_PCIDMA_END       (ZT_MAX_CHUNKSIZE * MAX_PORTS * 2 - 4)
+
+#define ID_DATA_MAXSIZE         30
+
+#define NUM_CAL_REGS 12
+#define NUM_FXO_REGS 60
+
+#define TREG(addr)      (wc->ioaddr + addr)
+
+#define TJ_CNTL         TREG(0x00)
+#define TJ_OPER         TREG(0x01)
+#define TJ_AUXC         TREG(0x02)
+#define TJ_AUXD         TREG(0x03)
+#define TJ_MASK0        TREG(0x04)
+#define TJ_MASK1        TREG(0x05)
+#define TJ_INTSTAT      TREG(0x06)
+#define TJ_AUXR         TREG(0x07)
+
+#define TJ_DMAWS        TREG(0x08)
+#define TJ_DMAWI        TREG(0x0c)
+#define TJ_DMAWE        TREG(0x10)
+#define TJ_DMAWC        TREG(0x14)
+#define TJ_DMARS        TREG(0x18)
+#define TJ_DMARI        TREG(0x1c)
+#define TJ_DMARE        TREG(0x20)
+#define TJ_DMARC        TREG(0x24)
+
+#define TJ_AUXINTPOL    TREG(0x2A)
+
+#define TJ_AUXFUNC      TREG(0x2b)
+#define TJ_SFDELAY      TREG(0x2c)
+#define TJ_SERCTL       TREG(0x2d)
+#define TJ_SFLC         TREG(0x2e)
+#define TJ_FSCDELAY     TREG(0x2f)
+
+#define TJ_REGBASE      TREG(0xc0)
+
+#define PIB(addr)       (TJ_REGBASE + addr * 4)
+
+#define HTXF_READY      (inb(PIB(0)) & 0x10)
+#define HRXF_READY      (inb(PIB(0)) & 0x20)
+
+
+#define VT_PORT_EMPTY	0
+#define VT_PORT_VDAA	1   /* Voice DAA - FXO */
+#define VT_PORT_PROSLIC	2   /* ProSLIC - FXS */
+
+#define VBAT 0xC7
+
+#define HKMODE_FWDACT   1
+#define HKMODE_FWDONACT	2
+#define HKMODE_RINGING	4
+
+#define HOOK_ONHOOK     0
+#define HOOK_OFFHOOK    1
+
+#define	DSP_CODEC_RING		12	/* RING rising edge detected		*/
+#define	DSP_CODEC_HKOFF		22	/* station port off hook                */
+#define	DSP_CODEC_HKON		23	/* station port on hook                 */
+#define	DSP_RING_OFF		24	/* RING falling edge detected		*/
+
+#define	DSP_CODEC_FLASH		26	/* station port hook flash              */
+
+#define DSP_LOOP_OFFHOOK	38	/* Loop Off hook from OpenPCI           */
+#define DSP_LOOP_ONHOOK		39	/* Loop On hook from OpenPCI            */
+#define DSP_LOOP_POLARITY	40	/* Loop Polarity from OpenPCI           */
+
+#define DSP_PROSLIC_SANITY	50	/* Sanity alert from a ProSLIC port 	*/
+#define DSP_PROSLIC_PWR_ALARM	51	/* Power Alarm from a ProSLIC port 	*/
+#define DSP_VDAA_ISO_FRAME_E	52	/* ISO-cap frame sync lost on VDAA port*/
+
+#if VERBOSE_TIMING
+ #define REPORT_WAIT(n,x)						    \
+	 cardinfo(card->cardnum, #n " wait at %d, " #x " = %d", __LINE__, x )
+#else
+ #define REPORT_WAIT(n,x)
+#endif
+
+#define BUSY_WAIT(countvar,cond,delay,iter,failret)			    \
+	countvar=0;							    \
+	while(cond){							    \
+	    udelay(delay);						    \
+	    if(++countvar > iter){					    \
+		cardcrit(wc->boardnum, "busy wait FAILED at %d", __LINE__); \
+		return failret;						    \
+	    }								    \
+	}								    \
+	REPORT_WAIT(busy,i)
+
+#define LOCKED_WAIT(countvar,cond,delay,iter,failret)			    \
+	countvar=0;							    \
+	while(cond){							    \
+	    udelay(delay);						    \
+	    if(++countvar > iter){					    \
+		dbginfo(wc->boardnum,"busy wait failed at %d",__LINE__);    \
+		spin_unlock_irqrestore(&wc->lock, flags);		    \
+		return failret;						    \
+	    }								    \
+	}								    \
+	REPORT_WAIT(locked,i)
+
+#define HTXF_WAIT()                 BUSY_WAIT(i,HTXF_READY,5,50,RET_FAIL)
+#define HRXF_WAIT()                 BUSY_WAIT(i,!HRXF_READY,5,300,RET_FAIL)
+#define HTXF_WAIT_RET(failret)      BUSY_WAIT(i,HTXF_READY,5,50,failret)
+#define HRXF_WAIT_RET(failret)      BUSY_WAIT(i,!HRXF_READY,5,100,failret)
+
+#define HTXF_WAIT_LOCKED()	    LOCKED_WAIT(i,HTXF_READY,5,50,RET_FAIL)
+#define HRXF_WAIT_LOCKED()	    LOCKED_WAIT(i,!HRXF_READY,5,100,RET_FAIL)
+#define HTXF_WAIT_LOCKED_RET(failret) LOCKED_WAIT(i,HTXF_READY,5,50,failret)
+#define HRXF_WAIT_LOCKED_RET(failret) LOCKED_WAIT(i,!HRXF_READY,5,100,failret)
+
+
+struct openpci {
+	struct pci_dev *dev;
+	char *variety;
+	int boardnum;
+	int portcount;
+	int porttype[MAX_PORTS];
+
+        int firmware;
+        char serial[ID_DATA_MAXSIZE];
+
+	spinlock_t lock;
+
+	//XXX Replace these with proper try_module_get locking in the zaptel driver.
+	//int usecount;	//XXX
+	//int dead;	//XXX
+	union {
+		struct {
+			int offhook;
+		} fxo;
+		struct {
+			int ohttimer;
+			int idletxhookstate;  /* IDLE changing hook state */
+			int lasttxhook;
+		} fxs;
+	} mod[MAX_PORTS];
+
+	unsigned long  ioaddr;
+	dma_addr_t     readdma;
+	dma_addr_t     writedma;
+	volatile int  *writechunk;  /* Double-word aligned write memory */
+	volatile int  *readchunk;   /* Double-word aligned read memory */
+
+	struct zt_chan chans[MAX_PORTS];
+	struct zt_span span;
+} *cards[MAX_CARDS];
+
+// You must hold this lock anytime you access or modify the cards[] array.
+DEFINE_MUTEX(cards_mutex);
+
+static unsigned char fxo_port_lookup[8] = { 0x0, 0x8, 0x4, 0xc, 0x10, 0x18, 0x14, 0x1c};
+static unsigned char fxs_port_lookup[8] = { 0x0, 0x1, 0x2, 0x3, 0x10, 0x11, 0x12, 0x13};
+static char wcopenpci[] = "VoiceTronix OpenPCI";
+
+static char *country = DEFAULT_COUNTRY;
+static int reversepolarity;  // = 0
+static int debug;            // = 0
+
+module_param(country, charp, 0444);
+module_param(debug, int, 0600);
+module_param(reversepolarity, int, 0600);
+MODULE_PARM_DESC(country, "Set the default country name");
+MODULE_PARM_DESC(debug, "Enable verbose logging");
+
+//#define DEBUG_LOOP_VOLTAGE 1
+#ifdef DEBUG_LOOP_VOLTAGE
+ // This param is a 32 bit bitfield where bit 1 << cardnum * 8 << portnum
+ // will enable voltage monitoring on that port (fxo only presently)
+ static int voltmeter;        // = 0
+ module_param(voltmeter, int, 0600);
+ MODULE_PARM_DESC(voltmeter, "Enable loop voltage metering");
+#endif
+
+
+/* boolean return values */
+#define RET_OK   1
+#define RET_FAIL 0
+
+/* Convenience macros for logging */
+#define info(format,...) printk(KERN_INFO NAME ": " format "\n" , ## __VA_ARGS__)
+#define warn(format,...) printk(KERN_WARNING NAME ": " format "\n" , ## __VA_ARGS__)
+#define crit(format,...) printk(KERN_CRIT NAME ": " format "\n" , ## __VA_ARGS__)
+#define cardinfo(cardnum,format,...) info("[%02d] " format, cardnum , ## __VA_ARGS__)
+#define cardwarn(cardnum,format,...) warn("[%02d] " format, cardnum , ## __VA_ARGS__)
+#define cardcrit(cardnum,format,...) crit("[%02d] " format, cardnum , ## __VA_ARGS__)
+#define dbginfo(cardnum,format,...) if(debug) info("[%02d] " format, cardnum , ## __VA_ARGS__)
+
+
+static inline const char *porttype(struct openpci *wc, int port)
+{ //{{{
+	switch( wc->porttype[port] ) {
+	    case VT_PORT_VDAA:    return "VDAA";
+	    case VT_PORT_PROSLIC: return "ProSLIC";
+	    case VT_PORT_EMPTY:   return "empty port";
+	    default:              return "unknown type";
+	}
+} //}}}
+
+
+static int __read_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char *value)
+{ //{{{
+	unsigned char portadr = fxo_port_lookup[port];
+	int i;
+
+	if (HRXF_READY) *value = inb(PIB(1));
+
+	outb(0x11, PIB(1));    HTXF_WAIT();
+	outb(0x2, PIB(1));     HTXF_WAIT();
+	outb(portadr, PIB(1)); HTXF_WAIT();
+	outb(reg, PIB(1));     HTXF_WAIT();
+	HRXF_WAIT(); *value = inb(PIB(1));
+
+	return RET_OK;
+} //}}}
+
+static int read_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char *value)
+{ //{{{
+	unsigned long flags;
+
+	spin_lock_irqsave(&wc->lock, flags);
+	if( __read_reg_fxo(wc, port, reg, value) ){
+		spin_unlock_irqrestore(&wc->lock, flags);
+		return RET_OK;
+	}
+	spin_unlock_irqrestore(&wc->lock, flags);
+	cardcrit(wc->boardnum, "FXO port %d, reg %d, read failed!", port, reg);
+	return RET_FAIL;
+} //}}}
+
+static int __read_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char *value)
+{ //{{{
+	unsigned char portadr = fxs_port_lookup[port];
+	int i;
+
+	if (HRXF_READY) *value = inb(PIB(1));
+
+	outb(0x13, PIB(1));    HTXF_WAIT();
+	outb(0x2, PIB(1));     HTXF_WAIT();
+	outb(portadr, PIB(1)); HTXF_WAIT();
+	outb(reg, PIB(1));     HTXF_WAIT();
+	HRXF_WAIT(); *value = inb(PIB(1));
+
+	return RET_OK;
+} //}}}
+
+static int read_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char *value)
+{ //{{{
+	unsigned long flags;
+
+	spin_lock_irqsave(&wc->lock, flags);
+	if( __read_reg_fxs(wc, port, reg, value) ) {
+		spin_unlock_irqrestore(&wc->lock, flags);
+		return RET_OK;
+	}
+	spin_unlock_irqrestore(&wc->lock, flags);
+	cardcrit(wc->boardnum, "FXS port %d, reg %d, read failed!", port, reg);
+	return RET_FAIL;
+} //}}}
+
+static int __write_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char value)
+{ //{{{
+	unsigned char portadr = fxo_port_lookup[port];
+	int i;
+
+        outb(0x10, PIB(1) );   HTXF_WAIT();
+        outb(0x3, PIB(1));     HTXF_WAIT();
+        outb(portadr, PIB(1)); HTXF_WAIT();
+        outb(reg, PIB(1));     HTXF_WAIT();
+        outb(value, PIB(1));   HTXF_WAIT();
+
+	return RET_OK;
+} //}}}
+
+static int write_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char value)
+{ //{{{
+	unsigned long flags;
+
+	spin_lock_irqsave(&wc->lock, flags);
+	if( __write_reg_fxo(wc, port, reg, value) ){
+		spin_unlock_irqrestore(&wc->lock, flags);
+		return RET_OK;
+	}
+	spin_unlock_irqrestore(&wc->lock, flags);
+	cardcrit(wc->boardnum, "FXO port %d, reg %d, write(%d) failed!", port, reg, value);
+	return RET_FAIL;
+} //}}}
+
+static int __write_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char value)
+{ //{{{
+	unsigned char portadr = fxs_port_lookup[port];
+	int i;
+
+        outb(0x12, PIB(1) );   HTXF_WAIT();
+        outb(0x3, PIB(1));     HTXF_WAIT();
+        outb(portadr, PIB(1)); HTXF_WAIT();
+        outb(reg, PIB(1));     HTXF_WAIT();
+        outb(value, PIB(1));   HTXF_WAIT();
+
+	return RET_OK;
+} //}}}
+
+static int write_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char value)
+{ //{{{
+	unsigned long flags;
+
+	spin_lock_irqsave(&wc->lock, flags);
+	if( __write_reg_fxs(wc, port, reg, value) ){
+		spin_unlock_irqrestore(&wc->lock, flags);
+		return RET_OK;
+	}
+	spin_unlock_irqrestore(&wc->lock, flags);
+	cardcrit(wc->boardnum, "FXS port %d, reg %d, write(%d) failed!", port, reg, value);
+	return RET_FAIL;
+} //}}}
+
+static int __wait_indreg_fxs(struct openpci *wc, int port)
+{ //{{{
+	unsigned char value;
+	int count = 100;
+
+	while (--count)
+	{
+		if( __read_reg_fxs(wc, port, I_STATUS, &value) ){
+			if( value == 0 )
+				return RET_OK;
+		} else {
+			cardcrit(wc->boardnum,
+				 "failed to read port %d PS_IND_ADDR_ST, retrying...",
+				 port);
+		}
+		udelay(5);
+	}
+	cardcrit(wc->boardnum, "Failed to wait for indirect reg write to port %d", port);
+	return RET_FAIL;
+} //}}}
+
+static int write_indreg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned short value)
+{ //{{{
+	unsigned long flags;
+
+	spin_lock_irqsave(&wc->lock, flags);
+	if( __wait_indreg_fxs(wc, port)
+	 && __write_reg_fxs(wc, port, IDA_LO, value & 0xff)
+	 && __write_reg_fxs(wc, port, IDA_HI, (value & 0xff00)>>8)
+	 && __write_reg_fxs(wc, port, IAA, reg)
+	 && __wait_indreg_fxs(wc, port) )
+	{
+		spin_unlock_irqrestore(&wc->lock, flags);
+		return RET_OK;
+	}
+	spin_unlock_irqrestore(&wc->lock, flags);
+	cardcrit(wc->boardnum, "FXS indreg %d write failed on port %d", reg, port);
+	return RET_FAIL;
+} //}}}
+
+static int read_indreg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned short *value)
+{ //{{{
+	unsigned long flags;
+	unsigned char lo, hi;
+
+	spin_lock_irqsave(&wc->lock, flags);
+	if( __wait_indreg_fxs(wc, port)
+	 && __write_reg_fxs(wc, port, IAA, reg)
+	 && __wait_indreg_fxs(wc, port)
+	 && __read_reg_fxs(wc, port, IDA_LO, &lo)
+	 && __read_reg_fxs(wc, port, IDA_HI, &hi) )
+	{
+		*value = lo | hi << 8;
+		spin_unlock_irqrestore(&wc->lock, flags);
+		return RET_OK;
+	}
+	spin_unlock_irqrestore(&wc->lock, flags);
+	return RET_FAIL;
+} //}}}
+
+static void start_dma(struct openpci *wc)
+{ //{{{
+	outb(0x0f, TJ_CNTL);
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout(1);
+	outb(0x01, TJ_CNTL);
+	outb(0x01, TJ_OPER);
+} //}}}
+
+static void restart_dma(struct openpci *wc)
+{ //{{{
+	/* Reset Master and TDM */
+	outb(0x01, TJ_CNTL);
+	outb(0x01, TJ_OPER);
+} //}}}
+
+/* You must hold the card spinlock to call this function */
+static int __ping_arm(struct openpci *wc)
+{ //{{{
+	int i;
+	int pong=0;
+
+	while(pong != 0x02){
+		outb(0x02, PIB(1)); HTXF_WAIT();
+		HRXF_WAIT(); pong = inb(PIB(1));
+		dbginfo(wc->boardnum, "ping_arm returned %x", pong);
+	}
+	while(pong == 0x02){
+		// Poke no-ops into the arm while it is still returning data,
+		// if 500 usec elapses with no further response from it then
+		// the message queue is should be completely cleared.
+		outb(0x00, PIB(1)); HTXF_WAIT();
+		i = 100;
+		while( !HRXF_READY && --i ) udelay(5);
+		if( i == 0 ) break;
+		pong = inb(PIB(1));
+		dbginfo(wc->boardnum, "ping_arm returned %x.", pong);
+	}
+	return RET_OK;
+} //}}}
+
+static void arm_event(struct openpci *wc, char *msg)
+{ //{{{
+	int port = msg[0];
+
+	switch(msg[1]){
+		case DSP_LOOP_OFFHOOK:
+			zt_hooksig(&wc->chans[port], ZT_RXSIG_OFFHOOK);
+			dbginfo(wc->boardnum, "Port %d Loop OffHook", port);
+			break;
+
+		case DSP_LOOP_ONHOOK:
+			zt_hooksig(&wc->chans[port], ZT_RXSIG_ONHOOK);
+			dbginfo(wc->boardnum, "Port %d Loop OnHook", port);
+			break;
+
+		case DSP_LOOP_POLARITY:
+			zt_qevent_lock(&wc->chans[port], ZT_EVENT_POLARITY);
+			dbginfo(wc->boardnum, "Port %d Loop Polarity", port);
+			break;
+
+		case DSP_CODEC_RING:
+			zt_hooksig(&wc->chans[port], ZT_RXSIG_RING);
+			dbginfo(wc->boardnum, "Port %d Ring On", port);
+			break;
+
+		case DSP_RING_OFF:
+			zt_hooksig(&wc->chans[port], ZT_RXSIG_OFFHOOK);
+			dbginfo(wc->boardnum, "Port %d Ring Off", port);
+			break;
+
+		case DSP_CODEC_HKOFF:
+			zt_hooksig(&wc->chans[port], ZT_RXSIG_OFFHOOK);
+			dbginfo(wc->boardnum, "Port %d Station OffHook", port);
+			if (reversepolarity)
+				wc->mod[port].fxs.idletxhookstate = 5;
+			else
+				wc->mod[port].fxs.idletxhookstate = 1;
+			break;
+
+		case DSP_CODEC_HKON:
+			zt_hooksig(&wc->chans[port], ZT_RXSIG_ONHOOK);
+			dbginfo(wc->boardnum, "Port %d Station OnHook", port);
+			if (reversepolarity)
+				wc->mod[port].fxs.idletxhookstate = 6;
+			else
+				wc->mod[port].fxs.idletxhookstate = 2;
+			break;
+
+		case DSP_CODEC_FLASH:
+			zt_qevent_lock(&wc->chans[port], ZT_EVENT_WINKFLASH);
+			dbginfo(wc->boardnum, "Port %d Station Flash", port);
+			break;
+
+			//XXX What to do to recover from these?
+		case DSP_PROSLIC_SANITY:
+			dbginfo(wc->boardnum, "Port %d ProSlic has gone insane!", port);
+			break;
+
+		case DSP_PROSLIC_PWR_ALARM:
+		{
+			char errbuf[32] = " Unknown", *p = errbuf;
+			int i = 49;
+
+			msg[2] >>= 2;
+			for(; i < 55; ++i, msg[2] >>= 1 )
+			    if(msg[2] & 1){ *(++p)='Q'; *(++p)=i; *(++p)=','; }
+			if( p != errbuf ) *p = '\0';
+			cardcrit(wc->boardnum,"%d: ProSlic power ALARM:%s",msg[0],errbuf);
+			//write_reg_fxs(wc, port, 64, wc->mod[port].fxs.lasttxhook );
+			return;
+		}
+
+		case DSP_VDAA_ISO_FRAME_E:
+			dbginfo(wc->boardnum, "Port %d VDAA has lost ISO-Cap frame lock", port);
+			break;
+
+		default:
+			cardwarn(wc->boardnum, "Unknown message from Arm[%d] for port %d",
+						msg[1], port);
+			break;
+	}
+} //}}}
+
+/* You must hold the card spinlock to call this function */
+static inline int __read_arm_byte( struct openpci *wc, unsigned char *msg )
+{ //{{{
+	int i;
+
+	HRXF_WAIT(); *msg = inb(PIB(1));
+	return RET_OK;
+} //}}}
+
+static inline int read_arm_msg( struct openpci *wc, unsigned char *msg )
+{ //{{{
+	unsigned long flags;
+	int i, d, count;
+	int ret = RET_OK;
+
+	spin_lock_irqsave(&wc->lock, flags);
+	outb(0x08, PIB(1)); HTXF_WAIT_LOCKED();
+	//XXX Do we need to clear the interrupt flag even if this fails?
+	HRXF_WAIT_LOCKED(); count = inb(PIB(1));
+	if( count == 0 ){
+		ret = RET_FAIL;
+	} else if( count < 3 || count > 4 ){
+		cardcrit(wc->boardnum, "BOGUS arm message size %d, flushing queue", count);
+		// NB: This may take a while (up to 500usec or more) to complete
+		//     and we are in the isr at present when this is called, so
+		//     we may miss an interrupt or two while this is done in the
+		//     bottom half, but we are already in trouble, so...
+		d = debug; debug = 5; __ping_arm( wc ); debug = d;
+		ret = RET_FAIL;
+	} else while( --count ){
+		if( ! __read_arm_byte(wc, msg) ){
+			cardcrit(wc->boardnum,
+				 "Failed to read arm message %d more bytes expected",
+				 count);
+			ret = RET_FAIL;
+			break;
+		}
+		++msg;
+	}
+	outb(0x09, PIB(1)); HTXF_WAIT_LOCKED();
+	spin_unlock_irqrestore(&wc->lock, flags);
+	return ret;
+} //}}}
+
+static void openpci_arm_work( void *cardptr )
+{ //{{{
+	struct openpci *wc = (struct openpci*)cardptr;
+	unsigned char armmsg[3];
+
+	if( read_arm_msg(wc, armmsg) )  arm_event(wc, armmsg);
+} //}}}
+
+
+static inline void openpci_write(struct openpci *wc, unsigned char flags)
+{ //{{{
+	int x,y;
+	volatile unsigned int *writechunk;
+
+	if (flags & 0x01)
+		writechunk = wc->writechunk;
+	else if (flags & 0x02)
+		writechunk = wc->writechunk + ZT_CHUNKSIZE*2;
+	else {
+		cardcrit(wc->boardnum, "bad write interrupt flags %x, at %x",
+					flags, inb(TJ_DMAWC) );
+		return;
+	}
+	/* get data */
+	zt_transmit(&wc->span);
+	for (y=0,x=0;x<ZT_CHUNKSIZE;++x) {
+		/* Send a sample, as a 32-bit word */
+#ifdef __BIG_ENDIAN
+#error No big endian support (yet)
+#else
+		/* transmit second 4 ports */
+		writechunk[y]=0;
+		if (wc->porttype[4])
+			writechunk[y] |= (wc->chans[4].writechunk[x] << 24);
+		else
+			writechunk[y] |= (0x01 << 24);
+		if (wc->porttype[5])
+			writechunk[y] |= (wc->chans[5].writechunk[x] << 16);
+		if (wc->porttype[6])
+			writechunk[y] |= (wc->chans[6].writechunk[x] << 8);
+		if (wc->porttype[7])
+			writechunk[y] |= (wc->chans[7].writechunk[x]);
+		++y;
+
+		/* transmit first 4 ports */
+		writechunk[y]=0x01000000;
+		/* Make sure first port doesnt equal 0x00 */
+		if (wc->porttype[0]){
+			if (wc->chans[0].writechunk[x] == 0)
+				writechunk[y] |= (0x01 << 24);
+			else
+				writechunk[y] |= (wc->chans[0].writechunk[x] << 24);
+		}
+		//else writechunk[y] |= (0x00 << 24);
+		if (wc->porttype[1])
+			writechunk[y] |= (wc->chans[1].writechunk[x] << 16);
+		if (wc->porttype[2])
+			writechunk[y] |= (wc->chans[2].writechunk[x] << 8);
+		if (wc->porttype[3])
+			writechunk[y] |= (wc->chans[3].writechunk[x]);
+		++y;
+#endif
+	}
+} //}}}
+
+static inline void openpci_read(struct openpci *wc, unsigned char flags)
+{ //{{{
+	int x,y;
+	volatile unsigned int *readchunk;
+
+	if (flags & 0x08)
+		readchunk = wc->readchunk + ZT_CHUNKSIZE*2;
+	else if (flags & 0x04)
+		readchunk = wc->readchunk;
+	else {
+		cardcrit(wc->boardnum, "bad read interrupt flags %x, at %x",
+					flags, inb(TJ_DMARC));
+		return;
+	}
+
+	for (y=0,x=0;x<ZT_CHUNKSIZE;++x) {
+#ifdef __BIG_ENDIAN
+#error No big endian support (yet)
+#else
+		/* Receive first 4 ports */
+
+		if (wc->porttype[0])
+			wc->chans[0].readchunk[x] = (readchunk[y] >> 24) & 0xff;
+		if (wc->porttype[1])
+			wc->chans[1].readchunk[x] = (readchunk[y] >> 16) & 0xff;
+		if (wc->porttype[2])
+			wc->chans[2].readchunk[x] = (readchunk[y] >> 8) & 0xff;
+		if (wc->porttype[3])
+			wc->chans[3].readchunk[x] = (readchunk[y]) & 0xff;
+		++y;
+		/* Receive second 4 ports */
+		if (wc->porttype[4])
+			wc->chans[4].readchunk[x] = (readchunk[y] >> 24) & 0xff;
+		if (wc->porttype[5])
+			wc->chans[5].readchunk[x] = (readchunk[y] >> 16) & 0xff;
+		if (wc->porttype[6])
+			wc->chans[6].readchunk[x] = (readchunk[y] >> 8) & 0xff;
+		if (wc->porttype[7])
+			wc->chans[7].readchunk[x] = (readchunk[y]) & 0xff;
+		++y;
+#endif
+	}
+	/* XXX We're wasting 8 taps.  We should get closer :( */
+	for (x = 0; x < MAX_PORTS; x++) {
+		if (wc->porttype[x])
+			zt_ec_chunk(&wc->chans[x], wc->chans[x].readchunk, wc->chans[x].writechunk);
+	}
+	zt_receive(&wc->span);
+} //}}}
+
+ZAP_IRQ_HANDLER(openpci_isr)
+{ //{{{
+	struct openpci *wc = dev_id;
+	unsigned long flags;
+	unsigned char status;
+
+	spin_lock_irqsave(&wc->lock, flags);
+	status = inb(TJ_INTSTAT);
+	outb(status, TJ_INTSTAT);
+
+	if (!status) {
+		if(inb(TJ_AUXR) & 0x02) {
+			spin_unlock_irqrestore(&wc->lock, flags);
+			return IRQ_NONE;
+		}
+		spin_unlock_irqrestore(&wc->lock, flags);
+		openpci_arm_work(wc);
+		return IRQ_HANDLED;
+	}
+	if (status & 0x10){
+		/* PCI Master abort */
+		cardcrit(wc->boardnum, "PCI Master Abort.");
+		/* Stop DMA, wait for watchdog */
+		outb(0x00, TJ_OPER);
+		spin_unlock_irqrestore(&wc->lock, flags);
+		return IRQ_HANDLED;
+	}
+	spin_unlock_irqrestore(&wc->lock, flags);
+
+	if (status & 0x20){
+		/* PCI Target abort */
+		cardcrit(wc->boardnum, "PCI Target Abort.");
+		return IRQ_HANDLED;
+	}
+	if (status & 0x03){
+		openpci_write(wc, status);
+	}
+	if (status & 0x0c){
+	    #ifdef DEBUG_LOOP_VOLTAGE
+		static int counter[MAX_CARDS];
+		int card = wc->boardnum;
+		int port = ++counter[card] & 0x07;
+		int ignore = counter[card] & 0xf0;
+
+		if( ! ignore && (voltmeter & ((1 << (card * 8)) << port)) ) {
+			unsigned char lv;
+			if( wc->porttype[port] == VT_PORT_VDAA && read_reg_fxo(wc, port, 29, &lv) )
+				cardinfo(wc->boardnum, "Port %d loop voltage %d",
+							port, lv < 128 ? lv : lv - 256);
+		}
+	    #endif
+		openpci_read(wc, status);
+	}
+
+	return IRQ_HANDLED;
+} //}}}
+
+static int openpci_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data)
+{ //{{{
+	struct wctdm_stats stats;
+	struct wctdm_regs regs;
+	struct wctdm_regop regop;
+	struct wctdm_echo_coefs echoregs;
+	struct openpci *wc = chan->pvt;
+	int port = chan->chanpos - 1;
+	int x;
+
+	switch (cmd) {
+	case ZT_ONHOOKTRANSFER:
+		if (wc->porttype[port] != VT_PORT_PROSLIC)
+			return -EINVAL;
+		if (get_user(x, (int *)data))
+			return -EFAULT;
+		wc->mod[port].fxs.ohttimer = x << 3;
+		if (reversepolarity)
+			wc->mod[port].fxs.idletxhookstate = 0x6;	/* OHT mode when idle */
+		else
+			wc->mod[port].fxs.idletxhookstate = 0x2;
+		if (wc->mod[port].fxs.lasttxhook == 0x1) {
+				/* Apply the change if appropriate */
+				if (reversepolarity)
+					wc->mod[port].fxs.lasttxhook = 0x6;
+				else
+					wc->mod[port].fxs.lasttxhook = 0x2;
+				if( ! write_reg_fxs(wc, port, 64, wc->mod[port].fxs.lasttxhook) )
+					return -EIO;
+		}
+		break;
+	case ZT_SETPOLARITY:
+		if (get_user(x, (int *)data))
+			return -EFAULT;
+		if (wc->porttype[port] != VT_PORT_PROSLIC)
+			return -EINVAL;
+		/* Can't change polarity while ringing or when open */
+		if ((wc->mod[chan->chanpos -1 ].fxs.lasttxhook == 0x04) ||
+		    (wc->mod[chan->chanpos -1 ].fxs.lasttxhook == 0x00))
+			return -EINVAL;
+
+		if ((x && !reversepolarity) || (!x && reversepolarity))
+			wc->mod[port].fxs.lasttxhook |= 0x04;
+		else
+			wc->mod[port].fxs.lasttxhook &= ~0x04;
+		if( ! write_reg_fxs(wc, port, 64, wc->mod[port].fxs.lasttxhook) )
+			return -EIO;
+		break;
+	case WCTDM_GET_STATS:
+		if (wc->porttype[port] == VT_PORT_PROSLIC) {
+			unsigned char	linevolt;
+			if( read_reg_fxs(wc, chan->chanpos-1, 80, &linevolt) )
+				stats.tipvolt = linevolt * -376;
+			else
+				return -EIO;
+			if( read_reg_fxs(wc, chan->chanpos-1, 81, &linevolt) )
+				stats.ringvolt = linevolt * -376;
+			else
+				return -EIO;
+			if( read_reg_fxs(wc, chan->chanpos-1, 82, &linevolt) )
+				stats.batvolt = linevolt * -376;
+			else
+				return -EIO;
+		} else if (wc->porttype[port] == VT_PORT_VDAA) {
+			unsigned char	linevolt;
+			if( read_reg_fxo(wc, port, 29, &linevolt) )
+				stats.tipvolt = stats.ringvolt = stats.batvolt = linevolt * 1000;
+			else
+				return -EIO;
+		} else
+			return -EINVAL;
+		if (copy_to_user((struct wctdm_stats *)data, &stats, sizeof(stats)))
+			return -EFAULT;
+		break;
+	case WCTDM_GET_REGS:
+		if (wc->porttype[port] == VT_PORT_PROSLIC) {
+			for (x=0;x<NUM_INDIRECT_REGS;x++)
+				if( ! read_indreg_fxs(wc, chan->chanpos-1, x, &regs.indirect[x]) )
+					return -EIO;
+			for (x=0;x<NUM_REGS;x++)
+				if( ! read_reg_fxs(wc, chan->chanpos-1, x, &regs.direct[x]) )
+					return -EIO;
+		} else {
+			memset(&regs, 0, sizeof(regs));
+			for (x=0;x<NUM_FXO_REGS;x++){
+				if( ! read_reg_fxo(wc, chan->chanpos-1, x, &regs.direct[x]) )
+					return -EIO;
+			}
+		}
+		if (copy_to_user((struct wctdm_regs *)data, &regs, sizeof(regs)))
+			return -EFAULT;
+		break;
+	case WCTDM_SET_REG:
+		if (copy_from_user(&regop, (struct wctdm_regop *)data, sizeof(regop)))
+			return -EFAULT;
+		if (regop.indirect) {
+			if (wc->porttype[port] != VT_PORT_PROSLIC)
+				return -EINVAL;
+			printk("Setting indirect %d to 0x%04x on %d\n", regop.reg, regop.val, chan->chanpos);
+			if( ! write_indreg_fxs(wc, port, regop.reg, regop.val) )
+				return -EIO;
+		} else {
+			regop.val &= 0xff;
+			printk("Setting direct %d to %04x on %d\n", regop.reg, regop.val, chan->chanpos);
+			if (wc->porttype[port] == VT_PORT_PROSLIC) {
+				if( ! write_reg_fxs(wc, port, regop.reg, regop.val) )
+					return -EIO;
+			} else {
+				if( ! write_reg_fxo(wc, port, regop.reg, regop.val) )
+					return -EIO;
+			}
+		}
+		break;
+	case WCTDM_SET_ECHOTUNE:
+		cardinfo(wc->boardnum, "Setting echo registers");
+		if (copy_from_user(&echoregs, (struct wctdm_echo_coefs*)data, sizeof(echoregs)))
+			return -EFAULT;
+
+		if (wc->porttype[port] == VT_PORT_VDAA) {
+			/* Set the ACIM and digital echo canceller registers */
+			if( ! write_reg_fxo(wc, port, 30, echoregs.acim)
+			 || ! write_reg_fxo(wc, port, 45, echoregs.coef1)
+			 || ! write_reg_fxo(wc, port, 46, echoregs.coef2)
+			 || ! write_reg_fxo(wc, port, 47, echoregs.coef3)
+			 || ! write_reg_fxo(wc, port, 48, echoregs.coef4)
+			 || ! write_reg_fxo(wc, port, 49, echoregs.coef5)
+			 || ! write_reg_fxo(wc, port, 50, echoregs.coef6)
+			 || ! write_reg_fxo(wc, port, 51, echoregs.coef7)
+			 || ! write_reg_fxo(wc, port, 52, echoregs.coef8) )
+			{
+				cardcrit(wc->boardnum, "Failed to set echo registers");
+				return -EIO;
+			}
+			break;
+		} else {
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -ENOTTY;
+	}
+	return 0;
+} //}}}
+
+static int openpci_open(struct zt_chan *chan)
+{
+	struct openpci *wc = chan->pvt;
+	if( ! wc->porttype[chan->chanpos-1] )
+		return -ENODEV;
+	
+	//XXX This is WRONG and can prang in a race.  We must pass THIS_MODULE
+	//    as the owner of the span that holds the pointer to this function,
+	//    then bump the refcount in the zaptel code _BEFORE_ the potentially
+	//    fatal call to an invalid pointer is made.
+	//if( wc->dead ) return -ENODEV;
+	//wc->usecount++;
+	try_module_get(THIS_MODULE);  //XXX
+
+	return 0;
+}
+
+static int openpci_watchdog(struct zt_span *span, int event)
+{
+	info("TDM: Restarting DMA");
+	restart_dma(span->pvt);
+	return 0;
+}
+
+static int openpci_close(struct zt_chan *chan)
+{
+	struct openpci *wc = chan->pvt;
+	int port = chan->chanpos - 1;
+
+	//XXX wc->usecount--;
+	//XXX This is WRONG and can prang in a race.  We must pass THIS_MODULE
+	//    as the owner of the span that holds the pointer to this function,
+	//    then bump the refcount in the zaptel code _BEFORE_ the potentially
+	//    fatal call to an invalid pointer is made.
+	module_put(THIS_MODULE);
+	if (wc->porttype[port] == VT_PORT_PROSLIC) {
+		if (reversepolarity)
+			wc->mod[port].fxs.idletxhookstate = 5;
+		else
+			wc->mod[port].fxs.idletxhookstate = 1;
+	}
+	/* If we're dead, release us now */
+	//XXX if (!wc->usecount && wc->dead) openpci_release(wc);
+
+	return 0;
+}
+
+static int openpci_hooksig(struct zt_chan *chan, zt_txsig_t txsig)
+{ //{{{
+	struct openpci *wc = chan->pvt;
+	int port = chan->chanpos - 1;
+	int new_hk_state;
+
+	dbginfo(wc->boardnum, "Setting %s port %d hook state %s",
+		 wc->porttype[port] == VT_PORT_VDAA ? "FXO" : "FXS",
+		 port,
+		 txsig == 0 ? "ONHOOK" :
+		 txsig == 1 ? "OFFHOOK" :
+		 txsig == 2 ? "START" :
+		 txsig == 3 ? "KEWL" : "UNKNOWN" );
+
+	switch(wc->porttype[port]) {
+	    case VT_PORT_VDAA:
+		switch(txsig) {
+		    case ZT_TXSIG_START:
+		    case ZT_TXSIG_OFFHOOK:
+			if( write_reg_fxo(wc, port, 5, 0x9)
+			 && write_reg_fxo(wc, port, 0x20, 0x0) )
+				wc->mod[port].fxo.offhook = 1;
+			else
+				cardcrit(wc->boardnum, "Failed set fxo off-hook");
+			break;
+
+		    case ZT_TXSIG_ONHOOK:
+			if( write_reg_fxo(wc, port, 5, 0x8)
+			 && write_reg_fxo(wc, port, 0x20, 0x3) )
+				wc->mod[port].fxo.offhook = 0;
+			else
+				cardcrit(wc->boardnum, "Failed set fxo on-hook");
+			break;
+
+		    default:
+			cardcrit(wc->boardnum,
+				 "Can't set FXO port %d tx state to %d",
+				 port, txsig);
+		}
+		break;
+
+	    case VT_PORT_PROSLIC:
+		new_hk_state = wc->mod[port].fxs.lasttxhook;
+		switch(txsig) {
+		    case ZT_TXSIG_ONHOOK:
+			switch(chan->sig) {
+			case ZT_SIG_EM:
+			case ZT_SIG_FXOKS:
+			case ZT_SIG_FXOLS:
+				new_hk_state = wc->mod[port].fxs.idletxhookstate;
+				break;
+			case ZT_SIG_FXOGS:
+				new_hk_state = 3;
+				break;
+			}
+			break;
+
+		    case ZT_TXSIG_OFFHOOK:
+			switch(chan->sig) {
+			case ZT_SIG_EM:
+				new_hk_state = 5;
+				break;
+			default:
+				new_hk_state = wc->mod[port].fxs.idletxhookstate;
+				break;
+			}
+			break;
+
+		    case ZT_TXSIG_START:
+			new_hk_state = 4;
+			break;
+
+		    case ZT_TXSIG_KEWL:
+			new_hk_state = 0;
+			break;
+
+		    default:
+			cardinfo(wc->boardnum,
+				 "Can't set FXS port %d tx state to %d",
+				 port, txsig);
+		}
+		dbginfo(wc->boardnum, "%s port %d hook state old %d, new %d",
+			 wc->porttype[port] == VT_PORT_VDAA ? "FXO" : "FXS",
+			 port, wc->mod[port].fxs.lasttxhook, new_hk_state );
+
+		if (new_hk_state != wc->mod[port].fxs.lasttxhook){
+			if( write_reg_fxs(wc, port, 64, new_hk_state) )
+				wc->mod[port].fxs.lasttxhook = new_hk_state;
+			else
+				cardcrit(wc->boardnum,
+					 "Failed to set port %d fxs hookstate from %d to %d",
+					 port, wc->mod[port].fxs.lasttxhook, new_hk_state);
+		}
+		break;
+
+	    default:
+		cardcrit(wc->boardnum,
+			 "Unknown module type %d in openpci_hooksig",
+			 wc->porttype[port] );
+	}
+	return 0;
+} //}}}
+
+static int span_initialize(struct openpci *wc)
+{ //{{{
+	int x;
+
+	//XXX Set a THIS_MODULE as the owner of the span...
+	/* Zapata stuff */
+	sprintf(wc->span.name, "WCTDM/%d", wc->boardnum);
+	sprintf(wc->span.desc, "%s Board %d", wc->variety, wc->boardnum + 1);
+	for (x = 0; x < MAX_PORTS; x++) {
+		sprintf(wc->chans[x].name, "WCTDM/%d/%d", wc->boardnum, x);
+		wc->chans[x].sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS
+				    | ZT_SIG_SF | ZT_SIG_EM | ZT_SIG_CLEAR;
+		wc->chans[x].sigcap |= ZT_SIG_FXSKS | ZT_SIG_FXSLS | ZT_SIG_SF | ZT_SIG_CLEAR;
+		wc->chans[x].chanpos = x+1;
+		wc->chans[x].pvt = wc;
+	}
+	wc->span.deflaw = ZT_LAW_MULAW;
+	wc->span.chans = wc->chans;
+	wc->span.channels = MAX_PORTS;
+	wc->span.hooksig = openpci_hooksig;
+	wc->span.open = openpci_open;
+	wc->span.close = openpci_close;
+	wc->span.flags = ZT_FLAG_RBS;
+	wc->span.ioctl = openpci_ioctl;
+	wc->span.watchdog = openpci_watchdog;
+	init_waitqueue_head(&wc->span.maintq);
+
+	wc->span.pvt = wc;
+	if (zt_register(&wc->span, 0)) {
+		cardcrit(wc->boardnum, "Unable to register span with zaptel");
+		return RET_FAIL;
+	}
+	return RET_OK;
+} //}}}
+
+static int get_port_type(struct openpci *wc, int port)
+{ //{{{
+	int i, type;
+	unsigned long flags;
+
+	spin_lock_irqsave(&wc->lock, flags);
+	outb(0x20, PIB(1)); HTXF_WAIT_LOCKED_RET(VT_PORT_EMPTY);
+	outb(port, PIB(1)); HTXF_WAIT_LOCKED_RET(VT_PORT_EMPTY);
+	HRXF_WAIT_LOCKED_RET(VT_PORT_EMPTY); type = inb(PIB(1));
+	spin_unlock_irqrestore(&wc->lock, flags);
+
+	return type;
+} //}}}
+
+static int check_ports(struct openpci *wc)
+{ //{{{
+	int i = 0;
+
+	for(; i < MAX_PORTS; ++i ){
+		wc->porttype[i] = get_port_type(wc, i);
+		dbginfo(wc->boardnum,"%d: %s", i, porttype(wc,i));
+
+		switch( wc->porttype[i] ) {
+		    case VT_PORT_PROSLIC:
+			/* By default, don't send on hook */
+			if (reversepolarity)
+				wc->mod[i].fxs.idletxhookstate = 5;
+			else
+				wc->mod[i].fxs.idletxhookstate = 1;
+
+		    case VT_PORT_VDAA:
+			++wc->portcount;
+		}
+	}
+	cardinfo(wc->boardnum, "found %d active ports", wc->portcount);
+	// we 'succeed' if any ports were discovered.
+	return wc->portcount ? RET_OK : RET_FAIL;
+} //}}}
+
+static int configure_vdaa_country(struct openpci *wc, int port, char *name)
+{ //{{{
+	unsigned char value;
+	int i;
+
+	for (i=0; i < sizeof(fxo_modes)/sizeof(struct fxo_mode); ++i){
+		if(!strcmp(fxo_modes[i].name, name)){
+			cardinfo(wc->boardnum, "Setting country to %s", name);
+			goto part2;
+		}
+	}
+	i = 3;
+	cardinfo(wc->boardnum, "Using default country %s", fxo_modes[i].name);
+
+    part2:
+	value  = (fxo_modes[i].ohs << 6);
+	value |= (fxo_modes[i].rz << 1);
+	value |= (fxo_modes[i].rt << 0);
+	if( ! write_reg_fxo(wc, port, 16, value) ) goto hell;
+
+	/* DC Termination Control - Register 26 */
+	value  = (fxo_modes[i].dcv << 6);
+	value |= (fxo_modes[i].mini << 4);
+	value |= (fxo_modes[i].ilim << 1);
+	if( ! write_reg_fxo(wc, port, 26, value) ) goto hell;
+
+	/* AC Termination Control - Register 30 */
+	value = (fxo_modes[i].acim << 0);
+	if( ! write_reg_fxo(wc, port, 30, value) ) goto hell;
+
+	/* DAA Control 5 - Register 31 */
+	msleep(1);
+	if( ! read_reg_fxo(wc, port, 31, &value) ) goto hell;
+
+	value = (value & 0xf7) | (fxo_modes[i].ohs2 << 3);
+	value = value | 0x02;
+	if( ! write_reg_fxo(wc, port, 31, value) ) goto hell;
+
+	return RET_OK;
+
+    hell:
+	cardcrit(wc->boardnum, "port %d failed configure vdaa country", port);
+	return RET_FAIL;
+} //}}}
+
+static int configure_vdaa_tdm(struct openpci *wc, int port)
+{ //{{{
+	int tmp;
+
+	/* Set the Receive/Transmit slot on the TDM bus */
+	/* On the OpenPCI4/8 card we have two groups of 4 slots */
+	if (port < 4) {
+		tmp = port * ZT_CHUNKSIZE;
+		//cardinfo(wc->boardnum, "Set port %d RX-TDM time slot to %d.",port,tmp);
+		if( ! write_reg_fxo(wc, port, 36, tmp) ) goto hell;
+		if( ! write_reg_fxo(wc, port, 37, 0x0) ) goto hell;
+		//cardinfo(wc->boardnum, "Set port %d TX-TDM time slot to %d.",port,tmp);
+		if( ! write_reg_fxo(wc, port, 34, tmp) ) goto hell;
+		if( ! write_reg_fxo(wc, port, 35, 0x0) ) goto hell;
+	}
+	else if ( port < 8 ){
+		tmp = (port -4) * ZT_CHUNKSIZE;
+		//cardinfo(wc->boardnum, "Set port %d TDM time slot to %d.",port,tmp+512);
+		if( ! write_reg_fxo(wc, port, 34, tmp) ) goto hell;
+		if( ! write_reg_fxo(wc, port, 35, 0x2) ) goto hell;
+		if( ! write_reg_fxo(wc, port, 36, tmp) ) goto hell;
+		if( ! write_reg_fxo(wc, port, 37, 0x2) ) goto hell;
+	}
+	return RET_OK;
+
+    hell:
+	cardcrit(wc->boardnum, "configure vdaa tdm failed for port %d", port);
+	return RET_FAIL;
+} //}}}
+
+// Do not call this from an interrupt context, it may sleep.
+static void configure_vdaa_port(struct openpci *wc, int port)
+{ //{{{
+	unsigned char value;
+	int i, count=3;
+
+	do {
+		/* Reset the port */
+		if( ! write_reg_fxo(wc, port, 1, 0x80) ) goto hell;
+		/* Enable the ISO-cap (Line Side) */
+		if( ! write_reg_fxo(wc, port, 6, 0x00) ) goto hell;
+		/* Check that Line Side came up by reading its type */
+		i = 0;
+		do {
+			msleep(1);
+			if( read_reg_fxo(wc, port, 11, &value) ){
+				if( value & 0xf0 ) goto part2;
+			} else goto hell;
+		} while (++i < 10);
+		cardwarn(wc->boardnum, "Line Side failed to start on port %d, retrying", port);
+
+	} while(--count > 0);
+
+	cardwarn(wc->boardnum, "Line Side failed to start on port %d, marking as empty.", port);
+	goto hell;
+
+    part2:
+	/* Set Country - default to Austrlia */
+	/* Set to Mu-Law */
+	/* Enable on-hook Line montoring - CID etc */
+	if( configure_vdaa_country(wc, port, country)
+	 && write_reg_fxo(wc, port, 33, 0x28)
+	 && configure_vdaa_tdm(wc, port)
+	 && write_reg_fxo(wc, port, 5, 0x08) )
+	{
+		++wc->portcount;
+		return;
+	}
+
+    hell:
+	cardcrit(wc->boardnum, "FAILED to configure vdaa port %d.  Disabled.", port);
+	wc->porttype[port] = VT_PORT_EMPTY;
+} //}}}
+
+// Do not call this from an interrupt context, it may sleep.
+static int proslic_dcdc_calibration(struct openpci *wc, int port)
+{ //{{{
+	const int tst_vbat = VBAT - (5 * 376/1000);
+	unsigned char value=0;
+	int tmp=0;
+
+	/* Set DC-DC Convertor PWM Period */
+	if( ! write_reg_fxs(wc, port, 92, 0xff) ) goto hell;
+
+	/* Set the DC-DC Convertor Switching Delay */
+	/* 0x14 is infact the reset/default value */
+	if( ! write_reg_fxs(wc, port, 93, 0x19) ) goto hell;
+
+	/* Turn on DC-DC Convertor */
+	if( ! write_reg_fxs(wc, port, 14, 0x00) ) goto hell;
+
+	/* Wait till PS_VBAT_VOLT1 is upto VBAT volts */
+	while (++tmp < 50 && value < tst_vbat){
+		msleep(1);
+		if( ! read_reg_fxs(wc, port, 82, &value) ) goto hell;
+	}
+	if (value < tst_vbat){
+		cardcrit(wc->boardnum, "proslic_dcdc_calibration: "
+					 "failed to power up!(%d < %d), retried %d times",
+			 value * 376/1000, tst_vbat * 376/1000, tmp);
+		if( ! write_reg_fxs(wc, port, 14, 1<<4) ){
+			cardcrit(wc->boardnum, "dcdc cal failed to reset PS_PWR_DN_CTRL1");
+		}
+		return RET_FAIL;
+	}
+	cardinfo(wc->boardnum, "power up %d >= %d after %d times",
+		 value * 376/1000, tst_vbat * 376/1000, tmp);
+
+	/* Start the DC-DC Converter Peak Current Monitor Calibration */
+	if( ! write_reg_fxs(wc, port, 93, (0x19|(1<<7))) ) goto hell;
+
+	tmp = 0;
+	do {
+		msleep(1);
+		if( ! read_reg_fxs(wc, port, 93, &value) ) goto hell;
+	} while( (value & (1<<7)) && (++tmp < 500) );
+	if(tmp == 500){
+		cardcrit(wc->boardnum,
+			 "proslic_dcdc_calibration: failed after %d attempts",tmp);
+		return RET_FAIL;
+	}
+
+	cardinfo(wc->boardnum, "calibration completed after %d attempts",tmp);
+	return RET_OK;
+
+    hell:
+	cardcrit(wc->boardnum, "proslic dcdc calibration failed for port %d", port);
+	return RET_FAIL;
+} //}}}
+
+// Do not call this from an interrupt context, it may sleep.
+static int proslic_power_leak_test(struct openpci *wc, int port)
+{ //{{{
+	unsigned char vbat;
+
+	if( ! write_reg_fxs(wc, port, 64, 0x00) ) goto hell;
+	if( ! write_reg_fxs(wc, port, 14, 0x10) ) goto hell;
+	msleep(1000);
+
+	if( ! read_reg_fxs(wc, port, 82, &vbat) ) goto hell;
+	if (vbat < 0x4){
+		cardcrit(wc->boardnum, "FAILED proslic leakage test (%d)",vbat);
+		return RET_FAIL;
+	}
+	return RET_OK;
+
+    hell:
+	cardcrit(wc->boardnum, "%d: FAILED to perform proslic leakdown test", port);
+	return RET_FAIL;
+} //}}}
+
+// Do not call this from an interrupt context, it may sleep.
+static int proslic_slic_calibration(struct openpci *wc, int port)
+{ //{{{
+	unsigned char value=1;
+	int tmp=0;
+	int reg96 = 0x47;
+	int reg97 = 0x1E; // From AN35
+
+    #if 0
+	/* disable interrupts */
+	if( ! write_reg_fxs(wc, port, 21, 0x0) ) goto hell;
+	if( ! write_reg_fxs(wc, port, 22, 0x0) ) goto hell;
+	if( ! write_reg_fxs(wc, port, 23, 0x0) ) goto hell;
+    #endif
+
+	/* set open */
+	if( ! write_reg_fxs(wc, port, 64, 0x0) ) goto hell;
+
+	if( ! write_reg_fxs(wc, port, 97, reg97) ) goto hell;
+	if( ! write_reg_fxs(wc, port, 96, reg96) ) goto hell;
+	while( value && ++tmp < 10){
+		msleep(160);
+		if( ! read_reg_fxs(wc, port, 96, &value) ) goto hell;
+	}
+	if(tmp == 10){
+		cardcrit(wc->boardnum,
+			 "proslic_slic_calibration: FAILED after %d attempts",tmp);
+		return RET_FAIL;
+	}
+
+	cardinfo(wc->boardnum, "slic calibration completed after %d attempts",tmp);
+	return RET_OK;
+
+    hell:
+	cardcrit(wc->boardnum, "%d: FAILED slic calibration", port);
+	return RET_FAIL;
+} //}}}
+
+// Do not call this from an interrupt context, it may sleep.
+static int proslic_manual_calibration(struct openpci *wc, int port)
+{ //{{{
+	unsigned char value=1;
+	int reg98 = 0x1F;
+	int reg99 = 0x1F;
+
+    #if 0
+	/* setting some indirect registers, from sample program, not documented! */
+	if( ! write_indreg_fxs(wc, port, 88, 0) ) goto hell;
+	if( ! write_indreg_fxs(wc, port, 89, 0) ) goto hell;
+	if( ! write_indreg_fxs(wc, port, 90, 0) ) goto hell;
+	if( ! write_indreg_fxs(wc, port, 91, 0) ) goto hell;
+	if( ! write_indreg_fxs(wc, port, 92, 0) ) goto hell;
+	if( ! write_indreg_fxs(wc, port, 93, 0) ) goto hell;
+
+	/* From sample program */
+	// This is necessary if the calibration occurs other than at reset time
+	if( ! write_reg_fxs(wc, port, 98, 0x10) ) goto hell;
+	if( ! write_reg_fxs(wc, port, 99, 0x10) ) goto hell;
+    #endif
+
+	if( ! write_reg_fxs(wc, port, 98, reg98) ) goto hell;
+	while(value > 0 && reg98 > 0){
+		msleep(40);
+		if( ! read_reg_fxs(wc, port, 88, &value) ) goto hell;
+		if (value > 0){
+			--reg98;
+			if( ! write_reg_fxs(wc, port, 98, reg98) ) goto hell;
+		}
+	}
+	if (reg98 == 0){
+		cardcrit(wc->boardnum,
+			 "proslic_manual_calibration: Failed to calibrate RING gain (%d)",value);
+		return RET_FAIL;
+	}
+	value=1;
+	if( ! write_reg_fxs(wc, port, 99, reg99) ) goto hell;
+	while(value > 0 && reg99 > 0){
+		msleep(40);
+		if( ! read_reg_fxs(wc, port, 89, &value) ) goto hell;
+		if (value > 0){
+			--reg99;
+			if( ! write_reg_fxs(wc, port, 99, reg99) ) goto hell;
+		}
+	}
+	if (reg99 == 0){
+		cardcrit(wc->boardnum,
+			 "proslic_manual_calibration: Failed to calibrate TIP gain (%d)",value);
+		return RET_FAIL;
+	}
+
+	return RET_OK;
+
+    hell:
+	cardcrit(wc->boardnum, "proslic manual calibration failed for port %d", port);
+	return RET_FAIL;
+} //}}}
+
+// Do not call this from an interrupt context, it may sleep.
+static int proslic_longitudinal_balance_calibration(struct openpci *wc, int port)
+{ //{{{
+	unsigned char value;
+	int tmp = 0;
+
+	/* Change to Active mode */
+	if( ! write_reg_fxs(wc, port, 64, 1) ) goto hell;
+	if( ! read_reg_fxs(wc, port, 68, &value) ) goto hell;
+	if(!(value & 0x04)){
+		cardcrit(wc->boardnum,
+			 "proslic_longitudinal_balance_calibration: Off-hook!(0x%02x)\n",value);
+		goto hell;
+	}
+	/* Open Port */
+	if( ! write_reg_fxs(wc, port, 64, 0) ) goto hell;
+
+	/* Turn on common mode calibration error interrupt */
+	if( ! write_reg_fxs(wc, port, 23, 1<<2) ) goto hell;
+	if( ! write_reg_fxs(wc, port, 97, 0x01) ) goto hell;
+	if( ! write_reg_fxs(wc, port, 96, 0x40) ) goto hell;
+
+	while( value && ++tmp < 20 ){
+		msleep(100);
+		if( ! read_reg_fxs(wc, port, 20, &value) ) goto hell;
+		if( value & 0x04 ) {
+			cardcrit(wc->boardnum,
+				 "common mode calibration error, interrupt %#x", value);
+			goto hell;
+		}
+		if( ! read_reg_fxs(wc, port, 96, &value) ) goto hell;
+	}
+	if( tmp == 20 ){
+		cardcrit(wc->boardnum,"FAILED lb calibration after %d attempts",tmp);
+		goto hell;
+	}
+	dbginfo(wc->boardnum,"lb calibration succeeded after %d attempts", tmp);
+	return RET_OK;
+
+    hell:
+	cardcrit(wc->boardnum, "longitudinal balance calibration failed for port %d", port);
+	return RET_FAIL;
+} //}}}
+
+static int configure_proslic_country(struct openpci *wc, int port, const char *name)
+{ //{{{
+	int i;
+
+	for (i=0; i < sizeof(ps_country_regs)/sizeof(struct ps_country_reg); ++i){
+		if(!strcmp(ps_country_regs[i].country, name)){
+			dbginfo(wc->boardnum, "%d: Setting country to %s", port, name);
+			goto part2;
+		}
+	}
+	return -EINVAL;
+
+    part2:
+
+	if( ! write_reg_fxs(wc, port, 10, ps_country_regs[i].value) ){
+		cardcrit(wc->boardnum,"%d: failed to write PS_IMPEDANCE", port);
+		return -EIO;
+	}
+	return 0;
+} //}}}
+
+// Do not call this from an interrupt context, it may sleep.
+static void configure_proslic_port(struct openpci *wc, int port)
+{ //{{{
+	int i = 0;
+	unsigned char value;
+
+	/* Write the initial indirect registers */
+	for(; i < sizeof(indirect_regs)/sizeof(indirect_regs[0]); ++i) {
+		if( ! write_indreg_fxs(wc, port, indirect_regs[i].address,
+						 indirect_regs[i].initial)) {
+			cardcrit(wc->boardnum,
+				 "failed to init proslic ind regs on port %d!", port);
+			goto hell;
+		}
+	}
+	for (i=35; i < 40; ++i)
+		if( ! write_indreg_fxs(wc, port, i, 0x8000) ) goto hell;
+
+	if( ! write_reg_fxs(wc, port, 8, 0) )      goto hell;
+	if( ! write_reg_fxs(wc, port, 108, 0xEB) ) goto hell;
+
+	// Taken from sample program
+	// Make VBat switch not automatic
+	if( ! write_reg_fxs(wc, port, 67, 0x17) ) goto hell;
+
+	// The above is a saftey measure to prevent Q7 from accidentaly turning on and burning out.
+	//  It works in combination with the statement below.  Pin 34 DCDRV which is used for the
+	//  battery switch on the Si3211 & Si3212
+	//    Q7 should be set to OFF for si3210
+	if( ! write_reg_fxs(wc, port, 66, 0x01) ) goto hell;
+
+	/* Take it out of loop back */
+	/* Not done in sample program
+	if( ! write_reg_fxs(card, port, 8, 0) ) goto hell;
+	*/
+
+	if( ! write_reg_fxs(wc, port, 108, 0xEB) ) goto hell;
+
+	// 0x32 => 75 volts
+	// 0x2e => 69 volts
+	// 0x21 => 49.5 volts
+	if( ! write_reg_fxs(wc, port, 74, 0x32) ) goto hell;
+	if( ! write_reg_fxs(wc, port, 75, 0x11) ) goto hell;
+
+	/* Perform DC-DC Calibration */
+	if( ! proslic_dcdc_calibration(wc, port) ) goto hell;
+
+	/* Perform power leak test, as sugested in sample program */
+	if( ! proslic_power_leak_test(wc, port) ) goto hell;
+
+	/* Perform DC-DC Calibration again to bring power up */
+	if( ! proslic_dcdc_calibration(wc, port) ) goto hell;
+
+	/* Perform SLIC calibration 'off-hook' */
+	if( ! proslic_slic_calibration(wc, port) )                 goto hell;
+	if( ! proslic_manual_calibration(wc, port) )               goto hell;
+	if( ! proslic_longitudinal_balance_calibration(wc, port) ) goto hell;
+
+	/* Flush out energy accumulators */
+	for(i=88; i < 96; ++i){
+		if( ! write_indreg_fxs(wc, port, i, 0x0000) ) goto hell;
+	}
+	if( ! write_indreg_fxs(wc, port, 97, 0x0000) ) goto hell;
+	for(i=193; i < 212; ++i){
+		if( ! write_indreg_fxs(wc, port, i, 0x0000) ) goto hell;
+	}
+
+	/* clear interrupts */
+	for(i=18; i < 21; ++i){
+		if( ! write_reg_fxs(wc, port, i, 0xFF) ) goto hell;
+	}
+	/* enable interrupts */
+	for(i=21; i < 24; ++i){
+		if( ! write_reg_fxs(wc, port, i, 0xFF) ) goto hell;
+	}
+
+	/* Set time slots */
+	if (port < 4) {
+		value = port * ZT_CHUNKSIZE;
+		//cardinfo(card->boardnum, "Set port %d RX-TDM time slot to %d.",port,value);
+		if( ! write_reg_fxs(wc, port, 2, value) ) goto hell;
+		if( ! write_reg_fxs(wc, port, 3, 0x0) )   goto hell;
+		//cardinfo(wc->boardnum, "Set port %d TX-TDM time slot to %d.",port,value);
+		if( ! write_reg_fxs(wc, port, 4, value) ) goto hell;
+		if( ! write_reg_fxs(wc, port, 5, 0x0) )   goto hell;
+	}
+	else if ( port < 8 ){
+		value = (port -4) * ZT_CHUNKSIZE;
+		//cardinfo(wc->boardnum, "Set port %d TDM time slot to %d.",port,value+512);
+		if( ! write_reg_fxs(wc, port, 2, value) ) goto hell;
+		if( ! write_reg_fxs(wc, port, 3, 0x02) )  goto hell;
+		if( ! write_reg_fxs(wc, port, 4, value) ) goto hell;
+		if( ! write_reg_fxs(wc, port, 5, 0x02) )  goto hell;
+	}
+
+	/* Turn on the audio and set to mu-law */
+	if( ! write_reg_fxs(wc, port, 1, 0x28) ) goto hell;
+
+	/* Set up audio path */
+	if( ! write_reg_fxs(wc, port, 8, 0x0) )     goto hell;
+	if( ! write_reg_fxs(wc, port, 9, 0x0) )     goto hell;
+	if( ! write_reg_fxs(wc, port, 11, 0x33) )   goto hell;
+
+	/* Initilize OSC1, OSC2, Ringing, Pulse Metering */
+	/* Leaving at defaults */
+
+	/* Write detect thresholds and filters */
+	/* Leaving at defaults */
+
+	/* Write DC feed parameters */
+	/* Leaving 65,67 at defaults */
+	if( ! write_reg_fxs(wc, port, 66, 0x01) ) goto hell;
+	/* 20 mA */
+	if( ! write_reg_fxs(wc, port, 71, 0x00) ) goto hell;
+	/* -48 */
+	if( ! write_reg_fxs(wc, port, 72, 0x20) ) goto hell;
+	/* -6V */
+	if( ! write_reg_fxs(wc, port, 73, 0x04) ) goto hell;
+
+	/* Re-write Power Coefficients */
+	for(i=35; i < 40; ++i){
+		if( ! write_indreg_fxs(wc, port, i, indirect_regs[i].initial) ) goto hell;
+	}
+
+	/* Set operation mode */
+	/* Forward active */
+	if( ! write_reg_fxs(wc, port, 64, 0x01) ) goto hell;
+
+	/* Set Country - default to Australia */
+	switch( configure_proslic_country(wc, port, country) ){
+	    case 0:
+		break;
+
+	    case -EINVAL:
+		cardwarn(wc->boardnum,"%d: Country '%s' unknown, using default", port, country);
+		if( configure_proslic_country(wc, port, DEFAULT_COUNTRY) == 0 )
+			goto hell;
+
+	    default:
+		goto hell;
+	}
+
+	++wc->portcount;
+	return;
+
+    hell:
+	cardcrit(wc->boardnum, "FAILED to configure proslic port %d.  Disabled.", port);
+	wc->porttype[port] = VT_PORT_EMPTY;
+} //}}}
+
+// Do not call this from an interrupt context, it may (indirectly) sleep.
+static int configure_ports(struct openpci *wc)
+{ //{{{
+	int i;
+
+	wc->portcount = 0;
+	for(i=0; i < MAX_PORTS; ++i){
+		switch (wc->porttype[i]){
+		    case VT_PORT_VDAA:    configure_vdaa_port(wc,i);    break;
+		    case VT_PORT_PROSLIC: configure_proslic_port(wc,i); break;
+		}
+	}
+	// we 'succeed' if any ports were configured successfully.
+	return wc->portcount ? RET_OK : RET_FAIL;
+} //}}}
+
+static int __get_arm_id(struct openpci *wc, int field, char *value)
+{ //{{{
+	int i;
+	int x=0;
+	int count=0;
+
+	outb(0x01, PIB(1));  HTXF_WAIT();
+	outb(field, PIB(1)); HTXF_WAIT();
+	HRXF_WAIT(); count = inb(PIB(1));
+	if (count > ID_DATA_MAXSIZE){
+		cardcrit(wc->boardnum, "Too many bytes of id(%d) data %d/%d",
+					 field, count, ID_DATA_MAXSIZE);
+		return RET_FAIL;
+	}
+	//cardinfo(wc->boardnum, "get_arm_id(%d): byte count %d",field,count);
+	for(; x < count; ++x){
+		HRXF_WAIT(); *value = inb(PIB(1));
+		//cardinfo(wc->boardnum, "get_arm_id(%d): byte %d => 0x%02x",field,x,tmp);
+		++value;
+	}
+	return RET_OK;
+} //}}}
+
+static void enable_interrupts(struct openpci *wc)
+{ //{{{
+	outb(0x3f, TJ_MASK0);
+	outb(0x02, TJ_MASK1);
+} //}}}
+
+static void disable_interrupts(struct openpci *wc)
+{ //{{{
+	outb(0x00, TJ_MASK0);
+	outb(0x00, TJ_MASK1);
+} //}}}
+
+// Do not call this from an interrupt context, it may sleep.
+static int check_arm(struct openpci *wc)
+{ //{{{
+	char model[ID_DATA_MAXSIZE+1] = { 0 };
+	char date[ID_DATA_MAXSIZE+1]  = { 0 };
+	unsigned long flags;
+	int i=0;
+	int tmp=0;
+
+	spin_lock_irqsave(&wc->lock, flags);
+	while ((tmp != 0x88)&&(++i<100)){
+		outb(0x88, PIB(0));
+		msleep(1);
+		tmp = inb(PIB(1));
+	}
+	if (i>=1000) goto limbo;
+	dbginfo(wc->boardnum, "Arm responded on attempt %d",i);
+
+	// Flush out the queue if we sent several pings before a response.
+	if(i>1)	__ping_arm(wc);
+
+	if( ! __get_arm_id(wc, 0, model) )  goto hell;
+	sscanf(model, "OpenPCI8.%02d", &(wc->firmware));
+	cardinfo(wc->boardnum, "  model: %s", model);
+
+	if( ! __get_arm_id(wc, 1, date) )   goto hell;
+	cardinfo(wc->boardnum, "  date: %s", date);
+
+	if( ! __get_arm_id(wc, 2, wc->serial) ) goto hell;
+	cardinfo(wc->boardnum, "  serial: %s", wc->serial);
+
+	spin_unlock_irqrestore(&wc->lock, flags);
+	return RET_OK;
+
+    hell:
+	spin_unlock_irqrestore(&wc->lock, flags);
+        cardwarn(wc->boardnum, "Found ARM processor, dumb firmware.");
+	return RET_OK;
+
+    limbo:
+	spin_unlock_irqrestore(&wc->lock, flags);
+	return RET_FAIL;
+} //}}}
+
+static int arm_monitor(struct openpci *wc, int on)
+{ //{{{
+	int i;
+	outb( on ? 0x06 : 0x07, PIB(1) ); HTXF_WAIT();
+	return RET_OK;
+} //}}}
+
+static int __devinit openpci_probe_board(struct pci_dev *pdev, const struct pci_device_id *ent)
+{ //{{{
+	struct openpci *wc;
+	int boardnum = 0;
+	int failret = -ENOMEM;
+	int i;
+	unsigned long flags;
+
+	if( ent->driver_data != (kernel_ulong_t)&wcopenpci )
+	{
+	    info("Probe of non-OpenPCI card, ignoring.");
+	    return -EINVAL;
+	}
+	wc = kzalloc(sizeof(struct openpci), GFP_KERNEL);
+	if (!wc){
+		return -ENOMEM;
+	}
+
+	mutex_lock(&cards_mutex);
+	for (; boardnum < MAX_CARDS && cards[boardnum]; ++boardnum);
+	if (boardnum >= MAX_CARDS){
+		crit("Too many OpenPCI cards(%d), max is %d.", boardnum, MAX_CARDS);
+		mutex_unlock(&cards_mutex);
+		goto hell;
+	}
+	cards[boardnum] = wc;
+	mutex_unlock(&cards_mutex);
+
+	spin_lock_init(&wc->lock);
+	pci_set_drvdata(pdev, wc);
+
+	wc->boardnum = boardnum;
+	wc->dev      = pdev;
+	wc->variety  = wcopenpci;
+
+	cardinfo(boardnum, "Initialising card");
+	if (pci_enable_device(pdev)) {
+		failret = -EIO;
+		goto hell_2;
+	}
+	wc->ioaddr = pci_resource_start(pdev, 0);
+	if( ! request_region(wc->ioaddr, 0xff, NAME) ){
+                cardcrit(boardnum, "Failed to lock IO region, another driver already using it");
+		failret = -EBUSY;
+		goto hell_2;
+	}
+
+	spin_lock_irqsave(&wc->lock, flags);
+	outb(0xff, TJ_AUXD);            /* Set up TJ to access the ARM */
+	outb(0x78, TJ_AUXC);            /* Set up for Jtag */
+	outb(0x00, TJ_CNTL);            /* pull ERST low */
+	spin_unlock_irqrestore(&wc->lock, flags);
+	msleep(1);	                /* Wait a bit */
+
+	dbginfo(boardnum,"Starting ARM");
+	spin_lock_irqsave(&wc->lock, flags);
+	outb(0x01, TJ_CNTL);            /* pull ERST high again */
+	spin_unlock_irqrestore(&wc->lock, flags);
+	msleep(100);                    /* Give it all a chance to boot */
+
+	if( ! check_arm(wc) ){
+		cardcrit(boardnum, "Couldnt find ARM processor");
+		failret = -EIO;
+		goto hell_3;
+	}
+	if( wc->firmware < 9 ){
+		cardcrit(boardnum,
+			 "Firmware version %d not supported by this driver",
+			 wc->firmware);
+		cardcrit(boardnum, " contact VoiceTronix to have it updated");
+		failret = -ENODEV;
+		goto hell_3;
+	}
+	if( ! check_ports(wc) ){
+		cardcrit(boardnum, "Couldnt find ports!");
+		failret = -EIO;
+		goto hell_3;
+	}
+
+	wc->writechunk = (int *)pci_alloc_consistent(pdev, VT_PCIDMA_BLOCKSIZE, &wc->writedma);
+	if (!wc->writechunk) {
+		cardcrit(boardnum, "Couldnt get DMA memory.");
+		goto hell_3;
+	}
+	wc->readchunk = wc->writechunk + ZT_MAX_CHUNKSIZE * (MAX_PORTS*2 / sizeof(int));
+	wc->readdma = wc->writedma + ZT_MAX_CHUNKSIZE * (MAX_PORTS*2);
+
+	memset((void*)wc->writechunk,0,VT_PCIDMA_BLOCKSIZE);
+
+	spin_lock_irqsave(&wc->lock, flags);
+	outb(0xc1, TJ_SERCTL);
+	outb(0x0, TJ_FSCDELAY);
+
+	outl(wc->writedma,                    TJ_DMAWS);
+	outl(wc->writedma + VT_PCIDMA_MIDDLE, TJ_DMAWI);
+	outl(wc->writedma + VT_PCIDMA_END,    TJ_DMAWE);
+	outl(wc->readdma,                     TJ_DMARS);
+	outl(wc->readdma + VT_PCIDMA_MIDDLE,  TJ_DMARI);
+	outl(wc->readdma + VT_PCIDMA_END,     TJ_DMARE);
+
+	/* Clear interrupts */
+	outb(0xff, TJ_INTSTAT);
+	spin_unlock_irqrestore(&wc->lock, flags);
+
+	if( ! arm_monitor(wc, 1) ){
+		cardcrit(boardnum, "failed to start arm monitoring");
+		failret = -EIO;
+		goto hell_4;
+	}
+
+	/* Configure the ports */
+	if( ! configure_ports(wc) ){
+		cardcrit(boardnum, "Failed to configure ports.");
+		failret = -EIO;
+		goto hell_4;
+	}
+
+	if( ! span_initialize(wc) ) {
+		cardcrit(boardnum, "Failed to register with zaptel driver");
+		failret = -EFAULT;
+		goto hell_4;
+	}
+
+	/* Finalize signalling  */
+	for (i=0; i < MAX_PORTS; ++i) {
+		if (wc->porttype[i] == VT_PORT_VDAA)
+		    wc->chans[i].sigcap = ZT_SIG_FXSKS | ZT_SIG_FXSLS
+					| ZT_SIG_CLEAR | ZT_SIG_SF;
+		else if (wc->porttype[i] == VT_PORT_PROSLIC)
+		    wc->chans[i].sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS
+					| ZT_SIG_FXOGS | ZT_SIG_SF
+					| ZT_SIG_CLEAR | ZT_SIG_EM;
+		else if (wc->porttype[i])
+		    cardcrit(wc->boardnum, "Port %d has unknown type (%d)",
+					   i, wc->porttype[i]);
+	}
+
+	/* Enable bus mastering */
+	pci_set_master(pdev);
+
+	if (request_irq(pdev->irq, openpci_isr, SA_SHIRQ, NAME, wc)) {
+		cardcrit(boardnum, "Cant get IRQ!");
+		failret = -EIO;
+		goto hell_5;
+	}
+	cardinfo(boardnum, "Got IRQ %d", pdev->irq);
+
+	enable_interrupts(wc);
+	start_dma(wc);
+
+	cardinfo(boardnum,"Initialised card.");
+	return 0;
+
+    hell_5:
+	zt_unregister(&wc->span);
+    hell_4:
+	if (wc->writechunk){
+		pci_free_consistent(pdev, VT_PCIDMA_BLOCKSIZE,
+				    (void*)wc->writechunk, wc->writedma);
+	}
+    hell_3:
+	outb(0x00, TJ_CNTL);
+	release_region(wc->ioaddr, 0xff);
+    hell_2:
+	cards[boardnum] = NULL;
+    hell:
+	kfree(wc);
+	return failret;
+} //}}}
+
+static void __devexit openpci_remove_board(struct pci_dev *pdev)
+{ //{{{
+	struct openpci *wc = pci_get_drvdata(pdev);
+
+	if(!wc) return;
+
+	arm_monitor(wc,0);
+
+	/* Stop DMA */
+	outb(0x00, TJ_OPER);
+	disable_interrupts(wc);
+
+	//XXX Replace this usecount business...
+	//    and do this BEFORE we invalidate everything above...
+	//    check that we wont try to write to it in the meantime.
+	/* Release span, possibly delayed */
+	//XXX if (!wc->usecount) openpci_release(wc); else wc->dead = 1;
+
+	zt_unregister(&wc->span);
+	outb(0x00, TJ_CNTL);
+
+	pci_free_consistent(pdev, VT_PCIDMA_BLOCKSIZE, (void *)wc->writechunk, wc->writedma);
+	free_irq(pdev->irq, wc);
+
+	release_region(wc->ioaddr, 0xff);
+
+	mutex_lock(&cards_mutex);
+	cards[wc->boardnum] = NULL;
+	mutex_unlock(&cards_mutex);
+
+	kfree(wc);
+	cardinfo(wc->boardnum, "Removed OpenPCI card.");
+} //}}}
+
+static struct pci_device_id openpci_pci_tbl[] = {
+	{ 0xe159, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (kernel_ulong_t) &wcopenpci },
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, openpci_pci_tbl);
+
+static struct pci_driver openpci_driver = {
+	name: 	  NAME,
+	probe: 	  openpci_probe_board,
+	remove:	  __devexit_p(openpci_remove_board),
+	suspend:  NULL,
+	resume:	  NULL,
+	id_table: openpci_pci_tbl,
+};
+
+static int __init openpci_init(void)
+{
+	if( zap_pci_module(&openpci_driver) )
+		return -ENODEV;
+
+	info("Module loaded %s", debug ? "with debug enabled" : "");
+	return 0;
+}
+
+static void __exit openpci_cleanup(void)
+{
+	pci_unregister_driver(&openpci_driver);
+	info("Module exit");
+}
+
+module_init(openpci_init);
+module_exit(openpci_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_VERSION(ZAPTEL_VERSION);
+MODULE_LICENSE("GPL");
+




More information about the Pkg-voip-commits mailing list